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算法环节...

相关推荐
weixin-a1530030831620 分钟前
vue疑难解答
前端·javascript·vue.js
Bellafu6663 小时前
selenium 常用xpath写法
前端·selenium·测试工具
blackorbird6 小时前
Edge 浏览器 IE 模式成攻击突破口:黑客借仿冒网站诱导攻击
前端·edge
谷歌开发者7 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (一)
前端·chrome·学习
名字越长技术越强7 小时前
Chrome和IE获取本机ip地址
前端
天***88967 小时前
Chrome 安装失败且提示“无可用的更新” 或 “与服务器的连接意外终止”,Chrome 离线版下载安装教程
前端·chrome
半梦半醒*7 小时前
zabbix安装
linux·运维·前端·网络·zabbix
大怪v8 小时前
【搞发🌸活】不信书上那套理论!亲测Javascript能卡浏览器Reader一辈子~
javascript·html·浏览器
清羽_ls8 小时前
React Hooks 核心规则&自定义 Hooks
前端·react.js·hooks
你的人类朋友8 小时前
“签名”这个概念是非对称加密独有的吗?
前端·后端·安全