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() //解析屬性
相關文章
相關標籤/搜索