# @stevenleep/storage

统一前端本地存储层工具

### 安装

```bash
pnpm install @stevenleep/storage
```

内部依赖

```bash
pnpm --filter <YourPackageName> add @stevenleep/storage
```

@\*表示默认同步最新版本

```bash
pnpm --filter <YourPackageName> add @stevenleep/storage@* #
pnpm --filter <YourPackageName> add @stevenleep/storage@\* # mac-zsh 终端
```

### 快速上手

```typescript
import EnhancedStorage from "@stevenleep/storage";

const xxStorage = new EnhancedStorage();

// EnhancedStorage设计时完全遵从StorageAPI

// 写数据，默认存在localstorage中
// 可更改位置，稍后提及
xxStorage.setItem("foo", "bar");
xxStorage.setItem("bar", "baz");

// 获取数据
xxStorage.getItem("foo"); // bar
xxStorage.getItem("bar"); // baz

// 删除
xxStorage.removeItem("foo");
xxStorage.removeItem("bar");

// 清空
xx.clear();
```

### 使用 Namespace

namespace 可以在调用当前时为每一个 key 都增加一个前前缀，默认的前缀为：`@app/`, 这意味着如果不做任何处理，你的 key 格式如下：

```typescript
// 你在存储和获取时候的key为a，实际存储到数据层的key为 @app/a
xxStorage.setItem("a");
```

**这样更改 namespace**

```typescript
import EnhancedStorage from "@stevenleep/storage";

const doActionStorage = new EnhancedStorage({
  namespace: "__app__/", // 后续的key前面都有一个 __app__/
});
```

### 使用内置其他存储层

内部默认实现了以下存储层

```typescript
export enum StorageType {
  LocalStorage = "localStorage",
  SessionStorage = "sessionStorage",
  MemoryStorage = "memoryStorage",
  Cookie = "cookie",
}
```

**这样替换存储层**

```typescript
import EnhancedStorage, { StorageType } from "@stevenleep/storage";

const xxStorage = new EnhancedStorage({
  storage: StorageType.SessionStorage, // 使用SessionStorage存储
});
```

### 自定义存储逻辑

为所有的实例增加一种存储层

```typescript
import { Storages } from "@stevenleep/storage";
Storages.set("key", {
  getItem: function (key) {
    // ...
  },
  // ...
});
```

完全替换

```typescript
import EnhancedStorage, { BaseStorageInterface } from "@stevenleep/storage";

class CusterStorage implements BaseStorageInterface {
  // ...
}
const customStorage = new EnhancedStorage({
  storage: new CusterStorage(),
});
```

### 高级自定义 - 基于核心层定制

依赖关系如下

- EnhancedStorage 对外暴露的存储层
  - `extends` BaseStorage 实现了最基础的存储层能力，数据的增删查清空等，以及抹平多种本地存储 API
    - `deps:data`: AbsStorage 真实的数据存储位置，即 localStorage/sessionStorage/cookie/memoryStorage
  - `deps` BaseReactive 实现了数据变更时候的通知机制

```typescript
import { BaseStorage } from "@stevenleep/storage";

class MyStorage extends BaseStorage {
  constructor() {
    super(); // 默认使用localStorage

    // super({
    // storage: CustomStorage， // 自定义的存储层
    // namespace: '' // 自定义namespace
    // )
  }
}
```

### 高级 APIS

**监听某个 key 值的变化**

```typescript
import EnhancedStorage from "@stevenleep/storage";
const storage = new EnhancedStorage();

storage.setItem("a", "aaaa");

function handleChange(newValue, oldValue) {
  // ...
}
const cancel = storage.observer.subscribe("a", handleChange);

// 取消
cancel(); // 通过返回的cancel函数

// 通过取消API
storage.observer.unsubscribe("a", handleChange);
```

**监听整个 Storage 层数据变化**

```typescript
import EnhancedStorage from "@stevenleep/storage";
const storage = new EnhancedStorage();

function handleChange(newValue, oldValue, key) {
  // ...
}
storage.observer.subscribeStateChange(handleChange); // 订阅
storage.observer.unsubscribeStateChange(handleChange); // 取消
```

**给所由订阅者广播 - 默认清空使用**

```typescript
import EnhancedStorage from "@stevenleep/storage";
const storage = new EnhancedStorage();
storage.observer.broadcast(newvalue, oldvalue);
```

**跨实例通信 - 跨服聊天**

一些时候我们会为可能会在 LocalStorage 的逻辑中增加对 SessionStorage/Cookie 的监听，类似场景下使用实例上 `subscribeWithScope` 方法即可实现

```typescript
import EnhancedStorage, { StorageType } from "@stevenleep/storage";

const enhancedLocalStorageNamespace = "@app1/";
const enhancedLocalStorage = new EnhancedStorage({
  namespace: enhancedLocalStorageNamespace,
  storage: StorageType.LocalStorage,
});

const enhancedSessionStorageNamespace = "@app2/";
const enhancedSessionStorage = new EnhancedStorage({
  namespace: enhancedSessionStorageNamespace,
  storage: StorageType.SessionStorage,
});

enhancedLocalStorage.observer.subscribeWithScope(
  enhancedSessionStorageNamespace, // 目标存储层的namespace
  "a", // 下面的key
  function (newValue, oldValue) {
    // ...
  }
);

enhancedSessionStorage.setItem("a", "20"); // 存储数值并会触发上面👆监听
```
