vue如何解析template模板

浅聊一下

在vue中,我们在template模板中写代码,vue将template模板中的代码解析成抽象语法树,再通过一系列操作变成DOM挂载在页面上,那么vue是如何识别template模板并且将其转为虚拟DOM的呢?

compile

在vue中,compile 是指将模板编译为渲染函数的过程。我们来看看在这个过程中,到底发生了什么?

js 复制代码
let template = `
    <div id="#app">
        <div @click="()=>console.log('xx')" :id="name">{{name}}</div>
        <h1 :name="title">玩转Vue3</h1>
        <p>编译原理</p>
    </div>
`

这是我们的一个模板,我们要将标签、属性、内容逐一分解出来,聪明的掘友可能已经想到了,这里使用正则来完成...

js 复制代码
function tokenizer(input) {
    let tokens = []
    let type = '' // 标签 属性 ....
    let val = ''
    // 逐一字符分词 
    for (let i = 0; i < input.length; i++) {
        let ch = input[i] // 每个字符 
        if (ch === '<') {
            push()
            if (input[i + 1] === '/') {
                type = 'tagend'
            } else {
                type= 'tagstart'
            }
        }
        if (ch === '>') {
            if (input[i-1] == '=') {
                // 箭头函数
            } else{
                push()
                type="text"
                continue
            }
        } else if (/[\s]/.test(ch)) {
            push() 
            type='props' 
            continue // div 拿完了 不需要再加ch 
        }
        val += ch 
    }
    console.log(tokens);
    return tokens;
    function push() {
        if (val) {
            // <div 
            if (type === 'tagstart') val = val.slice(1) // <div  div
            if (type === 'tagend') val = val.slice(2) // </div  div 
            tokens.push({
                type,
                val
            })
            val = ''
        }
    }
}
  1. 首先,定义了一个名为 tokenizer 的函数,该函数接收一个输入字符串 input,并返回一个 tokens 数组。

  2. 函数内部定义了 tokens 数组、type 和 val 两个变量,用于存储词法分析的结果和当前正在处理的标签类型及值。

  3. 使用 for 循环逐个遍历输入字符串中的字符。

  4. 在循环内部,根据当前字符的不同情况进行处理:

    • 如果当前字符是 "<",则调用 push 函数,并根据下一个字符是否为 "/" 来判断是标签的开始还是结束。
    • 如果当前字符是 ">",则调用 push 函数,并根据前一个字符是否是 "=" 来判断是箭头函数还是普通文本,然后继续处理下一个字符。
    • 如果当前字符是空白字符(包括空格、制表符等),则调用 push 函数,并将当前类型设置为属性,然后继续处理下一个字符。
    • 否则,将当前字符加入到 val 变量中。
  5. 在 push 函数中,将当前处理的值和类型推入 tokens 数组中,并根据类型对值进行修剪处理,然后重置 val 变量。

  6. 最后,输出 tokens 数组并返回。

来看看token数组

完美地将每一块内容都精准地分开,接下来的操作就是将这个token数组转为一个抽象语法树了,其实我在之前就已经讲过类似的内容了面试官:来道送分题 - 掘金 (juejin.cn),有点像将列表转为树...但是这里要复杂一点

js 复制代码
function parse(template) {
    // 分词
    const tokens = tokenizer(template);
    // console.log(tokens);
    let cur = 0
    let ast = {
        type: 'root',
        props: [],
        children: []
    }
    while(cur < tokens.length) {
        ast.children.push(walk())
    }
    return ast
    function walk() {
        let token = tokens[cur]
        if (token.type == 'tagstart') {
            let node = {
                type: 'element',
                tag: token.val,
                props: [],
                children: []
            }
            token = tokens[++cur]
            while (token.type !== 'tagend') {
                if (token.type == 'props') {
                    node.props.push(walk())
                } else {
                    node.children.push(walk())
                }
                token = tokens[cur]
            }
            cur++
            return node
        }

        if (token.type === 'tagend') {
            cur++
        } 
        if (token.type === 'text') {
            cur++ 
            return token
        }
        if (token.type === 'props') {
            cur++
            const [key,val] = token.val.replace('=', '~').split('~')
            return {
                key,
                val
            }
        }
    }
}
  1. 定义了一个名为 parse 的函数,该函数接收一个模板字符串作为参数,然后调用 tokenizer 函数对模板进行分词,得到 tokens 数组。

  2. 初始化 cur 变量为 0,表示当前处理的 token 索引,同时初始化一个空的 ast 对象作为最终的抽象语法树,其中包含一个根节点,具有类型、属性和子节点等字段。

  3. 使用 while 循环遍历 tokens 数组,并在循环内部调用 walk 函数来构建 AST 的子节点,将子节点添加到根节点的 children 数组中。

  4. walk 函数用于递归地处理每个 token,并根据不同类型的 token 构建对应的 AST 节点:

    • 如果 token 类型为 'tagstart',表示一个标签的开始,创建一个 element 类型的节点,包含标签名、属性和子节点等信息,然后递归处理其子节点。
    • 如果 token 类型为 'tagend',表示一个标签的结束,直接跳过。
    • 如果 token 类型为 'text',表示文本节点,直接返回该文本节点。
    • 如果 token 类型为 'props',表示属性节点,解析属性键值对并返回。
  5. 最后,返回构建好的抽象语法树 ast。

来看看输出

结尾

到这里我们已经完成从template模板到抽象语法树的过程了,接下来将该来到diff算法环节...

相关推荐
面向星辰14 小时前
html各种常用标签
前端·javascript·html
梦65014 小时前
HTML新属性
前端
东风西巷16 小时前
PDFgear:免费全能的PDF处理工具
前端·pdf·软件需求
森之鸟16 小时前
Mac电脑上如何打印出字体图标
前端·javascript·macos
mCell17 小时前
GSAP 入门指南
前端·javascript·动效
gnip17 小时前
组件循环引用依赖问题处理
前端·javascript
Aotman_18 小时前
el-input textarea 禁止输入中文字符,@input特殊字符实时替换,光标位置保持不变
前端·javascript·vue.js·前端框架·es6
Nan_Shu_61418 小时前
Web前端面试题(1)
前端·面试·职场和发展
EveryPossible18 小时前
选择数据展示
javascript
lypzcgf18 小时前
Coze源码分析-资源库-创建知识库-前端源码-核心组件
前端·typescript·react·coze·coze源码分析·ai应用平台·agent开发平台