【VUE2】综合练习——智慧商城
目录
0 前言
1 准备工作
1.1 vue-cli 脚手架 创建项目
1.2 调整目录结构
1.2.1 删除
1.2.2 修改
1.2.3 新增
1.3 引入 vant 组件
1.3.1 按需导入
1.3.2 全部导入(略)
1.4 vw适配
1.5 路由配置(略)
2 登录页面
2.1 静态资源(略)
2.2 request模块 - axios 封装
2.3 封装图片验证码 api (略)
2.4 toast 轻提示
2.5 短信验证倒计时(略)
2.6 封装登录 api(略)
2.7 响应拦截器统一处理错误提示(略)
2.8 将登录权证信息存入vuex及本地(略)
2.9 请求拦截器添加 loading 效果(略)
2.10 配置路由前置守卫
3 首页(略)
3.1 静态资源准备(略)
3.2 动态渲染(略)
4 搜索
4.1 静态布局准备(略)
4.2 历史记录-基本管理
4.3 历史记录-持久化(略)
5 搜索列表(略)
5.1 静态布局(略)
5.2 动态渲染(略)
5.2.1 关键字搜索(略)
5.2.2 分类id搜索(略)
6 商品详情(略)
6.1 静态布局(略)
6.2 动态渲染介绍(略)(图片懒加载)
6.3 动态渲染评论(略)
7 加入购物车
7.1 唤起弹窗(略)
7.2 封装数字框组件(略)
7.3 判断token登录提示
7.4 封装接口进行请求
8 购物车
8.1 静态布局(略)
8.2 构建vuex模块(略)
8.3 渲染购物车列表
8.4 封装getters
8.5 全选与反选功能(略)
8.6 数字框修改数量
8.7 编辑切换状态(略)
8.8 删除功能(略)
8.9 空购物车处理(略)
9 订单结算台(略)
9.1 静态布局(略)
9.2 获取收货地址列表(略)
9.3 订单结算(略)
9.4 mixins复用
9.5 提交订单并支付(略)
10 订单管理(略)
10.1 静态布局(略)
10.2 点击tab切换渲染(略)
11 个人中心(略)
11.1 基本渲染(略)
11.2 退出功能(略)
12 项目打包优化
12.1 打包命令
12.2 配置publicPath
12.3 路由懒加载
0 前言
接口文档:wiki - 智慧商城-实战项目
记录跟随黑马程序所做项目的重点步骤,简单复制粘贴步骤请见文档
为什么省略内容的标题要保留?为了看清整个项目的流程!
黑马程序员视频地址:104-项目演示-查看项目效果,明确功能模块
黑马文档:黑马文档.zip - 蓝奏云
注意:黑马文档中部分链接失效,在本文中可查找相应链接(若本文中链接也失效,请私信up!)
1 准备工作
1.1 vue-cli 脚手架 创建项目
见 【VUE2】第五期——VueCli创建项目
注意:本项目需要勾选下图模块
1.2 调整目录结构
1.2.1 删除
src/assets/logo.png
src/components/HelloWorld.vue
src/views/AboutView.vue
src/views/HomeView.vue
1.2.2 修改
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes: []
})
export default router
// app.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="less">
</style>
1.2.3 新增
src/api 目录:存储接口模块 (发送ajax请求接口的模块)
src/utils 目录:存储一些工具模块 (自己封装的方法)
新建 styles/common.less
重置默认样式
// 重置默认样式
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// 文字溢出省略号
.text-ellipsis-2 {
overflow: hidden;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
在main.js中引入
import '@/styles/common.less'
1.3 引入 vant 组件
文档:Vant 2 - 轻量、可靠的移动端组件库
其他组件库:
pc:
Element - 网站快速成型工具 | 一个 Vue 3 UI 框架 | Element Plus | iView / View Design 一套企业级 UI 组件库和前端解决方案 | Ant Design - 一套企业级 UI 设计语言和 React 组件库
移动:
Vant 4 | Mint UI (饿了么)| cube-ui(滴滴)
1.3.1 按需导入
1.安装vant-ui
yarn add vant@latest-v2
2.安装一个插件
yarn add babel-plugin-import -D
3.在babel.config.js
中配置
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
4.此时可以直接在main.js进行导入注册
但是:为了方便管理,新建utils/vant-ui.js,放入其中,再在main.js中导入
// utils/vant-ui.js
import { Button, Icon } from 'vant'
Vue.use(Button)
Vue.use(Icon)
// main.js 导入按需导入的配置文件
import '@/utils/vant-ui'
1.3.2 全部导入(略)
见黑马笔记或官方文档,此处不赘述,不推荐
1.4 vw适配
文档:Vant 2 - 轻量、可靠的移动端组件库
vw适配在文档的进阶用法中
步骤:
1.安装 postcss-px-to-viewport 插件
yarn add postcss-px-to-viewport@1.1.1 -D
2.项目根目录, 新建postcss的配置文件postcss.config.js
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375,
},
},
};
viewportWidth:设计稿的视口宽度
vant-ui中的组件就是按照375的视口宽度设计的
恰好面经项目中的设计稿也是按照375的视口宽度设计的,所以此时我们只需要配置375就可以了
如果设计稿不是按照375而是按照750的宽度设计请见 Vue 项目引入Vant
1.5 路由配置(略)
见黑马文档 或 【VUE2】第四期——路由_vue路由传参query-CSDN博客
2 登录页面
2.1 静态资源(略)
见黑马文档
2.2 request模块 - axios 封装
我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址,请求响应拦截器等等)
一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用
目的:为了配置不同的基地址、请求响应拦截器等
步骤:
1.安装axios
npm i axios
2.新建 utils/request.js
封装 axios 模块
利用 axios.create 创建一个自定义的 axios 来使用
文档:axios中文文档|axios中文网 | axios
/* 封装axios用于发送请求 */
import axios from 'axios'
// 创建一个新的axios实例
const request = axios.create({
baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
timeout: 5000
})
// 添加请求拦截器
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
// 添加响应拦截器
request.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
export default request
注意:在响应拦截器中分为成功与失败两个代码块区域,指的是是否能从服务器拿到数据,因此得到的response包含了响应状态,response.data是服务器响应的数据,而这个data数据中也包含了响应状态(服务器给的)和数据data
2.3 封装图片验证码 api (略)
见黑马文档
请求服务器数据api记得单独抽离出来放到api文件夹中
2.4 toast 轻提示
详情见文档:Vant 2 - 轻量、可靠的移动端组件库
1.导入调用 ( 组件内 或 非组件中均可 )
import { Toast } from 'vant';
Toast('提示内容');
2.通过this直接调用 ( 组件内 )
//在utils/vant-ui.js中注册
import { Toast } from 'vant';
Vue.use(Toast)
//直接在vue组件中使用
this.$toast('提示内容')
2.5 短信验证倒计时(略)
见黑马文档
注意:页面销毁事件触发时,记得销毁定时器
2.6 封装登录 api(略)
见黑马文档
记得封装单独函数结合正则表达式来判断输入内容是否正确,如:
// 校验输入框内容
validFn () {
if (!/^1[3-9]\d{9}$/.test(this.mobile)) {
this.$toast('请输入正确的手机号')
return false
}
if (!/^\w{4}$/.test(this.picCode)) {
this.$toast('请输入正确的图形验证码')
return false
}
return true
}
正则表达式见:【JavaScript】APIs篇03——正则表达式-CSDN博客
2.7 响应拦截器统一处理错误提示(略)
见黑马文档
2.8 将登录权证信息存入vuex及本地(略)
见黑马文档
记得将存入本地相关的代码封装为api,放在 utils/storage.js
中,便于调用与管理
2.9 请求拦截器添加 loading 效果(略)
文档:Vant 2 - 轻量、可靠的移动端组件库
注意toast的特性:单例
2.10 配置路由前置守卫
文档:导航守卫 | Vue Router
基础用法:
router.beforeEach((to, from, next) => {
// 1. to 往哪里去, 到哪去的路由信息对象
// 2. from 从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
// 如果next()调用,就是放行
// next(路径) 拦截到某个路径页面
})
示例:(图来源:黑马笔记)
// 需要权限的页面
const authUrls = ['/myorder', '/pay']
router.beforeEach((to, from, next) => {
const token = store.getters['user/getToken']
// 不需要权限的页面直接放行
if (!authUrls.includes(to.path)) {
next()
return // 执行完需要return,否则还会判断是否存在token
}
if (token) {
// 如果token存在,直接放行
next()
} else {
// 否则跳转到登录页面
next('/login')
}
})
3 首页(略)
3.1 静态资源准备(略)
见黑马文档
3.2 动态渲染(略)
见黑马文档
4 搜索
4.1 静态布局准备(略)
见黑马文档
4.2 历史记录-基本管理
注意:此处判断是否显示不能直接写history,因为在js中,空数组是真值
<div class="search-history" v-if="history.length > 0">
<!--省略一万行代码-->
<script>
data () {
return {
//...
history: []
}
}
</script>
ai解释:
其他见黑马文档,此处略
4.3 历史记录-持久化(略)
见黑马文档
5 搜索列表(略)
5.1 静态布局(略)
5.2 动态渲染(略)
5.2.1 关键字搜索(略)
5.2.2 分类id搜索(略)
6 商品详情(略)
6.1 静态布局(略)
见黑马文档
6.2 动态渲染介绍(略)(图片懒加载)
懒加载文档:Vant 2 - 轻量、可靠的移动端组件库
6.3 动态渲染评论(略)
评分文档:Vant 2 - 轻量、可靠的移动端组件库
7 加入购物车
7.1 唤起弹窗(略)
ActionSheet 动作面板文档:Vant 2 - 轻量、可靠的移动端组件库
7.2 封装数字框组件(略)
步进器文档:Vant 2 - 轻量、可靠的移动端组件库
也可以像黑马那样自己封装个组件
如果自己封装记得判断用户输入的数据是否为整数
可以将type改为number来限制用户输入(但无法避免负数与零的情况,仍然需要判断)
<input @change="update($event)" class="inp" type="text" :value="value">
update (e) {
const num = +e.target.value
if ((num % 1) !== 0 || num < 1) {
e.target.value = this.value
return
}
this.$emit('input', num)
}
7.3 判断token登录提示
1.注册dialog组件
import { Dialog } from 'vant'
Vue.use(Dialog)
2.按钮添加点击事件
<div class="btn" v-if="mode === 'cart'" @click="addCart">加入购物车</div>
3.添加 token 鉴权判断,跳转携带回跳地址
async addCart () {
// 判断用户是否有登录
if (!this.$store.getters.token) {
this.$dialog.confirm({
title: '温馨提示',
message: '此时需要先登录才能继续操作哦',
confirmButtonText: '去登录',
cancelButtonText: '再逛逛'
})
.then(() => {
this.$router.replace({
path: '/login',
query: {
backUrl: this.$route.fullPath
}
})
})
.catch(() => {})
return
}
console.log('进行加入购物车操作')
}
问题1:登录完成后跳转首页,而我想要跳回商品详情页
解决方案:向登录页面传参,再在登录时判断是否传参,传参则需跳转到参数对应页面
问题2:登录完跳回商品详情页,点击返回上一页又会跳回登录页而不是搜索列表
解决方案:将this.$router.push改为this.$router.replace
重点:这里要使用replace而不是push
区别:push是新增历史记录,replace是替换历史记录,点击上一页的区别不同
获取当前页面的完整路径:this.$route.fullPath
4.登录后,若有回跳地址,则回跳页面
// 判断是否有参数
this.$router.replace(this.$route.query.backUrl || '/')
7.4 封装接口进行请求
重点:请求拦截器中统一配置token
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
//...
const token = store.getters['user/getToken']
if (token) {
config.headers['Access-Token'] = token
config.headers.platform = 'H5'
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
其他请见黑马文档
8 购物车
8.1 静态布局(略)
8.2 构建vuex模块(略)
8.3 渲染购物车列表
记得判断是否存在token,从而选择是否显示购物车列表
created () {
if (this.token) {
this.getCartList()
} else {
this.$router.replace({
path: '/login',
query: {
backUrl: this.$route.fullPath
}
})
}
},
computed: {
token () {
return this.$store.getters['user/getToken']
}
},
其他见黑马文档
8.4 封装getters
这里新学了可以在getters中调用getters的方法
getters: {
cartTotal (state) {
return state.cartList.reduce((sum, item, index) => sum + item.goods_num, 0)
},
selCartList (state) {
return state.cartList.filter(item => item.isChecked)
},
selCount (state, getters) {
return getters.selCartList.reduce((sum, item, index) => sum + item.goods_num, 0)
},
selPrice (state, getters) {
return getters.selCartList.reduce((sum, item, index) => {
return sum + item.goods_num * item.goods.goods_price_min
}, 0).toFixed(2)
}
}
其他见黑马文档
8.5 全选与反选功能(略)
见黑马文档
8.6 数字框修改数量
传递除了子组件传过来以外的其他值的方法
@事件名称="形参1 => 函数(形参1, 其他参数1, 其他参数2)
这里的形参1就是子组件传递过来的值
用法示例:
<CountBox :value="item.goods_num" @input="value => changeCount(value, item.goods_id, item.goods_sku_id)"></CountBox>
注意要先判断服务器是否能提交,再修改本地vuex,防止库存不足
详细见黑马文档
8.7 编辑切换状态(略)
8.8 删除功能(略)
我在写调用接口时遇到一个bug:无论传几个值,都会把购物车清空,你们可以试一试,告诉我是不是我的问题
8.9 空购物车处理(略)
9 订单结算台(略)
9.1 静态布局(略)
9.2 获取收货地址列表(略)
9.3 订单结算(略)
9.4 mixins复用
1.新建一个 mixin 文件 mixins/loginConfirm.js
export default {
methods: {},
data () {
return {}
},
created () {}
}
注意:在此文件中可以使用vue中的data,methods等等属性,最后会合并到调用该文件的组件中,如果方法、属性冲突,则组件内的优先级更高
2.页面中导入,混入方法
import loginConfirm from '@/mixins/loginConfirm'
export default {
name: 'ProDetail',
mixins: [loginConfirm],
...
}
3.页面中即可直接使用
9.5 提交订单并支付(略)
10 订单管理(略)
10.1 静态布局(略)
10.2 点击tab切换渲染(略)
使用tab组件
11 个人中心(略)
11.1 基本渲染(略)
11.2 退出功能(略)
清除token以及vuex中的数据
在user中提供相应方法,及在user中清除user以及cart中的数据,这就涉及到了调用全局vuex方法
示例:
methods: {
logout () {
this.$dialog.confirm({
title: '温馨提示',
message: '你确认要退出么?'
})
.then(() => {
this.$store.dispatch('user/logout')
})
.catch(() => {
})
}
}
actions: {
logout (context) {
context.commit('setUserInfo', {})
context.commit('cart/setCartList', [], { root: true }) //跨模块调用
}
},
12 项目打包优化
12.1 打包命令
yarn build
在项目的根目录会自动创建一个文件夹dist
,dist中的文件就是打包后的文件,只需要放到服务器中即可
12.2 配置publicPath
// vue.config.js
module.exports = {
// 设置获取.js,.css文件时,是以相对地址为基准的。
// https://cli.vuejs.org/zh/config/#publicpath
publicPath: './'
}
12.3 路由懒加载
路由懒加载 & 异步组件, 不会一上来就将所有的组件都加载,而是访问到对应的路由了,才加载解析这个路由对应的所有组件
文档:路由懒加载 | Vue Router
const ProDetail = () => import('@/views/prodetail')
const Pay = () => import('@/views/pay')
const MyOrder = () => import('@/views/myorder')
快捷键:
Alt+Shift+单击起始行 :快速选中多行
Ctrl+右箭头:光标会直接跳到下一个单词的结尾