seo排名技術(shù)教程seo排名軟件價格
監(jiān)聽屬性watch
監(jiān)聽屬性介紹
我們可以使用 watch 函數(shù)在每次響應(yīng)式狀態(tài)發(fā)生變化時觸發(fā)回調(diào)函數(shù)wach 可以用于異步任務(wù)
監(jiān)聽屬性的初始化
watch和computed都先走initSate判斷傳入選項
export function initState(vm) {const opts = vm.$options; // 獲取所有的選項if (opts.data) {initData(vm);}if (opts.computed) {initComputed(vm);}if (opts.watch) {initWatch(vm);}
}
接下來initWatch進(jìn)入此函數(shù)
function initWatch(vm){let watch = vm.$options.watch;for(let key in watch){const handler = watch[key]; // 字符串 數(shù)組 函數(shù)if(Array.isArray(handler)){for(let i = 0; i < handler.length;i++){createWatcher(vm,key,handler[i]);}}else{createWatcher(vm,key,handler);}}}
通過原型方法$watch傳入處理參數(shù)創(chuàng)建一個觀察者收集依賴變化。
function createWatcher(vm,key,handler){// 字符串 函數(shù)if(typeof handler === 'string'){handler = vm[handler];}return vm.$watch(key,handler)
}
原型上的$watch函數(shù)
export function initStateMixin(Vue) {Vue.prototype.$nextTick = nextTick;// 最終調(diào)用的都是這個方法Vue.prototype.$watch = function (exprOrFn, cb) {// firstname// ()=>vm.firstname// firstname的值變化了 直接執(zhí)行cb函數(shù)即可new Watcher(this, exprOrFn, { user: true }, cb)}
}
計算屬性computed
計算屬性介紹
計算屬性是在 Vue 實例的computed選項中定義的,可以是一個函數(shù)或具有g(shù)et和set方法的對象。函數(shù)形式的計算屬性會在調(diào)用時被執(zhí)行,而對象形式的計算屬性則可以提供自定義的get和set方法
計算屬性適用于那些依賴其他響應(yīng)式數(shù)據(jù)的場景,而不適用于需要進(jìn)行異步操作或有副作用的場景。對于這些情況,可以使用偵聽器(watcher)或使用methods來處理。
計算屬性實現(xiàn)過程
計算屬性的初始化
1 在initComputed函數(shù)中,遍歷計算屬性對象,為每個計算屬性創(chuàng)建一個Watcher實例,并將其存儲在vm._computedWatchers中。
export function initState(vm) {const opts = vm.$options; // 獲取所有的選項if (opts.data) {initData(vm);}if (opts.computed) {initComputed(vm);}if (opts.watch) {initWatch(vm);}
}
function initComputed(vm) {const computed = vm.$options.computed;const watchers = vm._computedWatchers = {}; // 將計算屬性watcher保存到vm上for (let key in computed) {//獲取用戶定義的計算屬性let userDef = computed[key];// 我們需要監(jiān)控 計算屬性中g(shù)et的變化let fn = typeof userDef === 'function' ? userDef : userDef.get// 如果直接new Watcher 默認(rèn)就會執(zhí)行fn, 將屬性和watcher對應(yīng)起來 watchers[key] = new Watcher(vm, fn, { lazy: true })defineComputed(vm, key, userDef);}
}
屬性劫持
2 defineComputed 方法主要是重新定義計算屬性,其實最主要的是劫持get方法。
為啥要劫持呢? 因為我們需要根據(jù)依賴值是否發(fā)生變化來判斷計算屬性是否需要重新計算
function defineComputed(target, key, userDef) {// const getter = typeof userDef === 'function' ? userDef : userDef.get;const setter = userDef.set || (() => { })// 可以通過實例拿到對應(yīng)的屬性Object.defineProperty(target, key, {get: createComputedGetter(key),set: setter})
}
3 createComputedGetter判斷計算屬性的值是否變化 增加dirty
如果是true執(zhí)行更新
// 計算屬性根本不會收集依賴 ,只會讓自己的依賴屬性去收集依賴
function createComputedGetter(key) {// 我們需要檢測是否要執(zhí)行這個getterreturn function () {const watcher = this._computedWatchers[key]; // 獲取到對應(yīng)屬性的watcherif (watcher.dirty) {// 如果是臟的就去執(zhí)行 用戶傳入的函數(shù)watcher.evaluate(); // 求值后 dirty變?yōu)榱薴alse ,下次就不求值了}if (Dep.target) { // 計算屬性出棧后 還要渲染watcher, 我應(yīng)該讓計算屬性watcher里面的屬性 也去收集上一層watcherwatcher.depend();//計算屬性watcher收集渲染watcher}return watcher.value; // 最后返回的是watcher上的值}
}
watcher
新增了dirty屬性 標(biāo)識是否需要更新視圖
增加了evaluate方法 重新渲染 并且將dirty變成true
// src/observer/watcher.js// import { pushTarget, popTarget } from "./dep";
// import { queueWatcher } from "./scheduler";
// import {isObject} from '../util/index'
// // 全局變量id 每次new Watcher都會自增
// let id = 0;export default class Watcher {constructor(vm, exprOrFn, cb, options) {// this.vm = vm;// this.exprOrFn = exprOrFn;// this.cb = cb; //回調(diào)函數(shù) 比如在watcher更新之前可以執(zhí)行beforeUpdate方法// this.options = options; //額外的選項 true代表渲染watcher// this.id = id++; // watcher的唯一標(biāo)識// this.deps = []; //存放dep的容器// this.depsId = new Set(); //用來去重dep// this.user = options.user; //標(biāo)識用戶watcherthis.lazy = options.lazy; //標(biāo)識計算屬性watcherthis.dirty = this.lazy; //dirty可變 表示計算watcher是否需要重新計算 默認(rèn)值是true// 如果表達(dá)式是一個函數(shù)// if (typeof exprOrFn === "function") {// this.getter = exprOrFn;// } else {// this.getter = function () {// //用戶watcher傳過來的可能是一個字符串 類似a.a.a.a.b// let path = exprOrFn.split(".");// let obj = vm;// for (let i = 0; i < path.length; i++) {// obj = obj[path[i]]; //vm.a.a.a.a.b// }// return obj;// };// }// 非計算屬性實例化就會默認(rèn)調(diào)用get方法 進(jìn)行取值 保留結(jié)果 計算屬性實例化的時候不會去調(diào)用getthis.value = this.lazy ? undefined : this.get();}get() {pushTarget(this); // 在調(diào)用方法之前先把當(dāng)前watcher實例推到全局Dep.target上const res = this.getter.call(this.vm); //計算屬性在這里執(zhí)行用戶定義的get函數(shù) 訪問計算屬性的依賴項 從而把自身計算Watcher添加到依賴項dep里面收集起來popTarget(); // 在調(diào)用方法之后把當(dāng)前watcher實例從全局Dep.target移除return res;}// 把dep放到deps里面 同時保證同一個dep只被保存到watcher一次 同樣的 同一個watcher也只會保存在dep一次// addDep(dep) {// let id = dep.id;// if (!this.depsId.has(id)) {// this.depsId.add(id);// this.deps.push(dep);// // 直接調(diào)用dep的addSub方法 把自己--watcher實例添加到dep的subs容器里面// dep.addSub(this);// }// }// 這里簡單的就執(zhí)行以下get方法 之后涉及到計算屬性就不一樣了update() {// 計算屬性依賴的值發(fā)生變化 只需要把dirty置為true 下次訪問到了重新計算if (this.lazy) {this.dirty = true;} else {// 每次watcher進(jìn)行更新的時候 可以讓他們先緩存起來 之后再一起調(diào)用// 異步隊列機(jī)制queueWatcher(this);}}// 計算屬性重新進(jìn)行計算 并且計算完成把dirty置為falseevaluate() {this.value = this.get();this.dirty = false;}depend() {// 計算屬性的watcher存儲了依賴項的deplet i = this.deps.length;while (i--) {this.deps[i].depend(); //調(diào)用依賴項的dep去收集渲染watcher}}// run() {// const newVal = this.get(); //新值// const oldVal = this.value; //老值// this.value = newVal; //跟著之后 老值就成為了現(xiàn)在的值// if (this.user) {// if(newVal!==oldVal||isObject(newVal)){// this.cb.call(this.vm, newVal, oldVal);// }// } else {// // 渲染watcher// this.cb.call(this.vm);// }// }
}
computed和watch的區(qū)別
**相同點:**底層都會創(chuàng)建一個watcher computed定義的屬性可以在模板中使用 watch不能在視圖中國使用
不同點: computed不會默認(rèn)執(zhí)行 只有取值會執(zhí)行 內(nèi)部會以一個dirty屬性控制依賴的值是否變化
watch默認(rèn)用戶會提供一個回調(diào)函數(shù) 數(shù)據(jù)變化就使用用戶傳入的回調(diào)
本周總結(jié)
vue2手寫部分學(xué)習(xí)完了 其實感覺收集依賴那一部分還是有點繞 后續(xù)應(yīng)該會多看點別人總結(jié)的內(nèi)容對著自己代碼復(fù)習(xí)復(fù)習(xí)也學(xué)習(xí)了基礎(chǔ)的webpack
下周主要還是學(xué)習(xí)一下源碼 復(fù)習(xí)一下js高級啥的