Vue源码之AST抽象语法树

Vue源码之AST抽象语法树

AST抽象语法树全称Abstract Sybtax Tree,简称就是AST,其作用就是Vue在解析模板时,会把模板编译成抽象语法树,在进行diff最小量更新算法等

AST抽象语法树是Vue渲染引擎的第一道工序

他的结果类似于原生js的对象结构

AST抽象语法树底层采用了大量的正则表达式以及栈的结合使用

接下来手撸一个AST抽象语法树

1. 配置webpack环境,实现模块化编程

环境为

"webpack": "^4.46.0",
"webpack-cli": "^3.3.12"
"webpack-dev-server": "^3.11.3"

webpack.config.js文件配置

const path = require("path")

module.exports = {
    mode: "development",
    entry: './src/index.js',
    output: {
        filename: "bundle.js"
    },
    devServer: {
        contentBase: path.join(__dirname, "www"),
        compress: false,
        port: 8080,
        publicPath: "/xuni/"
    }
}

2. index.js入口文件

import parse from "./parse";

let templateString = `<div>
        <h3 class="demo" id="cmd">Hello</h3>
        <ul>
            <li id="bid">A</li>
            <li>B</li>
            <li>C</li>
        </ul>
    </div>`

const AST = parse(templateString)
console.log(AST)

准备一个模板字符串,我们把他转化为AST抽象语法树

该文件最核心的函数就是parse函数,在Vue的源码中转化为AST抽象语法树函数的名字也是parse

3. parse函数

import parseAttrsString from "./parseAttrsString";


export default function parse(templateString) {
    //Pointer
    let index = 0
    //Remaining string
    let restString = ""
    //Start tag
    let startRegExp = /^<([a-z]+[1-6]?)(\s[^<]+)?>/
    //The end tag
    let endRegExp = /^<\/([a-z]+[1-6]?)>/
    //Define two stacks one to store labels and one to store content
    let stackTag = []
    let stackLetter = [{children: []}]
    //Grab the text before the end tag
    let wordRegExp = /^([^<]+)<\/[a-z]+[1-6]?>/
    while (index < templateString.length - 1) {
        restString = templateString.substring(index)
        if (startRegExp.test(restString)) {
            let tag = restString.match(startRegExp)[1]
            let attrsString = restString.match(startRegExp)[2]
            //Push the tag and the object onto the stack, respectively
            stackTag.push(tag)
            stackLetter.push({'tag': tag, 'children': [], "attrs":
                              parseAttrsString(attrsString)})
            const attrsStringLength = attrsString !== undefined ? attrsString.length : 0
            index += tag.length + 2 + attrsStringLength
        } else if (endRegExp.test(restString)) {
            let tag = restString.match(endRegExp)[1]
            let pop_tag = stackTag.pop()
            let pop_letter = stackLetter.pop()
            if (tag === pop_tag) {
                if (stackLetter.length > 0) {
                    stackLetter[stackLetter.length - 1].children.push(pop_letter)
                }
            } else {
                throw new Error(`${pop_tag} tag is not closed`)
            }
            index += tag.length + 3
        } else if (wordRegExp.test(restString)) {
            let word = restString.match(wordRegExp)[1]
            if (!/^\s+$/.test(word)) {
                //Text is detected on top of stack
                stackLetter[stackLetter.length - 1].children.push({"text": word, 'type':
                                                                   3})
            }
            index += word.length
        } else {
            index++
        }
    }
    return stackLetter[0].children[0]
}

实现原理

首先准备2个栈,一个存放标签,一个存放内容,再准备一个指针,使用指针迭代一遍模板字符串

在迭代的同时一直进行正则匹配,如果遇到标签开始标记进行压栈,且将内容压入另一个栈中

当遇到结束标记时进行双弹栈,并将内容栈的栈顶元素压入栈顶的第二个元素中

就形成了树状绑定

4. parseAttrsString

为了让抽象语法树识别选择器,就有了parseAttrsString函数

//To deal with attrsString
export default function parseAttrsString(attrsString) {
    if (attrsString === undefined) return []
    //Whether the current is in quotation marks
    let isYinHao = false
    //breakpoint
    let point = 0
    //result array
    let result = []
    for (let i = 0; i < attrsString.length; i++) {
        let char = attrsString[i]
        if (char === '"') {
            isYinHao = !isYinHao
        } else if (char === " " && !isYinHao) {
            if (!/^\s*?$/.test(attrsString.substring(point, i))) {
                result.push(attrsString.substring(point, i).trim())
                point = i
            }
        }
    }
    result.push(attrsString.substring(point))
    result = result.map(item => {
        const o = item.match(/^(.+)=(.+)$/)
        return {
            name: o[1],
            value: o[2].substring(1, o[2].length - 1)
        }
    })
    return result
}

实现原理

准备一个结果数组,利用正则表达式匹配并且分开空格,最后利用映射,形成attrs对象,并压入栈中并返回结果数组

5. 最后

npm run dev

在这里插入图片描述

代码已上传至github仓库

https://github.com/Bald-heads/ASTByMyself.git
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值