上下两篇为了“语义的连贯性”,其实说的东西并不多,这一节来补充一下一些同样重要的知识点:
前文链接:
专栏链接(免费的)
Vue内部原理及运行机制
如何“优雅地”实现一个VNode
VNode归根结底就是一个JavaScript对象,只需这个类的一些属性可以正确直观地描述清楚当前节点的信息即可:
class VNode{
constructor(tag,data,children,text,elm){
//当前节点的标签名
this.tag=tag;
//当前节点的一些数据信息,比如:props、attrs等
this.data=data;
//当前节点的子节点 —— 是一个数组
this.children=children;
//当前节点的文本
this.text=text;
//当前虚拟节点对应的真实dom节点
this.elm=elm;
}
}
我们可以将其进一步封装一下,并实现一些产生常用的VNode的方法,如:
创建一个空节点:
function createEmptyNode(){
const node=new VNode();
node.text='';
return node;
}
创建一个文本节点:
function createTextNode(val){
return new VNode(undefined,undefined,undefined,String(val));
}
克隆一个VNode节点:
function createCloneNode(node){
const CloneNode=new VNode(
node.tag,
node.data,
node.children,
node.text,
node.elm
);
return CloneNode;
}
前面说了,template模板通过compile编译。
compile编译分为三阶段 —— parse(正则解析)、optimize(优化) & generate(转化:AST->render function),最终得到render function
主要来看下optimize
后面会说到patch:patch实际上是将VNode节点进行一层一层的比对,然后将“差异”更新到“视图”上。而一些静态节点不随数据变化而变化,我们就可以想法跳过这些静态节点的比对:在静态的节点上做些“标记”
而经过optimize这层的处理,每个节点都会被加上static属性(标记属性:true/false)
我们不得不实现一下optimize函数:
isStatic
我们应当知道,在节点type为2的时候(表达式节点)为非静态属性,而在节点type为3的时候(文本节点)为静态属性节点:(type为1时表示是节点)
function isStatic(node){
if(node.type===2){
return false;
}
if(node.type===3){
return true;
}
return (!node.if && !node.for);
}
markStatic
标记函数 —— 它会遍历所有节点(包括子节点)
function markStatic(node){
node.static=isStatic(node);
if(node.type===1){
for(let i=0,l=node.children.length;i<l;i++){
const child=node.children[i];
markStatic(child);
if(!child.static){
node.static=false;
}
}
}
}
markStaticRoots
用来标记staticRoot(静态“根”)—— 如果当前节点是静态节点,同时满足该节点并不是只有一个文本节点左右子节点时,标记staticRoot为true
function markStaticRoots(node){
if(node.type===1){
if(node.static && node.children.length && !(node.children.length===1 && node.children[0].type===3)){
node.staticRoot=true;
return;
}else{
node.staticRoot=false;
}
}
}
最后,我们将其整合为optimize函数:
function optimize(rootAst){
markStatic(rootAst);
markStaticRoots(rootAst);
}
数据状态更新时的差异diff与patch
跨平台
Virtual DOM终究只是一些JS对象罢了,它是如何实现调用不同平台API?
——依赖一层适配层,将不同平台的API封装在内,以同样的接口对外提供。
const nodes={
setTextContent(text){
if(platform==='weex'){
node.parentNode.setAttr('value',text);
}else if(platform==='web'){
node.textContent=text;
}
},
//...
}
而patch就更好说了,其核心diff —— 强大的搜索算法。
diff算法是通过同层的树节点进行比较而非对树进行逐层搜索的方式,故其时间复杂度仅为O(n)。
这些东西实在无需多言,有需要的可以取看vue源码中这部分内容。