[TOC]
## 1. 開始
vuex作為一個vue狀態(數據)管理的管理模式。尤其在中大型前端應用中,多組件共享狀態處理時應用效果最佳。
1. 組件發起事件
2. Actions接收到狀態變化事件,commit一個Mutation(變化)
3. 導致state中數據變化,然后渲染都組件

* [ ] state:數據存儲
* [ ] action:事件處理,可以寫處理邏輯(可以異步),然后調用mutation
* [ ] mutation:修改state中數據
### 1.1 安裝
**1.安裝webpack**
~~~
npm i webpack -g
~~~
**2. 新建一個test工程**
再創建項目時,選擇了store,router,所以有以下文件默認生成
```
vue create test
```

### **1.2 state數據獲取**
* [ ] 組件獲取state中數據
1)this.$store.state.count
2)或者mapState輔助函數
```
//?computed:?mapState({?//寫法一
//???count:?'count',})
computed: {
...mapState([
'count', //多個用逗號分開,寫法二
]),
},
```
**1. 構建一個store對象,并導出**
在vue cli創建的項目中,默認加載store目錄下的index.js文件
**定義state對象,內涵數據count**
```
importVuefrom"vue";
importVuexfrom"vuex";
Vue.use(Vuex);
exportdefaultnewVuex.Store({
state:?{
count:0,
??},
mutations:?{},
actions:?{},
modules:?{}
});
```
**2. main.js文件導入store**
```
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store"; // 1.引入store
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App) //將組件渲染到index.html
}).$mount("#app");
```
**3. app.vue中使用state中數據**
中通過計算屬性關聯state,在組件中通過
方法一、通過`this.$store.state.count` 使用數據
```
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<!-- 1.使用計算屬性 -->
<h1>count:{{count}}</h1>
</div>
<router-view />
</div>
</template>
<script>
export default {
computed: {
count() {
//2.獲取state數據
return this.$store.state.count
}
}
}
</script>
```
**方法二,使用mapstate輔助函數獲取state數據**
app.vue組件中
```
import?{?mapState?}?from'vuex'
exportdefault?{
//?computed:?
//???//獲取state數據方式一
//???count()?{
//?????return?this.$store.state.count
//???}
//?}
computed:mapState({?//?獲取state數據方式二
count:'count',})
}
```
mapState還可以這么寫
```
computed: {
...mapState([
'count', //多個用逗號分開
]),
},
```
### **1.3 mutation中方法觸發**
**mutation中的方法用于修改state中的值**
方式一、`$store.commit('mutationName')` 觸發狀態值的改變
方式二、mapMutations輔助函數進行映射,就是將vuex中的函數,映射到當前組件當中,通過this來調用
```
methods: {
// add() {
// this.$store.commit('addcount',2) //觸發mutation中方法,方式一
// }
...mapMutations({
'add': 'addcount' // 將 `this.add()` 映射為 `this.$store.commit('addcount', n)`
})
},
```
映射方法名相同可以簡寫
```
...mapMutations(['addcount'])
```

**1. 在store的mutation中定義方法**
```
mutations: {
addcount(state) {
state.count ++
}
},
```
2. vue.app中
```
<!-- 1.使用計算屬性 -->
<h1>count:{{count}}</h1>
<button @click="add"> 加一</button>
methods: {
add() {
this.$store.commit('addcount') //觸發mutation中方法
}
},
```
也可以提交載荷,即為傳參
```
this.$store.commit('addcount',2)?//app.vue 觸發mutation中方法,每次加2
mutations:?{
addcount(state,n)?{
state.count?+=n
????}
?},
```

