欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解Vue3響應(yīng)式原理

 更新時(shí)間:2022年12月19日 10:18:49   作者:helloworld1024fd  
響應(yīng)式就是當(dāng)對(duì)象本身(對(duì)象的增刪值)或者對(duì)象屬性(重新賦值)發(fā)生變化時(shí),將會(huì)運(yùn)行一些函數(shù),最常見的就是render函數(shù),下面這篇文章主要給大家介紹了關(guān)于Vue3響應(yīng)式原理的相關(guān)資料,需要的朋友可以參考下

響應(yīng)式原理

利用ES6中Proxy作為攔截器,在get時(shí)收集依賴,在set時(shí)觸發(fā)依賴,來(lái)實(shí)現(xiàn)響應(yīng)式。

手寫實(shí)現(xiàn)

1、實(shí)現(xiàn)Reactive

基于原理,我們可以先寫一下測(cè)試用例

 //reactive.spec.ts
 describe("effect", () => {
   it("happy path", () => {
     const original = { foo: 1 }; //原始數(shù)據(jù)
     const observed = reactive(original); //響應(yīng)式數(shù)據(jù)
     expect(observed).not.toBe(original);
     expect(observed.foo).toBe(1); //正常獲取數(shù)據(jù)
     expect(isReactive(observed)).toBe(true);
     expect(isReactive(original)).toBe(false);
     expect(isProxy(observed)).toBe(true);
   });
 });

首先實(shí)現(xiàn)數(shù)據(jù)的攔截處理,通過(guò)ES6的Proxy,實(shí)現(xiàn)獲取和賦值操作。

 //reactive.ts
 //對(duì)new Proxy()進(jìn)行包裝
 export function reactive(raw) {
   return createActiveObject(raw, mutableHandlers);
 }
 function createActiveObject(raw: any, baseHandlers) {
   //直接返回一個(gè)Proxy對(duì)象,實(shí)現(xiàn)響應(yīng)式
   return new Proxy(raw, baseHandlers);
 }
 //baseHandler.ts
 //抽離出一個(gè)handler對(duì)象
 export const mutableHandlers = {
   get:createGetter(),
   set:createSetter(),
 };
 function createGetter(isReadOnly: Boolean = false, shallow: Boolean = false) {
   return function get(target, key) {
     const res = Reflect.get(target, key);
     // 看看res是否是一個(gè)object
     if (isObject(res)) {
       //如果是,則進(jìn)行嵌套處理,使得返回的對(duì)象中的 對(duì)象 也具備響應(yīng)式
       return isReadOnly ? readonly(res) : reactive(res);
     }
     if (!isReadOnly) {
       //如果不是readonly類型,則收集依賴
       track(target, key);
     }
     return res;
   };
 }
 function createSetter() {
   return function set(target, key, value) {
     const res = Reflect.set(target, key, value);
     //觸發(fā)依賴
     trigger(target, key);
     return res;
   };
 }

從上述代碼中,我們可以??注意到track(target, key) 和trigger(target, key) 這兩個(gè)函數(shù),分別是對(duì)依賴的收集和觸發(fā)。

依賴:我們可以把依賴認(rèn)為是把用戶對(duì)數(shù)據(jù)的操控(用戶函數(shù),副作用函數(shù))包裝成一個(gè)東西,我們?cè)趃et的時(shí)候?qū)⒁蕾囈粋€(gè)一個(gè)收集起來(lái),set的時(shí)候全部觸發(fā),即可實(shí)現(xiàn)響應(yīng)式效果。

2、實(shí)現(xiàn)依賴的收集和觸發(fā)

 //effect.ts
 //全局變量
 let activeEffect: ReactiveEffect; //當(dāng)前的依賴
 let shouldTrack: Boolean; //是否收集依賴
 const targetMap = new WeakMap(); //依賴樹

targetMap結(jié)構(gòu):

targetMap: {

每一個(gè)target(depsMap):{

每一個(gè)key(depSet):[

每一個(gè)依賴

]

}

}

WeakMap和Map的區(qū)別

1、WeakMap只接受對(duì)象作為key,如果設(shè)置其他類型的數(shù)據(jù)作為key,會(huì)報(bào)錯(cuò)。

2、WeakMap的key所引用的對(duì)象都是弱引用,只要對(duì)象的其他引用被刪除,垃圾回收機(jī)制就會(huì)釋放該對(duì)象占用的內(nèi)存,從而避免內(nèi)存泄漏。

3、由于WeakMap的成員隨時(shí)可能被垃圾回收機(jī)制回收,成員的數(shù)量不穩(wěn)定,所以沒有size屬性。

4、沒有clear()方法

5、不能遍歷

