??(三)complier模板编译-vue源码动手写系列

2020/2/28 11:15:37

本文主要是介绍??(三)complier模板编译-vue源码动手写系列,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

20200221173344-2020-2-21-17-33-45.png

在上一篇文章,我们介绍了虚拟Dom的内容,创建vnode需要调用createElement去生成,但是在日常的开发当中,我们很少去写一堆复杂的createElement代码去描述页面,在Vue中通常是写一个template模板,而最终我们会把这个模板编译成一个渲染函数,用这个渲染函数去生成vnode,这个过程就是模板编译的过程,通常由webpack vue-loader完成,当然你也可以引入Vue的runtime + compiler版本,在运行时才去编译,但是一般不推荐这样做,除非你确实有项目运行期间编译模板的需求,不然都是选择一个不带编译器的vue版本去减少代码体积。

模板编译的过程到底干了什么到现在你可能还不是很清楚,这里简单放一段伪代码去说明一下总体的编译流程吧,现在我们有一个这样的模板:

<div>
  <p>{{name}}</p>
</div>
复制代码

1️⃣首先第一步,编译器会去进行一些词法分析,逐段字符串去解析,最终生成一个抽象语法树(AST)

{
  tag: "div"
  type: 1,
  staticRoot: false,
  static: false,
  plain: true,
  parent: undefined,
  attrsList: [],
  attrsMap: {},
  children: [
      {
      tag: "p"
      type: 1,
      staticRoot: false,
      static: false,
      plain: true,
      parent: {tag: "div", ...},
      attrsList: [],
      attrsMap: {},
      children: [{
          type: 2,
          text: "{{name}}",
          static: false,
          expression: "_s(name)"
      }]
    }
  ]
}

复制代码

2️⃣第二步主要是一些优化工作,它会去编译我们生成的AST,然后标记一下静态节点和静态根节点。

static: true,
staticRoot: false
复制代码

3️⃣第三步是代码字符串生成,它同样会去遍历这个AST,然后根据节点的类型去调用不同的vnode生成方法如createElementcreateTextNode去拼接渲染函数,下面最终生成的结果,其中_c_vcreateElement,createTextNode的简写,_s暂时可以理解为增加版的toString方法。

`with(this)(return _c('div', [_c('p', [_v(_s(name))])]))`
复制代码

所以编译的流程可以划分为三大模块,对应vue源码中的parse,optimize,generate三个方法:

  • 解析器
  • 优化器
  • 代码生成器

到这里我们只需要对它们有一个总体认识即可,下面的篇幅将会对每个模块的原理详细的分析,文章通读过后再回来看看总体的流程就会非常清晰了。

解析器(parse)

1️⃣HTML 解析器

解析器的作用就是把模板解析成抽象语法树AST,入口是一个parseHTML方法,方法里面会有一个while循环,这个循环会通过一些正则去截取模板中的内容,根据匹配到内容的不同再去调用钩子函数去生成不同类型的AST树的节点,包括start,end,chars,comment, 这几个钩子分别处理开始标签结束标签文本注释,过程中会不断截取一小段字符串,待整个模板html都被清空后模板才解析完成,看下面的伪代码:

function parseHTML(html, options) {
  while(html) {
    if (匹配到开始标签) {
      options.start(tag, attrs, unary)
      continue
    }
    if (匹配到结束标签) {
      options.end()
      continue
    }
    if (匹配到文本) {
      options.chars(text)
      continue
    }
    if (匹配到注释) {
      options.start(text)
      continue
    }
  }
}
复制代码
parseHTML(template, {
  start(tag, attrs, unary) {
    // 每当解析到标签的开始位置时,触发该函数
  },
  end() {
    // 每当解析到标签的结束位置时,触发该函数
  },
  chars(text) {
    // 每当解析到文本时,触发该函数
  },
  comment(text) {
    // 每当解析到注释时,触发该函数
  }
})
复制代码

为了方便理解,这里再手动模拟一下HTML解析器的运行过程,首先我们有一个模板内容:

<div>
  <p>{{name}}</p>
</div>
复制代码

最初的模板:

`<div>
	<p>{{name}}</p>
</div>`
复制代码

第一次循环时,截取一小段字符串<div>,并且触发钩子函数start,截取后模板剩下:

`
	<p>{{name}}</p>
</div>`
复制代码

第二次循环,截取了一段换行内容:


这篇关于??(三)complier模板编译-vue源码动手写系列的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程