Vue 3.0 new features
Reference: https://www.cnblogs.com/Highdoudou/p/9993870.html
https://www.cnblogs.com/ljx20180807/p/9987822.html
Performance optimization
-
Changes in the observer mechanism : Proxy replaces object.defineProperty
Object.defineProperty
Getters and setters used by Vue 2.x. However, Vue 3 will use ES2015 Proxy as its observer mechanism. This eliminates previous warnings, doubles the speed, and saves half of the memory overhead.
-
virtual DOM reconstruction (twice as fast as 2.5) [It is related to the size of the template --> It is related to the amount of dynamic content in the template]
-
-
Tradition: When the component is updated, the entire vdom tree needs to be recreated, and then traversed for diff, update
-
New update strategy: block tree
-
-
-
-
Distinguish between dynamic nodes and static nodes
-
Update based on dynamic node instructions (v-if, v-for, { {name }} etc.)
-
-
-
Compile-time optimization
-
Slot is compiled as a function by default
-
The creation function of vnode keeps the parameters consistent
-
Generate vnode type tags at compile time
Added composition-api,
https://composition-api.vuejs.org/
You can use composition-api in the vue2.x project by installing the @vue/composition-api package.
- Reactive: The role of reactive is to package objects into reactive objects -objects that are proxied through Proxy.
- ref: Return a responsive, variable ref object with only one attribute of value from the incoming value.
When ref is returned as a render context, and the ref object is used in the template, the internal value is automatically obtained, and the .value attribute is not required.
<template> <div>{ { count }}</div> </template> <script> export default { setup() { return { count: ref(0) } } } </script>
When the ref object is used as a property and passed into the reactive object reactive, it will automatically get its internal value (behave like a normal property value, not an object).
const count = ref(0) const state = reactive({ count }) console.log(state.count) // 0 state.count = 1 console.log(count.value) // 1
After the reactive property is bound to the new ref object, the value of the original ref remains unchanged (disconnected).
const otherCount = ref(2) state.count = otherCount console.log(state.count) // 2 console.log(count.value) // 1
In other cases, you need to bring .value when obtaining ref
const arr = reactive([ref(0)]) // need .value here console.log(arr[0].value) const map = reactive(new Map([['foo', ref(0)]])) // need .value here console.log(map.get('foo').value)
-
- The difference between reactive objects and ref objects
It can be compared by how to write standard JavaScript logic:
// Style 1: Separate variables let x = 0 let y = 0 function updatePosition(e) { x = e.pageX y = e.pageY } // Style 2: Single object const pos = { x: 0, y: 0, } function updatePosition(e) { pos.x = e.pageX pos.y = e.pageY }
-
You can convert the style (1) to use ref writing (in order to make the basic type value responsive ).
-
Convert style (2) to the way of using
reactive
objects.
If you only use reactive
the problem, you must always keep a reference to the returned object when using the composite function to maintain responsiveness. This object cannot be deconstructed or expanded :
// Combination function: function useMousePosition() { const pos = reactive({ x: 0, y: 0, }) // ... return pos } // Consumer components export default { setup() { // Responsiveness will be lost here! const { x, y } = useMousePosition() return { x, Y, } // Responsiveness will be lost here! return { ...useMousePosition(), } // This is the only way to stay responsive! // You must return `pos` itself and reference x and y in the template in the same way as `pos.x` and `pos.y`. return { post: useMousePosition (), } }, }
toRefs
API is used to provide a solution to this constraint-it converts each property of the responsive object into a corresponding ref.
function useMousePosition() { const pos = reactive({ x: 0, y: 0, }) // ... return toRefs(pos) } // x & y are now in ref form and can be deconstructed by i! const { x, y } = useMousePosition()
- computed: Pass in a getter() function and return an immutable responsive ref object.
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 count value changes, plusOne value changes accordingly plusOne.value++ // error
When an object with get and set functions is passed in, a writable ref object is returned.
const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0 The value of count changes, and the value of plusOne changes accordingly; when the value of plusOne is changed, the value of count also changes accordingly
- readonly: Convert the received object (whether it is a responsive object, ref object or ordinary object) into a read-only object that is proxied by Proxy.
const original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => { // works for reactivity tracking console.log(copy.count) }) // mutating original will trigger watchers relying on the copy original.count++ // mutating the copy will fail and result in a warning copy.count++ // warning!
- watchEffect: accept a function, when the dependency changes, call the function again.
const count = ref(0) watchEffect(() => console.log(count.value)) setTimeout(() => { count.value++ }, 100)
When watchEffect() is called in setup() or life cycle hook, the monitor will always exist in the life cycle of the component until the component is unmount.
Another case of unloading monitoring is that watchEffect() returns a stop handler, which can be called to stop the monitoring.
const stop = watchEffect(() => { /* ... */ }) // later stop()
When getting data from the background, watchEffect() accepts the async callback function.
const data = ref(null) watchEffect(async () => { data.value = await fetchData(props.id) })
The update function of the component also has a watch effect. The user-defined watchEffect will be called after the component is updated .
<template> <div>{ { count }}</div> </template> <script> export default { setup() { const count = ref(0) watchEffect(() => { console.log(count.value) }) return { count } } } </script>
In the above code, count.value will be printed synchronously in the first round (before the onmount life cycle); when the count changes, the component update will be performed first, and then log.
If you want to execute the callback function in watchEffect for the first time, put it after onmount,
onMounted(() => { watchEffect(() => { // access the DOM or template refs }) })
If you want the watchEffect() call to occur before the component update or re-run synchronization, you need to pass an option object with the flush attribute (the default value is post).
watchEffect(()=> { //... }, { flush:'sync' // trigger flush before update: "pre" })
In addition, the option object has two function attributes, ontrack and ontrigger, which are used to debug the behavior of the watcher.
onTrack
will be called when a reactive property or ref is tracked as a dependencyonTrigger
will be called when the watcher callback is triggered by the mutation of a dependency
watchEffect( () => { /* side effect */ }, { onTrigger (e) { debugger // Perform interactive debugging } } )
- watch: equivalent to this.$watch in vue 2.x.
Compared to watchEffect(), watch() can help us achieve:
- Perform the side effect lazily;
- Be more specific about what state should trigger the watcher to re-run;
- Access both the previous and current value of the watched state.
The data source of watch() can be a getter function that returns a value, or a ref object.
// watching a getter const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } ) // directly watching a ref const count = ref(0) watch(count, (count, prevCount) => { /* ... */ })
For monitoring of multiple data sources, an array can be used.
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
Life cycle api adjustment
<template> <div>{ {count}} { {obj.foo}}</div> </template> <script> import { ref, reactive, watchEffect } from '@vue/composition-api' export default { props: { name: String }, setup (props) { const count = ref(0) const obj = reactive({ foo: 'abc' }) watchEffect(() => { console.log(props.name) }) return { count, obj } } } </script>
The first parameter accepted by setup() is props. Props are responsive. Note: It cannot be deconstructed when passing parameters. The above code passes props to setup() and monitors them through watchEffect().
Setup() can also accept the second parameter context (equivalent to this in vue2.x, setup() does not allow this ). When context is used as a parameter, it can be deconstructed. Commonly used ones are:
context.attrs, context.slots, context.emit
setup(props, {attrs}) { function onClick() { console.log(attrs.foo) // can be guaranteed to be the latest value } }
Other life cycle functions can be registered synchronously in setup(). When the life cycle hooks are executed synchronously, the renderContext, watcher and computed properties of the component instance are also established synchronously. When the component is uninstalled, the internal lifecycle hooks will also be removed synchronously.
setup() { onMounted(() => { console.log('mounted!') }) onUpdated(() => { console.log('updated!') }) onUnmounted(() => { console.log('unmounted!') }) }
- <template> Same as before, vue-next also supports handwritten render. If template and render exist at the same time, render takes precedence.
- setup() is the new major change. As the name implies, the setup function will run once before the component is mounted . Similar to the initialization of the component, setup needs to return an object or function. The returned object will be assigned to the renderContext of the component instance , which can be accessed in the template scope of the component, similar to the return value of data. The return function will be treated as the render of the component.
Organize code based on logical concerns
In vue 2.x, the option api is used. To complete the data or computed attribute distance used by a function, the definition of this function may have many lines (code is scattered). This fragmentation makes code maintenance become difficult.
This problem has been resolved in the composition api. The code for each logical concern is now combined into a combinatorial function (named starting with use ). This greatly reduces the need to constantly "jump" when dealing with large components. At the same time, the combined function can also be folded in the editor to make the components easier to browse.
setup() { // ... }, } function useCurrentFolderData(networkState) { // ... } function useFolderNavigation({ networkState, currentFolderData }) { // ... } function useFavoriteFolder(currentFolderData) { // ... } function useHiddenFolders() { // ... } function useCreateFolder(openFolder) { // ... }
setup()
The function is now simply used as the entry point for calling all combined functions. The final return statement serves as a single exit to confirm the content exposed to the template.
export default { setup() { // network status const { networkState } = useNetworkState() // folder status const { folders, currentFolderData } = useCurrentFolderData(networkState) const folderNavigation = useFolderNavigation({ networkState, currentFolderData, }) const { favoriteFolders, toggleFavorite } = useFavoriteFolders( currentFolderData ) const { showHiddenFolders } = useHiddenFolders() const createFolder = useCreateFolder(folderNavigation.openFolder) // current working directory resetCwdOnLeave() const { updateOnCwdChanged } = useCwdUtils() // Utilities const { slicePath } = usePathUtils() return { networkState, folders, currentFolderData, folderNavigation, favoriteFolders, toggleFavorite, showHiddenFolders, createFolder, updateOnCwdChanged, slicePath, } }, }
Logic extraction and reuse
A composite function only depends on its parameters and Vue's globally exported API , not on the this
context. You can export any piece of logic in the component as a function for reuse.
import { ref, onMounted, onUnmounted } from 'vue' export function useMousePosition() { const x = ref(0) const y = ref(0) // Extract as a function function update(e) { x.value = e.pageX y.value = e.pageY } onMounted(() => { window.addEventListener('mousemove', update) }) onUnmounted(() => { window.removeEventListener('mousemove', update) }) return { x, y } }
Call the above combination function in a component
import { useMousePosition } from './mouse' export default { setup() { const { x, y } = useMousePosition() // Other logic... return { x, y } }, }
Cooperate with existing API
-
The combined API will be resolved before the 2.x options (
data
,computed
andmethods
), and the properties defined in these options cannot be accessed in advance. -
setup()
The property returned by the function will be exposed tothis
. They can be accessed in the 2.x options.
Comparison of vue3.0 and react:
-
-
Same logic combination and reuse ability
-
The setup() of the composition api will only be called once , compared with react hooks
-
In line with js intuition
-
No closure variable problem
-
Will not be repeated every time rendering, reducing the pressure of garbage collection;
-
There is no problem that inline callbacks cause subcomponents to be updated forever
- There is no problem of forgetting to record dependencies, and there is no need to "useEffect" and "useMemo" and pass in the dependency array to capture outdated variables. Vue's automatic dependency tracking can ensure that listeners and calculated values are always accurate.
-
-