最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue3中的響應(yīng)式原理-effect

 更新時間:2022年08月12日 17:16:56   作者:只愛喝白開水  
這篇文章主要介紹了vue3中的響應(yīng)式原理-effect,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

effect的基本實現(xiàn)

export let activeEffect = undefined;// 當(dāng)前正在執(zhí)行的effect
class ReactiveEffect {
? ? active = true;
? ? deps = []; // 收集effect中使用到的屬性
? ? parent = undefined;
? ? constructor(public fn) { }
? ? run() {
? ? ? ? if (!this.active) { // 不是激活狀態(tài)
? ? ? ? ? ? return this.fn();
? ? ? ? }
? ? ? ? try {
? ? ? ? ? ? this.parent = activeEffect; // 當(dāng)前的effect就是他的父親
? ? ? ? ? ? activeEffect = this; // 設(shè)置成正在激活的是當(dāng)前effect
? ? ? ? ? ? return this.fn();
? ? ? ? } finally {
? ? ? ? ? ? activeEffect = this.parent; // 執(zhí)行完畢后還原activeEffect
? ? ? ? ? ? this.parent = undefined;
? ? ? ? }
? ? }
}
export function effect(fn, options?) {
? ? const _effect = new ReactiveEffect(fn); // 創(chuàng)建響應(yīng)式effect
? ? _effect.run(); // 讓響應(yīng)式effect默認(rèn)執(zhí)行
}

依賴收集

get(target, key, receiver) {
? ? if (key === ReactiveFlags.IS_REACTIVE) {
? ? ? ? return true;
? ? }
? ? const res = Reflect.get(target, key, receiver);
? ? track(target, 'get', key); ?// 依賴收集
? ? return res;
}
const targetMap = new WeakMap(); // 記錄依賴關(guān)系
export function track(target, type, key) {
? ? if (activeEffect) {
? ? ? ? let depsMap = targetMap.get(target); // {對象:map}
? ? ? ? if (!depsMap) {
? ? ? ? ? ? targetMap.set(target, (depsMap = new Map()))
? ? ? ? }
? ? ? ? let dep = depsMap.get(key);
? ? ? ? if (!dep) {
? ? ? ? ? ? depsMap.set(key, (dep = new Set())) // {對象:{ 屬性 :[ dep, dep ]}}
? ? ? ? }
? ? ? ? let shouldTrack = !dep.has(activeEffect)
? ? ? ? if (shouldTrack) {
? ? ? ? ? ? dep.add(activeEffect);
? ? ? ? ? ? activeEffect.deps.push(dep); // 讓effect記住dep,這樣后續(xù)可以用于清理
? ? ? ? }
? ? }
}

將屬性和對應(yīng)的effect維護成映射關(guān)系,后續(xù)屬性變化可以觸發(fā)對應(yīng)的effect函數(shù)重新run

觸發(fā)更新

set(target, key, value, receiver) {
? ? // 等會賦值的時候可以重新觸發(fā)effect執(zhí)行
? ? let oldValue = target[key]
? ? const result = Reflect.set(target, key, value, receiver);
? ? if (oldValue !== value) {
? ? ? ? trigger(target, 'set', key, value, oldValue)
? ? }
? ? return result;
}
export function trigger(target, type, key?, newValue?, oldValue?) {
? ? const depsMap = targetMap.get(target); // 獲取對應(yīng)的映射表
? ? if (!depsMap) {
? ? ? ? return
? ? }
? ? const effects = depsMap.get(key);
? ? effects && effects.forEach(effect => {
? ? ? ? if (effect !== activeEffect) effect.run(); // 防止循環(huán)
? ? })
}

分支切換與cleanup

在渲染時我們要避免副作用函數(shù)產(chǎn)生的遺留

