Skip to content

Commit

Permalink
docs(zh): sync to 02a476d (#2674)
Browse files Browse the repository at this point in the history
* docs(zh): sync to 02a476d

* docs(zh): translate the new docs

* docs(zh): update the checkpoint

* docs(zh): make up the vscode snippets page
  • Loading branch information
Jinjiang authored Jun 11, 2024
1 parent 4a743fb commit 41eecd3
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 21 deletions.
4 changes: 4 additions & 0 deletions packages/docs/.vitepress/config/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
text: '组合式 Stores',
link: '/zh/cookbook/composing-stores.html',
},
{
text: 'VSCode 代码片段',
link: '/zh/cookbook/vscode-snippets.html',
},
{
text: '从 v0/v1 迁移至 v2',
link: '/zh/cookbook/migration-v1-v2.html',
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/.vitepress/translation-status.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"zh": {
"hash": "c67a5c9",
"date": "2023-12-07"
"hash": "02a476d",
"date": "2024-05-20"
}
}
6 changes: 5 additions & 1 deletion packages/docs/zh/cookbook/composables.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { defineStore, skipHydrate } from 'pinia'
import { useMediaControls } from '@vueuse/core'

export const useVideoPlayer = defineStore('video', () => {
// 我们不会直接暴露这个元素
// 我们不会直接暴露 (返回) 这个元素
const videoElement = ref<HTMLVideoElement>()
const src = ref('/data/video.mp4')
const { playing, volume, currentTime, togglePictureInPicture } =
Expand All @@ -67,6 +67,10 @@ export const useVideoPlayer = defineStore('video', () => {
})
```

:::warning
和常规的状态不同,`ref<HTMLVideoElement>()` 包含了一个不可序列化的 DOM 元素引用。这就是为什么我们不直接返回它的原因。由于它是客户端专用的状态,我们知道它不会被设置在服务器上,并且在客户端上**始终**`undefined` 作为开始。
:::

## 服务端渲染 %{#ssr}%

当处理[服务端渲染](../ssr/index.md)时,你有一些需要额外注意的内容,以便在 store 中使用组合式函数。
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/zh/cookbook/hot-module-replacement.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Pinia 支持热更新,所以你可以编辑你的 store,并直接在你的应用中与它们互动,而不需要重新加载页面,允许你保持当前的 state、并添加甚至删除 state、action 和 getter。

目前,只有 [Vite](https://vitejs.dev/) 被官方支持,不过任何实现 `import.meta.hot` 规范的构建工具都应该能正常工作。(例外的是,[webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) 似乎使用的是 `import.meta.webpackHot` 而不是 `import.meta.hot` )
目前,只有 [Vite](https://cn.vitejs.dev/guide/api-hmr#hmr-api) 被官方支持,不过任何实现 `import.meta.hot` 规范的构建工具都应该能正常工作。(例外的是,[webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) 似乎使用的是 `import.meta.webpackHot` 而不是 `import.meta.hot` )
你只需要在任何 store 声明旁边添加这段代码。比方说,你有三个 store:`auth.js``cart.js``chat.js`, 你必须在每个 **store 声明**后都添加(和调整)这段代码。

```js
Expand Down
47 changes: 47 additions & 0 deletions packages/docs/zh/cookbook/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,53 @@ store.someAction()
expect(store.someAction).toHaveBeenCalledTimes(1)
```

<!-- TODO: translation -->

### Mocking the returned value of an action

Actions are automatically spied but type-wise, they are still the regular actions. In order to get the correct type, we must implement a custom type-wrapper that is applies the `Mock` type to each action. **This type depends on the testing framework you are using**. Here is an example with Vitest:

```ts
import type { Mock } from 'vitest'
import type { Store, StoreDefinition } from 'pinia'

function mockedStore<TStoreDef extends () => unknown>(
useStore: TStoreDef
): TStoreDef extends StoreDefinition<
infer Id,
infer State,
infer Getters,
infer Actions
>
? Store<
Id,
State,
Getters,
{
[K in keyof Actions]: Actions[K] extends (
...args: infer Args
) => infer ReturnT
? // 👇 depends on your testing framework
Mock<Args, ReturnT>
: Actions[K]
}
>
: ReturnType<TStoreDef> {
return useStore() as any
}
```

This can be used in tests to get a correctly typed store:

```ts
import { mockedStore } from './mockedStore'
import { useSomeStore } from '@/stores/myStore'

const store = mockedStore(useSomeStore)
// typed!
store.someAction.mockResolvedValue('some value')
```

### 指定 createSpy 函数 %{#specifying-the-createspy-function}%

当使用 Jest,或 vitest 且设置 `globals: true` 时,`createTestingPinia` 会自动使用现有测试框架 (`jest.fn``vitest.fn`) 的 spy 函数存根 (stub) action。如果你使用的是不同的框架,你需要提供一个 [createSpy](/zh/api/interfaces/pinia_testing.TestingOptions.html#createspy) 选项:
Expand Down
49 changes: 49 additions & 0 deletions packages/docs/zh/cookbook/vscode-snippets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# VS Code 代码片段

有一些代码片段可以让你在 VS Code 中更轻松地使用 Pinia。

通过 <kbd>⇧</kbd> <kbd>⌘</kbd> <kbd>P</kbd> / <kbd>⇧</kbd> <kbd>⌃</kbd> <kbd>P</kbd> 然后输入 `Snippets: Configure User Snippets` 就可以管理用户代码片段。

```json
{
"Pinia Options Store Boilerplate": {
"scope": "javascript,typescript",
"prefix": "pinia-options",
"body": [
"import { defineStore, acceptHMRUpdate } from 'pinia'",
"",
"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', {",
" state: () => ({",
" $0",
" }),",
" getters: {},",
" actions: {},",
"})",
"",
"if (import.meta.hot) {",
" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
"}",
""
],
"description": "Bootstrap the code needed for a Vue.js Pinia Options Store file"
},
"Pinia Setup Store Boilerplate": {
"scope": "javascript,typescript",
"prefix": "pinia-setup",
"body": [
"import { defineStore, acceptHMRUpdate } from 'pinia'",
"",
"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', () => {",
" $0",
" return {}",
"})",
"",
"if (import.meta.hot) {",
" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
"}",
""
],
"description": "Bootstrap the code needed for a Vue.js Pinia Setup Store file"
}
}
```
6 changes: 3 additions & 3 deletions packages/docs/zh/core-concepts/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
title="Learn all about actions in Pinia"
/>

Action 相当于组件中的 [method](https://v3.vuejs.org/guide/data-methods.html#methods)。它们可以通过 `defineStore()` 中的 `actions` 属性来定义,**并且它们也是定义业务逻辑的完美选择。**
Action 相当于组件中的 [method](https://cn.vuejs.org/api/options-state.html#methods)。它们可以通过 `defineStore()` 中的 `actions` 属性来定义,**并且它们也是定义业务逻辑的完美选择。**

```js
export const useCounterStore = defineStore('main', {
Expand All @@ -28,7 +28,7 @@ export const useCounterStore = defineStore('main', {
})
```

类似 [getter](./getters.md),action 也可通过 `this` 访问**整个 store 实例**,并支持**完整的类型标注(以及自动补全✨)****不同的是,`action` 可以是异步的**,你可以在它们里面 `await` 调用任何 API,以及其他 action!下面是一个使用 [Mande](https://github.com/posva/mande) 的例子。请注意,你使用什么库并不重要,只要你得到的是一个`Promise`你甚至可以 (在浏览器中) 使用原生 `fetch` 函数:
类似 [getter](./getters.md),action 也可通过 `this` 访问**整个 store 实例**,并支持**完整的类型标注(以及自动补全✨)****不同的是,`action` 可以是异步的**,你可以在它们里面 `await` 调用任何 API,以及其他 action!下面是一个使用 [Mande](https://github.com/posva/mande) 的例子。请注意,你使用什么库并不重要,只要你得到的是一个`Promise`你甚至可以 (在浏览器中) 使用原生 `fetch` 函数:

```js
import { mande } from 'mande'
Expand Down Expand Up @@ -167,7 +167,7 @@ export default {

## 订阅 action %{#subscribing-to-actions}%

你可以通过 `store.$onAction()` 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。`after` 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,`onError` 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用,类似于[Vue docs 中的这个提示](https://v3.vuejs.org/guide/tooling/deployment.html#tracking-runtime-errors)
你可以通过 `store.$onAction()` 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。`after` 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,`onError` 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用,类似于[Vue docs 中的这个提示](https://cn.vuejs.org/guide/best-practices/production-deployment#tracking-runtime-errors)

这里有一个例子,在运行 action 之前以及 action resolve/reject 之后打印日志记录。

Expand Down
31 changes: 26 additions & 5 deletions packages/docs/zh/core-concepts/getters.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const useCounterStore = defineStore('counter', {
})
```

大多数时候,getter 仅依赖 state不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 `this` 访问到**整个 store 实例****但(在 TypeScript 中)必须定义返回类型**。这是为了避免 TypeScript 的已知缺陷,**不过这不影响用箭头函数定义的 getter,也不会影响不使用 `this` 的 getter**
大多数时候,getter 仅依赖 state不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 `this` 访问到**整个 store 实例****但(在 TypeScript 中)必须定义返回类型**。这是为了避免 TypeScript 的已知缺陷,**不过这不影响用箭头函数定义的 getter,也不会影响不使用 `this` 的 getter**

```ts
export const useCounterStore = defineStore('counter', {
Expand Down Expand Up @@ -60,9 +60,28 @@ const store = useCounterStore()

## 访问其他 getter %{#accessing-other-getters}%

与计算属性一样,你也可以组合多个 getter。通过 `this`,你可以访问到其他任何 getter。即使你没有使用 TypeScript,你也可以用 [JSDoc](https://jsdoc.app/tags-returns.html) 来让你的 IDE 提示类型
与计算属性一样,你也可以组合多个 getter。通过 `this`,你可以访问到其他任何 getter。在这种情况下,**你需要为这个 getter 指定一个返回值的类型**

```js
::: code-group

```ts [counterStore.ts]
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
doubleCount(state) {
return state.count * 2
},
doubleCountPlusOne(): number {
return this.doubleCount + 1
},
},
})
```

```js [counterStore.js]
// 你可以在 JavaScript 中使用 JSDoc (https://jsdoc.app/tags-returns.html)
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
Expand All @@ -85,6 +104,8 @@ export const useCounterStore = defineStore('counter', {
})
```

:::

## 向 getter 传递参数 %{#passing-arguments-to-getters}%

_Getter_ 只是幕后的**计算**属性,所以不可以向它们传递任何参数。不过,你可以从 _getter_ 返回一个函数,该函数可以接受任意参数:
Expand Down Expand Up @@ -115,7 +136,7 @@ const { getUserById } = storeToRefs(userList)
</template>
```

请注意,当你这样做时,**getter 将不再被缓存**它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好:
请注意,当你这样做时,**getter 将不再被缓存**它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好:

```js
export const useUserListStore = defineStore('userList', {
Expand Down Expand Up @@ -210,7 +231,7 @@ export default defineComponent({
</script>
```

这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**始终尽量不要在同一组件中混合两种 API 样式。
这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**始终尽量不要在同一组件中混合两种 API 样式。

### 不使用 `setup()` %{#without-setup}%

Expand Down
5 changes: 4 additions & 1 deletion packages/docs/zh/core-concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Setup store 也可以依赖于全局**提供**的属性,比如路由。任何[
```ts
import { inject } from 'vue'
import { useRoute } from 'vue-router'
import { defineStore } from 'pinia'

export const useSearchFilters = defineStore('search-filters', () => {
const route = useRoute()
Expand All @@ -101,7 +102,7 @@ export const useSearchFilters = defineStore('search-filters', () => {

## 你应该选用哪种语法? %{#what-syntax-should-i-pick}%

[在 Vue 中如何选择组合式 API 与选项式 API](https://cn.vuejs.org/guide/introduction.html#which-to-choose) 一样,选择你觉得最舒服的那一个就好。如果你还不确定,可以先试试 [Option Store](#option-stores)
[在 Vue 中如何选择组合式 API 与选项式 API](https://cn.vuejs.org/guide/introduction.html#which-to-choose) 一样,选择你觉得最舒服的那一个就好。两种语法都有各自的优势和劣势。Option Store 更容易使用,而 Setup Store 更灵活和强大。如果你想深入了解两者之间的区别,请查看 Mastering Pinia 中的 [Option Stores vs Setup Stores 章节](https://masteringpinia.com/lessons/when-to-choose-one-syntax-over-the-other)

## 使用 Store %{#using-the-store}%

Expand All @@ -128,6 +129,8 @@ const store = useCounterStore()
```vue
<script setup>
import { useCounterStore } from '@/stores/counter'
import { computed } from 'vue'
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
Expand Down
1 change: 1 addition & 0 deletions packages/docs/zh/core-concepts/outside-component-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Pinia store 依靠 `pinia` 实例在所有调用中共享同一个 store 实例

```js
import { useUserStore } from '@/stores/user'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'

Expand Down
66 changes: 62 additions & 4 deletions packages/docs/zh/core-concepts/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pinia.use(({ store }) => {
})
```

任何由插件返回的属性都会被 devtools 自动追踪,所以如果你想在 devtools 中调试 `hello` 属性,为了使 devtools 能追踪到 `hello`,请确保**在 dev 模式下**将其添加到 `store._customProperties` 中:
任何由插件*返回的*属性都会被 devtools 自动追踪,所以如果你想在 devtools 中调试 `hello` 属性,为了使 devtools 能追踪到 `hello`,请确保**在 dev 模式下**将其添加到 `store._customProperties` 中:

```js
// 上文示例
Expand Down Expand Up @@ -122,7 +122,7 @@ import { toRef, ref } from 'vue'
pinia.use(({ store }) => {
// 为了正确地处理 SSR,我们需要确保我们没有重写任何一个
// 现有的值
if (!Object.prototype.hasOwnProperty(store.$state, 'hasError')) {
if (!store.$state.hasOwnProperty('hasError')) {
// 在插件中定义 hasError,因此每个 store 都有各自的
// hasError 状态
const hasError = ref(false)
Expand All @@ -149,7 +149,7 @@ pinia.use(({ store }) => {
```js
import { set, toRef } from '@vue/composition-api'
pinia.use(({ store }) => {
if (!Object.prototype.hasOwnProperty(store.$state, 'hello')) {
if (!store.$state.hasOwnProperty('secret')) {
const secretRef = ref('secret')
// 如果这些数据是要在 SSR 过程中使用的
// 你应该将其设置在 `$state' 属性上
Expand All @@ -165,6 +165,34 @@ pinia.use(({ store }) => {

:::

#### 重置插件中添加的 state

默认情况下,`$reset()` 不会重置插件添加的 state,但你可以重写它来重置你添加的 state:

```js
import { toRef, ref } from 'vue'

pinia.use(({ store }) => {
// 和上面的代码一样,只是为了参考
if (!store.$state.hasOwnProperty('hasError')) {
const hasError = ref(false)
store.$state.hasError = hasError
}
store.hasError = toRef(store.$state, 'hasError')

// 确认将上下文 (`this`) 设置为 store
const originalReset = store.$reset.bind(store)

// 覆写其 $reset 函数
return {
$reset() {
originalReset()
store.hasError = false
},
}
})
```

## 添加新的外部属性 %{#adding-new-external-properties}%

当添加外部属性、第三方库的类实例或非响应式的简单值时,你应该先用 `markRaw()` 来包装一下它,再将它传给 pinia。下面是一个在每个 store 中添加路由器的例子:
Expand Down Expand Up @@ -373,7 +401,7 @@ declare module 'pinia' {

[在 Nuxt 中使用 pinia](../ssr/nuxt.md) 时,你必须先创建一个 [Nuxt 插件](https://nuxt.com/docs/guide/directory-structure/plugins)。这样你才能访问到 `pinia` 实例:

```ts
```ts{14-16}
// plugins/myPiniaPlugin.js
import { PiniaPluginContext } from 'pinia'
import { Plugin } from '@nuxt/types'
Expand All @@ -395,4 +423,34 @@ const myPlugin: Plugin = ({ $pinia }) => {
export default myPlugin
```

::: info

注意上面的例子使用的是 TypeScript。如果你使用的是 `.js` 文件,你必须删除类型标注 `PiniaPluginContext``Plugin` 以及它们的导入语句。

:::

### Nuxt.js 2

如果你使用的是 Nuxt.js 2,其类型会稍有不同:

```ts{3,15-17}
// plugins/myPiniaPlugin.ts
import { PiniaPluginContext } from 'pinia'
import { Plugin } from '@nuxt/types'
function MyPiniaPlugin({ store }: PiniaPluginContext) {
store.$subscribe((mutation) => {
// 响应 store 变更
console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)
})
// 请注意,如果你使用的是 TS,则必须添加类型。
return { creationTime: new Date() }
}
const myPlugin: Plugin = ({ $pinia }) => {
$pinia.use(MyPiniaPlugin)
}
export default myPlugin
```
2 changes: 2 additions & 0 deletions packages/docs/zh/core-concepts/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ const store = useStore()
store.count++
```

注意,新的属性**如果没有在 `state()` 中被定义**,则不能被添加。它必须包含初始状态。例如:如果 `secondCount` 没有在 `state()` 中定义,我们无法执行 `store.secondCount = 2`

## 重置 state %{#resetting-the-state}%

使用[选项式 API](/zh/core-concepts/index.md#option-stores) 时,你可以通过调用 store 的 `$reset()` 方法将 state 重置为初始值。
Expand Down
Loading

0 comments on commit 41eecd3

Please sign in to comment.