preface

XDM, hello, everyone, I am Panda Little brother, a relatively Buddha program ape, shame to say, Vue3 has been released for about a year, I just learn Vue3, recently through reading Vue3 source code simply write about Vue3 how to initialize, how to achieve responsive, how to update and diff virtual DOM, share personal shallow cognition, If you write badly, please spray lightly.

implementation

Initialize (mount)

const Vue = {
  // Multi-platform compatibility only requires the features of the passed platform
  createRenderer({ querySelector }) {
    return {
      Create createApp, which returns an instance of Vue
      createApp(options) {
        // 2. Return to mount
        return {
          // The mount method converts the data state to a DOM and appends it to the host element
          mount(selector) {
            // 1. Get the host element
            const parent = querySelector(selector)
​
            // 1.1 Save setup and data instances
            if (options.setup) {
              this.setupState = options.setup()
            }
            if (options.data) {
              this.data = options.data()
            }
​
            // 1.2 Determine the priority of setup and data by proxy method
            this.proxy = new Proxy(this, {
              get(target, key) {
                if (key in target.setupState) {
                  return target.setupState[key]
                } else {
                  return target.data[key]
                }
              },
              set(target, key, value) {
                if (key in target.setupState) {
                  target.setupState[key] = value
                } else {
                  target.data[key] = value
                }
              },
            })
​
            / / 2. The rendering
            // compile template > render, if the user writes render, the template will be compiled internally
            if(! options.render) { options.render =this.compile(parent.innerHTML)
            }
            
            this.update = effect(() = > {
              const el = options.render.call(this.proxy)
              // 3. Empty and mount
              parent.innerHTML = ' '
              parent.appendChild(el)
            })
​
            this.update()
          },
          // The simple compiler method low
          compile(template) {
            return function render() {
              const h3 = document.createElement('h3')
              h3.textContent = this.title1
              return h3
            }
          },
        }
      },
    }
  },
​
  createApp(options) {
    const renderer = this.createRenderer({
      querySelector(selector) {
        return document.querySelector(selector)
      },
    })
    return renderer.createApp(options)
  },
}
Copy the code
The reactive update for Vue3.0 takes a different approach, using side effect dependencynew ProxyPerform data proxy, collect dependencies and place them in the queue of side effects when getting the value, and update the side effects corresponding to the value when setting the value in the queue of side effects, so that accurate update can be achieved.

Reactive

/ / implementation reactive
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      const res = Reflect.get(target, key)
      return res
    },
    set(target, key, value) {
      const res = Reflect.set(target, key, value)
      return res
    },
  })
}
Copy the code

update

1. Implement side effects

// Implement side effects
const effectStack = []
function effect(fn) {
  const eff = function () {
    try {
      effectStack.push(fn)
      fn()
    } finally {
      effectStack.pop()
    }
  }
​
  eff()
​
  return eff
}
Copy the code

2. Collect dependencies

/ / implementation reactive
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      const res = Reflect.get(target, key)
      // Rely on collection
      track(target, key)
      return res
    },
    set(target, key, value) {
      const res = Reflect.set(target, key, value)
      // Notification update
      trigger(target, key)
      return res
    },
  })
}
Copy the code

3. Make connections

// Collect dependent data structures
// targetMap : {
// target : {
// deps: [eff, eff1, eff2]
/ /}
// }
const targetMap = new WeakMap(a)// Collect dependencies
function track(target, key) {
  // 1. Take the last side effect
  const effect = effectStack[effectStack.length - 1]
​
  if (effect) {
    // 2. Get target and check whether it exists. If not, create target
    let depMap = targetMap.get(target)
    if(! depMap) { depMap =new Map()
      targetMap.set(target, depMap)
    }
​
    // 3. Get the deps and check if it exists
    let deps = depMap.get(key)
    if(! deps) { deps =new Set()
      depMap.set(key, deps)
    }
​
    // 4. Add side effects
    deps.add(effect)
  }
}
Copy the code

4. Notification updates

// Find the corresponding side effect and execute it
function trigger(target, key) {
  // 1. Obtain the dependent Map
  const depMap = targetMap.get(target)
  if(! depMap)return
​
  const deps = depMap.get(key)
  if (deps) {
    deps.forEach((dep) = > dep())
  }
}
Copy the code

5. diff

this.update = effect(() = > {
  // const el = options.render.call(this.proxy)
  // // 3. Clear and mount the vm
  // parent.innerHTML = ''
  // parent.appendChild(el)
​
  // Get the virtual node
  const vnode = options.render.call(this.proxy)
  // this.isMounted Determines whether the initialization or update phase is in progress
  if (!this.isMounted) {
    // Initialize fetch element empty please mount
    const el = this.createElm(vnode)
    parent.innerHTML = ' '
    insert(el, parent)
    this.isMounted = true
  } else {
    / / update
    this.patch(this._vnode, vnode)
  }
​
  this._vnode = vnode
})
​
// diff
patch(n1, n2) {
  // Get the node
  const el = (n2.el = n1.el)
  // n1 old node
  // n2 new node
  // Only the same nodes are compared
  if (n1.tag === n2.tag) {
    const oldCh = n1.children
    const newCh = n2.children
    
     /** * The old node is a string, and the new node has children. The children of the new node are inserted into the corresponding node. * the old node has children, and the new node has children. The old node is a string, compare the new and old nodes are different in the update */
    if (typeof oldCh === 'string') {
      if (typeof newCh === 'string') {
        if(oldCh ! == newCh) { el.textContent = newCh } }else {
        el.innerHTML = ' '
        insert(this.createElm(newCh), el)
      }
    } else {
      if (typeof newCh === 'string') {
        el.textContent = newCh
      } else {
        // Update the child operation -- first compare the header, then at the end of the comparison, if none is found, traverse all byte points using the longest increasing subsequence quicksort
        this.updatedChildren(oldCh, newCh)
      }
    }
  }
}
Copy the code

conclusion

XDM simply realizes the basic functions of Vue3, Vue3 is more than these contents, Vue3 also designed algorithm thinking, compilation principle and many other knowledge. Moreover, Vue3 has also made a lot of optimization in static node improvement, cache black, PatchFlag judgment and compatible Vue2. X version. We’ll share it later.

Finally, if the article is useful to you, please help to like, follow + comment.