前语
在ES6中,"reactive"
一般指的是创立具有呼应式特性的方针,使得这些方针的特点能够主动地呼应数据改变而更新视图。Vue.js的呼应式体系就是根据这个概念构建的。在ES6中,咱们能够经过运用Proxy
方针来手动创立具有相似呼应式特性的方针。
下面咱们将逐渐创立一个简略的reactive
函数,来手动完成一个相似Vue.js中呼应式方针的功用(只完成了set及get功用):
1、 reactive.js:
import { mutableHandlers } from "./baseHandlers.js";
export const reactiveMap = new WeakMap(); // 创立一个WeakMap用来存储现已署理过的方针,WeakMap对内存的回收愈加友爱
export function reactive(target) {
// 将target变成呼应式方针
return createReactiveObject(target, reactiveMap, mutableHandlers); // 创立呼应式方针
}
export function createReactiveObject(target, proxyMap, proxyHandlers) {
// 创立呼应式的函数,参数别离是方针方针,存储署理方针的map,署理方针的处理函数
// 判别target是不是引证类型
if (typeof target !== "object" || target === null) {
// 不是引证类型直接回来
return target;
}
// 该方针是否现已被署理过(现已是呼应式方针)
const existingProxy = proxyMap.get(target); // 从reactiveMap中获取target
if (existingProxy) {
return existingProxy; // 假如现已署理过了,直接回来
}
// 履行署理操作(将target变成呼应式方针)
const proxy = new Proxy(target, proxyHandlers); //第二个参数:当target被读取值,设置值,判别值等等操作时,会触发的函数
// 往 reactiveMap中增加 proxy,把现已署理过的方针存储起来
proxyMap.set(target, proxy);
return proxy; // 回来署理方针
}
在上述代码中,主要是完成reactive
函数的主要功用之一,将引证类型转换为呼应式方针,下面让我逐渐解说这段代码的功用:
-
创立
reactiveMap
:这行代码创立了一个WeakMap
方针,用于存储现已署理过的方针。
WeakMap
中的键是弱引证的,这意味着假如键方针没有其他引证,因而对内存的回收愈加友爱,当被署理的方针被废物回收时,相应的条目也会被主动移除。 -
reactive
函数:这个函数承受一个一般方针作为参数,并将其转换为具有呼应式特性的方针。它调用了createReactiveObject
函数来创立呼应式方针,并传入了方针方针、reactiveMap
和mutableHandlers
作为参数,参数别离是方针方针,存储署理方针的map,署理方针的处理函数。 -
createReactiveObject
函数:这个函数用于创立呼应式方针。- 首要它判别传入的方针方针是否是引证类型(即方针或数组),假如不是,则直接回来方针方针自身。
- 然后它检查方针方针是否现已被署理过,假如是,则直接回来之前创立的署理方针。假如方针方针尚未被署理过,则运用
Proxy
方针创立一个新的署理方针,并将其存储到reactiveMap
中。 - 最终回来创立的署理方针。
2、baseHandlers.js
import { track, trigger } from "./effect.js";
const get = createGetter(); // 创立一个get函数
const set = createSetter(); // 创立一个set函数
function createGetter() {
return function get(target, key, receiver) {
// console.log('target被读取值');
const res = Reflect.get(target, key, receiver); // 获取源方针中的键值
// 这个特点终究还有哪些地方用到了,(副作用函数的搜集,computed,watch...)
track(target, key); // 依靠搜集
return res;
}
}
function createSetter() {
return function set(target, key, value, receiver) {
// console.log('target被设置值', key, value);
const res = Reflect.set(target, key, value, receiver); // 设置源方针中的键值 === target[key] = value
trigger(target, key); // 触发副作用函数
// 需求记录下来此刻是哪一个key的值变更了,再去告诉其他依靠该值的函数收效,更新浏览器的视图(呼应式)
// 触发被修正的特点身上的副函数 依靠搜集(被修正的key在哪些地方被运用了)发布订阅
return res;
}
}
export const mutableHandlers = {
// get: function (target, key, receiver) { // target 被署理的源方针,key是源方针中的键,receiver是署理后方针
// console.log('target被读取值');
// const res = Reflect.get(target, key, receiver); // 获取源方针中的键值
// return res;
// },
get,
set,
// set: function (target, key, value, receiver) {
// console.log('target被设置值', key, value);
// const res = Reflect.set(target, key, value, receiver); // 设置源方针中的键值 === target[key] = value
// return res;
// // 更新浏览器的视图(呼应式)
// },
}
以上代码完成了对署理方针的操作处理,包含了对特点的读取和设置。被署理方针中的恣意特点的值产生修正,都应该将用到了这个特点的各个函数从头履行一遍,那么在履行之前就需求先为每一个特点都做好副作用函数的搜集,也称为依靠搜集.这也是完成 Vue.js 呼应式体系的关键之一,它保证了在特点改变时能够正确地触发浏览器视图更新。下面让我来逐渐解说这段代码的功用:
-
createGetter
函数:- 这个函数回来一个用于处理特点读取的函数。在这个函数内部,首要调用了
Reflect.get()
办法获取方针方针中指定键的值,并将成果存储在变量res
中。 - 接着调用了外部导入的
track
函数,用于搜集当时特点的依靠联系。这意味着该特点被拜访时,需求盯梢它的依靠,以便在特点改变时触发相应的副作用函数。 - 最终回来特点的值
res
。
- 这个函数回来一个用于处理特点读取的函数。在这个函数内部,首要调用了
-
createSetter
函数:- 这个函数回来一个用于处理特点设置的函数。在这个函数内部,首要调用了
Reflect.set()
办法将指定键的值设置为指定的值,并将成果存储在变量res
中。 - 接着调用了外部导入的
trigger
函数,用于触发与当时特点相关联的副作用函数。这意味着当特点被修正时,需求告诉一切依靠该特点的函数履行相应的副作用操作。 - 最终回来设置操作的成果
res
。
- 这个函数回来一个用于处理特点设置的函数。在这个函数内部,首要调用了
-
mutableHandlers
方针:- 这个方针包含了对可变方针的操作处理函数,其间包含了
get
和set
操作的具体完成。在这里,get
和set
的处理函数别离由createGetter
和createSetter
函数回来。 -
mutableHandlers
方针被导出,能够被其他模块引证和运用。
- 这个方针包含了对可变方针的操作处理函数,其间包含了
3、effect.js
const targetMap = new WeakMap();
let activeEffect = null; //得是一个副作用函数
export function effect(fn,options={}) { //watch,computed的核心逻辑
const effectFn = () => {
try {
activeEffect = effectFn
return fn()
} finally {
activeEffect = null
}
}
if(!options.lazy){
effectFn()
}
return effectFn
}
// 为某个特点增加effect
export function track(target,key) {
// targetMap = { //存成这样的结构
// target: {
// key: [effect1,effect2,...]
// }
// }
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) { //改特点未增加过effect
dep = new Set()
}
if (!dep.has(activeEffect) && activeEffect) {
// 存入一个effect函数
dep.add(activeEffect)
}
depsMap.set(key, dep)
}
// 触发特点effect
export function trigger(target,key) {
const depsMap = targetMap.get(target)
if(!depsMap){ //当时方针中一切的key都没有副作用函数,从来都没有被运用过
return
}
const deps = depsMap.get(key)
if (!deps) { //这个特点没有依靠
return
}
deps.forEach(effectFn => {
effectFn() //将该特点上的一切副作用函数悉数触发
});
}
以上代码完成了一个状况管理的功用,用于完成对方针特点的依靠盯梢和副作用函数的履行,并管理方针特点的依靠联系和履行副作用函数。这样,在特点值产生改变时,就能主动履行相应的副作用函数,然后完成数据的呼应式处理。下面我将详细的介绍一下以上代码的功用:
-
effect
函数:- 这个函数用于创立副作用函数。副作用函数是一个函数,它会在呼应式数据产生改变时被履行。
-
effect
函数承受两个参数:fn
是一个函数,代表需求履行的副作用函数;options
是一个包含选项的方针,默以为空方针。其间,lazy
是一个布尔值选项,表示是否延迟履行副作用函数,默以为false
。 - 在
effect
函数内部,首要创立了一个名为effectFn
的函数,它是一个封装了传入的副作用函数fn
的函数。在effectFn
函数内部,经过try...finally
结构保证了activeEffect
变量的正确设置和清除。然后依据options.lazy
的值决定是否立即履行effectFn
函数,并回来effectFn
函数。
-
track
函数:- 这个函数用于为某个特点增加副作用函数。当某个特点被拜访时,需求调用
track
函数来搜集对应的依靠联系,以便在特点值产生改变时履行相应的副作用函数。 -
track
函数承受两个参数:target
是方针方针,key
是方针方针的特点名。 - 在
track
函数内部,首要依据target
从targetMap
中获取对应的依靠映射联系depsMap
,假如不存在,则创立一个新的空映射联系并存储到targetMap
中。然后依据key
从depsMap
中获取对应的依靠调集dep
,假如不存在,则创立一个新的空调集。接着判别当时副作用函数activeEffect
是否现已存在于dep
中,假如不存在且activeEffect
存在,则将其增加到dep
中。最终将更新后的dep
存储回depsMap
中。
- 这个函数用于为某个特点增加副作用函数。当某个特点被拜访时,需求调用
-
trigger
函数:- 这个函数用于触发特点的副作用函数。当某个特点的值产生改变时,需求调用
trigger
函数来履行一切依靠于该特点的副作用函数。 -
trigger
函数承受两个参数:target
是方针方针,key
是方针方针的特点名。 - 在
trigger
函数内部,首要依据target
从targetMap
中获取对应的依靠映射联系depsMap
,假如不存在,则说明方针方针中一切的特点都没有副作用函数,直接回来。然后依据key
从depsMap
中获取对应的依靠调集deps
,假如不存在,则说明该特点没有任何依靠,直接回来。接着遍历deps
调集,顺次履行其间的副作用函数。
- 这个函数用于触发特点的副作用函数。当某个特点的值产生改变时,需求调用
值得注意的是,对于effect
函数,实际上大部分完成监听作用的函数都会运用到这种相似的函数,就如watch
和 computed
函数。因为它用于创立副作用函数,而 watch
和 computed
又都是根据副作用函数完成的,它们都依靠于副作用函数来完成对数据的调查和计算。
结语
就这样,咱们别离经过创立reactive.js
、baseHandlers.js
和effect.js
三个js文件完成了一个简略的reactive
函数。
总结
在ES6中,咱们能够经过运用Proxy
方针和一些基本的JavaScript技巧手动创立具有呼应式特性的数据处理体系。本文介绍了怎么运用Proxy
方针来完成一个简略的reactive
函数,以及怎么合作运用WeakMap
、Reflect
和副作用函数来构建一个简易的呼应式体系。经过对依靠联系的追寻和副作用函数的履行,咱们能够完成数据的主动更新和视图的同步更新,这也是现代JavaScript结构中常见的核心功用之一。
补充:什么是副作用函数
副作用函数指的是在函数履行过程中,除了回来一个值之外,还对函数外部的状况产生了影响,或许履行了与函数自身的意图无关的操作。
在呼应式体系中,副作用函数一般用于创立调查者模式或完成数据的主动更新。例如,当一个数据产生改变时,会触发与之相关联的副作用函数,然后履行一些预订的操作,比如更新UI界面或触发其他相关的事情。Vue.js中的watch
和computed
就是根据副作用函数完成的,它们能够监视数据改变并履行相应的操作。
最终,假如您也和我一样,在准备春招。欢迎加微信shunwuyu,这里有几十位一心去大厂的友友能够相互鼓舞,共享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!