diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d61dd62b..3d910f509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# [4.1.0](https://github.com/vuejs/vuex/compare/v4.0.2...v4.1.0) (2022-10-14) + +### Vue Core Version Requirement Change + +**This release contains an important fix (#1883) that relies on the `effectScope` API from Vue core, which is only available in Vue 3.2+.** + +### Bug Fixes + +* **build:** node deprecated warning in export module ([#2048](https://github.com/vuejs/vuex/issues/2048)) ([397e9fb](https://github.com/vuejs/vuex/commit/397e9fba45c8b4ec0c4a33d2578e34829bd348d7)) +* getters being destroyed on component destroy ([#1878](https://github.com/vuejs/vuex/issues/1878)) ([#1883](https://github.com/vuejs/vuex/issues/1883)) ([b2f851f](https://github.com/vuejs/vuex/commit/b2f851f427aa872d1e4f5a4774e07c4c69562789)) + + + ## [4.0.2](https://github.com/vuejs/vuex/compare/v4.0.1...v4.0.2) (2021-06-17) ### Bug Fixes diff --git a/README.md b/README.md index a362363a7..901829091 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,25 @@ # Vuex -[![npm](https://img.shields.io/npm/v/vuex/next.svg)](https://npmjs.com/package/vuex) -[![ci status](https://circleci.com/gh/vuejs/vuex/tree/dev.png?style=shield)](https://circleci.com/gh/vuejs/vuex) +[![npm](https://img.shields.io/npm/v/vuex.svg)](https://npmjs.com/package/vuex) +[![ci status](https://circleci.com/gh/vuejs/vuex/tree/main.png?style=shield)](https://circleci.com/gh/vuejs/vuex) --- -:fire: **HEADS UP!** You're currently looking at Vuex 4 branch. If you're looking for Vuex 3, [please check out `dev` branch](https://github.com/vuejs/vuex). +**Pinia is now the new default** + +The official state management library for Vue has changed to [Pinia](https://pinia.vuejs.org). Pinia has almost the exact same or enhanced API as Vuex 5, described in [Vuex 5 RFC](https://github.com/vuejs/rfcs/pull/271). You could simply consider Pinia as Vuex 5 with a different name. Pinia also works with Vue 2.x as well. + +Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead. --- Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official [devtools extension](https://github.com/vuejs/vue-devtools) to provide advanced features such as zero-config time-travel debugging and state snapshot export / import. -Learn more about Vuex at "[What is Vuex?](https://next.vuex.vuejs.org/)", or get started by looking into [full documentation](http://next.vuex.vuejs.org/). +Learn more about Vuex at "[What is Vuex?](https://vuex.vuejs.org/)", or get started by looking into [full documentation](http://vuex.vuejs.org/). ## Documentation -To check out docs, visit [vuex.vuejs.org](https://next.vuex.vuejs.org/). +To check out docs, visit [vuex.vuejs.org](https://vuex.vuejs.org/). ## Examples @@ -34,7 +38,7 @@ For questions and support please use the [Discord chat server](https://chat.vuej ## Issues -Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vuex/blob/dev/.github/contributing.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately. +Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vuex/blob/main/.github/contributing.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately. ## Changelog @@ -46,7 +50,7 @@ For latest releases and announcements, follow on Twitter: [@vuejs](https://twitt ## Contribution -Please make sure to read the [Contributing Guide](https://github.com/vuejs/vuex/blob/dev/.github/contributing.md) before making a pull request. +Please make sure to read the [Contributing Guide](https://github.com/vuejs/vuex/blob/main/.github/contributing.md) before making a pull request. ## License diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 300264a62..9f7824ae9 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -54,7 +54,7 @@ module.exports = { { text: 'v4.x', items: [ - { text: 'v3.x', link: 'https://vuex.vuejs.org/' } + { text: 'v3.x', link: 'https://v3.vuex.vuejs.org/' } ] } ], @@ -113,7 +113,7 @@ module.exports = { { text: 'v4.x', items: [ - { text: 'v3.x', link: 'https://vuex.vuejs.org/zh' } + { text: 'v3.x', link: 'https://v3.vuex.vuejs.org/zh' } ] } ], @@ -172,7 +172,7 @@ module.exports = { { text: 'v4.x', items: [ - { text: 'v3.x', link: 'https://vuex.vuejs.org/ja' } + { text: 'v3.x', link: 'https://v3.vuex.vuejs.org/ja' } ] } ], @@ -231,7 +231,7 @@ module.exports = { { text: 'v4.x', items: [ - { text: 'v3.x', link: 'https://vuex.vuejs.org/ptbr/' } + { text: 'v3.x', link: 'https://v3.vuex.vuejs.org/ptbr/' } ] } ], diff --git a/docs/guide/composition-api.md b/docs/guide/composition-api.md index 4ad444de8..6e0cb027e 100644 --- a/docs/guide/composition-api.md +++ b/docs/guide/composition-api.md @@ -37,7 +37,7 @@ export default { ## Accessing Mutations and Actions -When accessing mutations and actions, you can simply provide the `commit` and `dispatch` function inside the `setup` hook. +When accessing mutations and actions, you can simply provide the `commit` and `dispatch` method inside the `setup` hook. ```js import { useStore } from 'vuex' diff --git a/docs/guide/getters.md b/docs/guide/getters.md index f2242af88..00dba7b70 100644 --- a/docs/guide/getters.md +++ b/docs/guide/getters.md @@ -17,11 +17,7 @@ If more than one component needs to make use of this, we have to either duplicat Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. ::: warning WARNING -As of Vue 3.0, the getter's result is **not cached** as the computed property does. This is a known issue that requires Vue 3.1 to be released. You can learn more at [PR #1878](https://github.com/vuejs/vuex/pull/1883). -::: - -::: warning WARNING -As of Vue 3.0, the getter's result is **not cached** as the computed property does. This is a known issue that requires Vue 3.2 to be released. You can learn more at [PR #1883](https://github.com/vuejs/vuex/pull/1883). +As of Vue 3.0, the getter's result is **not cached** as the computed property does. This is a known issue that requires Vue 3.1 to be released. You can learn more at [PR #1878](https://github.com/vuejs/vuex/pull/1878). ::: Getters will receive the state as their 1st argument: diff --git a/docs/guide/hot-reload.md b/docs/guide/hot-reload.md index f46e0ea0b..fa59baaab 100644 --- a/docs/guide/hot-reload.md +++ b/docs/guide/hot-reload.md @@ -38,7 +38,7 @@ if (module.hot) { } ``` -Checkout the [counter-hot example](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) to play with hot-reload. +Checkout the [counter-hot example](https://github.com/vuejs/vuex/tree/main/examples/counter-hot) to play with hot-reload. ## Dynamic module hot reloading diff --git a/docs/guide/typescript-support.md b/docs/guide/typescript-support.md index 87bb72568..9f86dbdc9 100644 --- a/docs/guide/typescript-support.md +++ b/docs/guide/typescript-support.md @@ -12,7 +12,6 @@ To do so, declare custom typings for Vue's `ComponentCustomProperties` by adding ```ts // vuex.d.ts -import { ComponentCustomProperties } from 'vue' import { Store } from 'vuex' declare module '@vue/runtime-core' { diff --git a/docs/index.md b/docs/index.md index 35f007735..6f13308d4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,10 @@ # What is Vuex? -::: tip NOTE -This is the docs for Vuex 4, which works with Vue 3. If you're looking for docs for Vuex 3, which works with Vue 2, [please check it out here](https://vuex.vuejs.org/). +::: tip Pinia is now the new default + +The official state management library for Vue has changed to [Pinia](https://pinia.vuejs.org). Pinia has almost the exact same or enhanced API as Vuex 5, described in [Vuex 5 RFC](https://github.com/vuejs/rfcs/pull/271). You could simply consider Pinia as Vuex 5 with a different name. Pinia also works with Vue 2.x as well. + +Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead. ::: Vuex is a **state management pattern + library** for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. diff --git a/docs/installation.md b/docs/installation.md index 4c1b36b43..bb2e3e537 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -27,31 +27,6 @@ npm install vuex@next --save yarn add vuex@next --save ``` -## Promise - -Vuex requires [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). If your supporting browsers do not implement Promise (e.g. IE), you can use a polyfill library, such as [es6-promise](https://github.com/stefanpenner/es6-promise). - -You can include it via CDN: - -```html - -``` - -Then `window.Promise` will be available automatically. - -If you prefer using a package manager such as NPM or Yarn, install it with the following commands: - -```bash -npm install es6-promise --save # NPM -yarn add es6-promise # Yarn -``` - -Furthermore, add the below line into anywhere in your code before using Vuex: - -```js -import 'es6-promise/auto' -``` - ## Dev Build You will have to clone directly from GitHub and build `vuex` yourself if you want to use the latest dev build. diff --git a/docs/ja/guide/getters.md b/docs/ja/guide/getters.md index fa8e0fb80..c77f305ff 100644 --- a/docs/ja/guide/getters.md +++ b/docs/ja/guide/getters.md @@ -17,7 +17,7 @@ computed: { Vuex を利用するとストア内に "ゲッター" を定義することができます。それらをストアの算出プロパティと考えることができます。 ::: warning 警告 -Vue 3.0 では、ゲッターの結果は算出プロパティのように**キャッシュされません**。これは既知の問題で、Vue 3.1 がリリースされる必要があります。詳細は [PR #1878](https://github.com/vuejs/vuex/pull/1883) をご確認ください。 +Vue 3.0 では、ゲッターの結果は算出プロパティのように**キャッシュされません**。これは既知の問題で、Vue 3.2 がリリースされる必要があります。詳細は [PR #1878](https://github.com/vuejs/vuex/pull/1883) をご確認ください。 ::: ゲッターは第1引数として、state を受け取ります: diff --git a/docs/ja/guide/hot-reload.md b/docs/ja/guide/hot-reload.md index d1a31db13..1a48462ae 100644 --- a/docs/ja/guide/hot-reload.md +++ b/docs/ja/guide/hot-reload.md @@ -38,7 +38,7 @@ if (module.hot) { } ``` -ホットリローディングを試したい場合は、[counter-hot example](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)をチェックアウトしてください。 +ホットリローディングを試したい場合は、[counter-hot example](https://github.com/vuejs/vuex/tree/main/examples/counter-hot)をチェックアウトしてください。 ## 動的モジュールホットリローディング diff --git a/docs/ja/installation.md b/docs/ja/installation.md index 0f280f26f..abad4e180 100644 --- a/docs/ja/installation.md +++ b/docs/ja/installation.md @@ -27,31 +27,6 @@ npm install vuex@next --save yarn add vuex@next --save ``` -## Promise - -Vuex は [Promise (プロミス)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) を必要とします。ブラウザで Promise が実装されていない(例 IE)場合は、[es6-promise](https://github.com/stefanpenner/es6-promise) のようなポリフィルライブラリを使用できます。 - -CDN 経由でそれを含めることができます: - -```html - -``` - -`window.Promise` は自動的に有効になります。 - -NPM または Yarn のようなパッケージマネージャーを使用するのを希望する場合は、以下のコマンドでインストールします: - -```bash -npm install es6-promise --save # NPM -yarn add es6-promise # Yarn -``` - -さらに、Vuex を使用する前に、コードのどこかに次の行を追加します: - -```js -import 'es6-promise/auto' -``` - ## 開発版ビルド 最新の開発版ビルドを利用したい場合には、 GitHub から直接クローンし `vuex` を自身でビルドする必要があります。 diff --git a/docs/ptbr/guide/hot-reload.md b/docs/ptbr/guide/hot-reload.md index 729b6d8f7..f20c6fb17 100644 --- a/docs/ptbr/guide/hot-reload.md +++ b/docs/ptbr/guide/hot-reload.md @@ -38,7 +38,7 @@ if (module.hot) { } ``` -Confira o [counter-hot example](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) para brincar com o _hot-reload_. +Confira o [counter-hot example](https://github.com/vuejs/vuex/tree/main/examples/counter-hot) para brincar com o _hot-reload_. ## Módulo dinâmico de hot reloading diff --git a/docs/ptbr/installation.md b/docs/ptbr/installation.md index 7ef8a8f38..cce69cb23 100644 --- a/docs/ptbr/installation.md +++ b/docs/ptbr/installation.md @@ -27,31 +27,6 @@ npm install vuex@next --save yarn add vuex@next --save ``` -## Promise - -Vuex requer [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Se os seus navegadores não implementarem o _Promise_ (e.g. IE), você pode usar uma biblioteca _polyfill_, como a [es6-promise](https://github.com/stefanpenner/es6-promise). - -Você pode incluí-la via CDN: - -```html - -``` - -Então o `window.Promise` estará disponível automaticamente. - -Se você preferir usar um gerenciador de pacotes como NPM ou Yarn, instale-o com os seguintes comandos: - -```bash -npm install es6-promise --save # NPM -yarn add es6-promise # Yarn -``` - -Além disso, adicione a linha abaixo em qualquer lugar no seu código antes de usar o Vuex: - -```js -import 'es6-promise/auto' -``` - ## Dev Build Você terá que clonar diretamente do GitHub e fazer a distribuição (_build_) do `vuex` se diff --git a/docs/zh/api/index.md b/docs/zh/api/index.md index 9a7e7343e..67a35c09c 100644 --- a/docs/zh/api/index.md +++ b/docs/zh/api/index.md @@ -40,9 +40,7 @@ sidebar: auto - 类型: `{ [type: string]: Function }` - 在 store 上注册 action。处理函数总是接受 `context` 作为第一个参数,`payload` 作为第二个参数(可选)。 - - `context` 对象包含以下属性: + 在 store 上注册 action。处理函数总是接受 `context` 作为第一个参数,`context` 对象包含以下属性: ``` js { @@ -67,15 +65,15 @@ sidebar: auto ``` state, // 如果在模块中定义则为模块的局部状态 - getters, // 等同于 store.getters + getters // 等同于 store.getters ``` 当定义在一个模块里时会特别一些: ``` state, // 如果在模块中定义则为模块的局部状态 - getters, // 等同于 store.getters - rootState // 等同于 store.state + getters, // 当前模块的局部 getters + rootState, // 全局 state rootGetters // 所有 getters ``` @@ -89,12 +87,12 @@ sidebar: auto 包含了子模块的对象,会被合并到 store,大概长这样: - ``` js + ```js { key: { state, namespaced?, - mutations, + mutations?, actions?, getters?, modules? @@ -130,7 +128,7 @@ sidebar: auto 为某个特定的 Vuex 实例打开或关闭 devtools。对于传入 `false` 的实例来说 Vuex store 不会订阅到 devtools 插件。对于一个页面中有多个 store 的情况非常有用。 - ``` js + ```js { devtools: false } @@ -154,15 +152,15 @@ sidebar: auto ### commit -- `commit(type: string, payload?: any, options?: Object)` -- `commit(mutation: Object, options?: Object)` +- `commit(type: string, payload?: any, options?: Object)` +- `commit(mutation: Object, options?: Object)` 提交 mutation。`options` 里可以有 `root: true`,它允许在[命名空间模块](../guide/modules.md#命名空间)里提交根的 mutation。[详细介绍](../guide/mutations.md) ### dispatch -- `dispatch(type: string, payload?: any, options?: Object): Promise` -- `dispatch(action: Object, options?: Object): Promise` +- `dispatch(type: string, payload?: any, options?: Object): Promise` +- `dispatch(action: Object, options?: Object): Promise` 分发 action。`options` 里可以有 `root: true`,它允许在[命名空间模块](../guide/modules.md#命名空间)里分发根的 action。返回一个解析所有被触发的 action 处理器的 Promise。[详细介绍](../guide/actions.md) @@ -174,7 +172,7 @@ sidebar: auto ### watch -- `watch(fn: Function, callback: Function, options?: Object): Function` +- `watch(fn: Function, callback: Function, options?: Object): Function`  响应式地侦听 `fn` 的返回值,当值改变时调用回调函数。`fn` 接收 store 的 state 作为第一个参数,其 getter 作为第二个参数。最后接收一个可选的对象参数表示 Vue 的 [`vm.$watch`](https://cn.vuejs.org/v2/api/#vm-watch) 方法的参数。 @@ -182,50 +180,58 @@ sidebar: auto ### subscribe -- `subscribe(handler: Function, options?: Object): Function` +- `subscribe(handler: Function, options?: Object): Function` 订阅 store 的 mutation。`handler` 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数: - ``` js - store.subscribe((mutation, state) => { + ```js + const unsubscribe = store.subscribe((mutation, state) => { console.log(mutation.type) console.log(mutation.payload) }) + + // 你可以调用 unsubscribe 来停止订阅。 + unsubscribe() ``` 默认情况下,新的处理函数会被添加到其链的尾端,因此它会在其它之前已经被添加了的处理函数之后执行。这一行为可以通过向 `options` 添加 `prepend: true` 来覆写,即把处理函数添加到其链的最开始。 - ``` js + ```js store.subscribe(handler, { prepend: true }) ``` -  要停止订阅,调用此方法返回的函数即可停止订阅。 + + `subscribe` 方法将返回一个 `unsubscribe` 函数,当不再需要订阅时应该调用该函数。例如,你可能会订阅一个 Vuex 模块,当你取消注册该模块时取消订阅。或者你可能从一个 Vue 组件内部调用 `subscribe`,然后不久就会销毁该组件。在这些情况下,你应该记得手动取消订阅。 通常用于插件。[详细介绍](../guide/plugins.md) ### subscribeAction -- `subscribeAction(handler: Function, options?: Object): Function` +- `subscribeAction(handler: Function, options?: Object): Function` - 订阅 store 的 action。`handler` 会在每个 action 分发的时候调用并接收 action 描述和当前的 store 的 state 这两个参数: + 订阅 store 的 action。`handler` 会在每个 action 分发的时候调用并接收 action 描述和当前的 store 的 state 这两个参数。 + `subscribe` 方法将返回一个 `unsubscribe` 函数,当不再需要订阅时,应调用该函数。例如,当取消注册一个 Vuex 模块或销毁一个 Vue 组件之前。 - ``` js - store.subscribeAction((action, state) => { + ```js + const unsubscribe = store.subscribeAction((action, state) => { console.log(action.type) console.log(action.payload) }) + + // 你可以调用 unsubscribe 来停止订阅。 + unsubscribe() ``` 默认情况下,新的处理函数会被添加到其链的尾端,因此它会在其它之前已经被添加了的处理函数之后执行。这一行为可以通过向 `options` 添加 `prepend: true` 来覆写,即把处理函数添加到其链的最开始。 - ``` js + ```js store.subscribeAction(handler, { prepend: true }) ``` - 要停止订阅,调用此方法返回的函数即可停止订阅。 + `subscribeAction` 方法将返回一个 `unsubscribe` 函数,当不再需要订阅时,应该调用该函数。例如,你可能会订阅一个 Vuex 模块,并在取消注册该模块时取消订阅。或者你可能从 Vue 组件内部调用`subscribeAction`,然后不久就会销毁该组件。在这些情况下,你应该记得手动取消订阅。 `subscribeAction` 也可以指定订阅处理函数的被调用时机应该在一个 action 分发*之前*还是*之后* (默认行为是*之前*): - ``` js + ```js store.subscribeAction({ before: (action, state) => { console.log(`before action ${action.type}`) @@ -238,7 +244,7 @@ sidebar: auto `subscribeAction` 也可以指定一个 `error` 处理函数以捕获分发 action 的时候被抛出的错误。该函数会从第三个参数接收到一个 `error` 对象。 - ``` js + ```js store.subscribeAction({ error: (action, state, error) => { console.log(`error action ${action.type}`) @@ -251,7 +257,7 @@ sidebar: auto ### registerModule -- `registerModule(path: string | Array, module: Module, options?: Object)` +- `registerModule(path: string | Array, module: Module, options?: Object)` 注册一个动态模块。[详细介绍](../guide/modules.md#模块动态注册) @@ -259,7 +265,7 @@ sidebar: auto ### unregisterModule -- `unregisterModule(path: string | Array)` +- `unregisterModule(path: string | Array)` 卸载一个动态模块。[详细介绍](../guide/modules.md#模块动态注册) @@ -271,7 +277,7 @@ sidebar: auto ### hotUpdate -- `hotUpdate(newOptions: Object)` +- `hotUpdate(newOptions: Object)` 热替换新的 action 和 mutation。[详细介绍](../guide/hot-reload.md) @@ -279,7 +285,7 @@ sidebar: auto ### mapState -- `mapState(namespace?: string, map: Array | Object): Object` +- `mapState(namespace?: string, map: Array | Object): Object` 为组件创建计算属性以返回 Vuex store 中的状态。[详细介绍](../guide/state.md#mapstate-辅助函数) @@ -289,7 +295,7 @@ sidebar: auto ### mapGetters -- `mapGetters(namespace?: string, map: Array | Object): Object` +- `mapGetters(namespace?: string, map: Array | Object): Object` 为组件创建计算属性以返回 getter 的返回值。[详细介绍](../guide/getters.md#mapgetters-辅助函数) @@ -297,7 +303,7 @@ sidebar: auto ### mapActions -- `mapActions(namespace?: string, map: Array | Object): Object` +- `mapActions(namespace?: string, map: Array | Object): Object` 创建组件方法分发 action。[详细介绍](../guide/actions.md#在组件中分发-action) @@ -307,7 +313,7 @@ sidebar: auto ### mapMutations -- `mapMutations(namespace?: string, map: Array | Object): Object` +- `mapMutations(namespace?: string, map: Array | Object): Object` 创建组件方法提交 mutation。[详细介绍](../guide/mutations.md#在组件中提交-mutation) @@ -378,7 +384,7 @@ sidebar: auto 最后,将 key 传递给 `useStore` 方法以获取指定类型的 store 实例。 ```ts - // vue 组件 + // 在 vue 组件内 import { useStore } from 'vuex' import { key } from './store' diff --git a/docs/zh/guide/actions.md b/docs/zh/guide/actions.md index ed58c30cb..3b75834aa 100644 --- a/docs/zh/guide/actions.md +++ b/docs/zh/guide/actions.md @@ -39,7 +39,7 @@ actions: { } ``` -### 分发 Action +## 分发 Action Action 通过 `store.dispatch` 方法触发: @@ -81,7 +81,8 @@ actions: { checkout ({ commit, state }, products) { // 把当前购物车的物品备份起来 const savedCartItems = [...state.cart.added] - // 发出结账请求,然后乐观地清空购物车 + // 发出结账请求 + // 然后乐观地清空购物车 commit(types.CHECKOUT_REQUEST) // 购物 API 接受一个成功回调和一个失败回调 shop.buyProducts( @@ -97,7 +98,7 @@ actions: { 注意我们正在进行一系列的异步操作,并且通过提交 mutation 来记录 action 产生的副作用(即状态变更)。 -### 在组件中分发 Action +## 在组件中分发 Action 你在组件中使用 `this.$store.dispatch('xxx')` 分发 action,或者使用 `mapActions` 辅助函数将组件的 methods 映射为 `store.dispatch` 调用(需要先在根节点注入 `store`): @@ -120,7 +121,7 @@ export default { } ``` -### 组合 Action +## 组合 Action Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程? diff --git a/docs/zh/guide/forms.md b/docs/zh/guide/forms.md index 919095f78..d9095c728 100644 --- a/docs/zh/guide/forms.md +++ b/docs/zh/guide/forms.md @@ -15,6 +15,7 @@ ``` html ``` + ``` js // ... computed: { @@ -40,13 +41,14 @@ mutations: { } ``` -### 双向绑定的计算属性 +## 双向绑定的计算属性 必须承认,这样做比简单地使用“`v-model` + 局部状态”要啰嗦得多,并且也损失了一些 `v-model` 中很有用的特性。另一个方法是使用带有 setter 的双向绑定计算属性: ``` html ``` + ``` js // ... computed: { diff --git a/docs/zh/guide/getters.md b/docs/zh/guide/getters.md index 10af41eba..daf790e20 100644 --- a/docs/zh/guide/getters.md +++ b/docs/zh/guide/getters.md @@ -31,14 +31,14 @@ const store = createStore({ ] }, getters: { - doneTodos (state) => { + doneTodos (state) { return state.todos.filter(todo => todo.done) } } }) ``` -### 通过属性访问 +## 通过属性访问 Getter 会暴露为 `store.getters` 对象,你可以以属性的形式访问这些值: @@ -73,7 +73,7 @@ computed: { 注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。 -### 通过方法访问 +## 通过方法访问 你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。 @@ -92,7 +92,7 @@ store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } 注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。 -### `mapGetters` 辅助函数 +## `mapGetters` 辅助函数 `mapGetters` 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性: diff --git a/docs/zh/guide/hot-reload.md b/docs/zh/guide/hot-reload.md index 7ba5db6fc..c7d3fbf9f 100644 --- a/docs/zh/guide/hot-reload.md +++ b/docs/zh/guide/hot-reload.md @@ -38,7 +38,7 @@ if (module.hot) { } ``` -参考热重载示例 [counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)。 +参考热重载示例 [counter-hot](https://github.com/vuejs/vuex/tree/main/examples/counter-hot)。 ## 动态模块热重载 diff --git a/docs/zh/guide/index.md b/docs/zh/guide/index.md index c2b457c15..832a7233e 100644 --- a/docs/zh/guide/index.md +++ b/docs/zh/guide/index.md @@ -8,7 +8,7 @@ 2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地**提交 (commit) mutation**。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。 -### 最简单的 Store +## 最简单的 Store :::tip 提示 我们将在后续的文档示例代码中使用 ES2015 语法。如果你还没能掌握 ES2015,[你得抓紧了](https://babeljs.io/docs/learn-es2015/)! diff --git a/docs/zh/guide/modules.md b/docs/zh/guide/modules.md index 1f9a3cd4e..4976aa482 100644 --- a/docs/zh/guide/modules.md +++ b/docs/zh/guide/modules.md @@ -31,7 +31,7 @@ store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态 ``` -### 模块的局部状态 +## 模块的局部状态 对于模块内部的 mutation 和 getter,接收的第一个参数是**模块的局部状态对象**。 @@ -46,7 +46,6 @@ const moduleA = { state.count++ } }, - getters: { doubleCount (state) { return state.count * 2 @@ -83,9 +82,9 @@ const moduleA = { } ``` -### 命名空间 +## 命名空间 -默认情况下,模块内部的 action 和 mutation 仍然是注册在**全局命名空间**的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间,但是目前这并非出于功能上的目的(仅仅是维持现状来避免非兼容性变更)。必须注意,不要在不同的、无命名空间的模块中定义两个不同的 getter 从而导致错误。 +默认情况下,模块内部的 action 和 mutation 仍然是注册在**全局命名空间**的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间,但是目前这并非出于功能上的目的(仅仅是维持现状来避免非兼容性变更)。必须注意,不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 `namespaced: true` 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如: @@ -134,7 +133,7 @@ const store = createStore({ 启用了命名空间的 getter 和 action 会收到局部化的 `getter`,`dispatch` 和 `commit`。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 `namespaced` 属性后不需要修改模块内的代码。 -#### 在带命名空间的模块内访问全局内容(Global Assets) +### 在带命名空间的模块内访问全局内容(Global Assets) 如果你希望使用全局 state 和 getter,`rootState` 和 `rootGetters` 会作为第三和第四参数传入 getter,也会通过 `context` 对象的属性传入 action。 @@ -151,6 +150,7 @@ modules: { someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // -> 'foo/someOtherGetter' rootGetters.someOtherGetter // -> 'someOtherGetter' + rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter' }, someOtherGetter: state => { ... } }, @@ -161,6 +161,7 @@ modules: { someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // -> 'foo/someGetter' rootGetters.someGetter // -> 'someGetter' + rootGetters['bar/someGetter'] // -> 'bar/someGetter' dispatch('someOtherAction') // -> 'foo/someOtherAction' dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction' @@ -174,7 +175,7 @@ modules: { } ``` -#### 在带命名空间的模块注册全局 action +### 在带命名空间的模块注册全局 action 若需要在带命名空间的模块注册全局 action,你可添加 `root: true`,并将这个 action 的定义放在函数 `handler` 中。例如: @@ -200,7 +201,7 @@ modules: { } ``` -#### 带命名空间的绑定函数 +### 带命名空间的绑定函数 当使用 `mapState`、`mapGetters`、`mapActions` 和 `mapMutations` 这些函数来绑定带命名空间的模块时,写起来可能比较繁琐: @@ -209,7 +210,11 @@ computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b - }) + }), + ...mapGetters([ + 'some/nested/module/someGetter', // -> this['some/nested/module/someGetter'] + 'some/nested/module/someOtherGetter', // -> this['some/nested/module/someOtherGetter'] + ]) }, methods: { ...mapActions([ @@ -226,7 +231,11 @@ computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b - }) + }), + ...mapGetters('some/nested/module', [ + 'someGetter', // -> this.someGetter + 'someOtherGetter', // -> this.someOtherGetter + ]) }, methods: { ...mapActions('some/nested/module', [ @@ -261,7 +270,7 @@ export default { } ``` -#### 给插件开发者的注意事项 +### 给插件开发者的注意事项 如果你开发的[插件(Plugin)](plugins.md)提供了模块并允许用户将其添加到 Vuex store,可能需要考虑模块的空间名称问题。对于这种情况,你可以通过插件的参数对象来允许用户指定空间名称: @@ -277,7 +286,7 @@ export function createPlugin (options = {}) { } ``` -### 模块动态注册 +## 模块动态注册 在 store 创建**之后**,你可以使用 `store.registerModule` 方法注册模块: @@ -290,6 +299,7 @@ const store = createStore({ /* 选项 */ }) store.registerModule('myModule', { // ... }) + // 注册嵌套模块 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... @@ -304,13 +314,13 @@ store.registerModule(['nested', 'myModule'], { 注意,你可以通过 `store.hasModule(moduleName)` 方法检查该模块是否已经被注册到 store。需要记住的是,嵌套模块应该以数组形式传递给 `registerModule` 和 `hasModule`,而不是以路径字符串的形式传递给 module。 -#### 保留 state +### 保留 state 在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过 `preserveState` 选项将其归档:`store.registerModule('a', module, { preserveState: true })`。 当你设置 `preserveState: true` 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。 -### 模块重用 +## 模块重用 有时我们可能需要创建一个模块的多个实例,例如: diff --git a/docs/zh/guide/mutations.md b/docs/zh/guide/mutations.md index 8ee1fb1f8..9a8a7b6fb 100644 --- a/docs/zh/guide/mutations.md +++ b/docs/zh/guide/mutations.md @@ -24,7 +24,7 @@ const store = createStore({ store.commit('increment') ``` -### 提交载荷(Payload) +## 提交载荷(Payload) 你可以向 `store.commit` 传入额外的参数,即 mutation 的**载荷(payload)**: @@ -36,6 +36,7 @@ mutations: { } } ``` + ``` js store.commit('increment', 10) ``` @@ -57,7 +58,7 @@ store.commit('increment', { }) ``` -### 对象风格的提交方式 +## 对象风格的提交方式 提交 mutation 的另一种方式是直接使用包含 `type` 属性的对象: @@ -78,7 +79,7 @@ mutations: { } ``` -### 使用常量替代 Mutation 事件类型 +## 使用常量替代 Mutation 事件类型 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然: @@ -95,7 +96,8 @@ import { SOME_MUTATION } from './mutation-types' const store = createStore({ state: { ... }, mutations: { - // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 + // 我们可以使用 ES2015 风格的计算属性命名功能 + // 来使用一个常量作为函数名 [SOME_MUTATION] (state) { // 修改 state } @@ -105,7 +107,7 @@ const store = createStore({ 用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。 -### Mutation 必须是同步函数 +## Mutation 必须是同步函数 一条重要的原则就是要记住 **mutation 必须是同步函数**。为什么?请参考下面的例子: @@ -121,7 +123,7 @@ mutations: { 现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。 -### 在组件中提交 Mutation +## 在组件中提交 Mutation 你可以在组件中使用 `this.$store.commit('xxx')` 提交 mutation,或者使用 `mapMutations` 辅助函数将组件中的 methods 映射为 `store.commit` 调用(需要在根节点注入 `store`)。 @@ -144,7 +146,7 @@ export default { } ``` -### 下一步:Action +## 下一步:Action 在 mutation 中混合异步调用会导致你的程序很难调试。例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。在 Vuex 中,**mutation 都是同步事务**: diff --git a/docs/zh/guide/plugins.md b/docs/zh/guide/plugins.md index 6f164f35f..99d4c4a31 100644 --- a/docs/zh/guide/plugins.md +++ b/docs/zh/guide/plugins.md @@ -23,11 +23,11 @@ const store = createStore({ }) ``` -### 在插件内提交 Mutation +## 在插件内提交 Mutation 在插件中不允许直接修改状态——类似于组件,只能通过提交 mutation 来触发变化。 -通过提交 mutation,插件可以用来同步数据源到 store。例如,同步 websocket 数据源到 store(下面是个大概例子,实际上 `createPlugin` 方法可以有更多选项来完成复杂任务): +通过提交 mutation,插件可以用来同步数据源到 store。例如,同步 websocket 数据源到 store(下面是个大概例子,实际上 `createWebSocketPlugin` 方法可以有更多选项来完成复杂任务): ``` js export default function createWebSocketPlugin (socket) { @@ -54,7 +54,7 @@ const store = createStore({ }) ``` -### 生成 State 快照 +## 生成 State 快照 有时候插件需要获得状态的“快照”,比较改变的前后状态。想要实现这项功能,你需要对状态对象进行深拷贝: @@ -85,14 +85,14 @@ const store = createStore({ 上面插件会默认启用。在发布阶段,你需要使用 webpack 的 [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) 或者是 Browserify 的 [envify](https://github.com/hughsk/envify) 使 `process.env.NODE_ENV !== 'production'` 为 `false`。 -### 内置 Logger 插件 +## 内置 Logger 插件 Vuex 自带一个日志插件用于一般的调试: ``` js -import createLogger from 'vuex/dist/logger' +import { createLogger } from 'vuex' -const store = new Vuex.Store({ +const store = createStore({ plugins: [createLogger()] }) ``` diff --git a/docs/zh/guide/state.md b/docs/zh/guide/state.md index 411e64a86..51138701e 100644 --- a/docs/zh/guide/state.md +++ b/docs/zh/guide/state.md @@ -1,6 +1,6 @@ # State -### 单一状态树 +## 单一状态树 @@ -10,7 +10,7 @@ Vuex 使用**单一状态树**——是的,用一个对象就包含了全部 存储在 Vuex 中的数据和 Vue 实例中的 `data` 遵循相同的规则,例如状态对象必须是纯粹 (plain) 的。**参考:**[Vue#data](https://v3.cn.vuejs.org/api/options-data.html#data-2)。 -### 在 Vue 组件中获得 Vuex 状态 +## 在 Vue 组件中获得 Vuex 状态 那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在[计算属性](https://cn.vuejs.org/guide/computed.html)中返回某个状态: @@ -43,7 +43,7 @@ const Counter = { } ``` -### `mapState` 辅助函数 +## `mapState` 辅助函数 @@ -79,7 +79,7 @@ computed: mapState([ ]) ``` -### 对象展开运算符 +## 对象展开运算符 `mapState` 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 `computed` 属性。但是自从有了[对象展开运算符](https://github.com/tc39/proposal-object-rest-spread),我们可以极大地简化写法: @@ -93,6 +93,6 @@ computed: { } ``` -### 组件仍然保有局部状态 +## 组件仍然保有局部状态 使用 Vuex 并不意味着你需要将**所有的**状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。 diff --git a/docs/zh/guide/strict.md b/docs/zh/guide/strict.md index b19a4fea1..6ff0f3a59 100644 --- a/docs/zh/guide/strict.md +++ b/docs/zh/guide/strict.md @@ -11,7 +11,7 @@ const store = createStore({ 在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。 -### 开发环境与发布环境 +## 开发环境与发布环境 **不要在发布环境下启用严格模式**!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。 diff --git a/docs/zh/guide/testing.md b/docs/zh/guide/testing.md index 11f5a6ee8..c2ea95327 100644 --- a/docs/zh/guide/testing.md +++ b/docs/zh/guide/testing.md @@ -119,7 +119,7 @@ const testAction = (action, args, state, expectedMutations, done) => { describe('actions', () => { it('getAllProducts', done => { - testAction(actions.getAllProducts, [], {}, [ + testAction(actions.getAllProducts, null, {}, [ { type: 'REQUEST_PRODUCTS' }, { type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } } ], done) diff --git a/docs/zh/guide/typescript-support.md b/docs/zh/guide/typescript-support.md index f8cc501bd..70c3967c6 100644 --- a/docs/zh/guide/typescript-support.md +++ b/docs/zh/guide/typescript-support.md @@ -12,7 +12,6 @@ Vuex 没有为 `this.$store` 属性提供开箱即用的类型声明。如果你 ```ts // vuex.d.ts -import { ComponentCustomProperties } from 'vue' import { Store } from 'vuex' declare module '@vue/runtime-core' { @@ -28,7 +27,7 @@ declare module '@vue/runtime-core' { } ``` -## `useStore` 组合式函数类型声明 +## `useStore` 组合式函数类型声明 当使用组合式 API 编写 Vue 组件时,您可能希望 `useStore` 返回类型化的 store。为了 `useStore` 能正确返回类型化的 store,必须执行以下步骤: @@ -91,7 +90,6 @@ export default { 本质上,Vuex 将store 安装到 Vue 应用中使用了 Vue 的 [Provide/Inject](https://v3.cn.vuejs.org/api/composition-api.html#provide-inject) 特性,这就是 injection key 是很重要的因素的原因。 - ### 简化 `useStore` 用法 引入 `InjectionKey` 并将其传入 `useStore` 使用过的任何地方,很快就会成为一项重复性的工作。为了简化问题,可以定义自己的组合式函数来检索类型化的 store : diff --git a/docs/zh/index.md b/docs/zh/index.md index 6a4740fd0..a9113faa5 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -1,12 +1,12 @@ # Vuex 是什么? ::: tip 提示 -这是与 Vue 3 匹配的 Vuex 4 的文档。如果您在找与 Vue 2 匹配的 Vuex 3 的文档,[请在这里查看](https://vuex.vuejs.org/)。 +这是与 Vue 3 匹配的 Vuex 4 的文档。如果您在找与 Vue 2 匹配的 Vuex 3 的文档,[请在这里查看](https://v3.vuex.vuejs.org/zh/)。 ::: Vuex 是一个专为 Vue.js 应用程序开发的**状态管理模式 + 库**。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 -### 什么是“状态管理模式”? +## 什么是“状态管理模式”? 让我们从一个简单的 Vue 计数应用开始: @@ -62,7 +62,7 @@ createApp(Counter).mount('#app') ![vuex](/vuex.png) -### 什么情况下我应该使用 Vuex? +## 什么情况下我应该使用 Vuex? Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。 diff --git a/docs/zh/installation.md b/docs/zh/installation.md index ddf3cb4b1..34baaacdc 100644 --- a/docs/zh/installation.md +++ b/docs/zh/installation.md @@ -1,6 +1,6 @@ # 安装 -### 直接下载 / CDN 引用 +## 直接下载 / CDN 引用 [https://unpkg.com/vuex@4](https://unpkg.com/vuex@4) @@ -15,44 +15,19 @@ ``` -### npm +## npm ``` bash npm install vuex@next --save ``` -### Yarn +## Yarn ``` bash yarn add vuex@next --save ``` -### Promise - -Vuex 依赖 [Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises)。如果你支持的浏览器并没有实现 Promise (比如 IE),那么你可以使用一个 polyfill 的库,例如 [es6-promise](https://github.com/stefanpenner/es6-promise)。 - -你可以通过 CDN 将其引入: - -``` html - -``` - -然后 `window.Promise` 会自动可用。 - -如果你喜欢使用诸如 npm 或 Yarn 等包管理器,可以按照下列方式执行安装: - -``` bash -npm install es6-promise --save # npm -yarn add es6-promise # Yarn -``` - -或者更进一步,将下列代码添加到你使用 Vuex 之前的一个地方: - -``` js -import 'es6-promise/auto' -``` - -### 自己构建 +## 自己构建 如果需要使用 dev 分支下的最新版本,您可以直接从 GitHub 上克隆代码并自己构建。 diff --git a/examples/classic/shopping-cart/components/ShoppingCart.vue b/examples/classic/shopping-cart/components/ShoppingCart.vue index 948d6c54e..b39a4c54c 100644 --- a/examples/classic/shopping-cart/components/ShoppingCart.vue +++ b/examples/classic/shopping-cart/components/ShoppingCart.vue @@ -1,11 +1,11 @@