標簽: vue
[toc]
大綱: 什么時候用到vuex聘萨,簡介vuex课竣,舉例子說明vuex的最佳實踐;
細節(jié): action與mutation的區(qū)別叉信、場景亩冬;
例子: 常用的寫法、巧妙的方案硼身;
1. 用處
管理大型應用的公共數(shù)據(jù)硅急,方便數(shù)據(jù)的獲取與設置枢冤;方便組件之間的通訊;
Flux 架構(gòu)就像眼鏡:您自會知道什么時候需要它铜秆。
用了vuex 就不要再用其他方式來傳數(shù)據(jù)淹真,不要用params,不要用事件连茧,只用vuex核蘸!
1.2 場景
- 一處修改多處同步的數(shù)據(jù)
- 跨頁共享的數(shù)據(jù)
- 跨組件共享的數(shù)據(jù)
2. 概念
state
單一狀態(tài)樹,管理應用層面需要共享的全部數(shù)據(jù)啸驯;
// 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
},
})
有個輔助函數(shù)mapState
,可以方便取多個值罚斗;
// app.vue
{
computed: {
...mapState(['data1', 'data2'])
}
}
mutations
唯一允許更改狀態(tài)樹的方法徙鱼,通過mutations實現(xiàn)了數(shù)據(jù)的單向流動;只能是同步執(zhí)行针姿;
// store.js
new Vuex.Store({
mutations: {
increment (state, payload) {
state.count += payload.num
}
}
})
// app.vue
{
methods: {
increment() {
this.$store.commit('increment', {num: 1})
}
}
}
有輔助函數(shù)mapMutations
,方便注入多個方法袱吆;
//app.vue
{
methods: {
...mapMutations({
add: 'increment'
})
}
}
actions
一個衍生的概念,初衷是為了方便使用vue-devtool距淫;尤雨溪的回答
一般用于發(fā)送請求等異步操作绞绒,也可以組合多個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})
}
}
}
同樣也有輔助函數(shù)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. 實踐
目錄結(jié)構(gòu)
從簡單到模塊化的三種項目結(jié)構(gòu)
參考文章:大型項目的vuex結(jié)構(gòu) (文章末尾有g(shù)ithub地址)
- 簡單的vuex結(jié)構(gòu):僅僅是針對vuex的文件目錄結(jié)構(gòu)蓬衡,適用于小型,數(shù)據(jù)簡單的項目
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,
// ...
}
- 按功能劃分組件:當邏輯越來越多,數(shù)據(jù)越來越多盟猖,按vuex模塊來封裝組件讨衣,更高度的模塊化,更容易找到對應的數(shù)據(jù)
views # 頁面式镐,用于路由顯示反镇,基礎(chǔ)容器
store # 現(xiàn)在store僅僅用于新建一個空的store
modules # 獲取數(shù)據(jù),傳遞數(shù)據(jù)的容器組件
├── atricle # 文章容器
| ├── index.vue # 導出組件娘汞,注冊自身的store
| ├── api # 關(guān)于文章的api文件夾歹茶,放一個index.js
| ├── components # 展示組件,存放.vue文件,展示容器傳下來的
| └── store # 現(xiàn)在每個組件自己擁有store文件夾
| └── ... # 與上面的結(jié)構(gòu)相同惊豺,也是存放index.js, actions.js ...
└── comment # 另一個容器燎孟,結(jié)構(gòu)同上
└── ...
// modules/article/index.vue
import store from './store'
export default {
// ...
created() {
// 動態(tài)注冊store
this.$store.registerModule('$_article', store)
},
computed: {
...mapGetters({ articles: '$_article/articles' })
},
// ...
}
4. 細節(jié)
- 開發(fā)環(huán)境使用嚴格模式,生產(chǎn)環(huán)境關(guān)閉
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)
}
}
}
- 區(qū)分容器組件與展示組件:
redux引入的概念,適用于flux架構(gòu)
React 之容器組件和展示組件相分離解密
譯文《容器組件和展示組件》
簡單來說烹俗,
容器組件爆侣,負責數(shù)據(jù)的傳遞與獲取,通常只含有一個元素來包裹展示組件
展示組件幢妄,展示數(shù)據(jù)兔仰,數(shù)據(jù)從 props 中取得
5. 一些例子
簡單的計數(shù)器,例子
loading
隊列來控制loading蕉鸳;每次loading就入隊乎赴,清空隊列才關(guān)閉;
倉庫vuex的插件,5個Vuex插件潮尝,給你的下個VueJS項目
參考文獻:
Vuex官方文檔
Vuex框架原理與源碼分析 - 美團
Vuex2.0源碼分析 - 滴滴
Vuex — The core of Vue application