这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
Compile-解析指令的基本步骤
(核心方法)解析html标签 : compileElement(node)
- 获取到当前节点下的所有的属性
调用 node.attributes (attributes可以获取一个对象中的一个属性,并且作为对象来调用,注意在这里要使用“[]”;attributes 属性返回指定节点属性的集合。你可以使用 length 属性确定属性的数量,然后你可以遍历所有的属性节点提取你想要的信息。 每个属性都是可用属性节点对象。)
对象.attributes //获得所有属性节点,返回一个数组(伪数组)
通过isDirective识别指令
isDirective(attrName) {
//命名以什么开头
return attrName.startsWith('v-')
}
解析Vue的指令 通过compileElement
// 解析html标签
compileElement(node) {
// 1. 获取到当前节点下的所有的属性
let attributes = node.attributes
this.toArray(attributes).forEach(attr => {
// 2. 解析Vue的指令 (所有以V-开头的属性)
let attrName = attr.name
if (this.isDirective(attrName)) {
let type = attrName.slice(2)
let expr = attr.value
// 如果是v-text指令
if (type === "text") {
node.textContent = this.vm.$data[expr]
}
// 解析v-html 指令
if (type === "html") {
node.innerHTML = this.vm.$data[expr]
}
// 解析v-model 指令
if (type === "model") {
node.value = this.vm.$data[expr]
}
// 解析v-on指令
}
})
}
补充
1.V-ON 实现
问题:里面this的指向是当前按钮 <button v-on:click="clickFn">v-on</button>
所以通过 node.addEventListener(eventType, this.vm.$methods[expr].bind(this.vm))改变this指向,同时因为data还没有挂载到vm实例,所以只能先使用 console.log(this.$data.msg)
const vm = new Vue({
el: "#app",
data: {
msg: 'hello vue',
tag: '<h3>哈哈</h3>'
},
methods: {
clickFn() {
// 在vue的methods 中this应该指向当前实例
console.log(this.$data.msg)
}
},
},
)
v-on具体实现:
// 解析v-on指令
if (this.isEventDirective(type)) {
let eventType = type.split(":")[1]
console.log(this.vm.$methods[expr])
node.addEventListener(eventType, this.vm.$methods[expr].bind(this.vm))
}
// 工具方法
isEventDirective(type) {
return type.split(":")[0] === "on"
}
2.工具方法
/*工具方法*/
//类数组(NodeList,arguments)变成数组
toArray(linkArray) {
return [].slice.call(linkArray)
}
isElementNode(node) {
//nodeType :节点类型 1:元素节点 3:文本节点
return node.nodeType === 1
}
isTextNode(node) {
return node.nodeType === 3
}
isDirective(attrName) {
//命名以什么开头
return attrName.startsWith('v-')
}
isEventDirective(type) {
return type.split(":")[0] === "on"
}
代码优化:
如果有更多的指令,再通过判断进行实现就会使代码复杂化难以维护。 创建一个 CompileUtil 对象,借助自定义构造函数创建 text html model eventHandler 函数 ,将判断语句里的方法抽取到这里
let CompileUtil = {
//处理 v-text指令 (自定义构造函数)
text(node, vm, expr) {
node.textContent = vm.$data[expr]
},
html(node, vm, expr) {
node.innerHTML = vm.$data[expr]
},
model(node, vm, expr) {
node.value = vm.$data[expr]
},
eventHandler(node, vm, type, expr) {
let eventType = type.split(":")[1]
let fn = vm.$methods && vm.$methods[expr]
console.log(fn)
if (eventType && fn) {
node.addEventListener(eventType, fn.bind(vm))
}
}
}
CompileElement
compileElement(node) {
// 1. 获取到当前节点下的所有的属性
let attributes = node.attributes
this.toArray(attributes).forEach(attr => {
// 2. 解析Vue的指令 (所有以V-开头的属性)
let attrName = attr.name
if (this.isDirective(attrName)) {
let type = attrName.slice(2)
let expr = attr.value
if (this.isEventDirective(type)) {
CompileUtil["eventHandler"](node, this.vm, type, expr)
} else {
//当有这个方法的时候再调用就不会报错了
CompileUtil[type] && CompileUtil[type](node, this.vm, expr)
}
}
})
}
(核心方法)解析html标签 : compileText(node)
补充:(解析插值表达式)
正则表达式常用方法
- 校验数据
test(字符串)
测试字符是否满足正则表达式规则,如果测试到有,则返回true;没有则返回flase
语法:正则表达式.test(字符串) 正则表达式提供的方法
search(正则表达式)
search() 方法执行正则表达式和 String 对象之间的一个搜索匹配。
语法:字符串.search(正则表达式) 字符串提供的方法
区别: .test()方法是正则表达式提供的,.search()是字符串提供的, .test()方法返回布尔值,search()返回下标
RegExp 是javascript中的一个内置对象。为正则表达式。
RegExp.2,RegExp.99总共可以有99个匹配
显示复杂数据 (拿对象类型的数据)
要拿到data.car里面的数据
data: {
msg: 'hello vue',
tag: '<h3>哈哈</h3>',
car: {
brand: '大众',
color: 'yellow'
}
},
methods: {
clickFn() {
// 在vue的methods 中this应该指向当前实例
console.log(this.$data.msg)
}
},
调用正则表达式,通过RegExp.$1获取到里面的值,将mustache方法放到CompileUtil对象里面,getVMValue作用是获通过data = data[item]得到对象里的值
mustache(node, vm) {
let txt = node.textContent
let reg = /\{\{(.+)\}\}/
if (reg.test(txt)) {
let expr = RegExp.$1
node.textContent = txt.replace(reg, CompileUtil.getVMValue(vm, expr))
}
},
getVMValue(vm, expr) {
// 获取data中的数据
let data = vm.$data
expr.split('.').forEach(item => {
data = data[item]
})
return data
}