寫法二
```
methods: {
// add() {
// this.$store.commit('addcount',2) //觸發mutation中方法,方式一
// }
// ...mapMutations({
// 'add': 'addcount' // 將 `this.add()` 映射為 `this.$store.commit('addcount', n)`
// })
...mapMutations(['addcount']) // //觸發mutation中方法,方式二,如果映射的方法名相同,則可以如此簡寫
},
```
### 1.4 action
**Action 類似于 mutation,不同在于:**
* Action 提交的是 mutation,而不是在組件中直接變更狀態, 通過它間接更新 state(action可以寫一下邏輯業務處理)。
* 在組件中通過 this.$store.dispatch('actionName') 觸發狀態值間接改變
* Action 也支持載荷
* Action 可以包含任意異步操
1. 在store中index.js添加函數
```
actions:?{
add(context,?n)?{
context.commit('addcount',n)
????},
decrement({commit,state},n){ //es6寫法,直接從context中獲取commit
commit('decrementcount',n)
????}
??},
```
2. app.vue 中
方法中映射action函數
```
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<!-- 1.使用計算屬性 -->
<h1>count:{{count}}</h1>
<!-- <button @click="addcount(2)"> 加一</button> -->
<button @click="add(2)">加二</button>
<button @click="decrement(3)">減三</button>
</div>
<router-view />
</div>
</template>
import?{?mapState,mapMutations,?mapActions?}?from'vuex'
exportdefault?{
methods:?{
//?add()?{
//???this.$store.commit('addcount',2)?//觸發mutation中方法,方式一
//?}
//?...mapMutations({
//?????'add':?'addcount'??//?將?`this.add()`?映射為?`this.$store.commit('addcount',?n)`
//???})
???...mapMutations(['addcount']),?//?//觸發mutation中方法,方式二,如果映射的方法名相同,則可以如此簡寫
???...mapActions(['add','decrement'])
??},
}
```
### 1.3 派生屬性getter
1. 當需要從 store 中的 state 中派生出一些狀態。 例如:count值大于10,顯示加多了,大于12顯示加太多了。這時我們就需要用到 getter 為我們解決。
2. getter 其實就類似于計算屬性(get)的對象.
3. 組件中讀取 $store.getters.xxx
store/index.js
```
getters: {
desc(state) {
if (state.count > 10 && state.count <= 12) {
return '加多了'
} else if (state.count > 12) {
return '加太多了'
} else {
return '加的一般'
}
}
}
```
app.vue
寫法一,`$store.getters.xxx`獲取值
```
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<!-- 1.使用計算屬性 -->
<h1>count:{{count}}</h1>
<!-- <button @click="addcount(2)"> 加一</button> -->
<button @click="add(2)">加二</button>
<button @click="decrement(3)">減三</button>
getter-desc:{{$store.getters.desc}}
</div>
<router-view />
</div>
</template>
```

**寫法二,通過mapGetters輔助函數獲取值**
```
getter-desc:{{desc}}
// app.vue首先引入mapGetters
import { mapState,mapMutations, mapActions,mapGetters } from 'vuex'
computed: {
...mapState([
'count', //多個用逗號分開
]),
...mapGetters(['desc'])
},
```
### 1.4 子組件中如何獲取值

如上圖,home和about如何獲取state中的值呢?
一、組件傳值肯定是可以的
子組件props聲明一下
```
props:['count']
```
父組件
```
<子組件 :count="count"></子組件>
computed: {
...mapState([
'count', //多個用逗號分開
]),
...mapGetters(['desc'])
},
```
2. 通app.vue組件一樣,獲得vuex狀態
```
<template>
<div class="about">
<h1>This is an about page</h1>
<h1>about組件:{{count}}</h1>
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
export default {
computed: {
...mapState([
"count" //多個用逗號分開
]),
...mapGetters(["desc"])
}
};
</script>
```

