深入浅出 solid.js 源码 (二十二)—— store

318 阅读2分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

在 solid 中,store 通过 createStore 来创建,返回一个 state 和一个更新 state 的方法,整体十分简洁,更像是一个外置的升级版 signal。store 本身也延续了 solid 的特色,store 数据也是通过响应式的模式进行处理和更新。

export function createStore<T extends {}>(
  ...[store, options]: {} extends T
    ? [store?: T | Store<T>, options?: { name?: string }]
    : [store: object & (T | Store<T>), options?: { name?: string }]
): [get: Store<T>, set: SetStoreFunction<T>] {
  const unwrappedStore = unwrap((store || {}) as T);
  const isArray = Array.isArray(unwrappedStore);
  const wrappedStore = wrap(
    unwrappedStore,
    "_SOLID_DEV_" && ((options && options.name) || DEV.hashValue(unwrappedStore))
  );
  function setStore(...args: any[]): void {
    batch(() => {
      isArray && args.length === 1
        ? updateArray(unwrappedStore, args[0])
        : updatePath(unwrappedStore, args);
    });
  }

  return [wrappedStore, setStore];
}

这里返回的是一个 wrappedStore 和一个 setStore。在 wrappedStore 之前先来看一下什么是 unwrappedStore。createStore 本身接收一个或多个对象作为参数,这些对象就是最终的 store 中 state 的初始值,传入之后交给 unwrap 处理,这一步是把这个对象本身变成一个完全普通对象,这一步实际上是 wrap 的相反操作,为什么要 unwrap,因为后面要统一的进行 wrap,这里需要确保内容被重新初始化,避免副作用产生。接下来 wrap 操作,就是把对象变成一个非普通对象,不普通在哪里呢?我们来看 wrap 的逻辑,wrap 的关键就在这一行:

Object.defineProperty(value, $PROXY, { value: (p = new Proxy(value, proxyTraps)) });

这里创建了一个 Proxy 对象,代理的逻辑在 proxyTraps 中,这里对写操作做了限制,我们只能通过 setStore 来修改,对读操作做了一个 DataNode 的封装,取值的时候实际上是从 DataNode 中取的。当然重点还在通知更新逻辑,我们来看一下 setStore 的实现。

这里多个 store 使用 updateArray,否则使用 updatePath,在 update 内部执行更新逻辑,这里传入 setStore 的参数可以有很多种格式,对于对象类型直接覆盖相应的内容信息,如果传入函数就执行函数更新内容,这一步最终都会调用 setProperty 更新属性。在 setProperty 内部有一个关键的逻辑就是,这里执行了 DataNode 对象上面的 函数。我们来看一下函数。我们来看一下 是什么。

function createDataNode(value?: any, equals?: boolean) {
  const [s, set] = createSignal<any>(
    value,
    equals
      ? {
          internal: true
        }
      : {
          equals: false,
          internal: true
        }
  );
  (s as Accessor<any> & { $: (v: any) => void }).$ = set;
  return s as Accessor<any> & { $: (v: any) => void };
}

这里就是响应式的关键,这里的底层还是使用了 createSignal,前面从 DataNode 中取的实际上是一个 signal,因此他可以响应 signal 的更新函数,在这里 setStore 触发了 signal 的更新,这样也就更新了 store 中的 state,也就会被上层监听到。

总结 solid store 的实现方式就是利用 proxy 把每一个对对象的操作转化为对 signal 的操作,把 signal 的读和写分别放在 wrappedStore 和 setStore 中,这样实现的 store 与 solid 本身的设计形成了一个很好的统一,无论是底层实现还是上层开发都会有一种熟悉的感觉。