首先我們定義一個(gè)依賴類,稱為ReactiveEffect,對(duì)用戶函數(shù)進(jìn)行包裝,賦予一些屬性和方法。參考:前端手寫面試題詳細(xì)解答

 //effect.ts
 //響應(yīng)式依賴 — ReactiveEffect類
 class ReactiveEffect {
   private _fn: any;  //用戶函數(shù),
   active = true; //表示當(dāng)前依賴是否激活,如果清除過(guò)則為false
   deps: any[] = []; //包含該依賴的deps
   onStop?: () => void;  //停止該依賴的回調(diào)函數(shù)
   public scheduler: Function;  //調(diào)度函數(shù)
   //構(gòu)造函數(shù)
   constructor(fn, scheduler?) {
     this._fn = fn;
     this.scheduler = scheduler;
   }
   //執(zhí)行副作用函數(shù)
   run() {
     //用戶函數(shù),可以報(bào)錯(cuò),需要用try包裹
     try {
       //如果當(dāng)前依賴不是激活狀態(tài),不進(jìn)行依賴收集,直接返回
       if (!this.active) {
         return this._fn();
       }
       //開啟依賴收集
       shouldTrack = true;
       activeEffect = this;
       //調(diào)用時(shí)會(huì)觸發(fā)依賴收集
       const result = this._fn();
       //關(guān)閉依賴收集
       shouldTrack = false;
       //返回結(jié)果
       return result;
     } finally {
       //todo
     }
   }
 }

effect影響函數(shù)

創(chuàng)建一個(gè)用戶函數(shù)作用函數(shù),稱為effect,這個(gè)函數(shù)的功能為基于ReactiveEffect類創(chuàng)建一個(gè)依賴,觸發(fā)用戶函數(shù)(的時(shí)候,觸發(fā)依賴收集),返回用戶函數(shù)。

 //創(chuàng)建一個(gè)依賴
 export function effect(fn, option: any = {}) {
   //為當(dāng)前的依賴創(chuàng)建響應(yīng)式實(shí)例
   const _effect = new ReactiveEffect(fn, option.scheduler);
   Object.assign(_effect, option);
   //最開始調(diào)用一次,其中會(huì)觸發(fā)依賴收集  _effect.run() -> _fn() -> get() -> track()
   _effect.run();
   const runner: any = _effect.run.bind(_effect);
   //在runner上掛載依賴,方便在其他地方通過(guò)runner訪問(wèn)到該依賴
   runner.effect = _effect;
   return runner;
 }

bind():在原函數(shù)的基礎(chǔ)上創(chuàng)建一個(gè)新函數(shù),使新函數(shù)的this指向傳入的第一個(gè)參數(shù),其他參數(shù)作為新函數(shù)的參數(shù)

用戶觸發(fā)依賴收集時(shí),將依賴添加到targetMap中。

收集/添加依賴

 //把依賴添加到targetMap對(duì)應(yīng)target的key中,在重新set時(shí)在trigger中重新觸發(fā)
 export function track(target: Object, key) {
   //如果不是track的狀態(tài),直接返回
   if (!isTracking()) return;
   // target -> key -> dep
   //獲取對(duì)應(yīng)target,獲取不到則創(chuàng)建一個(gè),并加進(jìn)targetMap中
   let depsMap = targetMap.get(target);
   if (!depsMap) {
     targetMap.set(target, (depsMap = new Map()));
   }
   //獲取對(duì)應(yīng)key,獲取不到則創(chuàng)建一個(gè),并加進(jìn)target中
   let depSet = depsMap.get(key);
   if (!depSet) {
     depsMap.set(key, (depSet = new Set()));
   }
   //如果depSet中已經(jīng)存在該依賴,直接返回
   if (depSet.has(activeEffect)) return;
   //添加依賴
   trackEffects(depSet);
 }
 export function trackEffects(dep) {
   //往target中添加依賴
   dep.add(activeEffect);
   //添加到當(dāng)前依賴的deps數(shù)組中
   activeEffect.deps.push(dep);
 }

觸發(fā)依賴

 //一次性觸發(fā)對(duì)應(yīng)target中key的所有依賴
 export function trigger(target, key) {
   let depsMap = targetMap.get(target);
   let depSet = depsMap.get(key);
   //觸發(fā)依賴
   triggerEffects(depSet);
 }
 export function triggerEffects(dep) {
   for (const effect of dep) {
     if (effect.scheduler) {
       effect.scheduler();
     } else {
       effect.run();
     }
   }
 }

3、移除/停止依賴