const state = reactive({ flag: true, name: 'jw', age: 30 })
effect(() => { // 副作用函數(shù) (effect執(zhí)行渲染了頁面)
? ? console.log('render')
? ? document.body.innerHTML = state.flag ? state.name : state.age
});
setTimeout(() => {
? ? state.flag = false;
? ? setTimeout(() => {
? ? ? ? console.log('修改name,原則上不更新')
? ? ? ? state.name = 'zf'
? ? }, 1000);
}, 1000)
function cleanupEffect(effect) {
? ? const { deps } = effect; // 清理effect
? ? for (let i = 0; i < deps.length; i++) {
? ? ? ? deps[i].delete(effect);
? ? }
? ? effect.deps.length = 0;
}
class ReactiveEffect {
? ? active = true;
? ? deps = []; // 收集effect中使用到的屬性
? ? parent = undefined;
? ? constructor(public fn) { }
? ? run() {
? ? ? ? try {
? ? ? ? ? ? this.parent = activeEffect; // 當(dāng)前的effect就是他的父親
? ? ? ? ? ? activeEffect = this; // 設(shè)置成正在激活的是當(dāng)前effect
+ ? ? ? ? ? cleanupEffect(this);
? ? ? ? ? ? return this.fn(); // 先清理在運行
? ? ? ? }
? ? }
}

這里要注意的是:觸發(fā)時會進行清理操作(清理effect),在重新進行收集(收集effect)。在循環(huán)過程中會導(dǎo)致死循環(huán)。

let effect = () => {};
let s = new Set([effect])
s.forEach(item=>{s.delete(effect); s.add(effect)}); // 這樣就導(dǎo)致死循環(huán)了

停止effect

export class ReactiveEffect {
? ? stop(){
? ? ? ? if(this.active){?
? ? ? ? ? ? cleanupEffect(this);
? ? ? ? ? ? this.active = false
? ? ? ? }
? ? }
}
export function effect(fn, options?) {
? ? const _effect = new ReactiveEffect(fn);?
? ? _effect.run();
? ? const runner = _effect.run.bind(_effect);
? ? runner.effect = _effect;
? ? return runner; // 返回runner
}

調(diào)度執(zhí)行

trigger觸發(fā)時,我們可以自己決定副作用函數(shù)執(zhí)行的時機、次數(shù)、及執(zhí)行方式

