Vue.js 源码 (12)—— 代码生成器

748 阅读2分钟

这是我参与更文挑战的第12天,活动详情查看: 更文挑战

前言

代码生成器是模板编译的最后一步,它的作用是将 AST 转换成渲染函数中的内容,这个内容可以称为代码字符串

<div id="el">Hello {{name}}</div>

上面的模板,最终生成的代码字符串类似这样:

`with(this){return _c("div", {attrs: {"id": "el"}}, [_v("Hello " _s(name))])}`

代码字符串中的 _c 其实是 createElement 的别名。createElement 是虚拟DOM中所提供的方法,它的作用是创建虚拟节点,有三个参数,分别是:

  • 标签名
  • 一个包含模板相关属性的数据对象
  • 子节点列表

调用 createElement 方法,我们可以得到一个 VNode

通过AST生成代码字符串

生成代码字符串是一个递归的过程,从顶向下依次处理每一个 AST 节点。

代码生成器的原理

节点有不同的类型,例如元素节点文本节点注释节点。不同类型节点的生成方式是不一样的。

元素节点

生成元素节点,其实就是生成一个 _c的函数调用字符串

function genElement(el, state){
    const data = el.plain ? undefined : genData(el, state);
    
    const children = genChildren(el, state);
    
    code = `_c('${el.tag}'${
        data ? `, ${data}` : ''
    }${
        children ? `,${children}`:''
    })`
    return code;
}

文本节点

生成文本节点很简单,我们只需要把文本放在 _v这个函数的参数中即可

function genText(text){
    return `_v(${text.type === 2 ? text.expression : JSON.stringify(text.text)})`
}

为什么text需要使用JSON.stringify方法?

因为JSON.stringify可以给文本包装一层字符串,例如:

JSON.stringify('Hello') // "'Hello'"

注释节点

function genComment(comment){
    return `_e(${JSON.stringify(comment.text)})`
}

总结

本文,我们学习了代码生成器的作用及其内部原理,了解了代码生成器其实就是字符串拼接的过程。通过递归 AST 来生成字符串,最先生成根节点,然后在子节点字符串生成后,将其拼接在根节点的参数中,子节点的子节点拼接在子节点的参数中,这样一层一层地拼接,直到最后拼接成完整的字符串。