This is the 18th day of my participation in Gwen Challenge

One, foreword

In the previous part, the generation of render function was introduced, mainly involving the following two points:

  • Use with to wrap the generated code once
  • Print the wrapped code string as the render Function via new Function

This article, according to the render function, generate virtual node vnode


Mount mountComponent

Previously on 1

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

  if(! opts.render) {let template = opts.template;
    if(! template) template = el.outerHTML;let render = compileToFunction(template);
    opts.render = render;
  }

  // Apply the current render to the el element:
  // 1, generate a virtual node according to the render function
  // 2. Generate a real node based on the virtual node and real data
}
Copy the code

Render function is generated and put into opts.render

Next, use the render function to render:

  • Generate virtual nodes according to the Render function
  • Real nodes are generated by adding real data to virtual nodes

2, mountComponent

  1. After the render function is executed, a virtual node vNode is eventually generated
  2. Vnode + Real data => Real node

So, the next step is to render the component and complete the hang

The mountComponent method: Mount the component to vm.$el

Create a SRC/lifecycle. Js

// src/lifecycle.js#mountComponent

export function mountComponent(vm) {
  render();// Call the render method
}
Copy the code

Import mountComponent and call:

// src/init.js

import { mountComponent } from "./lifecycle"; / / introduce mountComponent

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

  if(! opts.render) {let template = opts.template;
    if(! template) template = el.outerHTML;let render = compileToFunction(template);
    opts.render = render;
  }

  // Apply the current render to the el element
  mountComponent(vm);
}
Copy the code

3, encapsulate vm._render

The mountComponent method mounts components

And render is only one of them, there are other work to deal with;

Continue to consider the reusability of render methods; The render method needs to be wrapped separately

Create a SRC/render. Js

// src/render.js#renderMixin

