1.vuex的基本認知
2.構建多組件共享的數據環境
步驟:
1.在自己創建的文件夾下創建腳手架
2.創建三個組件
### 源代碼如下`App.vue`在入口組件中引入 Son1 和 Son2 這兩個子組件```html
<template><div id="app"><h1>根組件</h1><input type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'export default {name: 'app',data: function () {return {}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>````main.js````js
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App)
}).$mount('#app')
````Son1.vue````html
<template><div class="box"><h2>Son1 子組件</h2>從vuex中獲取的值: <label></label><br><button>值 + 1</button></div>
</template><script>
export default {name: 'Son1Com'
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>````Son2.vue````html
<template><div class="box"><h2>Son2 子組件</h2>從vuex中獲取的值:<label></label><br /><button>值 - 1</button></div>
</template><script>
export default {name: 'Son2Com'
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
```
整理好后目錄如下:
運行結果
3.創建一個空倉庫
1.安裝veux
這里注意一下如果創建腳手架時有選擇vuex選項就可以不要這一步了,這里是為了學習才沒選,所以要安裝
2.新建vuex模塊文件
記得新建后要導出,然后在main.js中還要引用和掛載
使用的話就調用this.$store,像下圖如果里面有的話就能輸出來倉庫里面的值。
4.如何提供&訪問vuex的數據
eg:
mapState輔助函數
同理其他組件也可以這樣訪問倉庫的值
修改vuex倉庫的值
首先按下面這樣寫是不規范的,其次是要開啟嚴格模式進行檢查,才能檢查出不規范的代碼并且報錯。
嚴格模式開啟如下:
下面是修改步驟:
1.
2.
結果:
接下來就是把每個方法進行封裝,按鈕要干嘛就傳什么參數
如果要多個參數傳參,那么就要封裝成obj對象
下面練習一下son2的減法操作:
5.輔助函數-mapMutations
6.核心概念-actions和getters
結果:
輔助函數方法:
7.vuex-分模塊_模塊創建
8.vuex-分模塊_訪問模塊中的state&mutations等
原生訪問方法:
基于模塊的寫法
為什么要像下面一樣書寫,因為該結構不一樣
結構如下,所以就要像上面那樣寫
輔助函數寫法
先介紹原生的:
然后寫對應兩種方法:
輔助函數
原生介紹:
先添加一個按鈕
然后寫點擊事件的方法:
輔助函數:
9.購物車案例-功能分析-創建項目-構建基本結構
創建成功后將資料里面的src文件夾替換到創建完的文件夾里
10.購物車案例-構建購物車模塊
步驟:
安裝全局工具 json-server (全局工具僅需要安裝一次)
yarn global add json-server 或 npm i json-server -g
代碼根目錄新建一個 db 目錄
將資料 index.json 移入 db 目錄
進入 db 目錄,執行命令,啟動后端接口服務 (使用--watch 參數 可以實時監聽 json 文件的修改)
json-server --watch index.json
要訪問的話直接輸入網址即可
然后在注意一下接口啟動后是不能關的,一但關了就不能啟動接口了。
11.購物車案例-請求獲取數據存入vuex,映射渲染
1.安裝 axios
yarn add axios
2.準備actions 和 mutations
import axios from 'axios'
?
export default {namespaced: true,state () {return {list: []}},mutations: {updateList (state, payload) {state.list = payload}},actions: {async getList (ctx) {const res = await axios.get('http://localhost:3000/cart')ctx.commit('updateList', res.data)}}
}
3.App.vue
頁面中調用 action, 獲取數據
import { mapState } from 'vuex'
?
export default {name: 'App',components: {CartHeader,CartFooter,CartItem},created () {this.$store.dispatch('cart/getList')},computed: {...mapState('cart', ['list'])}
}
4.動態渲染
<!-- 商品 Item 項組件 -->
<cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>
cart-item.vue
<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: {},props: {item: {type: Object,required: true}}
}</script>
12.購物車案例-修改數量和底部功能完成
注冊點擊事件
<!-- 按鈕區域 -->
<button class="btn btn-light" @click="onBtnClick(-1)">-</button>
<span class="count">{{item.count}}</span>
<button class="btn btn-light" @click="onBtnClick(1)">+</button>
2.頁面中dispatch action
onBtnClick (step) {const newCount = this.item.count + stepif (newCount < 1) return// 發送修改數量請求this.$store.dispatch('cart/updateCount', {id: this.item.id,count: newCount})
}
3.提供action函數
async updateCount (ctx, payload) {await axios.patch('http://localhost:3000/cart/' + payload.id, {count: payload.count})ctx.commit('updateCount', payload)
}
4.提供mutation處理函數
mutations: {...,updateCount (state, payload) {const goods = state.list.find((item) => item.id === payload.id)goods.count = payload.count}
},
提供getters
getters: {total(state) {return state.list.reduce((p, c) => p + c.count, 0);},totalPrice (state) {return state.list.reduce((p, c) => p + c.count * c.price, 0);},
},
2.動態渲染
<template><div class="footer-container"><!-- 中間的合計 --><div><span>共 {{total}} 件商品,合計:</span><span class="price">¥{{totalPrice}}</span></div><!-- 右側結算按鈕 --><button class="btn btn-success btn-settle">結算</button></div>
</template><script>
import { mapGetters } from 'vuex'
export default {name: 'CartFooter',computed: {...mapGetters('cart', ['total', 'totalPrice'])}
}
</script>
總的代碼:
cart-footer.vue
<template><div class="footer-container"><!-- 中間的合計 --><div><span>共 {{ total }} 件商品,合計:</span><span class="price">¥{{ totalPrice }}</span></div><!-- 右側結算按鈕 --><button class="btn btn-success btn-settle">結算</button></div>
</template><script>
import { mapGetters } from 'vuex'
export default {name: 'CartFooter',computed: {...mapGetters('cart', ['total', 'totalPrice'])}
}
</script><style lang="less" scoped>
.footer-container {background-color: white;height: 50px;border-top: 1px solid #f8f8f8;display: flex;justify-content: flex-end;align-items: center;padding: 0 10px;position: fixed;bottom: 0;left: 0;width: 100%;z-index: 999;
}.price {color: red;font-size: 13px;font-weight: bold;margin-right: 10px;
}.btn-settle {height: 30px;min-width: 80px;margin-right: 20px;border-radius: 20px;background: #42b983;border: none;color: white;
}
</style>
cart-header.vue
<template><div class="header-container">購物車案例</div>
</template><script>
export default {name: 'CartHeader'
}
</script><style lang="less" scoped>
.header-container {height: 50px;line-height: 50px;font-size: 16px;background-color: #42b983;text-align: center;color: white;position: fixed;top: 0;left: 0;width: 100%;z-index: 999;
}
</style>
cart-item.vue
<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>
cart.js
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: 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)}}
}
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'
Vue.use(Vuex)export default new Vuex.Store({modules: {cart}
})
App.vue
<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')},components: {CartHeader,CartFooter,CartItem},computed: {...mapState('cart', ['list'])}
}
</script><style lang="less" scoped>
.app-container {padding: 50px 0;font-size: 14px;
}
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({store,render: h => h(App)
}).$mount('#app')