// 简化的HTML解析器实现
function parseHTML(html, options) {
let index = 0
const stack = []
while(html) {
// 处理开始标签
const startTagMatch = parseStartTag()
if (startTagMatch) {
handleStartTag(startTagMatch)
continue
}
// 处理结束标签
const endTagMatch = html.match(endTag)
if (endTagMatch) {
advance(endTagMatch[0].length)
parseEndTag(endTagMatch[1])
continue
}
// 处理文本内容
let text
const textEnd = html.indexOf('<')
if (textEnd >= 0) {
text = html.substring(0, textEnd)
} else {
text = html
}
if (text) {
advance(text.length)
options.chars(text)
}
}
}
// 开始标签正则表达式
const startTagOpen = /^<([a-zA-Z_][\w\-]*)(?:\s*(?:\/)?>)/
// 属性匹配正则
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
// 生成的AST节点示例
{
type: 1, // 节点类型(1-元素,2-带插值文本,3-纯文本)
tag: 'div', // 标签名
attrsList: [{ // 原始属性列表
name: 'v-if',
value: 'show'
}],
attrsMap: { // 属性映射表
'v-if': 'show'
},
parent: undefined, // 父节点
children: [], // 子节点集合
ifConditions: [ // v-if条件处理
{
exp: 'show',
block: {/* 对应节点 */}
}
]
}
function markStatic(node) {
node.static = isStatic(node)
if (node.children) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i]
markStatic(child)
if (!child.static) {
node.static = false
}
}
}
}
function isStatic(node) {
// 规则1:包含动态绑定
if (node.type === 2) return false
// 规则2:包含v-if/v-for
if (node.if || node.for) return false
// 规则3:是组件或slot
if (node.tag === 'slot' || isPlatformReservedTag(node.tag)) return false
// 规则4:非保留标签且无inline-template
if (!isPlatformReservedTag(node.tag) && !node.inlineTemplate) return false
return true
}
优化前:
function render() {
return _c('div', [
_c('h1', [_v("Static Title")]), // 每次重新创建
_c('p', [_v(_s(dynamicContent))])
])
}
优化后:
const _hoisted_1 = _c('h1', [_v("Static Title")]) // 提升到外部
function render() {
return _c('div', [
_hoisted_1, // 直接复用
_c('p', [_v(_s(dynamicContent))])
])
}
function generate(ast) {
const code = ast ? genElement(ast) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: []
}
}
function genElement(el) {
// 处理组件
if (el.component) {
return genComponent(el.component, el)
}
// 处理slot
if (el.tag === 'slot') {
return genSlot(el)
}
// 生成属性数据
const data = genData(el)
// 处理子节点
const children = genChildren(el)
// 拼接代码
return `_c('${el.tag}'${data ? `,${data}` : ''}${children ? `,${children}` : ''})`
}
v-if指令处理:
function genIfConditions(conditions) {
if (!conditions.length) return '_e()'
const condition = conditions.shift()
return `(${condition.exp})?${genElement(condition.block)}:${genIfConditions(conditions)}`
}
v-for指令处理:
function genFor(el) {
const exp = el.for
const alias = el.alias
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
return `_l((${exp}),` +
`function(${alias}${iterator1}${iterator2}){` +
`return ${genElement(el)}` +
'})'
}
function closeElement(element) {
// 检查标签闭合
if (element.isUnary) return
if (element.tag === 'textarea' && !element.processed) {
checkTextareaClosingTag(element)
}
// 检查嵌套有效性
if (currentParent && !element.forbidden) {
if (element.elseif && !currentParent.if) {
warn('v-else-if used without v-ife')
}
if (element.else && !currentParent.if) {
warn('v-else used without v-if')
}
}
}
function handleCompilationError(error, vm) {
if (process.env.NODE_ENV !== 'production') {
warn(`Error compiling template:\n\n${template}\n\n${error.message}`, vm)
} else {
throw error
}
}
深度实践建议:
// 实践示例:比较不同模板的编译结果
const res1 = Vue.compile('静态内容')
const res2 = Vue.compile('{{ dynamic }}')
console.log(res1.render.toString())
console.log(res2.render.toString())