export function renderMixin(Vue) {
  // Extend the method on vue
  Vue.prototype._render = function () {
    // todo...}}Copy the code

SRC /index.js entry, call renderMixin to mix render methods:

// src/index.js

import { initMixin } from "./init";
import { renderMixin } from "./render";

function Vue(options){
    this._init(options);
}

initMixin(Vue)
renderMixin(Vue)   // Mix the render methods

export default Vue;
Copy the code

SRC /lifecycle. Js mountComponent calls the render function in a different way:

export function mountComponent(vm) {
  // render();
  vm._render();
}
Copy the code

When vm.render is called, three methods _c, _v, and _S will be called internally

So these three methods are all related to render and can be encapsulated together;

So, the vm._render method needs to do the following:

  • Calling the render function
  • Provide _c, _v, and _s methods
// src/render.js#renderMixin

export function renderMixin(Vue) {
  Vue.prototype._c = function () {  // createElement Creates a node of element type
    console.log(arguments)
  }
  Vue.prototype._v = function () {  // Create a virtual node for text
    console.log(arguments)
  }
  Vue.prototype._s = function () {  // JSON.stringify
    console.log(arguments)
  }
  Vue.prototype._render = function () {
    const vm = this;  XXX => vm._data.xxx
    let { render } = vm.$options;
    let vnode = render.call(vm);  // The _c,_v,_s methods are called internally, and the virtual node is returned after execution
    console.log(vnode)
    return vnode; // Return to the virtual node}}Copy the code

4. Code debugging

The demo sample:

<body>
  <div id="app">aaa {{name}} bbb {{age}} ccc</div>
  <script src="./vue.js"></script>
  <script>
    let vm = new Vue({
      el: '#app'.data() {
        return { name:  "Brave" , age : 123}}});</script>
</body>
Copy the code

Set breakpoints and debug:

Here, the mountComponent method takes the parameter VM and contains the Render function and all the data

To continue, call the vm.render method:

In the vm.render method, the render method is called:

When the render method is called, it executes:

Since the function will be executed from inside out, that is, the execution order is _s(name), _S (age), _v(), _c();

Execute _S (name) : first fetch name from _data when entering _S, pass in the value of nameCopy the code

Value of agent

The data was hijacked

Enter _s (name) :

Similarly, _s(age) :(omitted)

So we're going to take the age value from _data and when we go into _s, we're going to pass in the age valueCopy the code

Continuing, enter _v:

Since the current _s returns no value, the string concatenation result contains two undefined values;

Continuing, enter _c:

Parameters include: tag name, attribute, and child

5, implement _s

_s method: Converts an object to a string and returns it

// _s equals json.stringify
Vue.prototype._s = function (val) {  
  if(isObject(val)){  // is an object, converted to a string
    return JSON.stringify(val)
  } else {  					// It is not an object
    return val
  }
}
Copy the code

Debug:

Set a breakpoint in _v to see the string returned after _s processing

Call two _s first and pass the concatenation result to _v:

Print render function:

Vue.prototype._render = function () {
  const vm = this;
  let { render } = vm.$options;
  console.log(render.toString());	// Prints the result of the render function
  let vnode = render.call(vm);
  return vnode;
}
Copy the code

Look at the render function:

  • The two _s execute, passing the concatenated string to _v,
  • _v receives the text text and passes the result to _C when the text is created

Therefore, you need to create the virtual nodes of the text first, and then create the virtual nodes of the elements

Create a directory: SRC /vdom

There are two methods: create an element virtual node and create a text virtual node

Note: The _v and _c methods are both related to virtual nodes, so put them in the virtual DOM package

// src/vdom/index.js

export function createElement() { // Returns the element virtual node
  
}
export function createText() {  // Returns a text virtual node
  
}
Copy the code

RenderMixin is only responsible for rendering logic. How to create a VDOM needs to be considered by the VDOM, so the two parts of the logic need to be separated

RenderMixin only returns virtual nodes, but doesn’t care how they were created

6. Implement _v and _c

_v method: Create and return a virtual node with text

Vue.prototype._v = function (text) {  // Create a virtual node for text
  const vm = this;
  return createText(vm, text);// Vm function: Determine the instance to which the virtual node belongs
}
Copy the code

Vm: Determines the instance to which a virtual node belongs

How to create a text virtual node and leave it to createText

CreateText Generates a vNode: a vnode is an object that describes a node

export function createElement(vm, tag, data={}, ... children) { // Return to the virtual node
  // _c(' tag ', {attribute},... Son)
  return {
    vm,       // Whose virtual node is it
    tag,      / / label
    children, / / son
    data,     / / digital
    / /... / / other}}export function createText(vm, text) {  // Return to the virtual node
  return {
    vm,
    tag: undefined.// The text has no tag
    children,
    data,
    // ...}}Copy the code

Extract vNode methods: return objects via functions

// Return a vNode object via a function
// The following elements need to be diff
function vnode(vm, tag, data, children, key, text) {
  return {
    vm,
    tag,
    data,
    children,
    key,
    text
  }
}
Copy the code

Refactoring code:

// Argument: _c(' tag ', {attribute},... Son)
export function createElement(vm, tag, data={}, ... children) {
  // Return the virtual node of the element (the element has no text)
  return vnode(vm, tag, data, children, data.key, undefined);
}
export function createText(vm, text) {
  // Returns the virtual node of text (text without label, data, son, key)
  return vnode(vm, undefined.undefined.undefined.undefined, text);
}

// Return a vNode object via a function
// The following elements need to be diff
function vnode(vm, tag, data, children, key, text) {
  return {
    vm,       // whose instance
    tag,      / / label
    data,     / / data
    children, / / son
    key,      / / logo
    text      / / text}}Copy the code

Testing:

This completes the generation of the virtual node vNode according to the Render function

Next, the virtual nodes are rendered as real nodes

When updating, call Render to generate virtual nodes and complete updating of real nodes


Three, the end

This article, according to the render function, generate Vnode, mainly involves the following points:

  • Encapsulating vm._render returns the virtual node
  • Implementation of _S, _v, _c

Next, render real nodes from vNode virtual nodes