一、介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
二、在项目中安装 vuex
1、安装
在需要安装 vuex 的项目终端中,输入下面指令,回车等待安装:
npm install vuex@next --save
安装完成:
可以看到 package.json 里面,已经有了 vuex :
2、创建一个最简单的 store
新建一个文件夹 store ,新建文件 index.js :
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2)不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
三、基本使用——state(获取仓库store中的数据)
仓库 store 的 index.js 中:
main.js 中安装引入:
修改 HomeView.vue :
可以看到成功获取到数据:
四、基本使用——mutation(清楚地跟踪到数据的改变)
接上面的例子,在 HomeView.vue 中添加一个按钮来改变仓库 store 中的数据:
上面这个方法可以更改,但是 vuex 跟踪不到数据的变化(因为store中的数据是很多个组件共享的,不能判断出是哪个组件里面改变了数据)。这时就需要用到 mutation 。
添加 mutation 的方法:
在 HomeView.vue 中去实现这个方法:
这样就可以改变数据了:
如果想在 mutation 中传参,修改上面代码:
这样就可以实现传参,每点一次按钮就加5:
五、基本使用——getters(认为是store中的计算属性)
1、通过方法访问
接上面的例子,如果我们想翻转state中的msg :
index.js :Getter 接受 state 作为其第一个参数:
// 引入
import { createStore } from "vuex";
// 创建store实例
const store=createStore({
state(){ //是存储的单一状态,是存储的基本数据
return{
msg:'helloworld'
}
},
getters:{ //认为是store中的计算属性
reverMsg:function(state){
// 翻转state中的msg
return state.msg.split('').reverse().join('')
}
},
})
// 导出仓库
export default store
在 HomeView.vue 中去使用这个方法:
<template>
<div class="home">
<p>{{$store.getters.reverMsg}}</p>
</div>
</template>
可以看到已经翻转:
2、通过属性访问
接上面例子,如果我们要获取data里面的msg的长度:
index.js :
// 引入
import { createStore } from "vuex";
// 创建store实例
const store=createStore({
state(){ //是存储的单一状态,是存储的基本数据
return{
msg:'helloworld'
}
},
// 如果state的状态需要过滤或者其他的操作
getters:{ //认为是store中的计算属性
reverMsg:function(state){
// 翻转state中的msg
return state.msg.split('').reverse().join('')
},
// 如果我们要获取data里面的msg的长度
reverMsgLength:function(state,getters){ //getters表示当前store中的getters对象
return getters.reverMsg.length
}
},
})
// 导出仓库
export default store
HomeView.vue :
<template>
<div class="home">
<p>{{$store.getters.reverMsg}}</p>
<p>{{$store.getters.reverMsgLength}}</p>
</div>
</template>
六、基本使用——actions(可以发送异步请求)
1、介绍
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
2、例子
接上面的例子,我们使用猫眼电影的数据:
复制上面的链接 ,在 index.js 里面 :
// 引入
import { createStore } from "vuex";
// 创建store实例
const store=createStore({
// 获取猫眼电影的数据
actions:{
getHot:function(){
fetch('https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=成都&ci=59&channelId=4').then((res)=>{
console.log(res);
})
}
}
})
// 导出仓库
export default store
在 HomeView.vue 里面,Action 通过 store.dispatch 方法触发:
<script>
// @ is an alias to /src
export default {
name: 'HomeView',
mounted(){
// Action 通过 store.dispatch 方法触发:
this.$store.dispatch('getHot')
}
}
</script>
在 vue.config.js 中配置中转服务器来实现代理:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy:{ //通过代理实现跨域
'path':{
target:'https://i.maoyan.com', //替换的服务端地址
changeOrigin:true, //开启代理,允许跨域
//设置重写的路径
pathRewrite:{
'^/path':''
}
}
}
}
})
然后修改 index.js 里面的路径:
获取数据可以使用 axios ,安装 axios ,然后在 mutation 里面添加方法:
完整代码如下:
HomeView.vue :
<script>
// @ is an alias to /src
export default {
name: 'HomeView',
mounted(){
// Action 通过 store.dispatch 方法触发:
this.$store.dispatch('getHot','hhh') //后面传一个参数
}
}
</script>
index.js :
// 引入
import axios from "axios";
import { createStore } from "vuex";
// 创建store实例
const store=createStore({
state(){ //是存储的单一状态,是存储的基本数据
return{
hotList:[]
}
},
mutations:{
updateHotList:function(state,value){
state.hotList=value
}
},
// 获取猫眼电影的数据
actions:{
getHot:function(context,payload){ //context:与store实例具有相同的属性和方法的对象
// fetch('/path/api/mmdb/movie/v3/list/hot.json?ct=成都&ci=59&channelId=4').then((res)=>{
// console.log(res.json());
// console.log(context.state.hotList);
// })
axios.get('/path/api/mmdb/movie/v3/list/hot.json?ct=成都&ci=59&channelId=4').then(res=>{
console.log(res);
console.log(context);
// context.state.hotList=res.data.data.hot
context.commit('updateHotList',res.data.data.hot)
console.log(context.state.hotList);
console.log(payload); //payload 接收传参‘hhh’
})
}
}
})
// 导出仓库
export default store
vue.config.js :
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy:{ //通过代理实现跨域
'path':{
target:'https://i.maoyan.com', //替换的服务端地址
changeOrigin:true, //开启代理,允许跨域
//设置重写的路径
pathRewrite:{
'^/path':''
}
}
}
}
})
获取到数据:
七、基本使用——module(模块分割)
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
八、基本使用——辅助函数
1、mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数帮助我们生成计算属性,让你少按几次键:
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState
传一个字符串数组。
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
2、对象展开运算符
mapState
函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed
属性。但是自从有了对象展开运算符,我们可以极大地简化写法:
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}