Where is vuE3 optimized/solved?

  • Ts type support friendly (functional programming such as createApp)
  • Tree-shaking (use() plugins, not packaged if not used)
  • API simplification, consistency: Render function, sync modifier, instruction definition, etc
  • Composition API organizes code and reusability
  • Performance optimization: reactive (Object.defineProperty() -> Proxy), compilation optimization
  • Extensibility: customRenderer

Vue3 initialization

    <div id="app">
      <h3>{{title}}</h3>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
      const { createApp } = Vue;
      const app = createApp({
        data() {
          return {
            title: "option api"}; },setup() {
          // No reactive
          const state = {
            title: "composition api"};returnstate; }}); app.mount("#app");
    </script>
Copy the code

The code above implements a minimal VUe3 application that, when run into a browser, displays the Composition API on the page.

The above code returns title in both setup and data, so why does it finally work in Setup? This is because vue3 does something internally to make vue3 compatible with vue2’s Options API. In the case of conflicts such as the fields returned from setup and data in the code above, the fields in Setup are preferred.

The following vue3 initialization process will also implement compatible Options API.

Initialization of vue3 by hand

Take the code of the last step as a test case, adopt TDD driven test method, no longer introduce VUe3 JS file, to write the vue3 initialization process by ourselves, if we achieve the same function as the above code, it means we have succeeded.

Initialize the overall idea of the process

  • The basic structurecreatApp() mount()
  • Mount the mount
  • compilecompile() render()
  • Compatible withoptions apiandsetup
  • scalabilitycreateRenderer

The basic structure

As you can see from the test case, a createApp creates the Vue instance within the Vue object, and the mount method takes care of initialization. The code is as follows:

    <script>
        const Vue = {
            createApp(options){
                // Return an App instance
                return {
                    mount(selector){
                        // 1. Get the host element
                        / / 2. The rendering
                        / / 3. Additional
                    }
                }
            }
        }
    </script>
Copy the code

The options parameter is passed as a configuration object, and the selector parameter is the host element.

Mount and compile the render

<script>
	const Vue = {
		createApp(options) {
			// Return an App instance
			return {
				mount(selector) {
					// 1. Get the host element
					const parent = document.querySelector(selector);
					// 2. Render (check that the test case does not write the render function)
					if(! options.render){ options.render =this.compile(parent.innerHTML);
					}
					// 3. Execute render to get the view and append
					const el = options.render.call(options.data());
					parent.innerHTML = ' ';
					parent.appendChild(el);
				},
				compile(template) {
					// Compile to get render
					return function render(){
						const h3 = document.createElement('h3');
						h3.textContent = this.title;
						returnh3; }}}; }}; </script>Copy the code

Compile () is called when the render function is not written. Compile () actually converts the template into the render function, which is a very complicated process, so in this article we will simply make it return the H3 tag element, and this article will focus on the initialization process.

The code above has implemented the vue3 initialization process by running the test case and observing that the page displays the Option API. However, vue3’s setup should take precedence over the Options API, and the page should display the Composition API. So the next step is to implement the VUe2-compliant options API and let setup work when both exist.

Options API compatible with VUe2

Mount () createApp = data; mount() createApp = setup;

// 1. Get the host element
// ...
// 2. Render (check that the test case does not write the render function)
// ...

// Compatibility with data Setup priority issues
if(options.setup){
	// Save the setup result on the app instance
	this.superState = options.setup();
}
// If there is data for vue2, it is also processed
if(options.data){
	this.data = options.data();
}
// Construct a context for the render function
this.proxy = new Proxy(this, {
	get(target, key){
            // If the key exists in setupState, use this, otherwise use data
            if(key in target.superState){
			return target.superState[key]
		} else {
			return target.data[key]
		}
	},
	set(target, key, val){
		// No updates in this article}})// 3. Execute render to get an attempt and append
// const el = options.render.call(options.data());
const el = options.render.call(this.proxy);
parent.innerHTML = ' ';
parent.appendChild(el);
Copy the code

The above code uses the new Proxy to construct the context of the Render function, so that the context of the render function is handled by the Proxy, thus implementing compatible writing.

CreateRenderer extensibility

To provide extensibility for third party platforms like UniApp, VUe3 implements a custom renderer API called createRenderer, which allows different operations to be performed on different platforms.

CreateRenderer provides an opportunity to pass in parameters. Rewrite the example above by assuming that querySelector is a third-party platform-specific method and pass it to createRenderer for custom customization.

The entire code rewritten to createRenderer extensibility is as follows:

<div id="app">
	<h3>{{title}}</h3>
</div>

<script>
	const Vue = {
		// Provide a new renderer PAI
		createRenderer({ querySelector }) {
		// Return a renderer object to the user
		// Users can pass in platform feature actions
			return {
			// The method returned is createApp
				createApp(options) {
					return {
                                            mount(selector) {
							const parent = querySelector(selector);
							if(! options.render) { options.render =this.compile(parent.innerHTML);
							}
							if (options.setup) {
								this.superState = options.setup();
							}
							if (options.data) {
								this.data = options.data();
                                                    }
							this.proxy = new Proxy(this, {
								get(target, key) {
									if (key in target.superState) {
										return target.superState[key];
									} else {
										returntarget.data[key]; }},set(target, key, val) {
									// No updates in this article}});const el = options.render.call(this.proxy);
							parent.innerHTML = "";
							parent.appendChild(el);
						},
						compile(template) {
							return function render() {
								const h3 = document.createElement("h3");
								h3.textContent = this.title;
								returnh3; }; }}; }}; },// Provide a createApp for the Web platform
		createApp(options) {
			const renderer = this.createRenderer({
				querySelector(selector) {
					return document.querySelector(selector); }});returnrenderer.createApp(options); }};</script>
<script>
	const app = Vue.createApp({
		data() {
			return {
				title: "option api"}; },setup() {
			// No reactive
			const state = {
				title: "composition api"};returnstate; }}); app.mount("#app");
</script>
Copy the code

Related articles

Vue3 related articles