
开篇寄语
接着前文继续深化学习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。
- 我的微信
- 微信扫一扫加好友
-
- 我的微信公众号
- 扫描关注公众号
-





