vue3-compile编译原理(一)-- 脱胎换骨

背景引入

   vue3的学习已经有一段时间了,本着奔浪不奔就可能变成后浪的态度,学习梳理了下vue3的编译,基于vue3的整个编译源码的学习,准备把编译源码按照编译过程分三部分记录学习下来,同各位奔浪一块儿奔跑.....html

1、编译入口及流程

一、目录结构前端

上面是整个代码的目录结构以及相关文件的主要做用,与模板编译相关的核心代码是compiler-core 中的整个目录,vue3源码链接奉上:github.com/vuejs/vue-n…vue

二、编译入口
/vue/src/index.ts
git

在完整版的 index.js 中,调用了 registerRuntimeCompilercompile 注入编译方法,即为模板编译的入口,固然其中还有一些其余的判断,有兴趣的能够进入继续查看源码github

2、模板编译流程

vue2编译web

vue3编译babel

整个编译流程其实主要分为三部分:整个编译过程跟vue2的编译的过程有markdown

template模板字符串 ----》编译为最基础的AST ----> transform基础AST增长patchflag等熟悉 ----》 generate基于AST生成render函数数据结构

二、vue2 vs vue3

<div name="test">
    <!-- 这是注释 -->
    <p>{{ test }}</p>
     一个文本节点
    <div>good job</div>
</div>
复制代码

 vue2编译后结果:函数

function render() {
    with(this) {
        return _c('div', {
            attrs: {
                "name": "test"
            }
        }, [_c('p', [_v(_s(test))]), _v("\n     一个文本节点\n    "), _c('div', [_v(
            "good job")])])
    }
}
复制代码

vue3编译后结果:

import {
    createCommentVNode as _createCommentVNode,
    toDisplayString as _toDisplayString,
    createVNode as _createVNode,
    createTextVNode as _createTextVNode,
    openBlock as _openBlock,
    createBlock as _createBlock
} from "vue"

const _hoisted_1 = {
    name: "test"
}
const _hoisted_2 = /*#__PURE__*/ _createTextVNode(" 一个文本节点 ")
const _hoisted_3 = /*#__PURE__*/ _createVNode("div", null, "good job", -1 /* HOISTED */ )

export function render(_ctx, _cache, $props, $setup, $data, $options) {
    return (_openBlock(), _createBlock("div", _hoisted_1, [
    _createCommentVNode(" 这是注释 "),
    _createVNode("p", null, _toDisplayString(_ctx.test), 1 /* TEXT */ ),
    _hoisted_2,
    _hoisted_3
  ]))
}
复制代码

经过对比发现最终都返回render函数,可是render函数的结构和样式上有了很大的差异,同时有了特殊的含义注释,在接下来的过程当中咱们会一点点接口神秘面纱....

三、源码初现

源码的第一部分 baseParse主要的做用就是将 template模板字符串编译为基础AST树,可是咱们能够思考下他说怎么转换生成的呢?

(1)、类比前端模板引擎

(2)、类比babel编译过程

(3)、vue3解析

之因此用前端模板和bable的编译过程来类别vue3的编译,其实他们是有不少共性的,

好比:前端模板编译

const data = {
    'title':'模板标题',
    'content':'模板内容'
}
const temp = '
    <div>{{d.title}}</div>
   <div>{{d.content}}</div>
复制代码

这种 data + temp---》html的 很容易想到基于正则的进行匹配替换。

好比:bable编译

function test(){
    const a = b
}
复制代码

这个函数字符串如何进行词法解析 、预发解析呢?其实也是基于正则不断的对字符串进行选择、截取、 匹配.....直到最有一个字符完成,function等关键字等同于div等标签的关键字,函数的"{"(开始左括号) 和 ”}“(结束右括号) 等同于div标签的"

"(开始标签) 和”
“(结束标签)。

vue3中将template--->baseParse解析为AST的过程也是同样的:

四、父子关系

      单纯的正则匹配和解析只是获取了标签自己的属性、特性等数据,可是template自己的标签是有父子层级关系的,标签与标签直接的关系怎么来维护呢?了解过webkit渲染原理的应该很容易想到,栈:一个先进后出的数据结构

//解析过程当中:
<div name="test">
    <!-- 这是注释 -->
    <p>{{ test }}</p>
     一个文本节点
    <div>good job</div>
</div>
复制代码

总结:

vue3的第一部分实际上是不断将模板字符串经过正则去解析、截取、匹配特殊数据和属性......过程,同时在解析中用栈的形式维护保存正在解析的标签

  • parseChildren() // 主入口
  • parseInterpolation() //解析插值表达式
  • parseComment() //解析注释
  • parseBogusComment() //解析文档声明
  • parseTag() //解析标签
  • parseText() //解析普通文本
  • parseAttribute() //解析属性
相关文章
相关标签/搜索