目录
- 背景
- 我的心路历程
- 实现
- 嵌入现有我的项目试验一下
- 存在危险
- 结束语
1. 背景
Vue.js 3.0 ,整个源码是通过 monorepo 的形式保护的,依据性能将不同的模块拆分到 packages 目录上面不同的子目录中:
能够看出绝对于 Vue.js 2.x 的源码组织形式,monorepo 把这些模块拆分到不同的 package 中,每个 package 有各自的 API、类型定义和测试。这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易浏览、了解和更改所有模块源码,进步代码的可维护性。
另外一些 package(比方 reactivity 响应式库)是能够独立于 Vue.js 应用的,这样用户如果只想应用 Vue.js 3.0 的响应式能力,能够独自依赖这个响应式库而不必去依赖整个 Vue.js,减小了援用包的体积大小,而 Vue.js 2 .x 是做不到这一点的。
2. 我的心路历程
这个其实说起来有点搞笑,当我有这个想法并实现出这个货色的时候,并且我还在咱们组吹牛了一波,我是这样。
然而,我在知乎、掘金上发现竟然有人曾经玩过了。本认为发现了新大陆,然而竟然是玩剩下的,只有我还在鼓里。而后我是这样的。
3. 实现
import { effect, reactive } from '@vue/reactivity';import { useRef, useEffect, useMemo, useState } from 'react';// 避免初始化的时候进行let initUpdate = false;const useReactive = (initState: any) => { // 应用一个状态,这个状态自身是没有用的,只是为了做强制更新 const [, forceUpdate] = useState(0); // 保留响应式对象(useRef始终保留的的原始对象,避免响应式对象失落) const reactiveState = useRef(initState); // 对响应式对象缓存 const state = useMemo(() => reactive(reactiveState.current), [ reactiveState.current ]); // 监听响应式对象的扭转 useEffect(() => { effect(() => { for (const i in state) { // 拜访触发依赖收集 state[i]; } // 强制更新,利用后面无用的状态 if (initUpdate) { forceUpdate((num) => num + 1); } if (!initUpdate) initUpdate = true; }); }, [state]); return state;};export default useReactive;
测试源代码看这里。
实现很简略,这里有几个外围点须要留神。
外围1:reactive
接管一个一般对象而后返回该一般对象的响应式代理。等同于 2.x 的 Vue.observable()。
响应式转换是“深层的”:会影响对象外部所有嵌套的属性。基于 ES2015 的 Proxy 实现,返回的代理对象不等于原始对象。倡议仅应用代理对象而防止依赖原始对象。
外围2:effect
effect 作为 reactive 的外围,次要负责收集依赖,更新依赖。
外围3:触发依赖收集
for (const i in state) { // 拜访触发依赖收集 state[i];}
在底层源码中,track 收集依赖(get操作)。
/** * @description: * @param {target} 指标对象 * @param {type} 收集的类型, 函数的定义在下方 * @param {key} 触发 track 的 object 的 key */export function track(target: object, type: TrackOpTypes, key: unknown) { // activeEffect为空代表没有依赖,间接return if (!shouldTrack || activeEffect === undefined) { return } // targetMap 依赖管理中心,用于收集依赖和触发依赖 // 查看targetMap中有没有以后target let depsMap = targetMap.get(target) if (!depsMap) { // 没有则新建一个 targetMap.set(target, (depsMap = new Map())) } // deps 来收集依赖函数,当监听的 key 值发生变化时,触发 dep 中的依赖函数 let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } if (!dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) // 开发环境会触发onTrack, 仅用于调试 if (__DEV__ && activeEffect.options.onTrack) { activeEffect.options.onTrack({ effect: activeEffect, target, type, key }) } }}// get、 has、 iterate 三种类型的读取对象会触发 trackexport const enum TrackOpTypes { GET = 'get', HAS = 'has', ITERATE = 'iterate'}
4. 嵌入现有我的项目试验一下
之前定义状态
之前更新状态
当初定义状态
当初更新状态
5. 危险点
如果你认真的去看,其实这代码是存在危险点的。
代码中咱们应用 for 循环 get 属性,进行依赖的收集,这恰好就是问题,每次更新状态都会去触发 for 循环,你懂的,如果存在大量的key就无奈防止的带来了性能问题。
在Vue2.0会用一个__ob__属性进行标识是否被依赖收集过,如果被收集过,就不会再被收集!vue3收集形式和2.0有点区别,vue3.0 当援用了才会被收集,比如说当咱们调用this.obj ,obj上面的属性才会被收集,如果被收集过,不会被收集,然而对于没有援用过的,就会被再收集。而且采纳了WeakMap,一旦不须要,就会自行隐没。 好吧 ,咱们回到最开始的问题 , 咱们在react jsx援用的属性不能像vue3 解决 template模版一样,能够收集依赖,所以第一次更新之后,响应式对象不须要了被回收了,所以第二次就无奈更新了,所以每一次须要我先遍历一次。
6. 结束语
如果文章中什么不对或者写的不好的中央,请大家多多斧正,谢谢!码字不易,点个赞加个关注吧!
题外话
笔者在「深圳虾皮」,一家口碑还不错的东南亚电商公司,2021大量招人,机会多多!快来退出咱们吧!
当初有想法,还是当前有想法的同学,都能够加我微信[stone---999]!内推你退出咱们的小家庭!
参考
- https://vue3js.cn/vue-composi...
- https://juejin.cn/post/694783...