Vue实现简单购物车
这里先分析一下vue项目的文件结构,组件、入口文件及配置可以添加的位置。其中:assets表示静态资源,如css,jscomponents是公共组件存放的位置App.vue是根组件main.js为入口文件组件化需要在项目src文件夹下components文件夹内创建Cart.vue文件作为购物车组件。所以在新建完成后需要在App.vue根组件内使用。组件按需引入及注册import Cart fro
这里先分析一下vue项目的文件结构,组件、入口文件及配置可以添加的位置。
其中:
assets表示静态资源,如css,js
components是公共组件存放的位置
App.vue是根组件
main.js为入口文件
组件化
需要在项目src文件夹下components文件夹内创建Cart.vue
文件作为购物车组件。
所以在新建完成后需要在App.vue根组件内使用。
组件按需引入及注册
import Cart from './components/Cart.vue'
export default{
name: 'App',
components: {
cart:Cart
}
}
// 组件使用
<cart></cart> <!-- 在App.vue组件内id为App的div内 -->
这个过程就是,导入Cart.vue组件,然后对组件进行注册(components内的Cart注册),然后在#app内使用。
Cart组件的功能
实现了数据在cart内的添加,删减,数据的统计等。
在添加之前先实现商品展示界面(只写逻辑,没有样式):
今天突然发现我不太会写一步一步添加的代码步骤,能理解整个过程,但是不知道要一步一步怎么断开。
只能先放上整个代码过程,先一步一步解释吧,可能会比较乱,但是我会先放上整个文件内的全部代码。
首先:购物车数据:{
text:‘名字’,
price:‘单价’,
active: 是否选中,
count: 购买数量
}
这里有用到axios,需要先安装,使用下方命令,需要可以了解axios详细内容
npm i axios -S
全量代码
现在是先以App.vue作为父组件使用,全部代码如下:
App.vue文件
<template>
<div id="app">
<ul>
<li v-for="(good, index) in goods" :key="good.id">
<span>{{good.text}}</span>
<span>¥{{good.price}}</span>
<button @click="addGood(index)">加购物车</button>
</li>
</ul>
<!-- 购物车 -->
<cart :name="name"></cart>
</div>
</template>
<script>
import Cart from './components/Cart.vue'
import axios from 'axios'
export default {
data() {
return {
name: '测试购物车',
text: '',
goods:[]
}
},
components: {
cart:Cart
},
async created(){
// 查询产品列表 使用axios
try{
const response = await axios.get('/api/goods');
console.log(response);
this.goods = response.data.list;
}catch(err){
console.error("error",err);
}
},
methods: {
addGood(i) {
const good = this.goods[i]; // 获取goods中对应项 good为正在添加的商品
this.$bus.$emit('addCart', good); //第一个参数为事件名称,第二个参为需要派发的数据
}
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Cart.vue
在components文件夹内:
<template>
<div>
<p>{{name}}</p>
<table border="1">
<tr>
<th>#</th>
<th>课程名</th>
<th>单价</th>
<th>数量</th>
<th>价格</th>
</tr>
<tr v-for="(c, i) in cart" :key="c.id" :class="{active:c.active}">
<td>
<input type="checkbox" v-model="c.active"/>
</td>
<td>{{c.text}}</td>
<td>¥{{c.price}}</td>
<td>
<button @click="minus(i)">-</button>
{{c.count}}
<button @click="add(i)">+</button>
</td>
<td>{{c.price * c.count}}</td>
</tr>
<!-- 使用计算属性 -->
<tr>
<td></td>
<td colspan="2">{{activeCount}}/{{count}}</td>
<td colspan="2">¥{{total}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
props: ['name'], //传递给子元素的数据
data() {
return {
// cart: []
cart: JSON.parse(localStorage.getItem("cart")) || []
}
},
// 数据持久化添加
watch:{
cart: {
handler(n, o){
localStorage.setItem("cart", JSON.stringify(n));
console.log(o);
},
deep: true
}
},
created(){
this.cart = JSON.parse(window.localStorage.getItem('cart'));
// 组件创建完成后执行一次,仅执行一次
// 监听一下父组件添加商品的事件
this.$bus.$on('addCart',good => {
const ret = this.cart.find(v => v.id == good.id); // 判断购物车内是否有该good,若有直接加数量,若无,直接加good
if(ret){ // 购物车内已有该商品
ret.count += 1
}else{
this.cart.push({
...good, //rest参数
count:1,
active: true //添加的东西是否是勾选状态
})
}
})
},
methods: {
// 数据持久化
setLocal(){
window.localStorage.setItem('cart',JSON.stringify(this.cart));
},
minus(i) {
const count = this.cart[i].count;
if(count > 1){
this.cart[i].count -= 1;
}else{
this.remove(i);
}
},
add(i){
this.cart[i].count += 1;
},
remove(i){
if(window.confirm("确定删除?")){
this.cart.splice(i,1);
}
}
},
computed: {
activeCount() { // 过滤处激活项的数量
return this.cart.filter(v => v.active).length;
},
count(){
return this.cart.length;
},
total(){ //计算激活项总价
let num = 0;
this.cart.forEach(c => {
if(c.active){
num += c.price * c.count;
}
});
return num;
}
},
}
</script>
<style scoped>
.active{
color: lightgreen;
}
</style>
大体两个文件完了,还需要修改其他文件。
main.js
在main.js
文件内,需要添加:
Vue.prototype.$bus= new Vue();
这个是修改了Vue实例,创建了只用于派发消息的实例。
在代码内用到了axios,则需要我们来mock数据,新建vue.config.js
文件,与package.json同级。
vue.config.js
// 用于mock数据
module.exports = {
configureWebpack: {
devServer: {
before(app){
app.get('/api/goods', function(req,res){
//当请求成功时
res.json({
code: 0, //成功
list: [
{id:1,text:'JS高程',price: 100},
{id:2,text:'Java基础',price: 100}
]
})
})
}
}
}
}
创建这个购物车,所需要的修改及模拟数据部分就全部涉及这四个文件。
操作过程解析
因为先po了全部的代码,现在需要分析几个要点:
这实现了购物车的添加、删除、选中、金额统计、模拟数据获取等操作。
模拟数据(vue.config.js文件)
采用了vue cli
官网内使用webpack的做法,模拟数据,配置文件参考vue cli
内的Workding with webpack。这里就用到了之前安装的axios插件。简单的mock,使用自带的webpack-dev-server即可,新建vue.config.js扩展webpack配置。
直接访问http://localhost:8080/api/goods
就可以看到mock数据。
模拟数据准备好了,使用axios获取数据,在App.vue内可以看到在created内获取初始数据。
//请求数据 这个是直接使用同步方式写异步代码
axios.get("/api/goods").then(response => {
this.goods = response.data.list;
}).catch(err => {
// 错误处理
})
但是采用async await异步方式来进行访问,下方的原代码:
async created(){
// 查询产品列表 使用axios
try{
const response = await axios.get('/api/goods');
console.log(response);
this.goods = response.data.list;
}catch(err){
console.error("error",err);
}
},
现在这个里面采用了async /await异步处理方式来通过axios来查询数据,同时还采用了try…catch来处理可能抛出的异常信息。
具体可以了解的async/await和axios
现在可以看到最后的结果是this.goods
,所以在使用时,全量的数据在goods
内。所以在App.vue内的li
标签内的数据使用goods。
循环
这里在li中使用了循环,用于将goods数据内的每一项都渲染到页面内。
<ul>
<li v-for="(good, index) in goods" :key="good.id">
<span>{{good.text}}</span>
<span>¥{{good.price}}</span>
<button @click="addGood(index)">加购物车</button>
</li>
</ul>
这里采用了v-for指令进行循环,提供了两个参数(good, index)为键名,现在使用循环必须使用key(不明可看此文)。
在循环渲染完成后,可以看到:
组件传值
1.props
在App.vue内使用组件<cart :name="name"></cart>
, 可以看到组件App.vue给子组件cart
传递了属性name
,这个name
获取的是data内的值:
export default {
data() {
return {
name: '测试购物车',
text: '',
goods:[]
}
}
}
父组件传递这个值之后,子组件通过props获取到这个值:
props: ['name'],
然后在html内渲染:
<p>{{name}}</p>
最后渲染可以看到:
2.总线(bus)传值
总线传值可用于非父子组件间的传值,在代码内有使用总线传值。
需要先在Vue上创建总线的实例,等于为创建了一个新的vue,用于派发消息:
Vue.prototype.$bus= new Vue();
在添加时间的按钮上添加点击事件addGood(index)
事件,
methods: {
addGood(i) {
const good = this.goods[i]; // 获取goods中对应项 good为正在添加的商品
this.$bus.$emit('addCart', good); //第一个参数为事件名称,第二个参为需要派发的数据
}
},
上面的代码:good为现在正在点击 加购物车 的那一项东西,获取该项的索引,然后使用this.$bus.$emit
对添加购物车addGood
的操作,将每个加购物车的数据good
派发出去。
这个等于是父组件通知,我组件内的某一个东西加购物车了,加的是这个东西,然后将这些全部发给组件,由子组件进行购物车动作添加的步骤。
子组件Cart.vue
通过this.$bus.$on
来接受这个动作,得到了需要添加的数据,然后将这些数据渲染或存储等。
子组件内对此过程的全部操作:
created(){
// 组件创建完成后执行一次,仅执行一次
// 监听一下父组件添加商品的事件
this.$bus.$on('addCart',good => {
const ret = this.cart.find(v => v.id == good.id); // 判断购物车内是否有该good,若有直接加数量,若无,直接加good
if(ret){ // 购物车内已有该商品
ret.count += 1
}else{
this.cart.push({
...good, //rest参数
count:1,
active: true //添加的东西是否是勾选状态
})
}
})
},
在这个渲染完成后,可能会:
事件处理
同时在数量上有对数量的加减统计,minus
和add
:
<tr v-for="(c, i) in cart" :key="c.id" :class="{active:c.active}">
<td>
<input type="checkbox" v-model="c.active"/>
</td>
<td>{{c.text}}</td>
<td>¥{{c.price}}</td>
<td>
<button @click="minus(i)">-</button>
{{c.count}}
<button @click="add(i)">+</button>
</td>
<td>{{c.price * c.count}}</td>
</tr>
通过方法实现:
methods: {
minus(i) {
const count = this.cart[i].count;
if(count > 1){
this.cart[i].count -= 1;
}else{
this.remove(i);
}
},
add(i){
this.cart[i].count += 1;
},
remove(i){
if(window.confirm("确定删除?")){
this.cart.splice(i,1);
}
}
},
动态样式
在$bus
内传值时,有给cart添加了active
属性,同时设置了多选框,
<tr v-for="(c, i) in cart" :key="c.id" :class="{active:c.active}">
<td>
<input type="checkbox" v-model="c.active"/>
</td>
......
</tr>
在这里多选框内使用v-model
双向绑定,控制上面的tr
这一行是否添加class
为active
的属性,若选中则将这一行字体变为浅绿色。
统计数据(计算属性)
这里采用计算属性。
子组件html部分:
<tr>
<td></td>
<td colspan="2">{{activeCount}}/{{count}}</td>
<td colspan="2">¥{{total}}</td>
</tr>
添加后,计算这几个需要存放的数量。activeCount是选中的数量,count是总数,total是总价。
在子组件内添加computed属性:
computed: {
activeCount() { // 过滤处激活项的数量
return this.cart.filter(v => v.active).length;
},
count(){
return this.cart.length;
},
total(){ //计算激活项总价
let num = 0;
this.cart.forEach(c => {
if(c.active){
num += c.price * c.count;
}
});
return num;
}
},
数据持久化
每次在刷新网页时,内部添加的购物车数据会被全部清除。所以需要将需要的购物车数据保存在localStorage中,在每次刷新购物车之后然后获取localStorage内存储的数据,进行展示处理。
对数据缓存的一个获取:
data() {
return {
cart: JSON.parse(localStorage.getItem("cart")) || []
}
},
监听数据,在数据发生变化时,修改内部的缓存:
watch:{
cart: {
handler(n, o){
localStorage.setItem("cart", JSON.stringify(n));
console.log(o);
},
deep: true
}
},
这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:
// JSON.stringify()的作用是将 JavaScript 对象转换为 JSON 字符串
window.localStorage.setItem('cart',JSON.stringify(this.cart));
//JSON.parse()将JSON字符串转为一个对象。
this.cart = JSON.parse(window.localStorage.getItem('cart'));
大体这个Vue实现简单购物车涉及到的完了,比较简单,但是涉及到的东西也不少,axios、async/await、事件处理、动态样式、mock数据、组件传值、数据持久化等。
更多推荐
所有评论(0)