1. New advantages of Vue3

1.1 options API -> composition API

Composition API, which literally means Composition API, was created to implement function-based logic reuse mechanisms.

1) Declare variables

const { reactive } = Vue
let App = {
    template: ` 
      
{{ message }}
`
.setup() { const state = reactive({ message: "Hello World!!!" }) return { ...state } } } Vue.createApp().mount(App, '#app') Copy the code

2) Bidirectional binding

const { reactive } = Vue
let App = {
    template: ` 
      
{{ state.value }}
`
.setup() { const state = reactive({ value: ' ' }) return { state } } } Vue.createApp().mount(App, '#app') Copy the code

Setup is a component entry that runs when the component is instantiated and the props property is defined. This is equivalent to Vue2’s beforeCreate and created. Reactive is an API for creating a responsive data object that is almost equivalent to vue.Observable () in Vue2, and renamed in Vue3 to avoid confusion with the Observable in RXJS.

3) Observe attributes

import { reactive, watchEffect } from 'vue'

const state = reactive({
    count: 0
})

watchEffect(() = > {
    document.body.innerHTML = `count is ${state.count}`
})

return {
    ...state
}
Copy the code

Note: watchEffect is similar to Watch in 2.x, but it does not need to separate dependent data sources from side effect callbacks. The composite API also provides a Watch function that behaves exactly like 2.x.

4) the ref

Note: Vue3 allows users to create a single responsive object.

const App = {
    template: ` 
      
{{ value }}
`
.setup() { const value = ref(0) return { value } } } Vue.createApp().mount(App, '#app') Copy the code

5) Calculate attributes

setup() {
    const state = reactive({
        count: 0.double: computed(() = > state.count * 2)})function increment() {
        state.count++
    }
    
    return {
        state,
        increment
    }
}
Copy the code

6) Life cycle changes

Vue2 Vue3
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestory onBeforeUnmount
destoryed onUnmounted
errorCaptured onErrorCaptured
import { onMounted } from 'vue'

export default {
    setup() {
        onMounted(() = > {
            console.log('component is mounted! ')}}}Copy the code

1.2 Performance Optimization

  • The virtual DOM was reconstructed to maintain compatibility and make DOM separate from template rendering to improve performance.
  • Optimized the template compilation process and added patchFlag to skip static nodes when traversing nodes.
  • Efficient component initialization.
  • The process performance of component Upload was improved by 1.3 ~ 2 times.
  • SSR speed increased 2-3 times.

How does Vue3 optimize diff and VDOM?

1) Compile the template's static markup

For example:

<div id="app">
    <p>On Monday?</p>
    <p>Tomorrow is Tuesday</p>
    <div>{{ week }}</div>
</div>
Copy the code

In Vue2 it is parsed into the following code

function render() {
    with(this) {
        return _c('div', {
            attrs: {
                "id": "app"
            }
        }, [_c('p', [_v(What about Monday?)]), _c('p', [_v("Tomorrow is Tuesday.")]), _c ('div', [_v(_s(week))])])
    }
}
Copy the code

As you can see, the two P tags are completely static, so that in subsequent rendering, there is actually no change, but in ve2. X, _c is still used to create a new VDOM, which still needs to be compared during diff, resulting in a performance cost.

In Vue3

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from 'vue'

export function render(_ctx, _cache) {
    return (_openBlock(), _createBlock("div", { id: "app" }, [
        _createVNode("p".null.What about Monday?),
        _createVNode("p".null."Tomorrow is Tuesday."),
        _createVNode("div".null, _toDisplayString(_ctx.week), 1 /* TEXT */)))}Copy the code

Static nodes will not be traversed unless the fourth parameter of _createVNode is null.

Also, we can see that after the last non-static node of Vue3 is compiled, /* TEXT */ appears. This is to mark the current content type for diff, just to compare the same type of content, so that we don’t waste time traversing other types.

export const enum PatchFlags {
    TEXT = 1.// Represents an element with a dynamic textContent
    CLASS = 1 << 1.// represents an element with a dynamic Class
    STYLE = 1 << 2.// static (e.g. Style ="color: red")
    PROPS = 1 << 3.// represents an element with non-class/style dynamic items
    FULL_PROPS = 1 << 4.// Represents the element of an item with a dynamic key, which is incompatible with the above three
    HYDRATE_EVENTS = 1 << 5.// represents an element with an event listener
    STABLE_FRAGMENT = 1 << 6.// a fragment whose child order does not change
    KEYED_FRAGMENT = 1 << 7.// represents a fragment with a keyed or partially keyed self element
    UNKEYED_FRAGMENT = 1 << 8.// represents a fragment with a keyless binding
    NEED_PATCH = 1 << 9.// Indicates that only elements with non-attribute patches are required, such as ref, hooks
    DYNAMIC_SLOTS = 1 << 10 // represents an element with a dynamic slot
}
Copy the code

2) Event storage

