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.
- No packaging is required once the development server is started.
- You can customize the development server:
const { createSever } = require('vite')
. - The performance of hot module replacement has nothing to do with the number of modules.
- 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
- You can only respond to the first level of properties, but not beyond that.
- 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
- 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.
- 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.
- Proxy supports interception in 13, a new standard performance bonus that defineProperty doesn’t.
- 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.
- Proxy compatibility is poor. Currently, there is no Polyfill scheme that fully supports Proxy interception methods.