# Storage

使用`localStorage`, `sessionStorage`或您希望的任何其他机制来支持存储。

## 安装

```bash
npm install @ngxs/storage-plugin --save

# or if you are using yarn
yarn add @ngxs/storage-plugin
```

## 使用

将 `NgxsStoragePluginModule` 导入到您的应用模块中，如下所示：

```typescript
import { NgxsModule } from '@ngxs/store';
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';

@NgModule({
  imports: [NgxsModule.forRoot([]), NgxsStoragePluginModule.forRoot()]
})
export class AppModule {}
```

建议在其他插件之前注册存储插件，以便这些插件可以获取初始状态。

### 选项

该插件具有以下选项：

* `key`: 要保留的状态名称. 您可以传递可以通过点表示法深度嵌套的字符串或字符串数组。 如果未提供，则使用, 所有状态都默认使用 `@@STATE` 。
* `storage`: 要使用的存储策略。 默认为LocalStorage，但您可以传递SessionStorage或任何实现了StorageEngine API的东西。
* `deserialize`: 自定义反序列化器。 默认为 `JSON.parse`
* `serialize`:  自定义序列化器。 默认为 `JSON.stringify`
* `migrations`: 迁移策略
* `beforeSerialize`: 在序列化之前执行的拦截器
* `afterDeserialize`: 在序列化之后执行的拦截器

### Key 选项

`key`选项用于确定哪些状态应保留在存储中。`key` 不能是随机字符串，它必须与您的状态名一致。 让我们看下面的例子：

```typescript
// novels.state.ts
@State<Novel[]>({
  name: 'novels',
  defaults: []
})
@Injectable()
export class NovelsState {}

// detectives.state.ts
@State<Detective[]>({
  name: 'detectives',
  defaults: []
})
@Injectable()
export class DetectivesState {}
```

为了保持所有状态，不需要提供`key`选项，因此只需编写即可：

```typescript
@NgModule({
  imports: [NgxsStoragePluginModule.forRoot()]
})
export class AppModule {}
```

但是，如果我们只想保存 `NovelsState` 怎么办？ 然后，我们需要将其名称传递给 `key` 选项：

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: 'novels'
    })
  ]
})
export class AppModule {}
```

也可以提供一个与其名称相反的状态类：

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: NovelsState
    })
  ]
})
export class AppModule {}
```

如果我们要保存 `NovelsState` 和 `DetectivesState` ：

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: ['novels', 'detectives']
    })
  ]
})
export class AppModule {}
```

Or using state classes:

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: [NovelsState, DetectivesState]
    })
  ]
})
export class AppModule {}
```

您甚至可以结合使用状态类和字符串：

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: ['novels', DetectivesState]
    })
  ]
})
export class AppModule {}
```

这非常方便，在仅运行时(runtime-only)状态可以避免持久保存不应将保存到任何存储的。

### 自定义存储引擎

您可以通过实现 `StorageEngine` 接口来添加自己的存储引擎。

```typescript
import { NgxsStoragePluginModule, StorageEngine, STORAGE_ENGINE } from '@ngxs/storage-plugin';

export class MyStorageEngine implements StorageEngine {
  get length(): number {
    // Your logic here
  }

  getItem(key: string): any {
    // Your logic here
  }

  setItem(key: string, val: any): void {
    // Your logic here
  }

  removeItem(key: string): void {
    // Your logic here
  }

  clear(): void {
    // Your logic here
  }

  key(val: number): string {
    // Your logic here
  }
}

@NgModule({
  imports: [NgxsModule.forRoot([]), NgxsStoragePluginModule.forRoot()],
  providers: [
    {
      provide: STORAGE_ENGINE,
      useClass: MyStorageEngine
    }
  ]
})
export class MyModule {}
```

### 序列化拦截器

可以在状态被序列化或反序列化之前或之后定义自己的逻辑。

* beforeSerialize: 此选项可在序列化状态之前更改状态。
* afterSerialize: 序列化后，使用此选项可以更改状态。 例如，您可以使用它实例化一个具体的类。

```typescript
@NgModule({
  imports: [
    NgxsStoragePluginModule.forRoot({
      key: 'counter',
      beforeSerialize: (obj, key) => {
        if (key === 'counter') {
          return {
            count: obj.count < 10 ? obj.count : 10
          };
        }
        return obj;
      },
      afterDeserialize: (obj, key) => {
        if (key === 'counter') {
          return new CounterInfoStateModel(obj.count);
        }
        return obj;
      }
    })
  ]
})
export class AppModule {}
```

### 迁移

You can migrate data from one version to another during the startup of the store. Below is a strategy to migrate my state from `animals` to `newAnimals`. 您可以在存储启动期间将数据从一个版本迁移到另一个版本。 以下是将我的状态从 `animals` 迁移到 `newAnimals` 的策略。

```typescript
@NgModule({
  imports: [
    NgxsModule.forRoot([]),
    NgxsStoragePluginModule.forRoot({
      migrations: [
        {
          version: 1,
          key: 'zoo',
          versionKey: 'myVersion',
          migrate: state => {
            return {
              newAnimals: state.animals,
              version: 2 // Important to set this to the next version!
            };
          }
        }
      ]
    })
  ]
})
export class MyModule {}
```

在迁移策略中，我们定义：

* `version`: 我们正在迁移的版本
* `versionKey`: 版本标识符(默认为'version')
* `migrate`: 接受状态并期望返回新状态的函数。
* `key`: 要迁移的项目的键。如果未指定，它将使用整个存储状态。

Note: Its important to specify the strategies in the order of which they should progress. 注意：重要的是要按其执行顺序指定策略。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mlwxx.gitbook.io/ngxs/plugins/storage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
