摘要:由來最近在看深入淺出,第一篇變化偵測,想把自己的理解總結一下。的變化偵測總結一下我看了后的理解將數據變成可響應式的,即將數據變成可監聽的。
由來最近在看“深入淺出vuejs”,第一篇變化偵測,想把自己的理解總結一下。
Object的變化偵測 總結一下我看了后的理解將數據變成可響應式的,即將數據變成可監聽的。通過Observer類來實現
依賴是什么?就是這個數據在哪里用到了,相當于this當前的上下文;所以當數據變化時,我們可以通知他,觸發update,從而觸發渲染
那么這個依賴,誰來收集存起來。通過Dep類來實現
class Observer {
constructor(value) {
this.value = value
if(!Array.isArray(value) {
this.walk(value)
}
}
walk (obj) {
const keys = Object.keys(obj)
for(let i = 0; i < keys.length; i++) {
definedReactive(obj, keys[i], obj[keys[i]])
}
}
}
function definedReactive(data, key, value) {
if(typeof val === "object") {
new Observer(value)
}
let dep = new Dep()
Object.defineProperty(data, key, {
enumberable: true,
configurable: true,
get: function () {
dep.depend()
return value
},
set: function (newVal) {
if(value === newVal) { //這邊最好是value === newVal || (value !== value && newVal !== newVal)
return
}
value = newVal //這邊新的newVal如果是引用類型也應該進行進行new Observer()
dep.notify()
}
})
}
將vue中的data對象進行遍歷設置其屬性描述對象
get的設置就是為了在數據被訪問時,將依賴dep.depend()進去,至于做了什么看詳細看Dep類
set的設置則是為了判斷新值和舊值是否一樣(注意NaN),若不一樣,則執行dep.notify(),通知相應依賴進行更新變化
class Dep {
constructor () {
this.subs = [] //存放依賴
}
addSub () {
this.subs.push(sub)
},
remove () {
remove(this.subs, sub)
},
depend () {
if(window.target) {
this.addSub(window.target) //window.target 是this,watcher的上下文
}
},
notify () {
const subs = this.subs.slice()
for(let i = 0, l = subs.length; i < l; i ++) {
subs[i].update() //update這個方法來自watcher實例對象的方法
}
}
}
function remove(arr, item) {
if(arr.length) {
const index = arr.indexOf(item)
if(index > -1) {
return arr.splice(index, 1)
}
}
}
主要就是對dep實例對象的增刪改查的操作
window.target 這個依賴怎么來,就看watcher實例對象了
初版:
class Watcher {
constructor (vm, expOrFn, cb) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
window.target = this
let value = this.getter.call(this.vm, this.vm)
window.target = undefined
return value
}
update() {
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
分析
怎么觸發?可以利用
vm.$watch("data.a", function (newValue, oldValue) {
//執行相關操作
})
parsePath(expOrFn)做了什么?從下面代碼中可以看出作用就是返回一個函數,這個函數用來讀取value值
const bailRE = /[^w.$]/ //
function parsePath(path) {
if(bailRE.test(path) {
return //當path路徑中有一個字符不滿足正則要求就直接return
}
return function () {
const arr = path.split(".")
let data = this
for(let i = 0, l = arr.length; i < l; i ++) {
let data = data.arr[i]
}
return data
}
}
在new Watcher時會執行this.value,從而執行this.get(),所以這時的window.target是當前watcher實例對象this;接著執行this.getter.call(this.vm, this.vm),觸發屬性描述對象的get方法,進行dep.depend(),最后將其window.target = undefined
update的方法是在數據改變后觸發,但這邊有個問題就是會重復添加依賴
依賴被重復添加
只能對已有key進行監聽
刪除key-value不會被監聽
對數組對象,并沒有添加監聽
對于數據變化時,并沒有對新數據判斷是否需要進行Observer
通過push, pop, shift, unshift, splice, sort, reverse這幾個方法的封裝來觸發dep.notify()
怎么的封裝?分兩種;第一種對于支持_proto_屬性的,直接改寫原型鏈的這些方法;第二種對于不支持的,直接在實例對象上添加改變后的7個方法
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto) //新建對象,繼承Array的原型鏈
class Observer {
constructor (value) {
this.value = value
this.dep = new Dep() //在Observer中添加dep屬性為了記錄數組的依賴
def(value, "_ob_", this) //在當前value上新增`_ob_`屬性,其值為this,當前observer實例對象
if(Array.isArray(value) {
const augment = hasProto ");else {
this.walk(value)
}
}
//新增
observerArray (items) {
for(let i = 0, l = items.length; i < l; i ++) {
observe(items[i])
}
}
}
//作用就是為obj,添加key值為val
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
function observe(value, asRootData) {
if(!isObject(value)) {
return
}
let ob
//判斷value是否已經是Observer實例對象,避免重復執行Observer
if(hasOwn(value, "_ob_") && value._ob_ instanceof Observer) {
ob = value._ob_
} else {
ob = new Observer(value)
}
return ob
}
function definedReactive(data, key, value) {
let childOb = observe(value) //修改
let dep = new Dep()
Object.defineProperty(data, key, {
enumberable: true,
configurable: true,
get: function () {
dep.depend()
if(childOb) { //新增
childOb.dep.depend()
}
return value
},
set: function (newVal) {
if(value === newVal) { //這邊最好是value === newVal || (value !== value && newVal !== newVal)
return
}
value = newVal //這邊新的newVal如果是引用類型也應該進行進行new Observer()
dep.notify()
}
})
}
//觸發數組攔截
;[
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
].forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator() {
const result = original.apply(this, args)
const ob = this._ob_ //this就是數據value
let inserted
//對于新增變化的元素頁進行observerArray()
switch (method) { //因為這幾個是有參數的
case "push":
case "unshift": //因為push和unshift都是一樣的取args,所以push不需要加break了
inserted = args
break
case "splice": //新增變化元素是從索引2開始的
inserted = args.slice(2)
break
}
ob.dep.notify() //通知依賴執行update
return result
})
}
首先對data對象進行Observer,將執行this.walk(data)
接著執行let childOb = observe(val),發現value是一個數組對象,進行Observer,主要進行是augment(value, arrayMethods, arrayKeys),將7個方法進行攔截,接著遍歷內部元素是否有引用數據類型,有繼續Observer,最后返回Observer實例對象ob
重點是get方法,當數據data被訪問時,首先執行dep.depend()這里將依賴添加到data的dep中;接著因為childOb為true所以執行childOb.dep.depend(),這里是將依賴加入到observer實例對象的dep中,為什么,這個dep是給數組發生變化時執行this._ob_.dep.notify(),這個this就是value對象,因為def(value, "_ob_", this),所以可以執行dep.notify()
對于進行this.list.length = 0進行清空時,不會觸發它的依賴更新,也就不會觸發視圖的渲染更新
對于this.list[0] = 2,這種通過索引來改變元素值時頁一樣不會觸發更新
所以我們盡量避免通過這種方式來改變數據
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6785.html
摘要:總結最后我們依照下圖參考深入淺出,再來回顧下整個過程在后,會調用函數進行初始化,也就是過程,在這個過程通過轉換成了的形式,來對數據追蹤變化,當被設置的對象被讀取的時候會執行函數,而在當被賦值的時候會執行函數。 前言 Vue 最獨特的特性之一,是其非侵入性的響應式系統。數據模型僅僅是普通的 JavaScript 對象。而當你修改它們時,視圖會進行更新。這使得狀態管理非常簡單直接,不過理解...
vm.$watch 用法: vm.$watch( expOrFn, callback, [options] ),返回值為unwatch是一個函數用來取消觀察;下面主要理解options中的兩個參數deep和immediate以及unwatch Vue.prototype.$watch = function (expOrFn, cb, options) { const vm = this ...
摘要:中的觀察者模式觀察者模式一般包含發布者和訂閱者兩種角色顧名思義發布者負責發布消息,訂閱者通過訂閱消息響應動作了。中主要有兩種類型的,一種是另外一種是是通過或者中的屬性定義的。結束好了,基本結束,如有錯漏,望指正。 碎碎念 四月份真是慵懶無比的一個月份,看著手頭上沒啥事干,只好翻翻代碼啥的,看了一會Vue的源碼,忽而有點感悟,于是便記錄一下。 Vue中的觀察者模式 觀察者模式一般包含發布...
摘要:雙向數據綁定指的是,將對象屬性變化與視圖的變化相互綁定。數據雙向綁定已經了解到是通過數據劫持的方式來做數據綁定的,其中最核心的方法便是通過來實現對屬性的劫持,達到監聽數據變動的目的。和允許觀察數據的更改并觸發更新。 1 MVVM 雙向數據綁定指的是,將對象屬性變化與視圖的變化相互綁定。換句話說,如果有一個擁有name屬性的user對象,與元素的內容綁定,當給user.name賦予一個新...
閱讀 2772·2021-11-02 14:42
閱讀 3163·2021-10-08 10:04
閱讀 1184·2019-08-30 15:55
閱讀 1025·2019-08-30 15:54
閱讀 2311·2019-08-30 15:43
閱讀 1680·2019-08-29 15:18
閱讀 863·2019-08-29 11:11
閱讀 2362·2019-08-26 13:52