专栏导航
前言
在pinia源码中有一些业务场景下不常用的vue3 api,如果没有预先了解将会给源码解读带来较大困难,建议先搞清楚相关API,阅读代码将会事半功倍~
正文
effectScope
在createPinia
中的遇到的第一行就是不认识的vue3 API,打开官网看了一下,最上方info中写道 effect作用域是一个高阶API,专为库作者服务。
他的作用是创建一片单独的effect
空间,该空间内的effect
将可以一起被处理,有点类似与docker
与k8s
的关系,例如ref computed watchEffect
都是docker
中的容器,而effectScope
就是k8s
,它可以统一管理effect
集群。
类型:
function effectScope(detached?: boolean): EffectScope
interface EffectScope {
run<T>(fn: () => T): T | undefined // 如果这个域不活跃则为 undefined
stop(): void
}
通过官网的类型可以看到,effectScope
存在一个boolean
类型的参数,但是在vue3
文档中并未找到参数说明,而在RFC中找到了更加详细的文档。接下来为effectScope
的相关API说明。
run
接受一个函数并返回该函数的返回值
const scope = effectScope();
const counter = ref(1);
const setupStore = scope.run(() => {
const doubled = computed(() => counter.value * 2);
watch(doubled, () => console.log('doubled',doubled.value));
watchEffect(() => console.log("count:", doubled.value));
return {
doubled,
};
});
// 打印 'count 2' watchEffect触发
console.log(setupStore!.counter.value); // 打印'1',可以正常访问返回值
setupStore!.counter.value = 2; // 打印 'doubled 4' 'count: 4' counter修改触发watch与watchEffect
stop
递归结束所有effect,包括后代effectScope
const setupStore = scope.run(() => {
const counter = ref(1);
const doubled = computed(() => counter.value * 2);
nestedScope = effectScope(true /* detached */);
nestedScope.run(() => {
watch(counter, () => console.log("doubled", counter.value * 2));
watchEffect(() => console.log("count:", counter.value*2));
});
return {
counter,
doubled,
};
});
scope.stop();
setupStore!.counter.value = 2;
// 打印 doubled 4 count: 4
// 因为nestedScope被指定为true,所以就算父级被销毁,nestedScope依旧存在反应。
// 如果想结束nestedScope,需要手动进行销毁nestedScope.stop()
detached
表示是否在分离模式下创建,该参数默认为false
;当为true
的时候,父级被停止,子集也不会受影响。
const scope = effectScope();
let nestedScope: any;
const setupStore = scope.run(() => {
const counter = ref(1);
const doubled = computed(() => counter.value * 2);
nestedScope = effectScope(true /* detached */);
nestedScope.run(() => {
watch(counter, () => console.log("doubled", counter.value * 2));
watchEffect(() => console.log("count:", counter.value * 2));
});
return {
counter,
doubled,
};
});
scope.stop();
setupStore!.counter.value = 2;
// 打印 doubled 4 count: 4
// 因为nestedScope被指定为true,所以就算父级被销毁,nestedScope依旧存在反应。
// 如果想结束nestedScope,需要手动进行销毁nestedScope.stop()
nestedScope.stop();
setupStore!.counter.value = 3;
// 不会出现任何打印
markRaw
标记一个对象,使其永远不会转换为 proxy
。返回对象本身。
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false
// 嵌套在其他响应式对象中时也可以使用
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false
markRaw
在pinia
源码中非常常见,主要用于优化pinia的自身性能。
toRaw
toRaw可以获取一个响应式对象的原始属性
const foo = {};
const reactiveFoo = reactive(foo);
console.log("toRaw", toRaw(reactiveFoo) === foo); // true
const foo1 = {};
const refFoo1 = ref(foo1);
console.log("toRaw", toRaw(refFoo1.value) === foo1); // true
在pinia
源码中用于获取reactive
的原始数据,并添加字段到其中
toRefs
toRefs
比较常见,简单来说:结果中的每个对象都指向原始属性;在实际开发中常用于reactive的解构。
在pinia
的源码中,针对store
中的state
的处理用到了toRefs
,不过它解构的是state(ref类型)
对象,如果解构的是普通对象将不具备响应式。
结语
以上就是pinia
源码中使用较多的vue3 api
,还有些非常基础的例如ref reactive
,就不做过多赘述了。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。