开篇寄语
接着前文继续深化学习Vue.js相关内容,前文基础内容可以看下方的前情提要,也可以在本站搜索关键词Vue,会有更多精彩的相关内容,本篇内容主要是聊聊Vue.js中的Vuex是什么东东,以及结合实例来看看它是如何使用的。
前情提要
- 《如何管理 VueJS Mixins》
- 《科学入门Vuejs,几个小示例让你爱上它》
- 《Vuejs中各种"v-xxx"都是什么,一篇带示例的文章让你搞清楚》
- 《Vuejs中的methods, computed和watch有何妙用?》
- 《Vuejs中组件开发从初级到高级(一)》
内容详情
以下内容是以Vue 3位范例,虽然与Vue 2差别不是很大。
Vuex 是 Vue.js 的官方Store库和状态管理模式。它的优势是作为 Vue 的第一个Store,因此已被世界各地成千上万的开发人员在数千个项目中使用,并且经过时间测试证明是有效的。让我们来看看它是如何工作的。
请注意,如果您使用的是 Vue 2,您将需要使用 Vuex 3。我将展示与 Vue 3 兼容的 Vuex 4 中的示例,但只有一些不同之处,因此这里描述的大部分内容都适用。
如何安装?
npm install vuex@4 --save # or with yarn yarn add vuex@4
然后通过类似于 Vue 3 的 createApp() 函数的 createStore() 函数实例化它,比如:
// store/index.js import {createStore} from 'vuex' export default createStore()
最后,像其他任何 Vue 插件一样使用 use() 方法向 Vue 注册它。
// main.js import { createApp } from 'vue' import store from '@/store' // short for @/store/index const app = createApp({ /* your root component */ }) app.use(store)
Vuex特性
- 是官方解决方案
- 是最长寿且经过实战考验的解决方案
- 在 Vue 社区中很受欢迎,拥有丰富的可用教育资源(因此可能最适合让新开发人员加入项目)
- 与 Vue Devtools 完美集成,提供出色的开发体验
Store定义
Vuex 中的存储是通过传递给 createStore 函数的对象定义的。该对象可以具有以下任何属性:state, getters, mutations, and actions。
// store/index.js export default createStore({ state:{}, getters:{}, mutations: {}, actions:{} })
分别看一下都是代表什么意思:
state
Store的状态在 state 属性中定义。状态只是一个花哨的词,意思是你想在你的Store中保存的数据。您可以将其视为组件上的数据属性,但可用于您的任何组件。你可以把你想要的任何类型的数据放在这里。此外,状态中定义的不同属性可以是您喜欢的任何数据类型,例如对象、数组、字符串、数字等。
state:{ user: { name: 'John Doe', email: '[email protected]', username: 'jd123'}, posts: [], someString: 'etc' }
为了在任何组件模板中访问Store的状态,您可以使用 $store.state[propertyNameHere]。例如,为了在配置文件组件中访问用户名,我们执行以下操作:
// ProfileComponent.vue <template> <h1>Hello, my name is {{$store.state.user.name}}</h1> </template>
或者我们可以使用计算属性稍微清理模板。
// ProfileComponent.vue <template> <h1>Hello, my name is {{name}}</h1> </template> <script> export default{ computed:{ name(){ return this.$store.user.name } } } </script>
随着您继续从组件访问Store的状态,以这种方式制作计算属性会变得越来越冗长。为了让事情变得轻松,您可以使用其中一个 Vuex 辅助函数 mapState,将我们希望在组件中访问的状态中的顶级属性数组传递给它。
<template> <h1>Hello, my name is {{user.name}}</h1> </template> <script> export default{ computed:{ ...mapState(['user']) } } </script>
Getters
除了存储在状态中的数据本身之外,Vuex 存储还可以拥有所谓的“getter”。您可以将 getter 视为计算属性的存储版本,就像计算属性一样,它们基于依赖关系进行缓存。 Vuex 中的所有 getter 都是在“getters”属性下定义的函数,并接收状态以及所有其他 getter 作为参数。然后从函数返回的就是那个 getter 的值。
{ state:{ posts: ['post 1', 'post 2', 'post 3', 'post 4'] }, // the result from all the postsCount getters below is exactly the same // personal preference dicates how you'd like to write them getters:{ // arrow function postsCount: state => state.posts.length, // traditional function postsCount: function(state){ return state.posts.length }, // method shorthand postsCount(state){ return state.posts.length }, // can access other getters postsCountMessage: (state, getters) => `${getters.postsCount} posts available` } }
访问 store 的 getter 与访问 state 非常相似,示例如下:
// FeedComponent.vue <template> <p>{{$store.getters.postsCount}} posts available</p> </template>
您还可以在组件中使用计算属性或辅助函数(这次是 mapGetters),就像state一样。
// FeedComponent.vue <template> <p>{{postsCount}} posts available</p> </template> <script> import {mapGetters} from 'vuex' export default{ computed:{ ...mapGetters(['postsCount']) } } </script>
Mutations and Actions
- Mutation总是同步的,并且是唯一可以直接改变状态的东西。Mutation只负责更改单个数据。
- Actions可以是同步的或异步的,不应该直接改变状态而是调用突变。操作可以调用多个突变。
此外,这里还有更多信息和最佳实践。
- Mutation
- 大写突变名称的约定
- 通过 commit('MUTATION_NAME', payload) 调用
- 有效载荷是可选的
- 不应该包含是否改变数据的任何逻辑
- 最佳做法是仅从您的操作中调用它们(即使您确实有权在组件中调用它们)
- Actions
- 可以从Store中的其他操作中调用
- 是从组件内部更改状态的主要方式
- 通过 dispatch('actionName', payload) 调用
- 有效载荷是可选的
- 可以包含改变或不改变什么的逻辑
- 良好的做法是先定义为异步,这样如果逻辑从同步更改为异步,您调度操作的位置就不必更改
来一个实际案例:
{ state: { posts: ['post 1', 'post 2', 'post 3', 'post 4'], user: { postsCount: 2 } errors: [] } mutations:{ // convention to uppercase mutation names INSERT_POST(state, post){ state.posts.push(post) }, INSERT_ERROR(state, error){ state.errors.push(error) }, INCREMENT_USER_POSTS_COUNT(state, error){ state.user.postsCount++ } }, actions:{ async insertPost({commit}, payload){ //make some kind of ajax request try{ await doAjaxRequest(payload) // can commit multiple mutations in an action commit('INSERT_POST', payload) commit('INCREMENT_USER_POSTS_COUNT') }catch(error){ commit('INSERT_ERROR', error) } } } }
为了说明起见,这些变更似乎过于简化,但在生产代码库中,变更应该同样简短,因为它们只负责更新单个状态。
如果你是 Vuex 的新手,Mutations and Actions之间的区别一开始可能有点麻烦,但过一段时间你就会习惯。尽管编写Mutations并不是最令人愉快的,因为它会产生大量的样板文件,但对于调试应用程序的时间旅行的开发工具体验来说是必要的。然而,Vuex 5 的 RFC 表明,未来将不再需要突变,这样我们就可以跳过突变样板并仍然享受 devtools 体验。
最后,要从组件运行操作,您可以调用 store 上的 dispatch 方法并将要运行的操作的名称作为第一个参数传递给它,并将有效负载作为第二个参数传递给它。
// PostEditorComponent.vue <template> <input type="text" v-model="post" /> <button @click="$store.dispatch('insertPost', post)">Save</button> </template>
从组件的方法中分派您的操作也很常见。
// PostEditorComponent.vue <template> <input type="text" v-model="post" /> <button @click="savePost">Save</button> </template> <script> export default{ methods:{ savePost(){ this.$store.dispatch('insertPost', this.post) } } } </script>
和 state 和 getter 一样,您可以使用辅助函数将操作映射到组件方法。
// PostEditorComponent.vue <template> <input type="text" v-model="post" /> <button @click="insertPost(post)">Save</button> </template> <script> import {mapActions} from 'vuex' export default{ methods:{ ...mapActions(['insertPost']) } } </script>
在模块中组织
随着应用程序的文件越来越多,您会发现单个全局存储文件变得难以忍受且难以快速找到文件。 Vuex 对此的解决方案称为模块,它允许您根据域逻辑(例如一个用于帖子的文件、另一个用于用户的文件、另一个用于产品的文件等)将您的Store划分为单独的文件,然后访问state、getter等来自特定命名空间下的模块。
您可以在官方 Vuex 文档中查看模块的确切实现细节。
Vue devtools
最后,如果我不强调 Vuex 与 Vue Devtools 的配合,我将是失职。为了使用 Vuex 3 处理 Vue 2 项目,您在 devtools 中有一个专用的 Vuex 选项卡,它允许您查看已提交变更的运行列表,到特定变更的时间旅行以查看您的应用程序和状态时间点,甚至将应用程序恢复到特定Mutations时的状态。
总结
Vuex 是 Vue.js 的经过实战测试和流行的Store,但不是您可以使用的唯一选择。对于轻量级应用程序,可以试试新成员新成员:Pinia。
ArrayArrayArray- 我的微信
- 微信扫一扫加好友
- 我的微信公众号
- 扫描关注公众号