compile vue 指令的简单实现
// MVue.js
const compileUtil = {
getVal (expr, vm) {
return expr.split('.').reduce((data, currentVal) => {
return data[currentVal]
}, vm.$data)
},
text (node,expr,vm){
let value
if (expr.indexOf('{{') !== -1) {
value = expr.replace(/\{\{(.+?)\}\}/g, (...a) => {
return this.getVal(a[1], vm)
})
} else {
value = this.getVal(expr, vm)
}
this.updater.textUpdater(node, value)
},
html (node,expr,vm){
const value = this.getVal(expr, vm)
this.updater.htmlUpdater(node, value)
},
model (node,expr,vm){
const value = this.getVal(expr, vm)
this.updater.modelUpdater(node, value)
},
on (node,expr,vm,eventName){
let fn = vm.$options.methods && vm.$options.methods[expr]
node.addEventListener(eventName, fn.bind(vm), false)
},
updater: {
modelUpdater (node, value) {
node.value = value
},
textUpdater (node, value) {
node.textContent = value
},
htmlUpdater (node, value) {
node.innerHTML = value
}
}
}
class Compile{
constructor(el, vm) {
this.el = this.isElementNode(el) ? el : document.querySelector(el)
this.vm = vm
// 1.获取文档碎片,放入内存中,防止页面的回流和重绘
const fragment = this.node2Fragment(this.el)
// 2.编译 fragment
this.compile(fragment)
// 3.追加子元素到根元素
this.el.appendChild(fragment)
}
compile(fragment) {
// 1.获取子节点
const childNode = fragment.childNodes
Array.from(childNode).forEach(child => {
if (this.isElementNode(child)) {
// 是元素节点, 进行编译
this.compileElement(child)
} else {
// 文本节点, 编译
this.compileText(child)
}
if (child.childNodes && child.childNodes.length) {
this.compile(child)
}
})
}
compileElement(node) {
const attributes = node.attributes
Array.from(attributes).forEach(attr => {
const {name, value} = attr
if (this.isDirective(name)) { // 是一个指令 v-text/v-html/v-on:click/v-model
const [, directive] = name.split('-') // text html model on:click
const [dirName, eventName] = directive.split(':');
// 更新数据 数据驱动视图
compileUtil[dirName](node, value, this.vm, eventName)
// 删除元素标签上的 指令属性
node.removeAttribute('v-' + dirName)
} else if (this.isEventName(name)) { // @click="handlerClick"
let [, eventName] = name.split('@')
compileUtil['on'](node, value, this.vm, eventName)
}
})
}
compileText(node) {
const content = node.textContent
if (/\{\{(.+?)\}\}/.test(content)) {
compileUtil['text'](node, content, this.vm)
}
}
isEventName (attrName) {
return attrName.startsWith('@')
}
isDirective (attrName) {
return attrName.startsWith('v-')
}
isElementNode(node){
return node.nodeType === 1
}
node2Fragment(el) {
// 创建文档碎片
const f = document.createDocumentFragment()
var firstChild
while (firstChild = el.firstChild){
f.appendChild(firstChild)
}
return f
}
}
class MVue{
constructor (options){
this.$el = options.el
this.$data = options.data
this.$options = options
if (this.$el){
// 1、实现数据观察者
// 2、指令的解析器
new Compile(this.$el, this)
// 做一个代理 this.$data.person 到 this.person
this.proxyData(this.$data)
}
}
proxyData (data) {
for(let key in data) {
Object.defineProperty(this, key, {
get () {
return data[key]
},
set (newVal) {
data[key] = newVal
}
})
}
}
}
网友评论