解读pinia源码之前必须知道的API

637 阅读3分钟

专栏导航

分析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集群。

类型:

function effectScope(detached?: boolean): EffectScopeinterface 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

markRawpinia源码中非常常见,主要用于优化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,就不做过多赘述了。

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