- 什么是vue响应式
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新 。 --官方文档
例如
<div id="app">
<div>{{price}}</div>
<div>{{num}}</div>
<div>{{total}}</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
price: 1,
num: 2
},
computed: {
total() {
return this.price * this.num
}
}
})
vue总共执行了三步
- price变化更新页面
- num变化更新页面
- 调用tatal函数计算属性,更新页面
total值是计算的相对应结果,这个不难理解,但是当data中数据发生变化的时候,total也发生了变化,这个也能计算出来,但是存在一个疑问?
vue是怎么知道data中的数据发生改变的呢?
这是我们会思考,如果把tota的计算的结果保存起来,在下次需要的时候,再次调用即可l
var price = 1
var num = 3
var total = 0
var taget = function() { //taget存储了一个匿名函数
return total = price * num
}
taget()
//缓存起来,在需要的时候调用
var storage = []
record()
function record() {
storage.push(taget)
}
function replay() {
storage.forEach(item => item())
}
price = 44
replay()
console.log(total);
不难看出当price发生变化的时候,调用replay方法执行taget方法,功能能实现,但是我们需要封装一个类,来进行对目标的管理,在需要重新执行的时候通知执行
class Dep {
constructor(){
this.subArr=[]
}
depand(){
if(taget&&!this.subArr.includes(taget)){
this.subArr.push(taget)
}
}
notify(){
this.subArr.forEach(item=>item())
}
}
定义了一个dep类,用来管理所有的依赖,现在用来记录函数的是depand函数,用来通知的函数是notify
然而我们在每次创建一个dep类时,不应该还是使用watcher函数来监听数据的变化,而不是用target函数
watcher(()=>{
total=price*num
})
现在可以实现一下watcher函数
function watcher(func){
target=func
dep.depend()
target()
target=null
}
我们已经实现一个简单的dep类,我们说观测的是一个一个的数据,现在我们可以把这些数据以属性值的方式写入对象
let data={price:1,num:3 }
这时就可以观测对象的每一个属性,通过Object.defineProperty()的setter和getter的方法来进行监测
let data = {
price: 1,
num: 3
}
Object.defineProperty(data, 'price', {
get() {
console.log('获取属性时触发');
},
set(newVal) {
console.log(newVal);
}
})
get方法每当读取data中price的值时就会触发的函数
set方法每当设置data中price的值时就会触发
目前我们通过get记录了price依赖项,但属性值发生变化的时候就去执行set
对于dep类来说:
1 当price被读取时,就带调用dep.depand()来记录当前目标函数
2 当price被写时,就调用dep.notify()函数去只想所有的目标函数
var data={
price:1,
num:3
}
var target =null
class Dep {
constructor(){
this.subArr=[]
}
depand(){
if(taget&&!this.subArr.includes(taget)){
this.subArr.push(taget)
}
}
notify(){
this.subArr.forEach(item=>item())
}
}
整合之后的全部代码
var data = {
price: 1,
num: 3
}
var target = null
class Dep {
constructor() {
this.subArr = []
}
depand() {
if (target && !this.subArr.includes(target)) {
this.subArr.push(target)
}
}
notify() {
this.subArr.forEach(item => item())
}
}
Object.keys(data).forEach(item => {
let value = data[item]
const dep = new Dep()
Object.defineProperty(data, item, {
get() {
dep.depand()
return value
},
set(newval) {
value = newval
dep.notify()
}
})
function watcher(func) {
target = func
dep.depand()
target()
target = null
}
watcher(() => {
total = data.price * data.num
console.log(total);
})
})
当明白了这些官网的这张图就不难理解了,data中的数据price和num始终是被监听的,但数据变化时就会depand函数就会去收集所有的依赖数据,而notfiy就会执行目标函数,从而更新视图,watcher把需要监听的函数添加到target中