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')