preface

Today is a special day I wish you a happy goddess goddess festival ha cover I put a kill temple handsome photo to express my blessing ha ha

This article mainly handwritten Vue2.0 source code – initial rendering principles

In the last article, we mainly introduced the Vue template compilation principle, which is the basis of Vue generating virtual DOM. After the template compilation is finally converted to render function, how can we generate real DOM nodes to replace the EL option configuration This article mainly includes the generation of virtual DOM and real DOM

Applicable to the crowd: no time to look at the official source code or look at the source code to see the more meng and do not want to see the students


The body of the

1. The component mounts the loading port

// src/init.js

Vue.prototype.$mount = function (el) {
  const vm = this;
  const options = vm.$options;
  el = document.querySelector(el);

  // If the render attribute does not exist
  if(! options.render) {// If there is a template attribute
    let template = options.template;

    if(! template && el) {// If render and template do not exist but el attribute does exist, assign the template to the outer HTML structure where EL is located.
      template = el.outerHTML;
    }

    // Finally we need to convert the Tempalte template to render function
    if (template) {
      constrender = compileToFunctions(template); options.render = render; }}// Mount the current component instance to the real EL node
  return mountComponent(vm, el);
};
Copy the code

Then we’ll look at the $mount method and we’ll focus on the last sentence mountComponent is the entry function for the component instance to be mounted. This method goes into the lifecycle file in the source code and represents the lifecycle because we should have before and after the component is initially rendered with beforeMount and beforeMount Mounted Lifecycle hooks

2. MountComponent, the core method for mounting components

// src/lifecycle.js
export function mountComponent(vm, el) {
  // The previous step generated the render function
  // The next step is to execute the render function generated by the vm._render() method call to generate the virtual DOM
  // Finally render the virtual DOM to the page using the vm._update() method

  // The real EL option assigns the $EL property to the instance to prepare the way for the new DOM to replace the old dom
  vm.$el = el;
  // The _update and._render methods are mounted on the Vue prototype similar to the _init methods
  vm._update(vm._render());
}
Copy the code

The mountComponent function uses the vm._update(vm._render()) method to mount instances

3. Convert the render function into the virtual DOM core method _render

// src/render.js

import { createElement, createTextNode } from "./vdom/index";

export function renderMixin(Vue) {
  Vue.prototype._render = function () {
    const vm = this;
    // Get the render method generated by the template compilation
    const { render } = vm.$options;
    // Generate vNode -- virtual DOM
    const vnode = render.call(vm);
    return vnode;
  };

  // The render function has _c _v _s methods to define
  Vue.prototype._c = function (. args) {
    // Create a virtual DOM element
    returncreateElement(... args); }; Vue.prototype._v =function (text) {
    // Create the virtual DOM text
    return createTextNode(text);
  };
  Vue.prototype._s = function (val) {
    Json.stringify is required if the template contains an object
    return val == null
      ? ""
      : typeof val === "object"
      ? JSON.stringify(val)
      : val;
  };
}
Copy the code

We define the _render method in the prototype and then execute the render function. We know that the core code of the render function compiled by the template is mainly return Similar to _c (‘ div ‘{id: “app”}, _c (‘ div’, undefined, _v (” hello “+ _s (name)), _c (‘ span, undefined, _v (” world”)))) this code Then we also need to define a _c _v _s these functions can finally be converted into the virtual DOM

// src/vdom/index.js

// Define the Vnode class
export default class Vnode {
  constructor(tag, data, key, children, text) {
    this.tag = tag;
    this.data = data;
    this.key = key;
    this.children = children;
    this.text = text; }}// create element vnode equal to h in render =>h(App)
export function createElement(tag, data = {}, ... children) {
  let key = data.key;
  return new Vnode(tag, data, key, children);
}

// Create a text vNode
export function createTextNode(text) {
  return new Vnode(undefined.undefined.undefined.undefined, text);
}
Copy the code

Create a new VDOM folder to represent the virtual DOM related functionality and define the Vnode class and createElement and createTextNode methods that return Vnode

4. Convert the virtual DOM into the real DOM core method _update

// src/lifecycle.js

import { patch } from "./vdom/patch";
export function lifecycleMixin(Vue) {
  // Mount _update to Vue's prototype
  Vue.prototype._update = function (vnode) {
    const vm = this;
    // Patch is to render vNode as the real DOM core
    patch(vm.$el, vnode);
  };
}
Copy the code
// src/vdom/patch.js

