vue响应式原理分析
vue的响应式原理请看下图
在 new Vue的时候,内部会触发一个Observer劫持监听所有属性,还会有一个Compile解析指令,Compile可以订阅数据变化,添加Watcher订阅者,在watcher里面会有一个update的方法,用于更新视图。在Observer里面会通过Object.definepropry方法对数据进行劫持,在get中添加依赖所依赖的watcher,当触发set时说明有数据变化,此时调用dep.notify方法,其中dep是一个依赖集合,里面搜集了订阅者(依赖于改数据),在dep.notify里面触发wacther的update方法。
发布者
Vue作为一个发布者
class Vue {
constructor(options) {
this.$options = options || {}
this.$data = options.data
this.$el = document.querySelector(options.el)
//this._dep = {}
this.Observer(this.$data)
this.Compile(this.$el)
}
// 劫持监听所有属性
Observer(data) {
for(let key in data) {
const dep = new Dep()
//this._dep[key] = []
let Val = data[key]
Object.defineProperty(this.$data, key, {
get: function () {
if(Dep.target) {
dep.addSubs(Dep.target)
}
return Val
},
set: function(newVal) {
if(newVal !== Val) {
Val = newVal
dep.notify()
}
}
})
}
}
// 解析指令
Compile(el) {
const children = el.children
if(!children.length) throw new Error('挂载元素里面内容为空!')
for(let i=0; i< children.length; i++) {
const node = children[i]
if(node.hasAttribute('v-text')) {
const expValue = node.getAttribute('v-text')
new Watcher(node, this, expValue, 'innerHTML')
//this._dep[expValue].push()
}
if(node.hasAttribute('v-model')) {
const expValue = node.getAttribute('v-model')
new Watcher(node, this, expValue, 'value')
//this._dep[expValue].push()
node.addEventListener('input', (function() {
return function() {
this.$data[expValue] = node.value
}
})().bind(this))
}
if(node.children.length) {
this.Compile(node)
}
}
}
}
订阅者
watcher作为订阅者
class Watcher {
constructor(el, vm, exp, attr) {
this.el = el
this.vm = vm
this.exp = exp
this.attr = attr
this.update()
this.value = this.get()
}
update() {
this.el[this.attr] = this.vm.$data[this.exp]
}
get() {
Dep.target = this // 缓存自己
const value = this.vm.$data[this.exp]
Dep.target = null // 释放自己
return value
}
}
依赖收集器
class Dep {
constructor() {
this.target = null
this.subs = []
}
addSubs(sub) {
this.subs.push(sub)
}
notify() {
this.subs.forEach(item => {
item.update()
})
}
}
调用方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>vue响应式原理</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./index.js"></script>
</head>
<body>
<div id="app">
<h1>VUE响应式原理</h1>
<div>
<div v-text="myText"></div>
<div v-text="myBox"></div>
<p v-text="myBox"></p>
<input type="text" v-model="myText">
<input type="text" v-model="myBox">
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
myText: '大吉大利,今晚吃鸡!!!',
myBox: '我是一个盒子'
}
})
</script>
</body>
</html>