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 directorymkdir snabbdom-demo
  • Enter the project directorycd snabbdom-demo
  • Create package. Jsonnpm init -y
  • Installing a Parcel locallynpm 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 byselThe selector
  • dataset
    • Set up thedata-*Custom property of
  • eventlisteners
    • Register and remove events
  • style
    • Set inline styles to support animation
    • delayed/remove/destory
Use of modules
  1. The import module
  2. Init () registers modules with an array, which is used to register modules
  3. 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 nodesameVnode(), is calledpatchVnode
    • sameVnodeThe principle is contrastkeyandselWhether or not the same
  • Not the same callcreateElmCreate 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
  • 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

  1. usesameVnode()To compareoldStartVnodeandnewStartVnodeIf 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
  2. usesameVnode()To compareoldEndVnodeandnewEndVnodeIf 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 –, letoldEndVnode/newEndVnodePoints to the penultimate node of their respective arrays
  3. usesameVnode()To compareoldStartVnodeandnewEndVnodeIf 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 –, letoldStartVnodePoint to the second node,newEndVnodePoint to the second to last
    • Put the old start node corresponding to the DOM elementoldStartVnode.elmMove after the old end nodeoldEndVnode.elmafter
  4. usesameVnode()To compareoldEndVnodeandnewStartVnodeIf 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
    • theoldEndVnode.elmCorresponding DOM elementoldStartVnode.elmCorresponding 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

  1. 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 itcreateElm()In the old array queueoldStartVnodeBefore I insert anewStartVnode
    • If found, first compare the two nodes are not the same node, if not, continue to step, generate a new point, insertoldStartVnodebefore
    • If the node is the same, run thepatchVnode()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.elmBefore starting the element corresponding to the node
    • Index ++ reassigns newStartVnode

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