商城项目整体构建
Posted Coding With you.....
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了商城项目整体构建相关的知识,希望对你有一定的参考价值。
一、
1.项目结构分配、模块的定义、组件封装调用(先定义、然后导入 注册 使用)、路由拦截、cookie理解、token权限、路由权限管理、动态路由、打包部署、vuex状态管理、接口文档
对于跳转,要先设置路由确保组件可以正常使用、路由可以正常跳转,再充实页面
对于左边导航与上边面包屑的联动,可以通过this.$route.matched获取该路由的所有信息,然后在每一个路由中写一个meta对应路由的内容,在面包屑那块通过遍历当前路由信息展示meta的值
对于导航栏的多级菜单,可以通过递归进行展示
父子组件传值的时候,子组件中通过props接收父组件传过来的值
二、流程
1.脚手架创建项目,并且 关闭eslint校验以防写代码时没错也报错
node_modules:放置项目依赖的地方。
public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面。
src:程序员源代码文件夹:
assets:经常放置一些静态资源(公用的图片(即很多组件都用此图)),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
components:一般放置非路由组件(如共用的组件)
App.vue:唯一的根组件
main.js:入口文件【程序最先执行的文件】
babel.config.js:babel配置文件
package.json:项目描述、项目依赖、项目运行
README.md:项目说明文件
2.在开发项目的时候:
非路由组件:
- 书写静态页面(html + CSS)
- 拆分组件
- 获取服务器的数据动态展示
- 完成相应的动态业务逻辑
路由组件
- 创建组件:Vue.component(tagName, options)
var 组件内容 = Vue.extend(template: '<div>自定义全局组件,使用Vue.extend</div>')Vue.component("组件名称",组件内容)
- 在router中创建并配置具体路由
- 在main.js中引入进行全局注册路由
- 在app.vue中:<!-- 路由组件出口的地方、路由组件展示 --> <router-view></router-view>
- 在需要的地方引入标签
3.mockjs模拟数据
使用Mock.js插件,生成随机数据,拦截 Ajax 请求。
- 前后端分离:让前端攻城师独立于后端进行开发。
- 增加单元测试的真实性:通过随机数据,模拟各种场景。
- 开发无侵入:不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。
- 用法简单:符合直觉的接口。
- 数据类型丰富:支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等。
- 方便扩展:支持支持扩展更多数据类型,支持自定义函数和正则。
4.购物车的管理
1.逻辑:点击加入购物车,将详情页的数据加入购物车中;
此时需要对state中的cartList进行修改,点击加入购物车就会执行store文件中actions对象里定义的加入购物车方法,actions对象中对加入的商品进行判断,若之前商品已存在,则数量加1,若商品不存在,则将商品添加进cartList。其返回一个promise对象。
2.组件:在购物车模块里可以查看详细信息 更新数量,全选反全选 取消加购 去结款
- 将加入购物车中的商品对象放到cartList中了,商品对象中包括需要在购物车中展示的详细信息。首先将通过store中的getters属性接收vuex中的数据,通过v-for来对cartList中的商品对象进行遍历,同时展示单个商品信息的组件通过props来接收父组件传来的单个商品对象;在计算属性computed中接收vuex中挂载的数据。
- 通过数组的filter方法找出选中的商品 ,然后通过数组的reduce方法对选中商品的价值总额进行计算;
- 全选的逻辑:若部分商品或者全部商品未被选中,则利用forEach使cartList中的每个商品为选中状态;若全部选中,则使cartList中的每个商品为未选中状态
3.数据交互:通过vuex状态管理机制来实现购物车的数据交互,创建store文件并挂载在vue实例上,在store文件中定义一个可以挂载数据的state,在其中定义一个数组cartList来存放商品信息,其他组件就可以获取并使用这个数据了。
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store(
state:
carList: [] //购物车的商品
,
mutations:
// 加
addCar(state, params)
let CarCon = state.carList;
// 判断如果购物车是否有这个商品,有就只增加数量,否则就添加一个
// some 只要有一个isHas为true,就为true
let isHas = CarCon.some((item) =>
if (params.id == item.id)
item.num++;
return true;
else
return false;
)
if (!isHas)
let obj =
"id": params.id,
"title": params.title,
"price": params.price,
"num": 1,
this.state.carList.push(obj)
,
// 减
reducedCar(state,params)
let len=state.carList.length;
for(var i=0;i<len;i++)
if(state.carList[i].id==params.id)
state.carList[i].num--
if(state.carList[i].num==0)
state.carList.splice(i,1);
break;
,
//移出
deleteCar(state,params)
let len=state.carList.length;
for(var i=0;i<len;i++)
if(state.carList[i].id==params.id)
state.carList.splice(i,1);
break;
,
// 初始化购物车,有可能用户一登录直接进入购物车
// initCar(state, car)
// state.carList = car
// ,
,
actions:
// 加
addCar( commit , params)
// console.log(params) //点击添加传过来的参数
// 使用setTimeout模拟异步获取购物车的数据
setTimeout(function ()
let result = 'ok'
if (result == 'ok')
// 提交给mutations
commit("addCar", params)
, 100)
,
// 减
reducedCar( commit , params)
// console.log(params) //点击添加传过来的参数
// 使用setTimeout模拟异步获取购物车的数据
setTimeout(function ()
let result = 'ok'
if (result == 'ok')
// 提交给mutations
commit("reducedCar", params)
, 100)
,
// 移出
deleteCar( commit , params)
// console.log(params) //点击添加传过来的参数
// 使用setTimeout模拟异步获取购物车的数据
setTimeout(function ()
let result = 'ok'
if (result == 'ok')
// 提交给mutations
commit("deleteCar", params)
, 100)
// initCar( commit )
// setTimeout(function ()
// let result = 'ok'
// if (result == 'ok')
// // 提交给mutations
// commit("initCar", [
// "id": 20193698,
// "title": '我是购物车原来的',
// "price": 30,
// "num": 100,
// ])
//
// , 100)
//
,
getters:
//返回购物车的总价
totalPrice(state)
let Carlen = state.carList;
let money = 0;
if (Carlen.length != 0)
Carlen.forEach((item) =>
money += item.price * item.num
)
return money;
else
return 0;
,
//返回购物车的总数
carCount(state)
return state.carList.length
,
)
list.vue
<template>
<!-- 商品列表 -->
<div id="listBox">
<!-- -->
<router-link :to="path:'/car'" style="line-height:50px">跳转到购物车</router-link>
<el-table :data="tableData" border style="width: 100%">
<el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
<el-table-column prop="title" align="center" label="商品标题"></el-table-column>
<el-table-column prop="price" align="center" label="商品价格"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button @click="addCar(scope.row)" type="text" size="small">加入购物车</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default
name: "listBox",
data()
return
tableData: [] //商品列表
;
,
methods:
// 初始化商品列表
initTable()
this.$gAjax(`../static/shopList.json`)
.then(res =>
console.log(res)
this.tableData=res;
)["catch"](() => );
,
// 加入购物车
addCar(row)
// console.log(row)
// 提交给store里面actions 由于加入购物车的数据要同步到后台
this.$store.dispatch('addCar',row)
,
mounted ()
this.initTable()
;
</script>
<style>
#listBox
width: 900px;
margin: 0 auto;
</style
cart.vue
<template>
<!-- 购物车 -->
<div id="carBox">
<!-- 商品总数 -->
<h2 style="line-height:50px;font-size:16px;font-weight:bold">合计:总共count个商品,总价totalPrice元</h2>
<p v-if="count==0">空空如也!·······</p>
<div v-else>
<el-table :data="carData" border style="width: 100%">
<el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
<el-table-column prop="title" align="center" label="商品标题"></el-table-column>
<el-table-column prop="price" align="center" label="商品价格"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button @click="reduceFun(scope.row)" type="text" size="small">-</el-button>
<span >scope.row.num</span>
<el-button @click="addCar(scope.row)" type="text" size="small">+</el-button>
<el-button @click="deleteFun(scope.row)" type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default
name: "carBox",
data()
return ;
,
computed:
//购物车列表
carData()
return this.$store.state.carList;
,
//商品总数
count()
return this.$store.getters.carCount;
,
//商品总价
totalPrice()
return this.$store.getters.totalPrice;
,
methods:
// 增加数量
addCar(row)
this.$store.dispatch('addCar',row)
,
// 减数量
reduceFun(row)
this.$store.dispatch('reducedCar',row)
,
// 删除
deleteFun(row)
this.$store.dispatch('deleteCar',row)
// 用户首次登录请求购物车的数据
// initCar()
// this.$store.dispatch('initCar')
//
,
created ()
// this.initCar();
,
mounted()
;
</script>
<style>
#carBox
width: 900px;
margin: 0 auto;
</style>
三、知识点记录
路由跳转的两种方式:
- 声明式导航:router-link,可以进行路由的跳转<router-link to="/login">登录</router-link>
- 编程式导航:利用组件实例的 $router.push | replace,可以进行路由跳转
编程式导航:声明式导航能做的,编程式导航都能;但是编程式导航除了可以进行路由跳转,还可以做一些其他的业务逻辑。
路由元信息:
将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过$route 的 meta属性 来实现,并且它可以在路由地址和导航守卫上都被访问到。
路由参数传递
params参数: 属于路径当中的一部分,需要注意,在配置路由的时候,需要占位
query参数: 不属于路径当中的一部分,类似于ajax中的queryString /home?k=v&kv=,不需要占位
路由传递参数(对象写法) path是否可以结合 params参数一起使用? 即:
this.$router.push( path: '/search', params: keyword: this.keyword , query: k: this.keyword.toUpperCase() , );
答:报错,不能。
如何指定 params参数 可传可不传? 即:
this.$router.push( name: "search", query: k: this.keyword.toUpperCase() , );
答:配置路由时,path上加个 ? 号,代表可传参数也可不传;若不加 ? ,则URL会出现问题。
params参数 可以传递也可以不传递,但是如果传递是空串,如何解决?
可以使用 undefined 来解决params参数可以传递也可不传递(空的字符串)
路由组件能不能传递 props数据?
可以采用三种写法
path: "/search/:keyword",
component: Search,
meta: show: true ,
// 对象形式路由传递参数
name: "search",
// 路由组件能不能传递 props数据?
// 1、布尔值写法,但是这种方法只能传递params参数
// props: true,
// 2、对象写法:额外给路由组件传递一些props
// props: a: 1, b: 2 ,
// 函数写法(常用):可以params参数、query参数,通过props传递给路由组件
props: ($route) =>
return keyword: $route.params.keyword, k: $route.query.k;
,
为什么进行axios 二次封装
为了请求拦截器、响应拦截器。
请求拦截器:在发请求之前可以处理一些业务;
响应拦截器:当服务器数据返回以后,可以处理一些事情。
// 对于axios进行二次封装
import axios from "axios"
// 利用axios对象的方法create,去创建一个axios实例
// 这里的request 就是 axios,在这里配置一下
const request = axios.create(
// 配置对象
// 基础路径,发请求的时候,路径当中会默认有/api,不用自己写了
baseURL: "/api",
// 请求超时5s
timeout: 5000,
)
// 请求拦截器:在发请求之前,请求拦截器可以检测到,在请求发出之前做一些事情;
requests.interceptors.request.use((config) =>
// config:配置对象,其有一个重要属性:header请求头
)
// 响应拦截器:当服务器数据返回以后,可以处理一些事情。
requests.interceptors.response.use(((res) =>
// 服务器响应成功的回调函数
return res.data;
, (error) =>
// 服务器响应失败的回调函数
return Promise.reject(new Error('faile'));
))
// 对外暴露
export default requests;
API接口统一管理
若项目很小,可以在组件的生命周期函数中发请求
但项目大,组件多,若有更改,将麻烦。所以API接口统一管理。比如跨域的代理可以统一管理
nprogress进度条的使用
在响应拦截器使用
// 请求拦截器:
requests.interceptors.request.use((config) =>
// config:配置对象,其有一个重要属性:header请求头
// 进度条开始动
nprogress.start();
return config;
)
// 响应拦截器:
requests.interceptors.response.use((res) =>
// 服务器响应成功的回调函数
// 进度条结束
nprogress.done();
return res.data;
, (err) =>
// 服务器响应失败的回调函数
return Promise.reject(new Error('faile'));
)
vuex 模块式开发
vuex 是官方提供的插件, 状态管理库,集中式管理项目中组件共用的数据 。
切记,并不是全部项目都需要 Vuex,如果项目很小,完全不需要Vuex,如果项目很大,组件很多、数据很多,数据维护很费劲,用Vuex
图片懒加载
// 引入图片懒加载插件
import VueLazeload from 'vue-lazyload';
// 引入懒加载默认图片(即真实图片没加载好之前,加载时显示的图片)
import tp from '@/assets/images/1.png';
// 注册插件
Vue.use(VueLazeload,
// 懒加载默认图片,(即真实图片没加载好之前,加载时显示的图片)
loading: tp,
)
<!-- v-lazy自定义指令图片懒加载 -->
<img v-lazy="good.defaultImg" />
以上是关于商城项目整体构建的主要内容,如果未能解决你的问题,请参考以下文章
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面,使用域名访问本地项目)