What is the virtual DOM
Virtual DOM is a common JS object to describe the DOM object, because it is not a real DOM object, so it is called virtual DOM
Why use the virtual DOM
- Manipulating the virtual DOM manually can be tricky, and the more complex the project, the more complex the DOM operation
- Most of the time, the overhead of manipulating the virtual DOM is much less than that of the real DOM
- We use a template engine for traditional view operations, but when the data state changes, we cannot solve the problem of tracking view operations, we can only delete and re-render
- When the state of the virtual DOM changes, there is no need to update the DOM immediately. You only need to create a virtual DOM tree to describe the DOM, and then internally calculate how to update the DOM effectively through the diff algorithm
The role of the virtual DOM
- Maintain the relationship between views and states
- Improve rendering performance in complex view situations
- In addition to rendering dom SSR Weex RN MPvue Uni-app
Virtual dom library
Before studying Virtual DOM of vue, let’s first study Snabbdom. Virtual DOM used in ve2. X is modified from Snabbdom. Is one of the fastest Virtual DOM
Snabbdom creates the project
Package tools For ease of use, choose Parcel
- Create a project directory
mkdir snabbdom-demo
- Enter the project directory
cd snabbdom-demo
- Create package. Json
npm init -y
- Installing a Parcel locally
npm i parcel-bundler
- Configure scripts for package.json
"scripts" : { "dev":"parcel index.html --open", "build":"parcel build index.html" } Copy the code
- Creating a directory structure
Module import issues
In commonJS syntax, all modules export an object, so we can always use a variable to receive es6 modules that do not use export default, must use curly braces, similar to structure, in fact is either destruct, or a fixed syntax
// import snabbdom from 'snabbdom' error import {h, init} from 'snabbdom' The patch function is used to compare the difference between two vNodes. Let patch = init([]) // the first argument tag + selector // the second argument is the content of the tag if it is a string let vnode = h('div#container.cls', 'Hello world') let app = document.querySelector('#app') Let oldVnode = patch(app, VNode) VNode = h('div', 'hello snabbdom') patch(oldVnode, VNode)Copy the code
The patch function
Is returned via the exported init function, and patch takes two arguments
About the init function
Parameter: array, module return patch function, used to compare the difference between two VNodes, update to the real DOM
About patch Function
The first argument can be a DOM element, which is internally converted to a vNode, or a vNode. The second argument is vnode
H function – handles arguments and returns a vNode object by calling the vNode function
The SNabbDOM will export the h function
The first argument tag + selector the second argument is the contents of the tag if it is a string, and the second argument can use an array if it has child elements
Function overloading
- Function overloading refers to two functions with the same name that have different numbers of arguments or different types of arguments, regardless of the return value
- There is no concept of overloading in JS, there is overloading in TS, but it is implemented by adjusting parameters in code
In JS, the following function overrides the preceding function. However, in TS, the number of arguments can be used to distinguish the two functions with the same name. The same applies to different parameter types.
Snabbdom module
Snabbdom’s core library does not handle element attributes/styles/events; modules can be used if needed
Functions of modules
- Snabbdom’s core library does not handle dom element attributes/styles/events, etc. This can be done by registering modules provided by Snabbdom by default
- Modules in the Snabbdom can be used to perform Snabbdom functions
- Modules in Snabbdom are implemented by registering global hook functions
Six commonly used modules are officially provided
- attributes
- Set the attributes of the DOM element using setAttribute()
- Handles properties of Boolean type
- props
- Like the Attributes module, set the DOM element’s element Element [attr]= value
- Boolean type attributes are not handled
- class
- Switching class styles
- Note that setting class styles for elements is done by
sel
The selector
- dataset
- Set up the
data-*
Custom property of
- Set up the
- eventlisteners
- Register and remove events
- style
- Set inline styles to support animation
- delayed/remove/destory
Use of modules
- The import module
- Init () registers modules with an array, which is used to register modules
- When you create a VNode using the h() function, you can set the second argument to the object (through which you set inline style events, etc.) and move the other arguments later
Import {init} from 'snabbdom/build/package/init / / @ snabbdom version 2.1.0 import {h} from' snabbdom/build/package/h / / The import module import {styleModule} from 'snabbdom/build/package/modules/style' import from {eventListenersModule} 'snabbdom/build/package/modules/eventlisteners' / / registered module const patch = init ([styleModule, EventListenersModule]) // Let vnode = h('div', [h('h1',{style: {backgroundColor: 'red' } }, 'hello snabbdom'), h('p', { on: { click: Function eventHandler() {console.log(' click on me '); } let app = document.querySelector('#app') patch(app, vnode)Copy the code
The overall execution process of patch
- patch(oldVnode, newVnode)
- Since Vnode is also a tree structure, the patch function is to find the difference between the two trees, and the process is often called diFF algorithm, which is used to find the difference between the two trees
- The core is to render the changing content of the new node into the real DOM, and finally return the new node as the old node for the next processing
- Inside patch, it will first judge whether the old and new VNodes are the same node (the key of the node is the same as sel of the node). If they are not the same node, the old node will be directly deleted and the new node will be rendered again
- If it is the same node, then check whether the new VNode has text. If it does and it is different from the old one, update the text directly
- If the children of the new VNode has a value, check whether there are any child nodes. Then compare the child nodes of the old and new nodes to check whether the child nodes have changed
The init function
Return patch function, use high-order function to change the patch function that should be four parameters into a function that only needs to pass two parameters, create multiple hook functions at the same time, do different things at different times
The working process of patch function
PatchVnode and createElm functions are called to compare the old and new nodes
- First, compare the old and new nodes to see if they are the same node
sameVnode()
, is calledpatchVnode
sameVnode
The principle is contrastkey
andsel
Whether or not the same
- Not the same call
createElm
Create a new node and delete an old node - Execute the INSERT hook function in the insert queue to iterate over the trigger POST hook function
- Returns a new VNode
Working process of patchVnode function
- Two hook functions are triggered before comparing the old and new nodes
- prepatch
- update
- Compare the difference between the old and new nodes and update the real DOM
- The new node has the text attribute and is not equal to the old node
- If the old node has children, the DOM element corresponding to the old node children is removed
- And set the new node to correspond to the DOM element textContent
- Both new and old nodes have children and are not equal
- Call the updateChildren hook function to compare and update the differences between the children
- Only the new node has the children attribute
- If the old node has a text attribute, clear the textContent of the corresponding DOM element
- Adds all children of the new node
- Only old nodes have children. Remove all old nodes
- Only the old node has the text attribute. Clear the textContent of the corresponding DOM element
- The new node has the text attribute and is not equal to the old node
- Trigger the PostPatch hook function
The overall execution of the updateChildren function
Compare the four cases at the beginning and end of the same level node
- use
sameVnode()
To compareoldStartVnode
andnewStartVnode
If the node is the same, compare the key and SEL- If it is the same node, call patchVnode() to compare the difference between the old node and the new node and update it to the real DOM
- Old and new start node index + +, let oldStartVnode/newStartVnode points to the second node array
- use
sameVnode()
To compareoldEndVnode
andnewEndVnode
If the node is the same, compare the key and SEL- If it is the same node, call patchVnode() to compare the difference between the old node and the new node and update it to the real DOM
- The index of the old and new start nodes –, let
oldEndVnode
/newEndVnode
Points to the penultimate node of their respective arrays
- use
sameVnode()
To compareoldStartVnode
andnewEndVnode
If the node is the same, compare the key and SEL- If it is the same node, call patchVnode() to compare the difference between the old node and the new node and update it to the real DOM
- The index of the old start node ++, the new end node –, let
oldStartVnode
Point to the second node,newEndVnode
Point to the second to last - Put the old start node corresponding to the DOM element
oldStartVnode.elm
Move after the old end nodeoldEndVnode.elm
after
- use
sameVnode()
To compareoldEndVnode
andnewStartVnode
If the node is the same, compare the key and SEL- If it is the same node, call patchVnode() to compare the difference between the old node and the new node and update it to the real DOM
- the
oldEndVnode.elm
Corresponding DOM elementoldStartVnode.elm
Corresponding to the DOM element before - Move the corresponding index
After comparing the start and end nodes, to move the corresponding DOM element, possibly in reverse order, if none of the above four cases are satisfied, go to the fifth case below
- Use the key of the new node to find the node with the same key in the old node array
- Use it if you can’t find it
createElm()
In the old array queueoldStartVnode
Before I insert anewStartVnode
- If found, first compare the two nodes are not the same node, if not, continue to step, generate a new point, insert
oldStartVnode
before - If the node is the same, run the
patchVnode()
Compare the two nodes, then clear the corresponding index value of the old node, and move the DOM element of the found old node pair to the correspondingoldStartVnode.elm
Before starting the element corresponding to the node - Index ++ reassigns newStartVnode
- Use it if you can’t find it
When the old node array or the new node array is traversed, we need to remove the remaining old nodes from the old node array or add the remaining points to the new node array
The meaning of the key
Set a key with a unique value for all child elements that have the same parent element, otherwise rendering errors may occur
Setting a key to a Vnode can improve performance by reusing the last dom object when sorting a list of elements or assigning entries or entries to the list, reducing the number of renders
When the key is not set, the key is undefined, which means that the same key is set. The DIff algorithm will still maximize the reuse of elements on the interface