// Patch is used to render and update views
export function patch(oldVnode, vnode) {
  // Determine if the passed oldVnode is a real element
  $el is the el option we passed in, so it is the real DOM
  $el is replaced with the old virtual DOM before the update if the view is updated instead of the original rendering
  const isRealElement = oldVnode.nodeType;
  if (isRealElement) {
    // Here is the logic for the first render
    const oldElm = oldVnode;
    const parentElm = oldElm.parentNode;
    // Convert the virtual DOM into a real DOM node
    let el = createElm(vnode);
    // Inserting an old EL node in front of the next node is equivalent to inserting an old EL node in front of the next node
    // The parent element appendChild is not used directly in order not to break the replacement location
    parentElm.insertBefore(el, oldElm.nextSibling);
    // Delete the old EL node
    parentElm.removeChild(oldVnode);
    returnel; }}// Converting the virtual DOM to the real DOM is to call native methods to generate a DOM tree
function createElm(vnode) {
  let { tag, data, key, children, text } = vnode;
  // Determine whether the virtual DOM is an element node or a text node
  if (typeof tag === "string") {
    // The EL attribute of the virtual DOM points to the real DOM
    vnode.el = document.createElement(tag);
    // Parse virtual DOM properties
    updateProperties(vnode);
    // If there are children, insert them recursively into the parent node
    children.forEach((child) = > {
      return vnode.el.appendChild(createElm(child));
    });
  } else {
    // Text node
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}

// Resolve the vNode data attribute to map to the real DOM
function updateProperties(vnode) {
  let newProps = vnode.data || {};
  let el = vnode.el; // Real node
  for (let key in newProps) {
    // style
    if (key === "style") {
      for (let styleName innewProps.style) { el.style[styleName] = newProps.style[styleName]; }}else if (key === "class") {
      el.className = newProps.class;
    } else {
      // Add an attribute value to this elementel.setAttribute(key, newProps[key]); }}}Copy the code

The _update core method is that patch initial rendering and subsequent updates share the same method. Only the first parameter passed in is different. The overall idea of initial rendering is to call native JS method to create real DOM node according to virtual DOM (vnode) and replace the location of EL option

5. Blending of _render and _update prototype methods

// src/index.js

import { initMixin } from "./init.js";
import { lifecycleMixin } from "./lifecycle";
import { renderMixin } from "./render";
// Vue is a constructor instantiated with the new keyword
function Vue(options) {
  // Start the Vue initialization
  this._init(options);
}
// the _init method is the way to mount the Vue prototype
// This is good for code segmentation
initMixin(Vue);

Infiltrate and _render / /
renderMixin(Vue);
/ / with _update
lifecycleMixin(Vue);
export default Vue;
Copy the code

Finally, the method defined in the stereotype is introduced into the Vue main file entry so that all instances can share the method

6. Mind maps compiled from templates

summary

At this point, Vue’s initial rendering principle has been completed, combined with the first two responsive data and template compilation, so at this time we can render the template written by ourselves to the page. You can look at the mind map and write the core code by yourself

Finally, if you find this article helpful, remember to like it three times. Thank you very much!

Series of links (will be updated later)

  • Handwriting Vue2.0 source code (a) – response data principle
  • Handwriting Vue2.0 source code (2) – template compilation principle
  • Handwriting Vue2.0 source code (three) – initial rendering principle
  • Handwriting Vue2.0 source code (four) – rendering update principle
  • Handwriting Vue2.0 source code (five) – asynchronous update principle
  • Handwriting Vue2.0 source code (six) -diff algorithm principle
  • Handwriting Vue2.0 source code (seven) -Mixin Mixin principle
  • Handwriting Vue2.0 source code (eight) – component principle
  • Handwriting Vue2.0 source code (nine) – listening attribute principle
  • Handwriting Vue2.0 source code (ten) – the principle of computing attributes
  • Handwriting Vue2.0 source code (eleven) – global API principle
  • The most complete Vue interview questions + detailed answers
  • Handwritten vue-Router source code
  • Write vuex source code
  • Handwriting vue3.0 source code

Shark brother front touch fish technology group

Welcome technical exchanges within the fish can be pushed for help – link