1.创建虚拟节点的类,这里使用es6语法糖。
export default class VNode {
constructor(
// 节点类型和标签类型的区别是什么?
tag, // 标签类型,DIV,SPAN,INPUT,#text
ele, // 对应的真实节点
children, // 当前节点下的子节点
text, // 当前虚拟节点的文本
data, // VNodeData,暂时保留,暂无意义
parent, // 父级节点
nodeType, // 节点类型
) {
this.tag = tag;
this.ele = ele;
this.children = children;
this.text = text;
this.data = data;
this.parent = parent;
this.nodeType = nodeType;
this.env = {}; // 当前节点的环境变量,这个是自己设定的
this.instructions = null; // 存放指令的
this.template = []; // 当前节点涉及到的模板
}
}
2.构建虚拟DOM树并且挂载到Vue上,使用深度先搜索构建虚拟DOM树。
import VNode from "../vnode/vnode.js";
export function initMount(Vue) {
Vue.prototype.$mount = function (el) {
let vm = this;
let rootDom = document.getElementById(el);
mount(vm, rootDom);
}
}
export function mount(vm, ele) {
// 进行挂载
vm._vnode = constructVNode(vm, ele, null);
// 进行预备渲染(简历渲染索引,通过模板找vnode,通过vnode找模板)
}
function constructVNode(vm, ele, parent) {
let vnode = null;
let children = [];
let text = getNodeText(ele); // 不确定是否有文本,只有文本节点才有文本
let data = null;
let nodeType = ele.nodeType;
let tag = ele.nodeName;
vnode = new VNode(tag, ele, text, data, parent, nodeType);
// 获取子节点
let childs = vnode.ele.childNodes;
// 深度优先算法DFS
for (let i = 0; i < childs.length; i++) {
let childNode = constructVNode(vm, childs[i], vnode);
if (childNode instanceof VNode) { // 返回单一节点的时候
vnode.children.push(childNode);
} else { // 返回节点数组
vnode.children = vnode.children.concat(childNode);
}
}
return vnode;
}
function getNodeText(ele) {
if (ele.nodeType === 3) { // 标签类型为#TEXT时
return ele.nodeValue;
} else {
return '';
}
}