生成render函数
标签:render,ast,text,AST,lastIndex,源码,let,attrs,vue2 From: https://www.cnblogs.com/dgqp/p/17321868.html
前言
- 上篇,生成
ast
语法树,而这篇使用ast
语法树生成render
函数。export function compileToFunction(template) { // 1,将模板编译称为 AST 语法树 let ast = parserHTML(template); // 2,使用 AST 生成 render 函数 let code = codegen(ast); }
生成render函数
codegen
方法:根据ast
语法树生成render
函数。即传入ast
语法树,返回render
函数。function codegen(ast) { // 儿子,后面会说 let children = genChildren(ast.children) // 字符串拼接render函数 let code = `_c('${ast.tag}',${ ast.attrs.length > 0 ? genProps(ast.attrs) : 'null' }${ast.children.length ? `,${children}` : ''})` // 返回render函数 return code }
在里面其中有
genChildren
方法和genProps
方法。将分别介绍这两个方法处理属性:
genProps(ast.attrs)
方法说明:格式化属性信息。
// 将属性(attrs)数组格式化为对象 function genProps(attrs) { let str = '' // {name,value} for (let i = 0; i < attrs.length; i++) { let attr = attrs[i] // 如果是style属性 if (attr.name === 'style') { let obj = {} // 进行处理 attr.value.split(';').forEach((item) => { let [key, value] = item.split(':') obj[key] = value }) attr.value = obj } // 如果不是,使用JSON.stringify将value转换为字符串 str += `${attr.name}:${JSON.stringify(attr.value)},` } // 去掉最后的多余的逗号,并且用{}包裹 return `{${str.slice(0, -1)}}` } // _c('div',{id:"app",a:"1",b:"2",style:{"color":" red"}},
处理儿子:
genChildren(ast.children)
方法实现就是进行循环递归:
function genChildren(children) { // 递归孩子 return children.map((child) => gen(child)).join(',') }
其中有一个
gen
方法,这个方法就是处理儿子的直接上代码:
// 匹配{{xxx}} const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g function gen(node) { // 如果是元素继续递归就好 if (node.type === 1) { return codegen(node) } else { // 如果不是就是文本类型,对文本进行处理 let text = node.text // 匹配不上,即使普通文本 if (!defaultTagRE.test(text)) { // 包装为_v return `_v(${JSON.stringify(text)})` } else { // 对表达式和普通值执行拼接操作 // <div>aaa{{xxx}}</div> let tokens = [] let match defaultTagRE.lastIndex = 0 let lastIndex = 0 // 执行循环获得结果 while ((match = defaultTagRE.exec(text))) { // 获取当前捕获到的位置 let index = match.index // 说明匹配到了内容,将前一段'<div>aaa '中的 aaa 放入 tokens 数组中 if (index > lastIndex) { tokens.push(JSON.stringify(text.slice(lastIndex, index))) } // match[1]:表示花括号中间的内容 name,需要处理表达式部分可能存在的换行或回车 tokens.push(`_s(${match[1].trim()})`) // 更新lastIndex,用于下次比对 lastIndex = index + match[0].length } // 如果还有剩余的部分要处理 if (lastIndex < text.length) { tokens.push(text.slice(lastIndex)) // 从lastIndex到最后 } // 返回 return `_v(${tokens.join('+')})` } } }