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
- After the render function is executed, a virtual node vNode is eventually generated
- 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