资讯专栏INFORMATION COLUMN

Vue源码解析:AST语法树转render函数

trilever / 557人阅读

摘要:源码解析这边解析的是从树转换成函数部分的源码,由于第一次提交的源码这部分不全,故做了部分更新,代码全在文件夹中。入口整个语法树转函数的起点是文件中的函数明显看到,函数传入参数为语法树,内部调用函数开始解析根节点容器节点。

通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写:

模版字符串转AST语法树

AST语法树转render函数

Vue双向绑定原理

Vue虚拟dom比较原理

其中包含自己的理解和源码的分析,尽量通俗易懂!由于是2.0的最早提交,所以和最新版本有很多差异、bug,后续将陆续补充,敬请谅解!包含中文注释的Vue源码已上传...

开始

今天要说的代码全在codegen文件夹中,在说实现原理前,还是先看个简单的例子!

{{msg}}

上述类名为container的元素节点包含5个子节点(其中3个是换行文本节点),转化成的AST语法树:

AST语法树转的render函数长这样:

function _render() {
  with (this) { 
    return __h__(
      "div", 
      {staticClass: "container"}, 
      [
        " ",
        __h__("span", {}, [String((msg))]),
        " ",
        __h__("button", {class: {active: isActive},on:{"click":handle}}, ["change msg"]),
        " "
      ]
    )
  };
}

可以的看出,render函数做的事情很简单,就是把语法树每个节点的指令进行解析。

看下render函数,它是由with函数包裹(为了改变作用域),要用的时候直接_render.call(vm);另外就是__h__函数,这个后面会说到,这个函数用于元素节点的解析,接收3个参数:元素节点标签名,节点数据,子节点数据。这个函数最后返回的就是虚拟dom了,不过今天先不深究,先说如何生成这样的render函数,主要是v-ifv-forv-bindv-on等指令的解析。

源码解析

这边解析的是从AST树转换成render函数部分的源码,由于vue2.0第一次提交的源码这部分不全,故做了部分更新,代码全在codegen文件夹中。

入口

整个AST语法树转render函数的起点是index.js文件中的generate()函数:

export function generate (ast) {
  const code = genElement(ast);
  return new Function (`with (this) { return ${code}}`);
}

明显看到,generate()函数传入参数为AST语法树,内部调用genElement()函数开始解析根节点(容器节点)。genElement()函数用于解析元素节点,它接收两个参数:AST对象节点标识(v-for的key),最后返回形如__h__("div", {}, [])的字符串,看一下内部逻辑:

function genElement (el, key) {
  let exp;
  if (exp = getAndRemoveAttr(el, "v-for")) { // 解析v-for指令
    return genFor(el, exp);
  } else if (exp = getAndRemoveAttr(el, "v-if")) { // 解析v-if指令
    return genIf(el, exp, key);
  } else if (el.tag === "template") { // 解析子组件
    return genChildren(el);
  } else {
    return `__h__("${el.tag}", ${genData(el, key) }, ${genChildren(el)})`;
  }
}

genElement()函数内部依次调用getAndRemoveAttr()函数判断了v-forv-if标签是否存在,若存在则删除并返回表达式;随后判断节点名为template就直接进入子节点解析;以上条件都不符合就返回__h__函数字符串,该字符串将使用到属性解析和子节点解析。

function getAndRemoveAttr (el, attr) {
  let val;
  // 如果属性存在,则从AST对象的attrs和attrsMap移除
  if (val = el.attrsMap[attr]) {
    el.attrsMap[attr] = null;
    for (let i = 0, l = el.attrs.length; i < l; i++) {
      if (el.attrs[i].name === attr) {
        el.attrs.splice(i, 1);
        break;
      }
    }
  }
  return val;
}
v-for 和 v-if 指令解析

让我们先看看v-for的编译:

function genFor (el, exp) {
  const inMatch = exp.match(/([a-zA-Z_][w]*)s+(?:in|of)s+(.*)/);
  if (!inMatch) {
    throw new Error("Invalid v-for expression: "+ exp);
  }
  const alias = inMatch[1].trim();
  exp = inMatch[2].trim();
  let key = getAndRemoveAttr(el, "track-by"); // 后面用 :key 代替了 track-by

  if (!key) {
    key ="undefined";
  } else if (key !== "$index") {
    key = alias + "["" + key + ""]";
  }

  return `(${exp}) && (${exp}).map(function (${alias}, $index) {return ${genElement(el, key)}})`;
}

该函数先进行正则匹配,如"item in items",将解析出别名(item)和表达式(items),再去看看当前节点是否含:key,如果有那就作为genElement()函数的参数解析子节点。举个

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/53484.html

相关文章

  • Vue源码解析AST语法树转render函数

    摘要:源码解析这边解析的是从树转换成函数部分的源码,由于第一次提交的源码这部分不全,故做了部分更新,代码全在文件夹中。入口整个语法树转函数的起点是文件中的函数明显看到,函数传入参数为语法树,内部调用函数开始解析根节点容器节点。 通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转r...

    Karuru 评论0 收藏0
  • Vue源码解析:模版字符串转AST语法

    摘要:通过对源码阅读,想写一写自己的理解,能力有限故从尤大佬第一次提交开始读,准备陆续写模版字符串转语法树语法树转函数双向绑定原理虚拟比较原理其中包含自己的理解和源码的分析,尽量通俗易懂由于是的最早提交,所以和最新版本有很多差异,后续将陆续补充, 通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法...

    chengjianhua 评论0 收藏0
  • Vue源码解析:模版字符串转AST语法

    摘要:通过对源码阅读,想写一写自己的理解,能力有限故从尤大佬第一次提交开始读,准备陆续写模版字符串转语法树语法树转函数双向绑定原理虚拟比较原理其中包含自己的理解和源码的分析,尽量通俗易懂由于是的最早提交,所以和最新版本有很多差异,后续将陆续补充, 通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法...

    王伟廷 评论0 收藏0

发表评论

0条评论

trilever

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<