前言
接下来 我们进入到Complier类的实现
Compiler
-
功能
- 负责编译模板,解析指令/差值表达式
- 负责页面的首次渲染
- 当数据变化后重新渲染视图
-
结构
判断元素属性是否指令
这里判断很简单我们可以会议一下 vue的指令就是 v-xxx开头,所以我们可以这样使用
// 判断元素属性是否指令
isDirective(attrName) {
// 这里判断很简单我们可以会议一下 vue的指令就是 v-xxx开头,所以我们可以这样使用
return attrName.startsWith('v-')
}
接着判断节点是否是文本节点、元素节点
该如何进行判断呢?
nodeType 属性返回节点类型。
- 如果节点是一个元素节点,nodeType 属性返回 1。
- 如果节点是属性节点, nodeType 属性返回 2。
- 如果节点是一个文本节点,nodeType 属性返回 3。
- 如果节点是一个注释节点,nodeType 属性返回 8。
- 该属性是只读的。 那这样就很简单了
判断节点是否是文本节点
isTextNode(node) {
return node.nodeType === 3
}
判断节点是否是元素节点
isElemenetNode(node) {
// nodeType === 1 就是元素节点
return node.nodeType === 1
}
complier 方法
有了上面的几个基础方法打底,我们可以开始complier方法的编译了
- 我们先获取 el 下的所有节点
let childNodes = el.childNodes
- 需要遍历 el 下的所有节点
Array.from(childNodes).forEach
- 判断节点的类型
-
如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式
if (this.isTextNode(node)) { // 如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式 this.complierText(node) } -
如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点
else if (this.isElemenetNode(node)) { // 如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点 this.complierElement(node) }
-
- 判断node节点是否有子节点,如果有子节点,要递归调用 complier
if(node.childNodes && node.childNodes.length){
this.complier(node)
}
完整代码
// 编译模板,处理文本节点和元素节点
complier(el) {
// 我们先获取 el 下的所有节点
let childNodes = el.childNodes
// 我们需要遍历 el 下的所有节点
Array.from(childNodes).forEach(node => {
// 接下来我们需要判断节点的类型
if (this.isTextNode(node)) {
// 如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式
this.complierText(node)
} else if (this.isElemenetNode(node)) {
// 如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点
this.complierElement(node)
}
// 判断node节点是否有子节点,如果有子节点,要递归调用 complier
if(node.childNodes && node.childNodes.length){
this.complier(node)
}
})
}
complierText 实现
- 首先先用正则表达式获取插值表达式中的值
{{ }}先解析花括号,利用/\{\{ \}\}/正则先解析花括号- 获取其中的值,
/\{\{(.+?)\}\}/
// 编译文本节点,处理差值表达式
complierText(node) {
// 正则表达式 解析插值表达式
let reg = /\{\{(.+?)\}\}/
// 文本节点的内容
let value = node.textContent
if (reg.test(value)) {
const key = RegExp.$1.trim()
node.textContent = value.replace(reg, this.vm[key])
}
}
编译元素节点,处理指令
// 编译元素节点,处理指令
complierElement(node) {
// 遍历所有的属性节点
Array.from(node.attributes).forEach(attr => {
// 判断是否是指令
let attrName = attr.name
if (this.isDirective(attrName)) {
// 对指令做处理
attrName = attrName.substr(2)
let key = attr.value
this.update(node, key, attrName)
}
})
}
update(node, key, attrName) {
let updateFn = this[`${attrName}Updater`]
updateFn && updateFn(node, this.vm[key])
}
// 处理 v-text 指令
textUpdater(node, value) {
node.textContent = value
}
modelUpdater(node, value) {
node.value = value
}