preface

Before a simple look at the Vue Patch part of the source code, understand is based on Snabbdom library implementation. Recently, I want to know the whole process of Vue processing vnode patch in detail, and what does it do on Snabbdom? So with this question in mind, I wrote this article to record.

What did Snabbdom do?

A virtual DOM library with focus on simplicity, modularity, powerful features and performance. Powerful features and performance.

Snabbdom core code is only about 200 lines. It provides a modular architecture with rich functionality that can be extended with custom modules. Before understanding the core patch, it is necessary to understand the modular architecture idea of SNABBDOM.

modules

Doing tasks that extend the Snabbdom during the lifecycle of a node is called a module. Snabbdom infuses many hooks during patch. The module implementation is based on these hooks, which can be understood as the lifecycle of a VNode node.

For example, the EventListeners module:

  1. Create: Add a time listener when a node is created (addEventListener)
  2. Update: Deletes old node events and adds new events when a node is updated
  3. Destroy: Deletes an old node when the node is destroyed

Hooks

Hooks are a way to mount the vNode lifecycle. There are similar design ideas in the software development world, such as the version management tool Git. The modules in Snabbdom are based on this extension. Of course, you can also pass hook configurations to do something in the vNode lifecycle (this is for a single node, while modules are for all nodes), such as:

h('div.row', {
  key: movie.rank,
  hook: {
    insert: vnode= > {
      movie.elmHeight = vnode.elm.offsetHeight
    }
  }
})
Copy the code

The instructions in Vue are based on this implementation. The specific life cycle can refer to the official document: github.com/snabbdom/sn…

Patch (oldVnode, newVnode) functions

As the core part of Snabbdom, the patch function is created by snabbdom.init, and the module binding hook is provided during initialization. If oldVnode is a DOM element with a parent node, newVnode becomes a DOM node, and the passed element is replaced by the created DOM node. If a VNode instance is passed, this node is recursively compared to its children and dom updates are made. This piece of online source code parsing articles are more, here is not much to introduce.

snabbdom/h

The h function is used to create an instance of vNode. It is analogous to the h parameter in Vue render, which is extended with the input parameter. For example, the first argument tag, Vue can be an object or a function; For the second parameter data, Vue adds some unique features such as scopedSlots, slot, directive, ref… And so on.

What are the differences in the patch process of Vue compared with Snabbdom?

component

  • Vue has the concept of components. Components can also be nested, so there are parent components and child components. After the parent render function is executed, the child component is not initialized, only a special vNode is created, which is bound with hooks: init, prepatch, Insert, destroy. When the parent component encounters a child component during the patch execution, it will call hook: init method, and only in this method will the component instance be initialized. The component Mounted lifecycle hook is called when a subsequent INSERT hook is triggered. This piece of source code can be viewed at github.com/vuejs/vue/b…

  • SetScope is also called during patch execution. It adds the ${scopedId} attribute to the DOM node to implement the scoped style.

  • The patch process updates the ref to the context instance.

  • Because of the component concept, the first argument to create a Vnode in Vue can be either an object or a function, and in Snabbdom can only be a string.

The life cycle

  • Vue has removedprepostTwo hooks, which are called before and after patch execution
  • The VueinitHooks are only valid for component nodes, and all nodes in the Snabbdom are definableinit hooks
  • Vue addedactiveHooks for handling<keep-alive>nested<transition>Component generatedThe boundary issue

Static node optimization

Vue performs performance optimization on static nodes.

  • At compile time: Vue Compiler takes special care of static nodes in the Template: the method to create a static node is storedstaticRenderFnsIn the properties. In addition to static nodes generated by pure HTML syntax,v-once.v-preStatic nodes are also generated.
  • Runtime: Static nodes are saved to the instance when the component is initialized_staticTreesIn the array. When a component update refires Render, the VNode node is not recreated, using the existing static nodes directly. So it doesn’t triggerpatchVnodeOperation.

hydrate

For the RENDERED HTML output from the Vue server, the client initializes the mount node by binding the rendered DOM to the Vnode one by one to achieve the homogeneous effect. This is done by the function hydrate.

Overall Patch process

I have compiled a detailed patch flow chart. Some details are not written, such as: updateChildren diff process, asynchronous components, keep-alive, hydrate…

What functions in Vue are implemented based on the Hooks in Patch?

directives

Directives in Vue 2 are implemented based on hooks. In terms of the lifecycle of directives:

  1. -> hooks: create, update
  2. Inserted: A new directive is bound on a node. If hooks == create, it is called when the node is inserted; If hooks == update, call it directly
  3. Update: call -> hooks: update when directives already exist on a node
  4. ComponentUpdated: the directive already exists on the node and is invoked when hooks: Postpatch triggers. At this time, the VNode and its child VNode of the component where the directive resides have all been updated.
  5. Unbind: called when a node is destroyed -> hooks: destroy

ref

When a VNode is created, updated, or destroyed, it updates the $refs attribute on its component. In the patch process, when the child component is empty, the boundary case pointed by the parent component is undefined

style

For dynamically binding styles, Vue also intelligently prefixes unsupported attributes with vendor prefixes.

const normalize = cached(function(prop) {
  emptyStyle = emptyStyle || document.createElement('div').style
  prop = camelize(prop)
  if(prop ! = ='filter' && prop in emptyStyle) {
    return prop
  }
  const capName = prop.charAt(0).toUpperCase() + prop.slice(1)
  for (let i = 0; i < vendorNames.length; i++) {
    const name = vendorNames[i] + capName
    if (name in emptyStyle) {
      return name
    }
  }
})
Copy the code

Vue also supports value array writing. Such as {display: [” – its – box “, “- ms – flexbox”, “flex”]}

Snabbdom adds style hooks: remove. This is to allow for transitions when nodes are removed. Vue handles transitions wrapped in the
component.

Write in the back

In fact, there are many functions in Vue that depend on the vnode patch process, and there are also many functions in transition, so I won’t go into it here. Due to my limited understanding, if there are any questions in the article, please leave a message to correct.

reference

  • Github.com/snabbdom/sn…
  • github.com/vuejs/vue