Note: Bound events are stored in the cache

<div id="app">
    <button @click="handleClick">On Friday!</button>
</div>
Copy the code

After conversion

import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from 'vue'

export function render(_ctx, _cache) {
    return (_openBlock(), _createBlock("div", { id: "app" }, [
        _createVNode("button", {
            onClick: _cache[1] || (_cache[1] = ($event, ... args) = >(_ctx.handleClick($event, ... args))) },"Friday.")))}Copy the code

In the code, we can see that when the click event is bound, an inline function is generated and cached to become a static node in the cache.

3) Static promotion

<div id="app">
    <p>On Monday the</p>
    <p>On Tuesday the</p>
    <div>{{ week }}</div>
    <div :class="red: isRed">On Wednesday?</div>
</div>
Copy the code

Converted to

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from 'vue'

const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createVNode("p".null.It's Monday., -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p".null.It's Tuesday., -1 /* HOISTED */)

export function render(_ctx, _cache) {
    return (_openBlock(), _createBlock("div", _hoisted_1, [
        _hoisted_2,
        _hoisted_3,
        _createVNode("div".null, _toDisplayString(_ctx.week), 1 /* TEXT */),
        _createVNode("div", {
            class: { red: _ctx.isRed }
        }, What about Wednesday?.2 /* CLASS */)))}Copy the code

It can be seen here that some static nodes are placed outside the render function to avoid generating static nodes for each render.

1.3 provides tree shaking

  • Automatically remove unused Vue modules when packing

1.4 Better TS support

  • Type definition hint
  • TSX composite support
  • Class component support

1.5 Family bucket modification

Use of vite, abandon the original use of webpack Vue2. X.

  1. No packaging is required once the development server is started.
  2. You can customize the development server:const { createSever } = require('vite').
  3. The performance of hot module replacement has nothing to do with the number of modules.
  4. The production environment is bundled with rollup.

2. Response comparison of Vue2 and Vue3

2.1 Vue2. X uses defineProperty and has two difficult problems

  1. You can only respond to the first level of properties, but not beyond that.
  2. Array problem: defineProperty cannot detect changes in array length, or more specifically, increases in length by changing the length method.
The length attribute is initialized to
enumberable: false
configurable: false
writable: true

// Delete or modify the length attribute directly
var a = [1.2.3]
Object.defineProperty(a, 'length', {
    enumberable: true.configurable: true.writable: true,})// Uncaught TypeError: Cannot redefine property: length 
Copy the code

2.2 Vue3 uses Proxy and Reflect to directly Proxy the whole object

function reactive(data) {
    if(typeofdata ! = ='object' || data === null) {
        return data
    }
    const observed = new Proxy(data, {
        get(target, key, receiver) {
            // Reflect returns a value without an error
            let ret = Reflect.get(target, key, receiver)
            
            // Multi-layer proxy
            return typeofret ! = ='object' ? ret : reactive(ret)
        },
        set(target, key, value, receiver) {
            effective()
            // Proxy + reflect
            const ret = Reflect.set(target, key, value, receiver)
            return ret
        },
        deleteProperty(target, key) {
            const ret = Reflect.deleteProperty(target, key)
            return ret
        }
    })
    return observed
}
Copy the code

2.3 summarize

  1. Because Object.defineProperty can only hold attributes, it needs to traverse every attribute of the Object and has low performance, while Proxy directly proxies the entire Object.
  2. Object.defineProperty Manually Observe the added attribute. When adding a property, Object.defineProperty needs to iterate over the Object to hold the new property. Therefore, when Vue2 is used to add attributes to arrays or objects in data, vm.$set is required to ensure that the new attributes are also responsive.
  3. Proxy supports interception in 13, a new standard performance bonus that defineProperty doesn’t.
  4. Proxy is the new standard. In the long run, Js engines will continue to optimize Proxy, but getters and setters will no longer be optimized.
  5. Proxy compatibility is poor. Currently, there is no Polyfill scheme that fully supports Proxy interception methods.