Vue四、vuex是个啥?怎么用?
一、vuex是什么
官方解释是:Vuex是通过全局注入store对象,来实现组件间的状态共享,是一个专为 Vue.js 应用程序开发的状态管理模式。
我的理解是,例如你的项目里某一个数据在前端多个组件中都有应用,如果一个改的话,那岂不是每个组件都需要改一次,特别是类似的数据多起来的话,操作起来想想就繁杂,于是,可以通过Vuex来实现组件间的状态共享,改一个,其他组件中的值的状态自动改变。
* 那么可能有人问:直接用全局对象不久可以了?
Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
* 那么可能有人问:Vuex与localStorage不一样么?
vuex 是 vue 的状态管理器,存储的数据是响应式的,但是并不会保存起来,刷新之后就回到了初始状态,但是localStorage是保存在浏览器中的,刷新之后还可以取出来继续使用。而且,vuex里,我们保存的一般都是数组,而localStorage保存到话只支持字符串
* 那么可能有人问:既然是存数据的,那什么时候用vuex,什么时候直接用简单的通信方式哩?
- 如果项目足够简单(只是多个组件间传递数据),最好不要使用 Vuex。一个简单的 store 模式就足够所需了,只使用组件间常用的通信方法即可,使用 Vuex 可能是繁琐冗余的。
Vue组件简单常用的通信方式有以下几种:
1、父向子
传值通过props的方式;
2、子向父
传值通过events ($emit),实际上就是子组件把自己的数据发送到父组件;
3、父调用子方法
通过ref;provide / inject。
4、兄弟之间
通信通过bus
5、跨级嵌套通信
可以使用bus;provide / inject等。
6、 vue组件间通信六种方式(完整版)
- 如果需要构建一个中大型单页应用(多级组件嵌套),一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,Vuex 将会成为自然而然的选择。
- 由上面官方给的图可以看出vuex由以下几部分构成
1. State
state是存储的单一状态,是存储的基本数据。对象必须是纯粹的对象 (含有零个或多个的 key/value 对)。它作为一个“唯一数据源 (SSOT)”而存在。
2. Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation更改数据【必须是同步函数】。(使用store.commit方法更改state存储的状态)
3. Action
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意【异步操作】。
Action 通过store.dispatch
方法触发,Mutation使用store.commit
触发。
4. Getter
getters是store的计算属性,可以通过store 中的 state 中派生出一些状态(比如说过滤)。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。
5. Module
当应用变得复杂时,store对象可能变得相当臃肿复杂。为了解决这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
* 所以vuex的是怎么运行的?
- vuex的运行流程
- 在组件内部,通过dispatch来分发action。
- 再通过action来第调用mutation
- 进而触发mutation内部的commit来修改state
- 最后state改变,导致页面重新render。
二、vuex怎么用
1.安装
NPM
npm install vuex --save
Yarn
yarn add vuex
2.中型数据不太复杂的项目中(直接使用)
其实使用cli工具初始化的项目中如果选择安装vuex的话就已经有了。如下:
在src
文件夹中新建一个store
文件夹,下面新建一个名为index.js
的文件。如下
在index.js
中注册vuex,并且在state中初始化
一个count
变量介绍mutations
一个tasks
数组 + 一个taskFinish
方法介绍getters
一个increment
方法介绍actions
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count:0,
tasks: [
{ id: 1, finish: true },
{ id: 2, finish: false }
]
},
mutations: {
// 更改 Vuex 的 store 中的状态的唯一方法
countAdd (state) {
// 自定义将传过来的参数操作操作
state.count++
}
},
getters: {
// getters是store的计算属性, 有时候需要从 store 中的 state 中派生出一些状态(比如说过滤)
taskFinish: state => {
return state.tasks.filter(a=> a.finish)
}
},
actions: {
// actions 提交的是 mutation,而不是直接变更状态.
increment(context) {
context.commit('countAdd')
}
},
modules: {}
});
在main.js
中导入store实例,我们就可以通过this.$store.state
访问这些状态,一般把它的值注入到computed
中
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 阻止启动生产消息,常用作指令 ⬇
Vue.config.productionTip = false;
new Vue({
router,
store, // 注册,全局使用vuex,(this.$store)
render: h => h(App)
}).$mount("#app");
在后缀名为.vue
的组件中使用
-
this.$store.state.count
实现数据调用 -
this.$store.commit('countAdd')
实现通过mutations实现数据修改(同步方式) -
this.$store.getters.taskFinish
实现通过getters实现 state 中的数据派生出一些状态(过滤数据)
<!-- 测试项目 -->
<template>
<div>
<h3>{{this.$store.state.count}}</h3>
<h4>{{this.$store.getters.taskFinish}}</h4>
<input type="button" value="count自增" @click="countAdd"></div>
<button @click="increment">按钮</button>
</template>
<script>
export default {
data() {
return {};
},
components: {},
computed: {},
mounted() {},
methods: {
countAdd() {
// commit不仅可以传state的参数,而且可以传额外的参数,只需在mutations里定义的函数后面的参数里与这里的一致就可以
this.$store.commit('countAdd')
},
increment(){
// 效果跟countAdd()是一样的
this.$store.dispatch('increment')
}
}
};
</script>
<style>
</style>
* 看完例子之后可能有人说了,mutations和action效果不是一样的么,直接使用mutations不就行了,何必再用action做一次类似于请求转发的操作呢?
答 :实际上并非如此。还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
当然这些方法只是简单的介绍理解一下vuex里面的东西,深入理解还需要自己实际写一下去探索。上面输出的格式可以简化,例如
将this.$store.state.count
放入computed
计算属性当中去export default { name: 'App', computed:{ count(){ return this.$store.state.count; } } }
然后直接通过
<h3>{{count}}</h3>
调用即可
3.大型数据密集型项目中使用(需要将其划分为模块使用)
方法一: 在src
文件夹中新建一个store
文件夹,下面分别新建名为index.js / actions.js / getter.js / mutations.js
的文件。
方法一例子来自于vuex最详细完整的使用用法,让大家知道每个文件里的内容的格式
在index.js
中
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 导入相应的模块,*相当于引入了这个组件下所有导出的事例,使用*和from关键字来实现的模块的继承
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)
// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
const state = {
resturantName: '飞歌餐馆' // 默认值
// id: xxx 如果还有全局状态也可以在这里添加
// name:xxx
}
// 注册上面引入的各大模块
const store = new Vuex.Store({
state, // 共同维护的一个状态,state里面可以是很多个全局状态
getters, // 获取数据并渲染
actions, // 数据的异步操作
mutations // 处理数据的唯一途径,state的改变或赋值只能在这里
})
export default store // 导出store并在 main.js中引用注册。
在actions.js
中
// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
return commit ('modifyBName', name)
}
// ES6精简写法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)
在mutations.js
中
// 提交 mutations是更改Vuex状态的唯一合法方法
export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
state.resturantName = name // 把方法传递过来的参数,赋值给state中的resturantName
}
export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
state.resturantName = name
}
在getters.js
中
// 获取最终的状态信息
export const resturantName = state => state.resturantName
在后缀为.vue
的文件中
像上面一样类似操作
方法二:新建一个名为modules
的文件夹,modules文件夹下面分别新建自己对应需要的模块。
每一个
modules
文件夹下的文件里
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
moudel.exports = moduleA
在index.js
中注册使用的话
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './modules/cart' // 导入相应的模块,使用*和from关键字来实现的模块的继承
import * as ...
Vue.use(Vuex)
// 注册上面引入的各大模块
const store = new Vuex.Store({
modules: {
a: moduleA,
...
}
})
export default store // 导出store并在 main.js中引用注册。
使用:
store.state.a.xx // -> moduleA 的状态