export function effect(fn, options:any = {}) {
? ? const _effect = new ReactiveEffect(fn,options.scheduler); // 創(chuàng)建響應(yīng)式effect
? ? // if(options){
? ? // ? ? Object.assign(_effect,options); // 擴展屬性
? ? // }
? ? _effect.run(); // 讓響應(yīng)式effect默認(rèn)執(zhí)行
? ? const runner = _effect.run.bind(_effect);
? ? runner.effect = _effect;
? ? return runner; // 返回runner
}
export function trigger(target, type, key?, newValue?, oldValue?) {
? ? const depsMap = targetMap.get(target);
? ? if (!depsMap) {
? ? ? ? return
? ? }
? ? let effects = depsMap.get(key);
? ? if (effects) {
? ? ? ? effects = new Set(effects);
? ? ? ? for (const effect of effects) {
? ? ? ? ? ? if (effect !== activeEffect) {?
? ? ? ? ? ? ? ? if(effect.scheduler){ // 如果有調(diào)度函數(shù)則執(zhí)行調(diào)度函數(shù)
? ? ? ? ? ? ? ? ? ? effect.scheduler()
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? effect.run();?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}

深度代理 

get(target, key, receiver) {
? ? if (key === ReactiveFlags.IS_REACTIVE) {
? ? ? ? return true;
? ? }
? ? // 等會誰來取值就做依賴收集
? ? const res = Reflect.get(target, key, receiver);
? ? track(target, 'get', key);
? ? if(isObject(res)){
? ? ? ? return reactive(res);
? ? }
? ? return res;
}

當(dāng)取值時返回的值是對象,則返回這個對象的代理對象,從而實現(xiàn)深度代理

總結(jié)

為了實現(xiàn)響應(yīng)式,我們使用了new Proxy

effect默認(rèn)數(shù)據(jù)變化要能更新,我們先將正在執(zhí)行的effect作為全局變量,渲染(取值),然后在get方法中進行依賴收集

依賴收集的數(shù)據(jù)格式weakMap(對象:map(屬性:set(effect))

用戶數(shù)據(jù)發(fā)生變化,會通過對象屬性來查找對應(yīng)的effect集合,全部執(zhí)行;

調(diào)度器的實現(xiàn),創(chuàng)建effect時,把scheduler存在實例上,調(diào)用runner時,判斷如果有調(diào)度器就調(diào)用調(diào)度器,否則執(zhí)行runner

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue中xlsx的使用方法指南

    Vue中xlsx的使用方法指南

    這篇文章主要給大家介紹了關(guān)于Vue中xlsx的使用方法指南,有很多辦法都可以實現(xiàn),其中最簡單的還是使用插件xlsx,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • antd Form組件表單在vue3中的使用方式

    antd Form組件表單在vue3中的使用方式

    這篇文章主要介紹了antd Form組件表單在vue3中的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • vue用h()函數(shù)創(chuàng)建Vnodes的實現(xiàn)

    vue用h()函數(shù)創(chuàng)建Vnodes的實現(xiàn)

    Vue提供了一個h()函數(shù)用于創(chuàng)建vnodes,本文就來介紹一下vue用h()函數(shù)創(chuàng)建Vnodes的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Vue跳轉(zhuǎn)頁面的幾種常用方法總結(jié)

    Vue跳轉(zhuǎn)頁面的幾種常用方法總結(jié)

    在Vue.js中,頁面跳轉(zhuǎn)是構(gòu)建單頁面應(yīng)用(SPA)的基本操作之一,本文將介紹Vue中實現(xiàn)頁面跳轉(zhuǎn)的幾種方法,并通過實例代碼幫助理解每種方法的用法,需要的朋友可以參考下
    2024-09-09
  • vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件

    vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件

    這篇文章主要介紹了vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-02-02
  • Vue2實現(xiàn)數(shù)字滾動翻頁效果的示例代碼

    Vue2實現(xiàn)數(shù)字滾動翻頁效果的示例代碼

    這篇文章主要為大家詳細(xì)介紹了Vue2如何實現(xiàn)數(shù)字滾動翻頁效果,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考一下
    2024-03-03
  • 詳解vue-cli與webpack結(jié)合如何處理靜態(tài)資源

    詳解vue-cli與webpack結(jié)合如何處理靜態(tài)資源

    本篇文章主要介紹了詳解vue-cli與webpack結(jié)合如何處理靜態(tài)資源,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Vue使用vm.$set()解決對象新增屬性不能響應(yīng)的問題

    Vue使用vm.$set()解決對象新增屬性不能響應(yīng)的問題

    這篇文章主要介紹了Vue使用vm.$set()解決對象新增屬性不能響應(yīng)的問題,為了解決這個問題,Vue提供了一個特殊的方法vm.$set(object, propertyName, value),也可以使用全局的Vue.set(object, propertyName, value)方法,需要的朋友可以參考下
    2023-05-05
  • 分享一個精簡的vue.js 圖片lazyload插件實例

    分享一個精簡的vue.js 圖片lazyload插件實例

    本篇文章主要介紹了分享一個精簡的vue.js 圖片lazyload插件實例。非常具有實用價值,需要的朋友可以參考下。
    2017-03-03
  • vue實現(xiàn)表格分頁功能

    vue實現(xiàn)表格分頁功能

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)表格分頁功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評論

离岛区| 安宁市| 奇台县| 临西县| 汝南县| 雅江县| 翁源县| 永春县| 库车县| 武隆县| 尚志市| 阿勒泰市| 汤阴县| 江源县| 平乐县| 普兰店市| 安庆市| 萍乡市| 交口县| 台前县| 商洛市| 商丘市| 泗水县| 乐东| 察隅县| 陇西县| 醴陵市| 密云县| 天峨县| 玉溪市| 筠连县| 育儿| 长岛县| 彰化市| 临洮县| 道真| 东乌| 灵石县| 邛崃市| 鄂尔多斯市| 秭归县|