实现Vue响应式原理 之 compiler.js部分
结构介绍
Compiler功能
1.负责编译模板,解析指令/插值表达式
2.负责页面的首次渲染
3.当数据变化后重新渲染视图
Compiler结构
代码实现
// 负责解析指令/插值表达式
class Compiler {
constructor(vm) {
this.vm = vm
this.el = vm.$el
// 立即编译模板
this.compile(this.el)
}
// 编译模板,处理文本节点和元素节点
compile(el) {
const nodes = el.childNodes
Array.from(nodes).forEach(node => {
// 判断是文本节点还是元素节点
if (this.isTextNode(node)) {
// 处理文本节点
this.compileText(node)
} else if (this.isElementNode(node)) {
// 处理元素节点
this.compileElement(node)
}
// 判断node节点,是否有子节点,如果有子节点,要递归调用compile
if (node.childNodes && node.childNodes.length) {
// 当前节点中还有子节点,递归编译
this.compile(node)
}
})
}
// 判断节点是否是文本节点
isTextNode(node) {
return node.nodeType === 3
}
// 判断节点是否是元素(属性)节点
isElementNode(node) {
return node.nodeType === 1
}
// 判断元素属性是否是指令 即,是否是以 v- 开头的指令
isDirective(attrName) {
return attrName.startsWith('v-')
}
// 编译文本节点,处理差值表达式
compileText(node) {
// 1. 匹配差值表达式 {{ msg }} 2. 要不花括号中变量的名字提取出来,后续要获取到这个变量的值
const reg = /\{\{(.+?)\}\}/ //.+后面的?号 表示非贪婪模式,尽可能早的结束匹配
// 获取文本节点的内容
const value = node.textContent
if (reg.test(value)) {
// 插值表达式中的值就是我们要的属性名称
const key = RegExp.$1.trim() // RegExp是正则的构造函数,RegExp.$1表示获取正则表达式中的第一个分组就是小括号中的内容
// 把插值表达式替换成具体的值
node.textContent = value.replace(reg, this.vm[key])
}
}
// 编译元素节点,处理指令
compileElement(node) {
// 遍历元素节点中的所有属性,找到指令
Array.from(node.attributes).forEach(attr => {
// 获取元素属性的名称
let attrName = attr.name
// 判断当前的属性名称是否是指令
if (this.isDirective(attrName)) {
// attrName 的形式 v-text v-model
// 截取属性的名称,获取 text model
attrName = attrName.substr(2)
// 获取属性的名称,属性的名称就是我们数据对象的属性 v-text="name",获取的是name
const key = attr.value
// 处理不同的指令
this.update(node, key, attrName)
}
})
}
// 负责更新 DOM
// 创建 Watcher
update(node, key, attrName) {
// node 节点,key 数据的属性名称,attrName 指令的后半部分
const updaterFn = this[attrName + 'Updater']
updaterFn && updaterFn(node, this.vm[key])
}
// v-text 指令的更新方法 处理v-text指令把值取出来赋给对应DOM元素
textUpdater(node, value) {
node.textContent = value
}
// v-model 指令的更新方法
modelUpdater(node, value) {
node.value = value
}
}
梳理一下文件的结构
- html文件结构
- node节点结构
- 节点属性结构
RegExp
上面代码实现中使用到的RegExp,是正则的构造函数,RegExp.$1表示获取正则表达式中的第一个分组就是小括号中的内容 => 用于保存最近一次exec或test产生的匹配结果中,括号内的内容。详细可网上查阅了解。