## **2. state的值與input和select表單綁定**
**vuex 強調的是Unidirection Dataflow,state中數據應該只能被讀取,寫入應該由mutation完成。**
因此通過v-model綁定state值,并通過表單元素進行修改,是不建議的,當然可以成功改變,但是控制臺會輸出錯誤信息,如下:
~~~
<el-form-item prop="rdn">
<el-col :span="12">
<el-input v-model="currentOrg.rdn" placeholder="" size="small"
@input="cnKeyUp"></el-input>
</el-col>
</el-form-item>
computed: {
...orgState({currentOrg: 'current', orgItems: 'items', tmpData: 'tmpData'}),
isNew() {
return Boolean(this.currentOrg && this.currentOrg.id);
},
~~~
```
vue.js:1897 Error: [vuex] do not mutate vuex store state outside mutation handlers.
at assert (vuex.esm.js?be43:90)
at Vue.store._vm.$watch.deep (vuex.esm.js?be43:793)
at Watcher.run (vue.js:4561)
at Watcher.update (vue.js:4535)
at Dep.notify (vue.js:745)
at Object.reactiveSetter [as rdn] (vue.js:1070)
at Proxy.set (vue.js:1091)
at callback (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d1305b30-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/parts/system/org/org-tabs.vue?vue&type=template&id=12c5e28c&scoped=true& (xms_user.js:4808), <anonymous>:62:45)
at invokeWithErrorHandling (vue.js:1863)
at VueComponent.invoker (vue.js:2184)
```
### **2.1 通過computed的set和get解決**
computed中計算數據
1. get從vuex中獲取數據
2. set調用mutation修改數據
~~~
<template>
<div>
<div>
<input type="text" v-model="name">
</div>
<div>
{{ name }}
</div>
</div>
</template>
<script>
/** computed */
const name = {
get() {
return this.$store.state.name;
},
set(value) {
this.$store.commit('setName', value);
},
};
const computed = {
name,
};
export default {
name: 'HelloWorld',
computed,
};
</script>
~~~
### 2.2 組件mounted時,深拷貝
~~~
<el-form :model="currentOrgIns" ref="orgForm" :inline="true" label-position="left" class="demo-form-inline"
label-width="90px" :rules="rules">
<el-form-item label="機構類別" prop="type">
<el-select v-model="currentOrgIns.type" placeholder="請選擇加密設備" size="small" @change="typeChange">
<el-option label="CA" value="ca"></el-option>
<el-option label="RA" value="ra"></el-option>
</el-select>
</el-form-item>
<el-form-item label="額外屬性" prop="dnAttrCo">
<el-input v-model="currentOrgIns.dnAttrCo" placeholder="" size="small"></el-input>
</el-form-item>
<el-form-item label="備注" prop="roMemo">
<el-input v-model="currentOrgIns.roMemo" placeholder="" size="small"></el-input>
</el-form-item>
</el-form>
import _ from "lodash";
data() {
return {
currentOrgIns: {},
},
// 組件mounted后,將state中的數據currentOrg深拷貝給data中數據
async mounted() {
this.currentOrgIns = _.cloneDeep(this.currentOrg);
},
computed: {
...orgState({currentOrg: 'current', orgItems: 'items', tmpData: 'tmpData'}),
},
~~~
# vuex中action和mutations (this.$store.dispatch和this.$store.commit)的區別
dispatch:含有異步操作,例如向后臺提交數據,寫法: this.$store.dispatch('action方法名',值)
commit:同步操作,寫法:this.$store.commit('mutations方法名',值)
action:
1、用于通過提交mutation改變數據
2、會默認將自身封裝為一個Promise
3、可以包含任意的異步操作
mutations:
1、通過提交commit改變數據
2、只是一個單純的函數
3、不要使用異步操作,異步操作會導致變量不能追蹤。也就是說,用action中的函數調用mutations中的函數,進行異步操作state中的數據
- vue
- 為什么要學vue
- 數據雙向綁定
- vue指令
- v-bind創建HTML節點屬性
- v-on綁定事件
- v-cloak
- v-text
- v-for和key屬性
- v-if和v-show
- 案例1
- 自定義指令
- vue樣式
- vue生命周期
- vue過濾器
- 自定義鍵盤修飾符
- 跨域請求
- vue組件
- 組件基礎
- 引入vue文件組件
- 引入render函數作為組件
- 兄弟間組件通信
- 組件函數數據傳遞練習
- 路由
- 數據監聽
- webpack
- vue校驗
- vue筆記
- form表單中input前部分默認輸入,切不可修改
- mixins
- 部署到nginx
- scope
- render
- 下載文件
- vue動態組件
- axios
- Promise
- vue進階
- node-vue-webpack搭建
- vue事件
- 插槽
- vuex
- vuex基礎
- vuex命名空間
- HTML遞歸?
- this.$nextTick異步更新dom
- elementui
- table
- 修改element ui樣式
- form
- 優質博客
- vuex state數據與form元素綁定
- es6
- Promise