簡易版vue命令解析
36699 ワード
2、シミュレーションVue.js応答式ソースコードに基づいてv-html命令とv-on命令を実現回答:命令解析のコードは以下の通りである(完全なコードはhttps://github.com/smallSix6/fed-e-task-liuhuijun/tree/master/fed-e-task-03-01/code/liuzi-minVue ): vue/vue.js vue/compiler.js vue/index.html v-htmlの実現構想: vueインスタンスの初期化時にnew Compiler解析命令と補間式 を呼び出す. Compilerクラスでテンプレートをコンパイルし、テキストノードと要素ノードを処理する.compile(this.el) 要素ノードを処理する方法はthisを参照する.compileElement(node) compileElement(node)メソッドではこのノードの下の属性を巡回し,命令であれば使いたい命令処理関数を呼び出す. textUpdater:v-text命令 の処理 htmlUpdater:v-html命令 の処理 modelUpdater:v-model命令 の処理 onUpdater:v-on命令 の処理
v-onの実現構想: vueインスタンスの初期化時にnew Compiler解析命令と補間式 を呼び出す. methodsのメンバーをvueインスタンスに注入します.this.proxyMethods(this.$methods) Compilerクラスでテンプレートをコンパイルし、テキストノードと要素ノードを処理する.compile(this.el) 要素ノードを処理する方法はthisを参照する.compileElement(node) 属性名がonで始まるとthisが呼び出される.eventUpdate(node, key, event) eventUpdateは、v-on命令 を処理するためにonUpdaterを呼び出す.
class Vue {
constructor(options) {
// 1、
this.$options = options || {}
this.$data = options.data || {}
this.$methods = options.methods || {}
this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
// 2、 data getter setter, vue
this._proxyData(this.$data)
// methods vue
this._proxyMethods(this.$methods)
// 3、 observer ,
new Observer(this.$data)
// 4、 compiler ,
new Compiler(this)
}
_proxyData(data) {
// data
Object.keys(data).forEach(key => {
// data vue
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
return data[key]
},
set(newValue) {
if (newValue !== data[key]) {
data[key] = newValue
}
}
})
})
}
_proxyMethods(methods) {
Object.keys(methods).forEach(key => {
// methods vue
this[key] = methods[key]
})
}
}
class Compiler {
constructor(vm) {
this.el = vm.$el
this.vm = vm
this.compile(this.el)
}
// ,
compile(el) {
let childNodes = el.childNodes
Array.from(childNodes).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)
}
})
}
// ,
compileElement(node) {
//
Array.from(node.attributes).forEach(attr => {
//
let attrName = attr.name
if (this.isDirective(attrName)) {
// v-text --> text
attrName = attrName.substr(2)
let key = attr.value
if (attrName.startsWith('on')) {
const event = attrName.replace('on:', '') //
//
return this.eventUpdate(node, key, event)
}
this.update(node, key, attrName)
}
})
}
// ,
compileText(node) {
let reg = /\{\{(.+?)\}\}/
let value = node.textContent
if (reg.test(value)) {
let key = RegExp.$1.trim()
node.textContent = value.replace(reg, this.vm[key])
// watcher ,
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
}
update(node, key, attrName) {
let updateFn = this[attrName + 'Updater']
updateFn && updateFn.call(this, node, this.vm[key], key)
}
eventUpdate(node, key, event) {
this.onUpdater(node, key, event)
}
// v-text
textUpdater(node, value, key) {
node.textContent = value
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
// v-html
htmlUpdater(node, value, key) {
node.innerHTML = value
new Watcher(this.vm, key, (newValue) => {
node.innerHTML = newValue
})
}
// v-model
modelUpdater(node, value, key) {
node.value = value
new Watcher(this.vm, key, (newValue) => {
node.value = newValue
})
//
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
// v-on
onUpdater(node, key, event) {
node.addEventListener(event, (e) => this.vm[key](e))
}
//
isDirective(attrName) {
return attrName.startsWith('v-')
}
//
isTextNode(node) {
return node.nodeType === 3
}
//
isElementNode(node) {
return node.nodeType === 1
}
}
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Mini Vue</title>
<script src="./vue/dep.js"></script>
<script src="./vue/watcher.js"></script>
<script src="./vue/compiler.js"></script>
<script src="./vue/observer.js"></script>
<script src="./vue/vue.js"></script>
</head>
<body>
<div id="app">
<h1 v-on:click="myClick"> </h1>
<h3>{{ msg }}</h3>
<h1>v-text</h1>
<div v-text="msg"></div>
<h1 v-html='html'></h1>
<input type="text" v-model="msg">
<input type="text" v-model="count">
<h1>{{dog.name}}</h1>
</div>
</body>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: 'object1111',
dog: {
name: ''
},
html: ''
},
methods: {
myClick() {
alert(' ')
},
clickHandler() {
this.dog
// name
this.$set(dog, name, 'Trump')
}
}
})
</script>
</html>