In increasingly complex front-end applications, state management is a topic that is often mentioned. From the early slash-and-burn era to jQuery, and then to the current popular MVVM era, the form of state management has undergone earth-shaking changes. There is no need to maintain a large number of event callbacks and listeners to update the view. Instead, use two-way data binding. You only need to maintain the corresponding data state to automatically update the view, which greatly improves development efficiency.
However, two-way data binding is not the only way. There is also a very rough and effective way: once the data changes, redraw the entire view, that is, reset Click on innerHTML. This approach is indeed simple, brutal, and effective, but if the entire view is updated only because of a small local data change, the cost-effectiveness is too low. Moreover, events, input boxes that acquire focus, etc., all need to be reprocessed. Therefore, for small applications or local small views, it is completely possible to do this, but it is not advisable for complex large-scale applications.
In order to solve the problem that the view can be updated reasonably and efficiently under large and frequent data updates, Vue introduced virtual DOM in version 2.0. On the premise of sacrificing some performance, Virtual DOM increases the maintainability and realizes the centralized operation of DOM. When the data changes, the virtual DOM is first modified, and then reflected in the real DOM, and updated with the minimum cost DOM to improve efficiency; in addition, Virtual DOM can also be rendered on terminals other than DOM, such as Weex.
Introduction to the diff algorithm
The purpose of the diff algorithm is to find out which nodes have changed and which nodes have not changed and can be reused. If you use the most traditional diff algorithm, as shown in the figure below, each node has to traverse all the nodes on the other tree for comparison, which is the complexity of o(n^2), plus the o( n) complexity, then the total complexity reaches o(n^3), which is very expensive for a page with a complex structure and a large number of nodes.
Actually vue and react Both have optimized the diff algorithm of the virtual dom to reduce the complexity to the o(n) level. The specific strategy is:
- The nodes of the same layer are compared with each other
Traverse the entire number of nodes from the root node, and only compare nodes at the same level. So in our code development, if the content of the node has not changed, then don’t change its level easily, otherwise the node will not be reused.
- When nodes are compared, if the types are different, the node and all its child nodes will be directly destroyed and newly created.
- For child nodes of the same type, use key to help find, and use algorithm to optimize search efficiency. Among them, the diff algorithms of react, vue2 and vue3 are different.
Next, we will introduce the specific code implementation in vue
Diff algorithm in Vue2
Overall flow chart
patch
First judge whether it is the first rendering, if it is the first rendering, then we can directly createElm; if not, judge whether the element types of the old and new nodes are the same; if Both nodes are the same, then drill down to check their children. If the two nodes are different, it means that the Vnode has been completely changed, and the oldVnode can be directly replaced.
function patch(oldVnode, vnode, hydrating, removeOnly) {// Determine whether the new vnode is empty if (isUndef(vnode)) {// Uninstall all if the old vnode is not empty The old vnodeif (isDef(oldVnode)) invokeDestroyHook(oldVnode)return}let isInitialPatch = false// Used to store the insert hook function, call const insertedVnodeQueue = []// If the old node does not exist, create a new node directly if (isUndef(oldVnode)) {isInitialPatch = truecreateElm(vnode, insertedVnodeQueue)} else {//Is it an element node const isRealElement = isDef(oldVnode.nodeType)// When the old node is not a real DOM node, and the new and old nodes Type and key are the same, update patchVnode if (!isRealElement && sameVnode(oldVnode, vnode)) {patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)} else {// If it is not the same element node // when When the old node is a real DOM node if (isRealElement) {// If it is an element node and modify the SSR_ATTR attribute in the SSR environment if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {// is the service side rendering, delete this attribute oldVnode.removeAttribute(SSR_ATTR)hydrating = true}// This judgment is the processing logic of server side rendering if (isTrue(hydrating)) {if (hydrate(oldVnode, vnode, insertedVnodeQueue)) { invokeInsertHook(vnode, insertedVnodeQueue, true) return oldVnode}}// If it is not rendered by the server, or the mixing fails, create an empty comment node to replace oldVnode oldVnode = emptyNodeAt(oldVnode)}// Get the parent node of oldVnode const oldElm = oldVnode.elmcr>// generate longest stable subsequence only when nodes have moved
const increasingNewIndexSequence = moved? getSequence(newIndexToOldIndexMap): EMPTY_ARR
j = increasingNewIndexSequence.length - 1
// looping backwards so that we can use last patched node as anchor
for (i = toBePatched - 1; i >= 0; i--) {const nextIndex = s2 + iconst nextChild = c2[nextIndex] as VNodeconst anchor =nextIndex + 1 <l2 ? (c2[ nextIndex + 1] as VNode).el : parentAnchorif (newIndexToOldIndexMap[i] === 0) {// mount newpatch(null,nextChild,container,anchor,parentComponent,parentSuspense,isSVG,slotScopeIds,optimized)} else if (moved ) {// move if:// There is no stable subsequence (e.g. a reverse)// OR current node is not among the stable sequence if (j <0 || i !== increasingNewIndexSequence[j]) {move(nextChild, container, anchor, MoveType.REORDER)} else {j--}}
}
Vue3 diff vs Vue2 optimization
- Vue2 is a full Diff (when the data changes, it will generate a new DOM tree, compare it with the previous DOM tree, find different nodes and update them. ); Vue3 is a static mark + non-full Diff (when Vue 3 creates a virtual DOM tree, it will add a static mark according to whether the content in the DOM will change. Then when comparing with the last virtual node, Only the nodes with static tags will be compared.)
- Using the longest increasing subsequence to optimize the comparison process can minimize DOM movement and achieve the least DOM operation
The role of Key attribute
When Vue is updating the list of elements rendered using v-for, it uses the “in-place update” strategy by default. If the order of the data items is changed, Vue will not move the DOM elements to match the order of the data items, but will update each element in place and make sure they render correctly at each index position. This is similar to track-by=”$index” in Vue 1.x. While this default mode is efficient, it is only suitable for list-rendered output that does not depend on child component state or temporary DOM state (e.g. form input values).
In order to give Vue a hint so that it can track each node so as to reuse or reorder existing elements, we need to provide a unique value for each item when v-for renders the list of elements Identifies the key attribute.
Key usage scenarios and how to use them correctly
Usage scenarios:
-
- v-for rendered list of elements
- When there are child elements of the same parent
- It is also possible when we want to force replace an element/component instead of reusing it Set the key attribute; for example: trigger transition
// When the text changes, will always be replaced instead of modified , so the transition is triggered.
{{ text }}
</transitio
-
- When traversing the output DOM content is very simple or we deliberately rely on the default behavior to obtain performance improvements, it is not necessary to set the key attribute.
Wrong usage:
- Use index or index to splice other values as key
- Use non-basic type values such as objects or arrays as the key of v-for
Correct usage:
- Use string or The value of the numeric type is used as the value of the key
- Use a unique value as the key, for example: id, etc.
Reference materials:
https: //stackoverflow.com/questions/44238139/why-is-vue-js-using-a-vdom
Announcing Vue.js 2.0 – Zhihu
https://cn.vuejs.org/v2 /guide/list.html
https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/renderer.ts
This article comes from internal sharing !