【Vue源码】- 编译器(compile)原理(2)

167 阅读2分钟

目录

  1. 模板编译原理(compile)
  2. Parse解析器
  3. Transform转换处理
  4. Generate生成渲染器
  5. 总结
  • template code -> ast -> 优化处理后的 ast -> 渲染函数
  • 编译器就是 将template code 转化成 渲染函数
  • 输入:视图模板, 输出:渲染函数

一、模板编译原理(compile)

// 编译函数
// 输入值为视图模板
const compile = (template) => {
  //渲染函数
  return (observed, dom) => {
  	// 渲染过程
	}
}

简单的说就是

  • 输入:视图模板
  • 输出:渲染函数

细分起来还可以分为三个个小步骤

image.png

  • Parse 模板字符串 -> AST(Abstract Syntax Treee)抽象语法树
  • Transform 转换标记 譬如 v-bind v-if v-for的转换
  • Generate AST -> 渲染函数
//  模板字符串 -> AST(Abstract Syntax Treee)抽象语法树
let ast = parse(template)
// 转换处理 譬如 v-bind v-if v-for的转换, (与打标静态节点,AST树的优化处理,提升性能)
ast = transfer(ast)
// AST -> 渲染函数
return generator(ast)

我们可以通过在线版的VueTemplateExplorer感受一下

vue-next-template-explorer.netlify.com/

image.png

编译函数解析

二、Parse解析器

解析器的工作原理其实就是一连串的正则匹配。

比如:

标签属性的匹配

  • class="title"
  • class='title'
  • class=title
const attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)=("([^"]*)"|'([^']*)'|([^\s"'=<>`]+)/

"class=abc".match(attr);
// output
(6) ["class=abc", "class", "abc", undefined, undefined, "abc", index: 0, input: "class=abc", groups: undefined]

"class='abc'".match(attr);
// output
(6) ["class='abc'", "class", "'abc'", undefined, "abc", undefined, index: 0, input: "class='abc'", groups: undefined]

这个等实现的时候再仔细讲。可以参考一下文章。

AST解析器实战

那对于我们的项目来讲就可以写成这个样子

// <input v-model="message"/>
// <button @click='click'>{{message}}</button>
// 转换后的AST语法树
const parse = template => ({
    children: [{
            tag: 'input',
            props: {
                name: 'v-model',
                exp: {
                    content: 'message'
                },
            },
        },
        {
            tag: 'button',
            props: {
                name: '@click',
                exp: {
                    content: 'message'
                },
            },
            content:'{{message}}'
        }
    ],
})

三、 Transform转换处理

前一段知识做的是抽象语法树,对于Vue3模板的特别转换就是在这里进行。

比如:vFor、vOn

在Vue三种也会细致的分为两个层级进行处理

  • compile-core 核心编译逻辑

    • AST-Parser

    • 基础类型解析 v-for 、v-on

image.png

compile-dom 针对浏览器的编译逻辑

  • v-html

  • v-model

  • v-clock

image.png

const transfer = ast => ({
    children: [{
            tag: 'input',
            props: {
                name: 'model',
                exp: {
                    content: 'message'
                },
            },
        },
        {
            tag: 'button',
            props: {
                name: 'click',
                exp: {
                    content: 'message'
                },
            },
            children: [{
                content: {
                    content: 'message'
                },
            }]
        }
    ],
})

四、 Generate生成渲染器

生成器其实就是根据转换后的AST语法树生成渲染函数。当然针对相同的语法树你可以渲染成不同结果。比如button你希望渲染成 button还是一个svg的方块就看你的喜欢了。这个就叫做自定义渲染器。这里我们先简单写一个固定的Dom的渲染器占位。到后面实现的时候我在展开处理。

const generator = ast => (observed, dom) => {
    // 重新渲染
    let input = dom.querySelector('input')
    if (!input) {
        input = document.createElement('input')
        input.setAttribute('value', observed.message)
        input.addEventListener('keyup', function () {
            observed.message = this.value
        })
        dom.appendChild(input)
    }
    let button = dom.querySelector('button')
    if (!button) {
        console.log('create button')
        button = document.createElement('button')
        button.addEventListener('click', () => {
            return config.methods.click.apply(observed)
        })
        dom.appendChild(button)
    }
    button.innerText = observed.message
}

参考

总结

  • template code -> ast -> 优化处理后的 ast -> 渲染函数
  • 编译器就是 将template code 转化成 渲染函数
  • 输入:视图模板, 输出:渲染函数

image.png