本文能帮你做什么?
。。猎奇vue双向绑定的同砚,
能够部份减缓猎奇心
还能够帮你相识如何完成$watch
前情回忆
我之前写了一篇没什么干货的文章。。而且刨了一个大坑。。
本日。。打算来填一天。。并再刨一个。。哈哈
不过话说说回来了.看本文之前,,
假如不晓得Object.defineProperty,还必须看看剖析奇异的 Object.defineProperty
不能不慨叹vue的作者,人长得帅,码写的也好。
本文是依据作者源码,摘取出来的
本文将完成什么
正如上一篇许下的许诺一样,本文要完成一个 $wacth
const v = new Vue({
data:{
a:1,
b:2
}
})
v.$watch("a",()=>console.log("哈哈,$watch胜利"))
setTimeout(()=>{
v.a = 5
},2000) //打印 哈哈,$watch胜利
为了协助人人理清思绪。。我们就做最简朴的完成。。只斟酌对象不斟酌数组
1. 完成 observer
思绪:我们晓得Object.defineProperty的特征了,
我们就利用它的set和get。。我们将要observe的对象,
经由过程递归,将它一切的属性,包含子属性的属性,都给加上set和get,
如许的话,给这个对象的某个属性赋值,就会触发set。。嗯。。最先吧
export default class Observer{
constructor(value) {
this.value = value
this.walk(value)
}
//递归。。让每一个字属性能够observe
walk(value){
Object.keys(value).forEach(key=>this.convert(key,value[key]))
}
convert(key, val){
defineReactive(this.value, key, val)
}
}
export function defineReactive (obj, key, val) {
var childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>val,
set:newVal=> {
childOb = observe(newVal)//假如新赋值的值是个庞杂范例。再递归它,加上set/get。。
}
})
}
export function observe (value, vm) {
if (!value || typeof value !== 'object') {
return
}
return new Observer(value)
}
代码很简朴,就给每一个属性(包含子属性)都加上get/set,
如许的话,这个对象的,有任何赋值,就会触发set要领。。
所以,我们是否是应当写一个音讯-定阅器呢?如许的话,
一触发set要领,我们就发一个关照出来,然后,定阅这个音讯的,
就会如何?。。。对咯。。收到音讯。。。触发还调。
2. 音讯-定阅器
很简朴,我们保护一个数组,,这个数组,就放定阅着,一旦触发notify,
定阅者就挪用本身的update要领
export default class Dep {
constructor() {
this.subs = []
}
addSub(sub){
this.subs.push(sub)
}
notify(){
this.subs.forEach(sub=>sub.update())
}
}
所以,每次set函数,挪用的时刻,我们是否是应当,触发notify,对吧。所以
我们把代码补充完全
export function defineReactive (obj, key, val) {
var dep = new Dep()
var childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>val,
set:newVal=> {
var value = val
if (newVal === value) {
return
}
val = newVal
childOb = observe(newVal)
dep.notify()
}
})
}
那末题目来了。。谁是定阅者。。对,是Watcher。。一旦 dep.notify()
就遍历定阅者,也就是Watcher,并挪用他的update()
要领
3. 完成一个 Watcher
我们设想这个Watcher,应当用什么东西。update要领,嗯这个毋庸置疑,
另有呢,
v.$watch("a",()=>console.log("哈哈,$watch胜利"))
对表达式(就是谁人“a”) 和 回调函数,这是最基本的,所以我们简朴写写
export default class Watcher {
constructor(vm, expOrFn, cb) {
this.cb = cb
this.vm = vm
//此处简化.要辨别fuction照样expression,只斟酌最简朴的expression
this.expOrFn = expOrFn
this.value = this.get()
}
update(){
this.run()
}
run(){
const value = this.get()
if(value !==this.value){
this.value = value
this.cb.call(this.vm)
}
}
get(){
//此处简化。。要辨别fuction照样expression
const value = this.vm._data[this.expOrFn]
return value
}
}
那末题目来了,我们如何将经由过程addSub()
,将Watcher
加进去呢。
我们发明var dep = new Dep()
处于闭包当中,
我们又发明Watcher
的组织函数里会挪用this.get
所以,我们能够在上面动动四肢,
修正一下Object.defineProperty
的get
要挪用的函数,
推断是否是Watcher的组织函数挪用,假如是,申明他就是这个属性的定阅者
坚决将他addSub()
中去,那题目来了,
我如何推断他是Watcher
的this.get
挪用的,而不是我们一般挪用的呢。
对,在Dep定义一个全局唯一的变量,随着思绪我们写一下
export default class Watcher {
....省略未修改代码....
get(){
Dep.target = this
//此处简化。。要辨别fuction照样expression
const value = this.vm._data[this.expOrFn]
Dep.target = null
return value
}
}
如许的话,我们只需要在Object.defineProperty
的get
要挪用的函数里,
推断有无值,就晓得究竟是Watcher 在get,照样我们本身在检察赋值,假如
是Watcher的话就addSub()
,代码补充一下
export function defineReactive (obj, key, val) {
var dep = new Dep()
var childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>{
// 申明这是watch 引发的
if(Dep.target){
dep.addSub(Dep.target)
}
return val
},
set:newVal=> {
var value = val
if (newVal === value) {
return
}
val = newVal
childOb = observe(newVal)
dep.notify()
}
})
}
末了不要遗忘,在Dep.js中加上这么一句
Dep.target = null
4. 完成一个 Vue
还差一步就功德圆满了,我们要把以上代码合营Vue的$watch要领来用,
要watch Vue实例的属性,算了,,不要剖析我在说什么,,直接看代码吧
import Watcher from '../watcher'
import {observe} from "../observer"
export default class Vue {
constructor (options={}) {
//这里简化了。。实在要merge
this.$options=options
//这里简化了。。实在要辨别的
let data = this._data=this.$options.data
Object.keys(data).forEach(key=>this._proxy(key))
observe(data,this)
}
$watch(expOrFn, cb, options){
new Watcher(this, expOrFn, cb)
}
_proxy(key) {
var self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return self._data[key]
},
set: function proxySetter (val) {
self._data[key] = val
}
})
}
}
异常简朴。。两件事,observe本身的data,代办本身的data,
使接见本身的属性,就是接见子data的属性。。
停止到现在,在我们只斟酌最简朴情况下。。全部流程终究跑通了。。一定会有
许多bug,本文重要目标是展现全部工作流,协助读者明白。。
代码在https://github.com/georgebbbb…,
我是一万个不想展现本身代码。。由于许多槽点,还请包涵
下一篇,有两个方向,将聊一聊如何完成双向绑定,或者是如何watch数组。
关于vue2.0的新文章
100行代码,明白和剖析vue2.0的相应式架构