我們?cè)赗eactiveEffect這個(gè)類中,增加一個(gè)stop方法,來(lái)暫停依賴收集和清除已經(jīng)存在的依賴

 //響應(yīng)式依賴 — 類
 class ReactiveEffect {
   private _fn: any;  //用戶函數(shù),
   active = true; //表示當(dāng)前依賴是否激活,如果清除過(guò)則為false
   deps: any[] = []; //包含該依賴的deps
   onStop?: () => void;  //停止該依賴的回調(diào)函數(shù)
   public scheduler: Function;  //調(diào)度函數(shù)
   //...
   stop() {
     if (this.active) {
       cleanupEffect(this);
       //執(zhí)行回調(diào)
       if (this.onStop) {
         this.onStop();
       }
       //清除激活狀態(tài)
       this.active = false;
     }
   }
 }
 //清除該依賴掛載的deps每一項(xiàng)中的該依賴
 function cleanupEffect(effect) {
   effect.deps.forEach((dep: any) => {
     dep.delete(effect);
   });
   effect.deps.length = 0;
 }
 //移除一個(gè)依賴
 export function stop(runner) {
   runner.effect.stop();
 }

衍生類型

1、實(shí)現(xiàn)readonly

readonly相比于reactive,實(shí)現(xiàn)上相對(duì)比較簡(jiǎn)單,它是一個(gè)只讀類型,不會(huì)涉及set操作,更不需要收集/觸發(fā)依賴。

 export function readonly(raw) {
   return createActiveObject(raw, readonlyHandlers);
 }
 export const readonlyHandlers = {
   get: readonlyGet,
   set: (key, target) => {
     console.warn(`key:${key} set 失敗,因?yàn)閠arget是一個(gè)readonly對(duì)象`, target);
     return true;
   },
 };
 const readonlyGet = createGetter(true);
 function createGetter(isReadOnly: Boolean = false, shallow: Boolean = false) {
   return function get(target, key) {
     if (key === ReactiveFlags.IS_REACTIVE) {
       return !isReadOnly;
     } else if (key === ReactiveFlags.IS_READONLY) {
       return isReadOnly;
     }
     //...
     // 看看res是否是一個(gè)object
     if (isObject(res)) {
       return isReadOnly ? readonly(res) : reactive(res);
     }
 ?
     if (!isReadOnly) {
       //收集依賴
       track(target, key);
     }
     return res;
   };
 }

2、實(shí)現(xiàn)shallowReadonly

我們先看一下shallow的含義

shallow:不深的, 淺的,不深的, 不嚴(yán)肅的, 膚淺的,淺薄的。

那么shallowReadonly,指的是只對(duì)最外層進(jìn)行限制,而內(nèi)部的仍然是一個(gè)普通的、正常的值。

 //shallowReadonly.ts
 export function shallowReadonly(raw) {
   return createActiveObject(raw, shallowReadonlyHandlers);
 }
 export const shallowReadonlyHandlers = extend({}, readonlyHandlers, {
   get: shallowReadonlyGet,
 });
 const shallowReadonlyGet = createGetter(true, true);
 function createGetter(isReadOnly: Boolean = false, shallow: Boolean = false) {
   return function get(target, key) {
     //..
     const res = Reflect.get(target, key);
     //是否shallow,是的話很直接返回
     if (shallow) {
       return res;
     }
     if (isObject(res)) {
       //...
     }
   };
 }

3、實(shí)現(xiàn)ref

ref相對(duì)reactive而言,實(shí)際上他不存在嵌套關(guān)系,就是一個(gè)value。

 //ref.ts
 export function ref(value: any) {
   return new RefImpl(value);
 }

我們來(lái)實(shí)現(xiàn)一下RefImpl類,原理其實(shí)跟reactive類似,只是一些細(xì)節(jié)處不同。

 //ref.ts
 class RefImpl {
   private _value: any; //轉(zhuǎn)化后的值
   public dep; //依賴容器
   private _rawValue: any; //原始值,
   public _v_isRef = true; //判斷ref類型
   constructor(value) {
     this._rawValue = value; //記錄原始值
     this._value = convert(value); //存儲(chǔ)轉(zhuǎn)化后的值
     this.dep = new Set(); //創(chuàng)建依賴容器
   }
   get value() {
     trackRefValue(this); //收集依賴
     return this._value;
   }
   set value(newValue) {
     //新老值不同,才觸發(fā)更改
     if (hasChanged(newValue, this._rawValue)) {
       // 一定先修改value,再觸發(fā)依賴
       this._rawValue = newValue;
       this._value = convert(newValue);
       triggerEffects(this.dep);
     }
   }
 }
 //ref.ts
 //對(duì)value進(jìn)行轉(zhuǎn)換(value可能是object)
 export function convert(value: any) {
   return isObject(value) ? reactive(value) : value;
 }
 export function trackRefValue(ref: RefImpl) {
   if (isTracking()) {
     trackEffects(ref.dep);
   }
 }
 //effect.ts
 export function isTracking(): Boolean {
   //是否開啟收集依賴 & 是否有依賴
   return shouldTrack && activeEffect !== undefined;
 }
 export function trackEffects(dep) {
   dep.add(activeEffect);
   activeEffect.deps.push(dep);
 }
 export function triggerEffects(dep) {
   for (const effect of dep) {
     if (effect.scheduler) {
       effect.scheduler();
     } else {
       effect.run();
     }
   }
 }

