ast语法树变成render字符串
-
ast语法树变成字符串generate(ast)
=>html元素:
<div id='app' style="color:red">hello {{msg}}</div>
=>转换成ast语法树:
{"tag":"div","type":1,"children":[{"type":3,"text":"hello{{msg}}"}],"attrs":[{"name":"id","value":"app"},{"name":"style","value":"color:red"}],"parent":null}
=>转换成render字符串:
_c('div',{id:"app",style:{"color":"red"}},_v("hello"+_s(msg)))
_c:处理标签 _v:处理文本 _s:处理{{}}
let code = generate(ast)
function generate(el) {
// console.log(el)
let children = genChildren(el)
//方法 拼接字符串 源码也是这样操作 [{}] ${el.attrs.length?`{style:{color:red}}`:'undefined'}
let code = `_c('${el.tag}',${el.attrs.length ? `${genProps(el.attrs)}` : 'undefined'}${
children ? `,${children}` : ''
})`
return code
}
-
处理属性
// 处理元素的属性 function genProps(attrs) { //处理属性 let str = '' for (let i = 0; i < attrs.length; i++) { let attr = attrs[i] //注意; style:"color:red;font-size: 20px if (attr.name === 'style') { let obj = {} //对样式进行特殊处理 attr.value.split(';').forEach(item => { let [key, value] = item.split(':') obj[key] = value }) attr.value = obj // } //其他 'id:app',注意最后会多个属性化 逗号 str += `${attr.name}:${JSON.stringify(attr.value)},` } return `{${str.slice(0, -1)}}` // -1为最后一个字符串的位置 演示一下 // let reg =/a/g reg.test('ad') false }
-
处理子节点genChildren()方法(1)
//判断是否有儿子 function genChildren(el) { const children = el.children if (children) { //将所有 return children.map(child => gen(child)).join(',') } }
-
处理子节点gen()方法(2)
判断{{}}的正则
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g // {{xx}} 默认的 双大括号
正则表达式通过exec返回匹配到的内容
ex: let res = /a/g res.exec('abc') 结果: ["a", index: 0, input: "abc", groups: undefined]
- 多次使用正则会在第二次使用时失效
- 例如:let reg = /a/g reg.test('ab') //true
- 接着re g.test('abc') // false
- 处理方案 每次使用前设置 reg.lastIndex = 0
// 类型:1:元素div 3:文本
function gen(node) { //获取到的元素
//注意 是什么类型 1.div 3.文本
if (node.type === 1) {
return generate(node) //生成元素节点的字符串
} else {
let text = node.text // 获取文本 注意 普通的文本 hello{{name}}?{{num}}
if (!defaultTagRE.test(text)) {
return `_v(${JSON.stringify(text)})` // _v(html) _v('hello'+_s(name))
}
let tokens = [] //存放每一段的代码
let lastIndex = defaultTagRE.lastIndex = 0;//如果正则是全局模式 需要每次使用前变为0
let match;// 每次匹配到的结果 exec 获取 {{name}}
while (match = defaultTagRE.exec(text)) {
// console.log(match) 获取到 又{{}} 元素
// console.log(match)
let index = match.index;// 保存匹配到的索引
// hello{{name}} ? {{num}}
if (index > lastIndex) {
tokens.push(JSON.stringify(text.slice(lastIndex,index))) //添加的是文本
}
//{{name}} 添加{{}} aa
tokens.push(`_s(${match[1].trim()})`)
lastIndex = index+match[0].length //最后 {{}} 索引位置
}
// 判读最后{{}}的位置是否小于当前文本的位置,如果是,说明还有未解析的文本
if(lastIndex<text.length){
tokens.push(JSON.stringify(text.slice(lastIndex)))
}
//最终返回出去
return `_v(${tokens.join("+")})`
}
}
trim()去除空格,但是trim是VB/VBScript的字符串处理函数,JS并没有同名或同功能的函数为了能更好的实现功能可以使用正则replace(/(^\s)|(\s$)/g
网友评论