标签: vue
[toc]
大纲: 什么时候用到vuex,简介vuex,举例子说明vuex的最佳实践;
细节: action与mutation的区别、场景;
例子: 常用的写法、巧妙的方案;
1. 用处
管理大型应用的公共数据,方便数据的获取与设置;方便组件之间的通讯;
Flux 架构就像眼镜:您自会知道什么时候需要它。
用了vuex 就不要再用其他方式来传数据,不要用params,不要用事件,只用vuex!
1.2 场景
- 一处修改多处同步的数据
- 跨页共享的数据
- 跨组件共享的数据
2. 概念
vuex流程图state
单一状态树,管理应用层面需要共享的全部数据;
// store.js
const store = new Vuex.Store({
state: {
data1: 1,
data2: 'text'
}
})
new Vue({
store
})
//app.vue
{
computed: {
count: this.$store.state.data1
}
}
也可以分模块来管理;
new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
},
})
有个辅助函数mapState
,可以方便取多个值;
// app.vue
{
computed: {
...mapState(['data1', 'data2'])
}
}
mutations
唯一允许更改状态树的方法,通过mutations实现了数据的单向流动;只能是同步执行;
// store.js
new Vuex.Store({
mutations: {
increment (state, payload) {
state.count += payload.num
}
}
})
// app.vue
{
methods: {
increment() {
this.$store.commit('increment', {num: 1})
}
}
}
有辅助函数mapMutations
,方便注入多个方法;
//app.vue
{
methods: {
...mapMutations({
add: 'increment'
})
}
}
actions
一个衍生的概念,初衷是为了方便使用vue-devtool;尤雨溪的回答
一般用于发送请求等异步操作,也可以组合多个mutation
到同一个action
里;
// store.js
new Vuex.Store({
actions: {
incrementAsync({commit}, payload) {
setTimeout(() => {
commit('increment', payload)
}, 1000)
}
}
})
// app.vue
{
methods: {
incrementAsync() {
this.$store.dispatch('incrementAsync', {num: 1})
}
}
}
同样也有辅助函数mapActions
来注入;
// app.vue
{
methods: {
...mapActions({
add: 'incrementAsync'
})
}
}
组合mutation
// store.js
{
actions: {
// 获取用户信息
GET_USER_INFO({ commit, state }) {
return new Promise((resolve, reject) => {
getUserInfo(state.token).then(response => {
if (!response.data) reject('error')
const data = response.data
commit('SET_ROLES', data.role)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
resolve(response)
}).catch(error => {
reject(error)
})
})
}
}
}
组合action
// store.js
{
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
}
3. 实践
目录结构
从简单到模块化的三种项目结构
参考文章:大型项目的vuex结构 (文章末尾有github地址)
- 简单的vuex结构:仅仅是针对vuex的文件目录结构,适用于小型,数据简单的项目
store # 放在根目录下
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
├── getters.js # 根级别的 getter
└── modules
├── a.js # a模块,包含各自的action,mutation...
└── b.js # b模块
- 按module划分文件夹:当每个模块的action,mutation有很多时,划分出单独的module文件夹,将action, mutation 抽出
store # 放在根目录下
├── index.js # 导出store
# 不再使用全局的action,mutation...
└── modules # 按模块划分文件夹
├── module-a # a模块
| ├── actions.js
| ├── getters.js
| ├── mutations.js
| └── index.js
└── modeule-b # b模块,同上
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './modules/module-a'
import moduleB from './modules/module-b'
export default new Vuex.Store({
modules: { //注册模块
a: moduleA,
b: moduleB
}
})
// store/modules/module-a/index.js
// 子模块demo
export default {
namespaced: true, // 使用命名空间
state,
// ...
}
- 按功能划分组件:当逻辑越来越多,数据越来越多,按vuex模块来封装组件,更高度的模块化,更容易找到对应的数据
views # 页面,用于路由显示,基础容器
store # 现在store仅仅用于新建一个空的store
modules # 获取数据,传递数据的容器组件
├── atricle # 文章容器
| ├── index.vue # 导出组件,注册自身的store
| ├── api # 关于文章的api文件夹,放一个index.js
| ├── components # 展示组件,存放.vue文件,展示容器传下来的
| └── store # 现在每个组件自己拥有store文件夹
| └── ... # 与上面的结构相同,也是存放index.js, actions.js ...
└── comment # 另一个容器,结构同上
└── ...
// modules/article/index.vue
import store from './store'
export default {
// ...
created() {
// 动态注册store
this.$store.registerModule('$_article', store)
},
computed: {
...mapGetters({ articles: '$_article/articles' })
},
// ...
}
4. 细节
- 开发环境使用严格模式,生产环境关闭
const store = new Vuex.Store({
// ...
strict: process.env.NODE_ENV !== 'production'
})
- 使用常量来定义
mutation
,action
的名字, IDE可以检查到有没有写错,也可以自动补全;
// type.js
export const INCREMENT = 'increment'
export const INCREMENT_ASYNC = 'incrementAsync'
// store.js
import * as type from './type'
{
mutations: {
[type.INCREMENT](state, payload) {
state.count += payload.num
}
},
actions: {
[type.INCREMENT_ASYNC]({commit}, payload) {
commit(type.INCREMENT, payload)
}
}
}
- 区分容器组件与展示组件:
redux引入的概念,适用于flux架构
React 之容器组件和展示组件相分离解密
译文《容器组件和展示组件》
简单来说,
容器组件,负责数据的传递与获取,通常只含有一个元素来包裹展示组件
展示组件,展示数据,数据从 props 中取得
5. 一些例子
-
简单的计数器,例子
-
loading
队列来控制loading;每次loading就入队,清空队列才关闭;
仓库 -
vuex的插件,5个Vuex插件,给你的下个VueJS项目
参考文献:
Vuex官方文档
Vuex框架原理与源码分析 - 美团
Vuex2.0源码分析 - 滴滴
Vuex — The core of Vue application
网友评论