笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
支付、打包优化
支付功能
组件准备
支付组件设置为单独的组件
处理路由,实现点击、跳转支付页面,使用路径参数,需要登陆才能访问
检查是否登录,登陆了带着课程id跳转,没登录先跳转登录然后再回来课程详情页
//src/router/index.js 添加支付路由
{ // 支付购买
name: 'pay',
path: '/pay/:courseId',
component: () => import(/* webpackChunkName: 'pay' */'@/views/pay'),
// 设置路径参数
props: true,
meta: { login: true }
},
src/views/pay/index.vue 新建目录和文件,创建支付页面
<template>
<div class="pay">支付组件</div>
</template>
<script>
export default {
name: 'pay'
}
</script>
src/views/course-info/index.vue 给立即购买按钮添加点击事件,判断是否登录,根据是否登录判断是否需要跳转登陆页
<!-- 立即购买按钮添加点击事件跳转购买页 -->
<van-button type="primary" @click="goPay">立即购买</van-button>
// 立即购买点击事件
goPay () {
if (this.$store.state.user) {
// 如果已经登陆,直接带课程id跳转购买
this.$router.push(`/pay/${this.courseId}`)
} else {
// 如果没登陆,跳转登陆,将要前往的页面路由保存为参数
this.$router.push({
name: 'login',
query: {
path: this.$route.fullPath
}
})
}
}
布局处理
依旧可以使用单元格组件
设置基本结构、处理样式,最后支付方式撑满剩余高度
src/views/pay/index.vue 调用接口获取数据,设置顶部和中间结构和样式
<template>
<!-- 单元格容器 -->
<van-cell-group>
<!-- 顶部课程信息 -->
<van-cell>
<!-- 课程图片 -->
<img :src="courseInfo.courseImgUrl"/>
<div>
<!-- 课程名称 -->
<p v-text="courseInfo.courseName"></p>
<!-- 价格 -->
<p>¥ {{courseInfo.discounts}}</p>
</div>
</van-cell>
<!-- 中间用户信息 -->
<van-cell>
<p>购买信息</p>
<p>购买课程后使用此账号登录【拉勾教育】学习课程</p>
<!-- 当前用户手机号 -->
<p>当前账号:{{phone}}</p>
</van-cell>
<!-- 支付信息 -->
<van-cell>支付</van-cell>
</van-cell-group>
</template>
<script>
// 引入接口:获取课程信息
import { getCourseById } from '@/api/course'
export default {
name: 'pay',
// 参数
props: {
// 路径参数
courseId: {
type: [Number, String],
required: true
}
},
data () {
return {
// 课程信息
courseInfo: {}
}
},
// 钩子函数
created () {
// 获取课程信息
this.getCourse()
},
methods: {
// 获取课程信息
async getCourse () {
// 调用接口
const { data } = await getCourseById(this.courseId)
// 获取成功将获取到的信息传递给data
if (data.state === 1) this.courseInfo = data.content
}
},
// 计算属性
computed: {
// 处理用户手机号
phone () {
// 使用replace正则替换中间四位为 *
return this.$store.state.user.organization.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
}
}
</script>
<style lang="scss" scoped>
// 整体容器
.van-cell-group {
display: flex;
flex-direction: column;
height: 100vh;
background: #f8f9fa;
}
// 去掉所有单元格底部分割线
.van-cell::after {
display: none;
}
// 顶部课程信息
.van-cell:nth-child(1) {
padding: 40px 20px 0;
margin-bottom: 10px;
.van-cell__value {
display: flex;
height: 130px;
img {
width: 80px;
height: 107px;
border-radius: 10px
}
div {
display: flex;
height: 107px;
box-sizing: border-box;
flex-direction: column;
justify-content: space-between;
padding: 5px 20px;
p {
margin: 0;
}
p:nth-child(1) {
font-size: 16px;
}
p:nth-child(2) {
font-size: 22px;
color: #ff7452;
font-weight: 700;
}
}
}
}
// 中间用户信息
.van-cell:nth-child(2) {
padding: 10px 16px;
margin-bottom: 10px;
p {
margin: 0;
}
p:nth-child(2) {
font-size: 12px;
color: #999;
}
p:nth-child(3) {
margin: 20px 0 10px;
font-size: 16px;
}
}
// 底部支付信息
.van-cell:nth-child(3) {
// 底部撑满剩余高度
flex: 1;
}
</style>
绑定数据
初始化获取课程信息和用户信息,进行数据绑定
手机号直接使用vuex就可以了,通过计算属性进行处理 - replace替换
上面已经绑定了
支付结构处理
使用Vant单选框组件,在基础上进行样式结构处理,使用cell插槽
进行数据绑定
修改单选按钮颜色可能需要样式穿透(我这里直接使用组件的属性)
src/views/pay/index.vue 增加支付信息及按钮
<!-- 支付信息 -->
<van-cell class="pay">
<p>支付方式</p>
<!-- 结合cell的单选框 -->
<van-radio-group v-model="radio">
<!-- 单选框 -->
<van-cell-group>
<van-cell title="单选框 1" clickable @click="radio = '1'">
<!-- 插槽 -->
<template #title>
<img src="http://www.lgstatic.com/lg-app-fed/pay/images/wechat_b787e2f4.png" alt="">
<span class="custom-title">微信支付</span>
</template>
<!-- 插槽 -->
<template #right-icon>
<van-radio name="1" checked-color="#fbc547" />
</template>
</van-cell>
<van-cell title="单选框 2" clickable @click="radio = '2'">
<template #title>
<img src="http://www.lgstatic.com/lg-app-fed/pay/images/ali_ed78fdae.png" alt="">
<span class="custom-title">支付宝支付</span>
</template>
<template #right-icon>
<van-radio name="2" checked-color="#fbc547" />
</template>
</van-cell>
</van-cell-group>
</van-radio-group>
<!-- 立即购买按钮 -->
<van-button>¥ {{courseInfo.discounts}} 立即支付</van-button>
</van-cell>
<style lang="scss" scoped>
.....................
// 底部支付信息
.pay {
// 底部撑满剩余高度
flex: 1;
van-cell_value {
padding: 40px;
}
.van-cell-group::after {
display: none;
}
.van-cell--clickable {
padding: 20px 10px;
.van-cell__title {
display: flex;
align-items: center;
img {
width: 28px;
height: 28px;
}
span {
margin-left: 10px;
font-size: 16px;
}
}
}
// 立即购买按钮
.van-button {
position: absolute;
bottom: 10px;
width: 100%;
border-radius: 22px;
background-image: linear-gradient(to right, #fbc547, #faad4a);
font-size: 18px;
}
}
</style>
接口封装
使用创建商品订单接口生成订单,userid不需要传递
使用创建订单(发起支付)进行支付
使用查询订单(支付结果)获取支付结果(轮询),这个就扣可以更换为json格式传递data,但是axios不能识别,不过后端可以解析出来
可以试着使用下获取支付方式接口
当前使用这个课程没有被屏蔽了支付功能(不能生成订单),可以新建一个课程(在后台项目),前台搜索就可以支付了,这里直接找同学们之前生成的课程即可
穿件订获取成订单号 => 获取允许的支付方式
// src/api/pay.js 创建文件封装支付需要的四个接口
// 引入axios
import axios from './axios'
// 创建订单
export const saveOrder = data => {
return axios({
method: 'post',
url: '/front/order/saveOrder',
data
})
}
// 获取支付方式
export const getPayInfo = shopOrderNo => {
return axios({
method: 'get',
url: `/front/pay/getPayInfo?shopOrderNo=${shopOrderNo}`
})
}
// 发起支付
export const saveOrderBuy = data => {
return axios({
method: 'post',
url: '/front/pay/saveOrder',
data
})
}
// 获取支付结果
export const getPayResult = params => {
return axios({
method: 'get',
url: '/front/pay/getPayResult',
headers: { 'content-type': 'application/json' },
params
})
}
src/views/pay/index.vue 添加支付功能
<template>
<!-- 单元格容器 -->
<van-cell-group>
<!-- 顶部课程信息 -->
<van-cell class="course">
<!-- 课程图片 -->
<img :src="courseInfo.courseImgUrl"/>
<div>
<!-- 课程名称 -->
<p v-text="courseInfo.courseName"></p>
<!-- 价格 -->
<p>¥ {{courseInfo.discounts}}</p>
</div>
</van-cell>
<!-- 中间用户信息 -->
<van-cell class="user">
<p>购买信息</p>
<p>购买课程后使用此账号登录【拉勾教育】学习课程</p>
<!-- 当前用户手机号 -->
<p>当前账号:{{phone}}</p>
</van-cell>
<!-- 支付信息 -->
<van-cell class="pay">
<p>支付方式</p>
<!-- 结合cell的单选框 -->
<van-radio-group v-model="radio">
<!-- 单选框 -->
<van-cell-group>
<van-cell title="单选框 1" clickable @click="radio = '1'">
<!-- 插槽 -->
<template #title>
<img src="http://www.lgstatic.com/lg-app-fed/pay/images/wechat_b787e2f4.png" alt="">
<span class="custom-title">微信支付</span>
</template>
<!-- 插槽 -->
<template #right-icon>
<van-radio name="1" checked-color="#fbc547" />
</template>
</van-cell>
<van-cell title="单选框 2" clickable @click="radio = '2'">
<template #title>
<img src="http://www.lgstatic.com/lg-app-fed/pay/images/ali_ed78fdae.png" alt="">
<span class="custom-title">支付宝支付</span>
</template>
<template #right-icon>
<van-radio name="2" checked-color="#fbc547" />
</template>
</van-cell>
</van-cell-group>
</van-radio-group>
<!-- 立即购买按钮 -->
<van-button @click="buyCourse">¥ {{courseInfo.discounts}} 立即支付</van-button>
</van-cell>
</van-cell-group>
</template>
<script>
// 引入接口:获取课程信息
import { getCourseById } from '@/api/course'
// 引入接口:创建订单、获取支付方式、发起支付,查询支付状态
import { saveOrder, getPayInfo, saveOrderBuy, getPayResult } from '@/api/pay'
export default {
name: 'pay',
// 参数
props: {
// 路径参数
courseId: {
type: [Number, String],
required: true
}
},
data () {
return {
// 课程信息
courseInfo: {},
// 当前选择的支付方式
radio: null,
// 创建的订单号(不是发起支付的订单号)
orderNo: ''
}
},
// 钩子函数
created () {
// 获取课程信息
this.getCourse()
},
methods: {
// 立即支付按钮点击事件!!!!!!!!!!!!!!!!!!!!!!!!!!!!
async buyCourse () {
// 调用接口创建订单
const { data } = await saveOrder({
goodsId: this.courseId
})
// 如果创建成功将订单好存储起来(也可以不存储)
if (data.state === 1) this.orderNo = data.content.orderNo
// 调用接口获取支付方式,利用上面创建的订单号(这个可以不要,我这里只是尝试获取一下)
const { data: data2 } = await getPayInfo(data.content.orderNo)
console.log(data2)
// 调用接口发起支付,使用上面的订单号、支付方式,returnUrl必须有但是可以随便填
const { data: data3 } = await saveOrderBuy({
goodsOrderNo: data.content.orderNo,
channel: this.radio === 1 ? 'weChat' : 'aliPay',
returnUrl: 'http://edufront.lagou.com/'
})
// 如果发起成功跳转支付(这里使用支付宝,因为微信的方式有点问题)
if (data.state === 1) window.location.href = data3.content.payUrl
// 创建定时器轮询
const timer = setInterval(async () => {
// 调用接口不断查询支付状态
const { data: data4 } = await getPayResult({
orderNo: data3.content.orderNo
})
// 一旦发现返回的数据status = 2(代表支付成功)
if (data4.content.status === 2) {
// 停止定时器
clearInterval(timer)
// 跳转学习页面
this.$router.push('/study')
}
}, 1000)
},
// 获取课程信息
async getCourse () {
// 调用接口
const { data } = await getCourseById(this.courseId)
// 获取成功将获取到的信息传递给data
if (data.state === 1) this.courseInfo = data.content
}
},
// 计算属性
computed: {
// 处理用户手机号
phone () {
// 使用replace正则替换中间四位为*
return this.$store.state.user.organization.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
}
}
</script>
<style lang="scss" scoped>
// 整体容器
body > .van-cell-group {
display: flex;
flex-direction: column;
height: 100vh;
background: #f8f9fa;
}
// 去掉所有单元格底部分割线
.van-cell::after {
display: none;
}
// 顶部课程信息
.course {
padding: 40px 20px 0;
margin-bottom: 10px;
.van-cell__value {
display: flex;
height: 130px;
img {
width: 80px;
height: 107px;
border-radius: 10px
}
div {
display: flex;
height: 107px;
box-sizing: border-box;
flex-direction: column;
justify-content: space-between;
padding: 5px 20px;
p {
margin: 0;
}
p:nth-child(1) {
font-size: 16px;
}
p:nth-child(2) {
font-size: 22px;
color: #ff7452;
font-weight: 700;
}
}
}
}
// 中间用户信息
.user {
padding: 10px 16px;
margin-bottom: 10px;
p {
margin: 0;
}
p:nth-child(2) {
font-size: 12px;
color: #999;
}
p:nth-child(3) {
margin: 20px 0 10px;
font-size: 16px;
}
}
// 底部支付信息
.pay {
// 底部撑满剩余高度
flex: 1;
van-cell_value {
padding: 40px;
}
.van-cell-group::after {
display: none;
}
.van-cell--clickable {
padding: 20px 10px;
.van-cell__title {
display: flex;
align-items: center;
img {
width: 28px;
height: 28px;
}
span {
margin-left: 10px;
font-size: 16px;
}
}
}
// 立即购买按钮
.van-button {
position: absolute;
bottom: 10px;
width: 100%;
border-radius: 22px;
background-image: linear-gradient(to right, #fbc547, #faad4a);
font-size: 18px;
}
}
</style>
支付请求
点击支付按钮判断支付方式,发起支付,支付之后获得一幅订单号,之后不断轮训获取支付结果
注意支付方式要使用weChat或者aliPay,微信的不能唤起微信(后台设置问题),所以建议使用支付宝
获取到返回数据直接跳转到数据里的地址,会自动跳转支付宝支付
跳转后不断轮询获取支付结果,支付一旦成功(status = 2)跳转即支付成功页面即可,或者支付失败(status = 2)跳转师傅失败页面即可
支付成功和失败都添加提示信息
已经在上面写了
打包优化
优化打包速度和打包后的文件体积,如果使用静态文件较多,可能一个项目非常大
配置文件
使用Vue-cli配置文件vue.config.js进行打包配置,没有这个文件会使用默认的配置项,具体可参考文档
推荐配置项:
- productionSourceMap:生产环境项目不需要map文件
- css.extract:当我们每个css文件体积都比较小,可以把这些文件打包为一个文件,可以减少请求次数
图片压缩
有被压缩的必要才进行压缩处理,如果需要保持高清晰度就不建议压缩
安装图片压缩loader:npm i image-webpack-loader - D,有些包可能使用github地址,可以设置hosts,之后就会安装快很多
loader安装失败先删掉旧的依赖文件,再重新安装,否则可能会出错
在vue.config.js配置文件中添加配置
可能是网络原因,使用图片压缩一直安装出问题,先不弄了
Vant按需引入组件
vue.config.jsant提供了方法自动按需引入组件,会把全局引入在编译过程中自动转化为按需引入,缩小打包体积
注意需要修改组件内使用vant组件的方式,要先引入再需要的组件,然后注册为子组件使用,注意Toast比较特殊(不需要注册直接使用)
按需引入后删除掉之前的全局引入
经过以上的步骤才能保证vant能够正常呈现
参考vant官方文档 => 安装npm包 => 修改babel文件内容 => 修改全部的vant组件引入方式(当然如果写的时候直接就按照按需引入就更好了,别问我怎么知道的)=> 删除掉之前的全局引入 => 之后会发现每个小每个模块js文件略微增加,而最大的js明显缩小,这样有利于初次加载的时候更快
CDN引入
内容分发网络 - 稳定,速度快,将某些大的包使用CND引入的方式加快响应速度
bootCdn提供了很多CDN服务,阿里云可以购买CDN服务
Vant提供了CDN引入方式本项目可以把vue和vant都使用CDN引入
CDN引入也先不做了,因为上面实在要修改的太多了,以后写代码的时候直接使用自动按需引入就可以了
以后需要的时候可以回去看视频