Vue偵測(cè)相關(guān)api的實(shí)現(xiàn)方法
vm.$watch
用法: vm.$watch( expOrFn, callback, [options] ) ,返回值為 unwatch 是一個(gè)函數(shù)用來(lái)取消觀察;下面主要理解 options 中的兩個(gè)參數(shù) deep 和 immediate 以及 unwatch
Vue.prototype.$watch = function (expOrFn, cb, options) {
const vm = this
options = options || {}
const watcher = new Watcher(vm, expOrFn, cb, options)
if(options.immediate) {
cb.call(vm, watcher,.value)
}
return function unwatchFn() {
watcher.teardown()
}
}
immediate
從上面代碼中可以看出當(dāng) immediate 為 true 時(shí),就會(huì)直接進(jìn)行執(zhí)行回調(diào)函數(shù)
unwatch
實(shí)現(xiàn)方式是:
- 將被訪問(wèn)到的數(shù)據(jù) dep 收集到 watchs 實(shí)例對(duì)象上,通過(guò) this.deps 存起來(lái)
- 將被訪問(wèn)到的數(shù)據(jù) dep.id 收集到 watchs 實(shí)例對(duì)象上,通過(guò) this.depIds 存起來(lái)
- 最后通過(guò) watchs 實(shí)例對(duì)象的 teardown 進(jìn)行刪除
class Watcher {
constructor (vm, expOrFn, cb) {
this.vm = vm
this.deps = []
this.depIds = new Set()
if(typeof expOrFn === 'function') {
this.getter = expOrFn
}else {
this.getter = parsePath(expOrFn)
}
this.cb = cb
this.value = this.get()
}
....
addDep (dep) {
const id = dep.id //參數(shù)dep是Dep實(shí)例對(duì)象
if(!this.depIds.has(id)) { //判斷是否存在避免重復(fù)添加
this.depIds.add(id)
this.deps.push(dep)
dep.addSub(this) //this 是依賴
}
}
teardown () {
let i = this.deps.length
while (i--) {
this.deps[i].removeSub(this)
}
}
}
let uid = 0
class Dep {
constructor () {
this.id = uid++
...
}
...
depend () {
if(window.target) {
window.target.addDep(this) //將this即當(dāng)前dep對(duì)象加入到watcher對(duì)象上
}
}
removeSub (sub) {
const index = this.subs.indexOf(sub)
if(index > -1) {
return this.subs.splice(index, 1)
}
}
}
分析
當(dāng)執(zhí)行 teardown() 時(shí)需要循環(huán);因?yàn)槔?expOrFn = function () { return this.name + this.age } ,這時(shí)會(huì)有兩個(gè) dep 分別是 name 與 age 分別都加入了 watcher 依賴( this ),都會(huì)加入到 this.deps 中,所以需要循環(huán)將含有依賴的 dep 都刪除其依賴
deep
需要明白的是
- deep 干啥用的,例如 data = {arr: [1, 2, {b: 6]} ,當(dāng)我們只是監(jiān)聽(tīng) data.arr 時(shí),在 [1, 2, {b: 66}] 這個(gè)數(shù)值內(nèi)部發(fā)生變化時(shí),也需要觸發(fā),即 b = 888
怎么做呢?
class Watcher {
constructor (vm, expOrFn, cb, options) {
this.vm = vm
this.deps = []
this.depIds = new Set()
if(typeof expOrFn === 'function') {
this.getter = expOrFn
}else {
this.getter = parsePath(expOrFn)
}
if(options) { //取值
this.deep = !!options.deep
}else {
this.deep = false
}
this.cb = cb
this.value = this.get()
}
get () {
window.target = this
let value = this.getter.call(vm, vm)
if(this.deep) {
traverse(value)
}
window.target = undefined
return value
}
...
}
const seenObjects = new Set()
function traverse (val) {
_traverse(val, seenObjects)
seenObjects.clear()
}
function _traverse(val, seen) {
let i, keys
const isA = Array.isArray(val)
if((!isA && isObject(val)) || Object.isFrozen(val)) { //判斷val是否是對(duì)象或者數(shù)組以及是否被凍結(jié)
return
}
if(val._ob_) {
const depId = val._ob_.dep.id //可以看前面一篇我們對(duì)Observer類添加了this.dep = new Dep(),所以能訪問(wèn)其dep.id
if(seen.has(depId)) {
return
}
seen.add(depId)
}
if(isA) {
i = val.length
while (i--) _traverse(val[i], seen)
} else {
keys = Object.keys(val)
i = keys.length
while (i--) _traverse(val[i], seen)
}
}
分析
- window.target = this ,寄存依賴
- let value = this.getter.call(vm, vm) 訪問(wèn)當(dāng)前val,并執(zhí)行 get
的 dep.depend() ,如果發(fā)現(xiàn) val 為數(shù)組,則將依賴加入到 observer 的 dep 中,也就實(shí)現(xiàn)了對(duì)當(dāng)前數(shù)組的攔截
- traverse(value) 也就是執(zhí)行 _traverse(val, seenObjects) ;核心就是對(duì)被 Observer 的 val 通過(guò) val[i] 通過(guò)這種操作,間接觸發(fā) get ,將依賴添加到當(dāng)前數(shù)值的 dep 中,這樣也就實(shí)現(xiàn)了,當(dāng)內(nèi)部數(shù)據(jù)發(fā)生變化,也會(huì)循環(huán) subs 執(zhí)行依賴的 update ,從而觸發(fā)回調(diào);當(dāng)是數(shù)組時(shí),只需進(jìn)行遍歷,看內(nèi)部是否有 Object 對(duì)象即可,因?yàn)樵诘诙降臅r(shí)候,會(huì)對(duì) val 進(jìn)行判斷是否是數(shù)組,變改變七個(gè)方法的value,在遍歷;所以這邊只要是內(nèi)部數(shù)組都會(huì)進(jìn)行攔截操作,添加依賴,即對(duì)象 {} 這種沒(méi)沒(méi)添加依賴。
- seenObjects.clear() 當(dāng)內(nèi)部所以類型數(shù)據(jù)都添加好其依賴后,就清空。
- window.target = undefined 消除依賴
vm.$set
用法: vm.$set(target, key, value)
作用
- 對(duì)于數(shù)組,進(jìn)行 set 則是添加新元素,并需要觸發(fā)依賴更新
- 對(duì)于對(duì)象,如果 key 值存在,則是修改 value ;不存在,則是添加新元素,需新元素要進(jìn)行響應(yīng)式處理,以及觸發(fā)更新
- 對(duì)于對(duì)象本身不是響應(yīng)式,則直接添加 key-value ,無(wú)需處理
Vue.prototype.$set = function (target, key, val) {
if(Array.isArray(target) && isValidArrayIndex(key)) { //是數(shù)組并且key有效
target.length = Math.max(target.length, key) //處理key > target.length
target.splice(key, 1, val) //添加新元素,并輸出依賴更新同時(shí)新元素也會(huì)進(jìn)行`Obsever`處理
return val
}
if(key in targert && !(key in Object.prototype) { //能遍歷并且是自身key
target[key] = val //觸發(fā)set,執(zhí)行依賴更新
return val
}
const ob = target._ob_
if(target.isVue || (ob && ob.vm.Count) { //不是vue實(shí)例也不是vue實(shí)例的根對(duì)象(即不是this.$data跟對(duì)象)
//觸發(fā)警告
return
}
if(!ob) { //只添加
target[key] = val
return val
}
defineReactive(ob.value, key, val) //進(jìn)行響應(yīng)式處理
ob.dep.notify() //觸發(fā)依賴更新
returnv val
}
vm.$delete
用法: vm.$delete( target, key)
作用
- 對(duì)于數(shù)組,進(jìn)行 delete 則是刪除新元素,并需要觸發(fā)依賴更新
- 對(duì)于對(duì)象,如果 key 值不存在,直接 return ,存在,刪除元素,
- 對(duì)于對(duì)象本身不是響應(yīng)式,則只刪除 key-value ,無(wú)需其他處理
Vue.prototype.$delete = function (target, key) {
if(Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = target._ob_
if(target.isVue || (ob && ob.vm.Count) { //不是vue實(shí)例也不是vue實(shí)例的根對(duì)象(即不是this.$data跟對(duì)象)
//觸發(fā)警告
return
}
if(!hasOwn(target, key)) {
return
}
delete target[key]
if(!ob) {
return
}
ob.dep.notify()
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue中Axios的封裝與API接口的管理詳解
- 詳解vue-cli本地環(huán)境API代理設(shè)置和解決跨域
- Vue2 配置 Axios api 接口調(diào)用文件的方法
- vue項(xiàng)目中api接口管理總結(jié)
- 深入理解Vue官方文檔梳理之全局API
- Nginx 解決WebApi跨域二次請(qǐng)求以及Vue單頁(yè)面的問(wèn)題
- vue 項(xiàng)目打包通過(guò)命令修改 vue-router 模式 修改 API 接口前綴
- Vue官方推薦AJAX組件axios.js使用方法詳解與API
- vue.js的vue-cli腳手架中使用百度地圖API的實(shí)例
相關(guān)文章
Vue+Element實(shí)現(xiàn)表格單元格編輯
這篇文章主要為大家詳細(xì)介紹了Vue+Element實(shí)現(xiàn)表格單元格編輯,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Vue使用json-server進(jìn)行后端數(shù)據(jù)模擬功能
這篇文章主要介紹了Vue使用json-server進(jìn)行后端數(shù)據(jù)模擬功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-04-04
react+?ts?vite搭建及二次封裝請(qǐng)求的過(guò)程解析
這篇文章主要介紹了react+?ts?vite搭建及二次封裝請(qǐng)求,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
Vue 實(shí)現(xiàn)從文件中獲取文本信息的方法詳解
這篇文章主要介紹了Vue 實(shí)現(xiàn)從文件中獲取文本信息的方法,結(jié)合實(shí)例形式詳細(xì)分析了vue.js基于export導(dǎo)出的文件信息讀取相關(guān)操作技巧,需要的朋友可以參考下2019-10-10
深入淺出 Vue 系列 -- 數(shù)據(jù)劫持實(shí)現(xiàn)原理
深入淺出 Vue 系列 -- 數(shù)據(jù)劫持實(shí)現(xiàn)原理2019-04-04

