对于理解Vue的工作机制还是有些帮助的,如果需要深入理解Vue还得自己上GitHub看源码的。
class Vue {
/**
* 构造方法
* 观察数据,编译模板
* /
constructor (options) {
this.$el = document.querySelector(options.el)
this.$methods = options.methods
this._binding = {}
this._observe(options.data)
this._compile(this.$el)
}
/**
* 将观察数据的key与watcher建立映射关系,便于数据更新时调用watcher.update方法
* watcher.key是一个数组,是因为观察数据(data)可能不止用于一个DOM属性,要达到更新多处就需要push多个watcher
* /
_pushWatcher (watcher) {
if (!this._binding[watcher.key]) this._binding[watcher.key] = []
this._binding[watcher.key].push(watcher)
}
/**
* Vue2.x用的是Object.defineProperty方法代理数据的更新操作
* Vue3.x是用Proxy,究其原理差不多
* 这里的作用是代理set方法,数据发生变化时更新视图UI
* /
_observe (data) {
var that = this
this.$data = new Proxy(data, {
set(target,key,value){
let res = Reflect.set(target,key,value)
that._binding[key].map(item => {item.update()})
return res
}
})
}
/**
* 这个compile方法比较简单,递归找DOM节点,取属性/方法,push到watcher
* /
_compile (root) {
const nodes = Array.prototype.slice.call(root.children)
let data = this.$data
nodes.map(node => {
if(node.children.length > 0) this._complie(node)
if(node.hasAttribute('v-bind')) {
let key = node.getAttribute('v-bind')
this._pushWatcher(new Watcher(node,'innerHTML',data,key))
}
if(node.hasAttribute('v-model')) {
let key = node.getAttribute('v-model')
this._pushWatcher(new Watcher(node,'value',data,key))
node.addEventListener('input',() => {data[key] = node.value})
}
if(node.hasAttribute('v-click')) {
let methodName = node.getAttribute('v-click')
let mothod = this.$methods[methodName].bind(data)
node.addEventListener('click',mothod)
}
})
}
}
class Watcher {
constructor (node,attr,data,key) {
this.node = node
this.attr = attr
this.data = data
this.key = key
}
update () {
this.node[this.attr] = this.data[this.key]
}
}
// 实例化一个Vue 测试用
<div id="app">
<input type="text" v-model="num">
<input type="button" value="增加" v-click="add">
<input type="button" value="减去" v-click="sub">
<div v-bind="num"></div>
</div>
<script>
window.app = new Vue({
el: '#app',
data:{
num:0
},
methods:{
add(){
this.num++
},
sub(){
this.num--
}
}
})
</script>