實(shí)現(xiàn)proxyRefs

 //實(shí)現(xiàn)對(duì)ref對(duì)象進(jìn)行代理
 //如user = {
 //  age:ref(10),
 //  ...
 //}
 export function proxyRefs(ObjectWithRefs) {
   return new Proxy(ObjectWithRefs, {
     get(target, key) {
       // 如果是ref 返回.value
       //如果不是 返回value
       return unRef(Reflect.get(target, key));
     },
     set(target, key, value) {
       if (isRef(target[key]) && !isRef(value)) {
         target[key].value = value;
         return true; //?
       } else {
         return Reflect.set(target, key, value);
       }
     },
   });
 }

4、實(shí)現(xiàn)computed

computed的實(shí)現(xiàn)也很巧妙,利用調(diào)度器機(jī)制和一個(gè)私有變量_value,實(shí)現(xiàn)緩存和惰性求值。

通過(guò)注解(一)(二)(三)可理解其實(shí)現(xiàn)流程

 //computed
 import { ReactiveEffect } from "./effect"; ?
 class computedRefImpl {
   private _dirty: boolean = true;
   private _effect: ReactiveEffect;
   private _value: any;
   constructor(getter) {
     //創(chuàng)建時(shí),會(huì)創(chuàng)建一個(gè)響應(yīng)式實(shí)例,并且掛載
     this._effect = new ReactiveEffect(getter, () => {
       //(三)
       //當(dāng)監(jiān)聽的值發(fā)生改變時(shí),會(huì)觸發(fā)set,此時(shí)觸發(fā)當(dāng)前依賴
       //因?yàn)榇嬖谡{(diào)度器,不會(huì)立刻執(zhí)行用戶fn(實(shí)現(xiàn)了lazy),而是將_dirty更改為true
       //在下一次用戶get時(shí),會(huì)調(diào)用run方法,重新拿到最新的值返回
       if (!this._dirty) {
         this._dirty = true;
       }
     });
   }
   get value() {
     //(一)
     //默認(rèn)_dirty是true
     //那么在第一次get的時(shí)候,會(huì)觸發(fā)響應(yīng)式實(shí)例的run方法,觸發(fā)依賴收集
     //同時(shí)拿到用戶fn的值,存儲(chǔ)起來(lái),然后返回出去
     if (this._dirty) {
       this._dirty = false;
       this._value = this._effect.run();
     }
     //(二)
     //當(dāng)監(jiān)聽的值沒有改變時(shí),_dirty一直為false
     //所以,第二次get時(shí),因?yàn)開dirty為false,那么直接返回存儲(chǔ)起來(lái)的_value
     return this._value;
   }
 }
 export function computed(getter) {
   //創(chuàng)建一個(gè)computed實(shí)例
   return new computedRefImpl(getter);
 }

工具類

 //是否是reactive響應(yīng)式類型
 export function isReactive(target) {
   return !!target[ReactiveFlags.IS_REACTIVE];
 }
 //是否是readonly響應(yīng)式類型
 export function isReadOnly(target) {
   return !!target[ReactiveFlags.IS_READONLY];
 }
 //是否是響應(yīng)式對(duì)象
 export function isProxy(target) {
   return isReactive(target) || isReadOnly(target);
 }
 //是否是對(duì)象
 export function isObject(target) {
   return typeof target === "object" && target !== null;
 }
 //是否是ref
 export function isRef(ref: any) {
   return !!ref._v_isRef;
 }
 //解構(gòu)ref
 export function unRef(ref: any) {
   return isRef(ref) ? ref.value : ref;
 }
 //是否改變
 export const hasChanged = (val, newVal) => {
   return !Object.is(val, newVal);
 };

判斷響應(yīng)式類型的依據(jù)是,在get的時(shí)候,檢查傳進(jìn)來(lái)的key是否等于某枚舉值來(lái)做為判斷依據(jù),在get中加入

 //reactive.ts
 export const enum ReactiveFlags {
   IS_REACTIVE = "__v_isReactive",
   IS_READONLY = "__v_isReadOnly",
 }
 //baseHandler.ts
 function createGetter(isReadOnly: Boolean = false, shallow: Boolean = false) {
   return function get(target, key) {
     //...
     if (key === ReactiveFlags.IS_REACTIVE) {
       return !isReadOnly;
     } else if (key === ReactiveFlags.IS_READONLY) {
       return isReadOnly;
     }
     //...
   };
 }

到此這篇關(guān)于深入理解Vue3響應(yīng)式原理的文章就介紹到這了,更多相關(guān)Vue3響應(yīng)式原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論