1. vuex概述
2. 構建 vuex [多組件數據共享] 環境
<template><div id="app"><h1>根組件- {{ title }}- {{ count }}</h1><input :value="count" @input="handleInput" type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { mapState } from 'vuex'
// console.log(mapState(['count', 'title']))export default {name: 'app',created () {// console.log(this.$router) // 沒配console.log(this.$store.state.count)},computed: {...mapState(['count', 'title'])},data: function () {return {}},methods: {handleInput (e) {// 1. 實時獲取輸入框的值const num = +e.target.value// 2. 提交mutation,調用mutation函數this.$store.commit('changeCount', num)}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>
<template><div class="box"><h2>Son1 子組件</h2>從vuex中獲取的值: <label>{{ $store.state.count }}</label><br><button @click="handleAdd(1)">值 + 1</button><button @click="handleAdd(5)">值 + 5</button><button @click="handleAdd(10)">值 + 10</button><button @click="handleChange">一秒后修改成666</button><button @click="changeFn">改標題</button><hr><!-- 計算屬性getters --><div>{{ $store.state.list }}</div><div>{{ $store.getters.filterList }}</div><hr><!-- 測試訪問模塊中的state - 原生 --><div>{{ $store.state.user.userInfo.name }}</div><button @click="updateUser">更新個人信息</button><button @click="updateUser2">一秒后更新信息</button><div>{{ $store.state.setting.theme }}</div><button @click="updateTheme">更新主題色</button><hr><!-- 測試訪問模塊中的getters - 原生 --><div>{{ $store.getters['user/UpperCaseName'] }}</div></div>
</template><script>
export default {name: 'Son1Com',created () {console.log(this.$store.getters)},methods: {updateUser () {// $store.commit('模塊名/mutation名', 額外傳參)this.$store.commit('user/setUser', {name: 'xiaowang',age: 25})},updateUser2 () {// 調用action dispatchthis.$store.dispatch('user/setUserSecond', {name: 'xiaohong',age: 28})},updateTheme () {this.$store.commit('setting/setTheme', 'pink')},handleAdd (n) {// 錯誤代碼(vue默認不會監測,監測需要成本)// this.$store.state.count++// console.log(this.$store.state.count)// 應該通過 mutation 核心概念,進行修改數據// 需要提交調用mutation// this.$store.commit('addCount')// console.log(n)// 調用帶參數的mutation函數this.$store.commit('addCount', {count: n,msg: '哈哈'})},changeFn () {this.$store.commit('changeTitle', '傳智教育')},handleChange () {// 調用action// this.$store.dispatch('action名字', 額外參數)this.$store.dispatch('changeCountAction', 666)}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
<template><div class="box"><h2>Son2 子組件</h2>從vuex中獲取的值:<label>{{ count }}</label><br /><button @click="subCount(1)">值 - 1</button><button @click="subCount(5)">值 - 5</button><button @click="subCount(10)">值 - 10</button><button @click="changeCountAction(888)">1秒后改成888</button><button @click="changeTitle('前端程序員')">改標題</button><hr><div>{{ filterList }}</div><hr><!-- 訪問模塊中的state --><div>{{ user.userInfo.name }}</div><div>{{ setting.theme }}</div><hr><!-- 訪問模塊中的state --><div>user模塊的數據:{{ userInfo }}</div><button @click="setUser({ name: 'xiaoli', age: 80 })">更新個人信息</button><button @click="setUserSecond({ name: 'xiaoli', age: 80 })">一秒后更新信息</button><div>setting模塊的數據:{{ theme }} - {{ desc }}</div><button @click="setTheme('skyblue')">更新主題</button><hr><!-- 訪問模塊中的getters --><div>{{ UpperCaseName }}</div></div>
</template><script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {name: 'Son2Com',computed: {// mapState 和 mapGetters 都是映射屬性...mapState(['count', 'user', 'setting']),...mapState('user', ['userInfo']),...mapState('setting', ['theme', 'desc']),...mapGetters(['filterList']),...mapGetters('user', ['UpperCaseName'])},methods: {// mapMutations 和 mapActions 都是映射方法// 全局級別的映射...mapMutations(['subCount', 'changeTitle']),...mapActions(['changeCountAction']),// 分模塊的映射...mapMutations('setting', ['setTheme']),...mapMutations('user', ['setUser']),...mapActions('user', ['setUserSecond'])// handleSub (n) {// this.subCount(n)// }}
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
3. 創建一個空倉庫
//這里面存放的就是vuex相關的核心代碼import Vue from 'vue'import Vuex from 'vuex//插件安裝Vue.use(Vuex)//創建倉庫(空倉庫)const store = new Vuex.Store()//到處給main.js使用export dafault store
導入掛載:
import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'
console.log(store.state.count)Vue.config.productionTip = falsenew Vue({render: h => h(App),store
}).$mount('#app')
驗證是否導入成功
<template><div id="app"><h1>根組件- {{ title }}- {{ count }}</h1><input :value="count" @input="handleInput" type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { mapState } from 'vuex'
// console.log(mapState(['count', 'title']))export default {name: 'app',created () {// console.log(this.$router) // 沒配console.log(this.$store.state.count)},computed: {...mapState(['count', 'title'])},data: function () {return {}},methods: {handleInput (e) {// 1. 實時獲取輸入框的值const num = +e.target.value// 2. 提交mutation,調用mutation函數this.$store.commit('changeCount', num)}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>
4. 核心概念 - state 狀態
// 這里面存放的就是 vuex 相關的核心代碼
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import setting from './modules/setting'// 插件安裝
Vue.use(Vuex)// 創建倉庫
const store = new Vuex.Store({// 嚴格模式 (有利于初學者,檢測不規范的代碼 => 上線時需要關閉)strict: true,// 1. 通過 state 可以提供數據 (所有組件共享的數據)state: {title: '倉庫大標題',count: 100,list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]},// 2. 通過 mutations 可以提供修改數據的方法mutations: {// 所有mutation函數,第一個參數,都是 state// 注意點:mutation參數有且只能有一個,如果需要多個參數,包裝成一個對象addCount (state, obj) {console.log(obj)// 修改數據state.count += obj.count},subCount (state, n) {state.count -= n},changeCount (state, newCount) {state.count = newCount},changeTitle (state, newTitle) {state.title = newTitle}},// 3. actions 處理異步// 注意:不能直接操作 state,操作 state,還是需要 commit mutationactions: {// context 上下文 (此處未分模塊,可以當成store倉庫)// context.commit('mutation名字', 額外參數)changeCountAction (context, num) {// 這里是setTimeout模擬異步,以后大部分場景是發請求setTimeout(() => {context.commit('changeCount', num)}, 1000)}},// 4. getters 類似于計算屬性getters: {// 注意點:// 1. 形參第一個參數,就是state// 2. 必須有返回值,返回值就是getters的值filterList (state) {return state.list.filter(item => item > 5)}},// 5. modules 模塊modules: {user,setting}
})// 導出給main.js使用
export default store
<template><div id="app"><h1>根組件- {{ $store.state.title }}- {{ $store.state.count }}</h1><input :value="count" @input="handleInput" type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { mapState } from 'vuex'
// console.log(mapState(['count', 'title']))export default {name: 'app',created () {// console.log(this.$router) // 沒配console.log(this.$store.state.count)},computed: {...mapState(['count', 'title'])},data: function () {return {}},methods: {handleInput (e) {// 1. 實時獲取輸入框的值const num = +e.target.value// 2. 提交mutation,調用mutation函數this.$store.commit('changeCount', num)}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>
5. 核心概念 - mutations
第一:
第二:·
第三
如果說要同時傳遞好幾個參數,可以包裝成一個對象
6. 核心概念 - mutations - 練習
練習1:
練習2:
<template><div id="app"><h1>根組件- {{ title }}- {{ count }}</h1>//1<input :value="count" @input="handleInput" type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { mapState } from 'vuex'
// console.log(mapState(['count', 'title']))export default {name: 'app',created () {// console.log(this.$router) // 沒配console.log(this.$store.state.count)},computed: {...mapState(['count', 'title'])},data: function () {return {}},//2methods: {handleInput (e) {// 1. 實時獲取輸入框的值const num = +e.target.value//4// 2. 提交mutation,調用mutation函數this.$store.commit('changeCount', num)}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>
7. 輔助函數 - mapMutations
8. 核心概念 - actions
9. 輔助函數 - mapActions
調用這個
10. 核心概念 - getters
<template><div class="box"><h2>Son2 子組件</h2>從vuex中獲取的值:<label>{{ count }}</label><br /><button @click="subCount(1)">值 - 1</button><button @click="subCount(5)">值 - 5</button><button @click="subCount(10)">值 - 10</button><button @click="changeCountAction(888)">1秒后改成888</button><button @click="changeTitle('前端程序員')">改標題</button><hr><div>{{ filterList }}</div><hr><!-- 訪問模塊中的state --><div>{{ user.userInfo.name }}</div><div>{{ setting.theme }}</div><hr><!-- 訪問模塊中的state --><div>user模塊的數據:{{ userInfo }}</div><button @click="setUser({ name: 'xiaoli', age: 80 })">更新個人信息</button><button @click="setUserSecond({ name: 'xiaoli', age: 80 })">一秒后更新信息</button><div>setting模塊的數據:{{ theme }} - {{ desc }}</div><button @click="setTheme('skyblue')">更新主題</button><hr><!-- 訪問模塊中的getters --><div>{{ UpperCaseName }}</div></div>
</template><script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {name: 'Son2Com',computed: {// mapState 和 mapGetters 都是映射屬性...mapState(['count', 'user', 'setting']),...mapState('user', ['userInfo']),...mapState('setting', ['theme', 'desc']),...mapGetters(['filterList']),...mapGetters('user', ['UpperCaseName'])},methods: {// mapMutations 和 mapActions 都是映射方法// 全局級別的映射...mapMutations(['subCount', 'changeTitle']),...mapActions(['changeCountAction']),// 分模塊的映射...mapMutations('setting', ['setTheme']),...mapMutations('user', ['setUser']),...mapActions('user', ['setUserSecond'])// handleSub (n) {// this.subCount(n)// }}
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
11. 核心概念 - 模塊 module (進階語法)
第一:
// user模塊
const state = {userInfo: {name: 'zs',age: 18},score: 80
}
const mutations = {setUser (state, newUserInfo) {state.userInfo = newUserInfo}
}
const actions = {setUserSecond (context, newUserInfo) {// 將異步在action中進行封裝setTimeout(() => {// 調用mutation context上下文,默認提交的就是自己模塊的action和mutationcontext.commit('setUser', newUserInfo)}, 1000)}
}
const getters = {// 分模塊后,state指代子模塊的stateUpperCaseName (state) {return state.userInfo.name.toUpperCase()}
}export default {namespaced: true,state,mutations,actions,getters
}
// setting模塊
const state = {theme: 'light', // 主題色desc: '測試demo'
}
const mutations = {setTheme (state, newTheme) {state.theme = newTheme}
}
const actions = {}
const getters = {}export default {namespaced: true,state,mutations,actions,getters
}
第二:
法一:
法二:
第三
法一:
法2:
第四
法一:
法2:
第五
法一:
法二
12. 綜合案例 - 購物車
1.
2
3
4
存儲數據
渲染
<template><div class="app-container"><!-- Header 區域 --><cart-header></cart-header><!-- 商品 Item 項組件 --><cart-item v-for="item in list" :key="item.id" :item="item"></cart-item><!-- Foote 區域 --><cart-footer></cart-footer></div>
</template><script>
import CartHeader from '@/components/cart-header.vue'
import CartFooter from '@/components/cart-footer.vue'
import CartItem from '@/components/cart-item.vue'import { mapState } from 'vuex'export default {name: 'App',created () {this.$store.dispatch('cart/getList')},computed: {...mapState('cart', ['list'])},components: {CartHeader,CartFooter,CartItem}
}
</script><style lang="less" scoped>
.app-container {padding: 50px 0;font-size: 14px;
}
</style>
<template><div class="goods-container"><!-- 左側圖片區域 --><div class="left"><img :src="item.thumb" class="avatar" alt=""></div><!-- 右側商品區域 --><div class="right"><!-- 標題 --><div class="title">{{ item.name }}</div><div class="info"><!-- 單價 --><span class="price">¥{{ item.price }}</span><div class="btns"><!-- 按鈕區域 --><button class="btn btn-light" @click="btnClick(-1)">-</button><span class="count">{{ item.count }}</span><button class="btn btn-light" @click="btnClick(1)">+</button></div></div></div></div>
</template><script>
export default {name: 'CartItem',methods: {btnClick (step) {const newCount = this.item.count + stepconst id = this.item.idif (newCount < 1) returnthis.$store.dispatch('cart/updateCountAsync', {id,newCount})}},//直接接收props: {item: {type: Object,required: true}}
}
</script><style lang="less" scoped>
.goods-container {display: flex;padding: 10px;+ .goods-container {border-top: 1px solid #f8f8f8;}.left {.avatar {width: 100px;height: 100px;}margin-right: 10px;}.right {display: flex;flex-direction: column;justify-content: space-between;flex: 1;.title {font-weight: bold;}.info {display: flex;justify-content: space-between;align-items: center;.price {color: red;font-weight: bold;}.btns {.count {display: inline-block;width: 30px;text-align: center;}}}}
}.custom-control-label::before,
.custom-control-label::after {top: 3.6rem;
}
</style>
5
<template><div class="goods-container"><!-- 左側圖片區域 --><div class="left"><img :src="item.thumb" class="avatar" alt=""></div><!-- 右側商品區域 --><div class="right"><!-- 標題 --><div class="title">{{ item.name }}</div><div class="info"><!-- 單價 --><span class="price">¥{{ item.price }}</span><div class="btns"><!-- 按鈕區域 --><button class="btn btn-light" @click="btnClick(-1)">-</button><span class="count">{{ item.count }}</span><button class="btn btn-light" @click="btnClick(1)">+</button></div></div></div></div>
</template><script>
export default {name: 'CartItem',methods: {btnClick (step) {const newCount = this.item.count + stepconst id = this.item.idif (newCount < 1) return
//調用this.$store.dispatch('cart/updateCountAsync', {id,newCount})}},props: {item: {type: Object,required: true}}
}
</script><style lang="less" scoped>
.goods-container {display: flex;padding: 10px;+ .goods-container {border-top: 1px solid #f8f8f8;}.left {.avatar {width: 100px;height: 100px;}margin-right: 10px;}.right {display: flex;flex-direction: column;justify-content: space-between;flex: 1;.title {font-weight: bold;}.info {display: flex;justify-content: space-between;align-items: center;.price {color: red;font-weight: bold;}.btns {.count {display: inline-block;width: 30px;text-align: center;}}}}
}.custom-control-label::before,
.custom-control-label::after {top: 3.6rem;
}
</style>
import axios from 'axios'
export default {namespaced: true,state () {return {// 購物車數據 [{}, {}]list: []}},mutations: {updateList (state, newList) {state.list = newList},// obj: { id: xxx, newCount: xxx }updateCount (state, obj) {// 根據 id 找到對應的對象,更新count屬性即可const goods = state.list.find(item => item.id === obj.id)goods.count = obj.newCount}},actions: {// 請求方式:get// 請求地址:http://localhost:3000/cartasync getList (context) {const res = await axios.get('http://localhost:3000/cart')context.commit('updateList', res.data)},// 請求方式:patch// 請求地址:http://localhost:3000/cart/:id值 表示修改的是哪個對象// 請求參數:// {// name: '新值', 【可選】// price: '新值', 【可選】// count: '新值', 【可選】// thumb: '新值' 【可選】// }async updateCountAsync (context, obj) {// 將修改更新同步到后臺服務器await axios.patch(`http://localhost:3000/cart/${obj.id}`, {//count不能改count: obj.newCount})// 將修改更新同步到 vuexcontext.commit('updateCount', {id: obj.id,newCount: obj.newCount})}},getters: {// 商品總數量 累加counttotal (state) {return state.list.reduce((sum, item) => sum + item.count, 0)},// 商品總價格 累加count * pricetotalPrice (state) {return state.list.reduce((sum, item) => sum + item.count * item.price, 0)}}
}