分析pinia源码之前必须知道的API

专栏导航

分析pinia源码之前必须知道的API

Pinia源码分析【1】- 源码分析环境搭建

Pinia源码分析【2】- createPinia

pinia源码分析【3】- defineStore

pinia源码分析【4】- Pinia Methods

前言

​ 在pinia源码中有一些业务场景下不常用的vue3 api,如果没有预先了解将会给源码解读带来较大困难,建议先搞清楚相关API,阅读代码将会事半功倍~

effectScope

​ 在createPinia中的遇到的第一行就是不认识的vue3 API,打开官网看了一下,最上方info中写道 effect作用域是一个高阶API,专为库作者服务

​ 他的作用是创建一片单独的effect空间,该空间内的effect将可以一起被处理,有点类似dockerk8s的关系,例如ref computed watchEffect 都是docker中的容器,而effectScope就是k8s,它可以统一管理effect集群。

类型:

1
2
3
4
5
6
function effectScope(detached?: boolean): EffectScope

interface EffectScope {
run<T>(fn: () => T): T | undefined // 如果这个域不活跃则为 undefined
stop(): void
}

​ 通过官网的类型可以看到,effectScope存在一个boolean类型的参数,但是在vue3文档中并未找到参数说明,而在RFC中找到了更加详细的文档。接下来为effectScope的相关API说明。

run

接受一个函数并返回该函数的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的时候,父级被停止,子集也不会受影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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。返回对象本身。

1
2
3
4
5
6
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 嵌套在其他响应式对象中时也可以使用
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false

markRawpinia源码中非常常见,主要用于优化pinia的自身性能。

toRaw

toRaw可以获取一个响应式对象的原始属性

1
2
3
4
5
6
7
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,就不做过多赘述了。