This series of articles is to share my own step-by-step process of writing the entire framework, and those interested in XDM can refer to the source code. Git repository: github.com/sullay/art-…

Data update

Through the previous efforts our framework has been able to complete the page rendering work, the data update actually only need to change the data of the component can be updated.

To review the previous code, for a vNode object, the DOM is regenerated if there is no $DOM attribute.

getDom() {
    if (!this.$dom) this.createDom(); 
    return this.$dom; 
}
Copy the code

So we just need to leave the original $DOM empty after the data update and then regenerate it in subsequent renders. Here we have the convention that the Component’s data property is used to hold all properties that support dynamic updates.

// Pass in data that needs to be updated to trigger rendering
setData(data) {
  for (const key in data) {
    this.data[key] = data[key];
  }
  let oldDom = this.$vNode.$dom;
  // The $dom of the custom node component points to the $dom of the child node, which is set to null to trigger createDom
  this.$vNode.$dom = null;
  // Pass oldDom as a replacement
  renderDomTree(this.$vNode, this.$vNode.$parentNode, oldDom);
}
Copy the code

The update is done by simply emptying $DOM, but there are some problems:

  • All dom of the custom component needs to be recreated
  • The initialized state of the child component is not preserved

$dom and $instance reuse

To solve these two problems, we need to create the dom as much as possible when creating the DOM. For vComponentNode, we need to replace the $instance attribute of the new node with the $instance attribute of the old node.

/ / vComponentNode class
createDom() {
    let preChildren = this.$children;
    let child = this.$instance.render();
    this.$children = [child];
    // Reuse the previous DOM or subcomponent through diff each time you create it
    vNode.diffDom(this.$children, preChildren);
    this.$dom = child.getDom();
  }
  
  // The DOM in the old node is reassigned according to the new node
  static updateDom(newNode, preNode) {
    let dom = preNode.$dom;
    for (let key in preNode.$props) dom[key] = null;
    for (let event in preNode.$events) dom.removeEventListener(event, preNode.$events[event]);
    // Remove the child nodes and organize the DOM structure when renderDomTree is called
    dom.innerHTML = null;
    // Set the properties
    for (let key in newNode.$props) dom[key] = newNode.$props[key];
    // Listen on events
    for (let event in newNode.$events) dom.addEventListener(event, newNode.$events[event]);
    newNode.$dom = dom;
  }

  // Use existing DOM or custom components as much as possible
  static diffDom(newNodes = [], preNodes = []) {
    if(! newNodes.length || ! preNodes.length)return;
    // All old nodes
    let preMap = new Map(a);for (const node of preNodes) {
      if(! preMap.has(node.$type)) preMap.set(node.$type, []);if (node.$dom) preMap.get(node.$type).push(node);
    }
    // Iterate over the new node to see if there are any nodes that can be reused
    for (const node of newNodes) {
      if (preMap.has(node.$type) && preMap.get(node.$type).length) {
        let preNode = preMap.get(node.$type).shift();
        if (vComponentNode.isVNode(node)) {
          let preChildren = preNode.$children;
          // Custom components, reuse old component instances
          node.$instance = preNode.$instance;
          // The component instance $vNode points to the new node
          node.$instance.$vNode = node;
          // Update the custom component virtual DOM tree
          let child = node.$instance.render();
          node.$children = [child];
          vNode.diffDom(node.$children, preChildren);
          // The custom component $dom points to the child component's $dom
          node.$dom = child.$dom;
        } else {
          // Regular node node, continue to diff child components after updatevNode.updateDom(node, preNode); vNode.diffDom(node.$children, preNode.$children); }}}}Copy the code

Currently, the above code only supports reuse at the same level. You are advised to use the specified key value for reuse at different levels.

Props and Events updated

At this point we can reuse the PREVIOUS DOM and component instances, but there is a hidden bug. Let’s review the code from the previous article.

export class vComponentNode extends vNode {
  constructor(type = ' ', allProps = {}, slots = []){...this.$instance = new type(this.$props, this.$events, slots);
    this.$instance.$vNode = this; }}Copy the code

Assuming that we have a parent component, we want the props property of the child component to change based on the parent component’s data property. The props property of the subcomponent was passed in when the component object was constructed, and we reused the original $instance property in the update, so the props property could not be updated.

We could consider using something similar to react, with an extra hook function to trigger an update to the props. I used the other approach here.


export class vComponentNode extends vNode {
  constructor(type = ' ', allProps = {}, slots = []) {
    super(type, allProps);
    // Identify custom components
    this.$isComponent = true;
    / / slots
    this.$slots = slots;
    // Create a component instance
    this.$instance = new type(this); }}// Customize the component class
export class Component {
  constructor(node) {
    // Bind the corresponding node
    this.$vNode = node;
    this.data = {}
  }
  // All attributes are retrieved from Node. Data and events can be updated without initialization when reusing the original component.
  get props() {
    return this.$vNode.$props;
  }
  get events() {
    return this.$vNode.$events;
  }
  get slots() {
    return this.$vNode.$slots; }}Copy the code

Instead of passing the props property to the component class, we pass the vNode object so that the props property is still bound to the vNode object. When a component is updated, $instance is reused, but the props property can still be updated.

So far we have completed the data update work, and in the next chapter we will start writing the framework’s task scheduling, which is the core function of the framework.

XDM think this series of articles are helpful to you!!