-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
看完编译器,能明白以下问题
-
template解析成ast,如何确定ast的父子关系?
stack栈的先入后出的特性,当遇到开始标签时入栈,遇到结束标签时出栈。dom树形结构具备这样的特性 -
template如何解析标签、属性、节点?
-
怎么匹配并解析非插值节点?
html.indexOf('{{') === 0.,如果你的正则表达式掌握的还不错,可以尝试引入正则匹配。
if (html.match(/{{(.*)}}/)) { RegExp.$1} 获取插值表达式
var template = `
<div class="container" id="container">
<input type="text" v-model="a.b"></input>
{{state}}
<div class="h1">
<span>标题</span>
{{title}}
</div>
</div>
`
parse(template)
function parse(template) {
let root = null, stack = [], html = template, i = 0;
return parseHTML()
function parseHTML() {
while(html) {
// 开始
// 保证当前是 < 开头
html = html.trim()
if(html.indexOf('<') === 0) {
if(html.indexOf('</') === 0) {
parseTagEnd()
} else {
parseTagStart()
}
} else if(html.indexOf('{{')===0) {
console.log('匹配插值',html)
// 文本节点中的插值节点
parseInterpolate()
} else {
parseStaticText()
}
}
return root
}
// 处理静态文本节点
function parseStaticText(){
let end = html.indexOf('<');
let content = html.slice(0, end);
html = html.slice(end)
let staticNode = {
type: 'text',
text: content,
isStatic: true,
}
if(stack.length) {
stack[stack.length - 1].children.push(staticNode)
}
}
// 找到插值,对象类型 a.b 如何处理?
function parseInterpolate() {
html = html.trim()
let end = html.indexOf('}}')
let content = html.slice(2, end)
html = html.slice(end+2).trim()
const textNode = {
type: 'interpolate',
text: content
}
console.log('插值节点', content)
stack[stack.length - 1].children.push(textNode)
}
function parseTagStart() {
html = html.trim()
let end = html.indexOf('>');
console.log('起始标签', html)
let content = html.slice(1,end).trim();
html = html.slice(end+1).trim()
let space = content.indexOf(' '), tagName = '';
if(space === -1) {
tagName = content
content = ''
} else {
tagName = content.slice(0, space).trim()
content = content.slice(space+1).trim()
}
console.log(html, space, 123456, tagName, 'starttttttt', content)
let attrsMap = content ? parseAttrs(content) : {}
let node = createVnode(tagName, attrsMap, [])
if(!root) {
console.log('start Node', root)
root = node
}
stack.push(node)
}
function parseTagEnd() {
// 处理>前面有空格的情况
html = html.slice(html.indexOf('>') + 1)
processElement()
}
function parseAttrs(attrStr) {
let list = attrStr.split(' ').map(attr => attr.trim())
let map = {}
console.log(list,1234567890)
list.forEach(attr => {
let kv = attr.split('=')
map[kv[0]] = kv[1].replace(/["']?/g, '')
})
return map
}
function createVnode(tag, rawAttrs, children = []) {
return { type: 'element', tag, rawAttrs, children }
}
function processElement() {
let currentEle = stack.pop(),
len = stack.length,
topEle = stack[len-1];
currentEle.attrs = {}
if(len) {
const { tag, rawAttrs } = currentEle;
let keys = Object.keys(rawAttrs)
// 处理属性
keys.forEach(prop => {
if(prop === 'v-model') {
processVmodel(currentEle)
} else if(prop.match(/^v-bind:(.*)/)) {
processVbind(currentEle, RegExp.$1, rawAttrs[`v-bind:${RegExp.$1}`])
} else if(prop.match(/^v-on:(.*)/)) {
processVon(currentEle,RegExp.$1, rawAttrs[`v-on:${RegExp.$1}`])
}
})
topEle.children.push(currentEle)
}
}
function processVmodel(ele) {
const { tag, rawAttrs, attrs } = ele;
const { type, 'v-model': vModelValue} = rawAttrs;
// type = 'text' 'checkbox' 'gradio'
if(tag === 'input') {
attrs.vModel = { tag, type, value: vModelValue}
} else if(type === 'textarea') {
attrs.vModel = { tag, value: vModelValue}
} else if(type === 'select') {
attrs.vModel = { tag, value: vModelValue}
}
}
function processVbind(ele, bindKey, bindValue) {
ele.attrs.vBind = {[bindKey]: bindValue}
}
function processVon(ele, onKey, onValue) {
ele.attrs.vOn = {[onKey]: onValue}
}
}
Metadata
Metadata
Assignees
Labels
No labels