文章目录
本篇主要总结出现的问题和一些解决方法
问题
vuex状态管理
在登录功能中,我使用了local storage进行了用户信息的持久化处理,为此,我在vuex里定义了一个方法,
详细代码如下:
index.js:
import Vue from "vue";
import Vuex from "vuex";
import user from "./modules/user";
Vue.use(Vuex)
export default new Vuex.Store({
getters: {
token (state) {
return state.user.userInfo.token
}
},
modules:{
user,
}
})
user.js
import { getInfo,setInfo,removeInfo } from "@/utils/storage"
export default {
namespaced: true,
state () {
return {
userInfo: getInfo()
}
},
mutations: {
setUserInfo (state, obj) {
state.userInfo = obj
setInfo(obj)
},
resetUserInfo(state) {
state.userInfo = {
userId: ''
}
removeInfo()
}
},
actions: {
updateUserInfo({ commit }, obj) {
commit('setUserInfo', obj);
},
logout({ commit }) {
commit('resetUserInfo');
}
}
}
storage.js
const INFO_KEY = 'eat_info'
// 获取个人信息
export const getInfo = () => {
const result = localStorage.getItem(INFO_KEY)
return result ? JSON.parse(result) : {
userId: '',
}
}
// 设置个人信息
export const setInfo = (info) => {
localStorage.setItem(INFO_KEY, JSON.stringify(info))
}
// 移除个人信息
export const removeInfo = () => {
localStorage.removeItem(INFO_KEY)
}
// 获取搜索历史
export const getHistoryList = () => {
const result = localStorage.getItem(HISTORY_KEY)
return result ? JSON.parse(result) : []
}
// 设置搜索历史
export const setHistoryList = (arr) => {
localStorage.setItem(HISTORY_KEY, JSON.stringify(arr))
}
在页面中使用:
this.$store.commit('user/setUserInfo',yonghu)
问题在于,在整个项目中vuex只用于了用户信息的存储,在最近看完vue3某个项目后,(虽然它用的是pinia进行状态管理),发现vuex的作用有很多,其实还可以把一些通用的方法在vuex中进行封装,在组件中进行调用,提高效率。
父子组件数据展示
提高父子组件,出现的首个问题就是子组件复用问题,因为我的项目有很多页面获取到的数据都是相同的,例如在不同模式下展示帖子数据,评论的数据,这些只有样式有一些小的差别,但是我没有进行组件复用,现在想起来,是可以进行组件复用的,不用的数据就丢在那里,不用管就可以。由于没有复用,导致我的子组件有很多,冗杂繁琐
第二个问题就是父组件得到数据,向子组件展示中,由于我用数组包起来向子组件传递,而且是两层数组,传到子组件时,有的展不开,导致数据错乱,理想状态下是单条数据向子组件传递,这样好展示,在网上搜索后,用这种方法得到了解决。
两层for循环遍历
<div class="commend-list">
<template v-for="nestedArray in CommendList">
<CommendItems v-for="comment in nestedArray" :item="comment" :key="comment.commentId"></CommendItems>
</template>
</div>
在子组件中:
props: {
item: {
type: Object,
default: () => {
return {}
},
}
},
路由跳转
在用push方法进行跳转并携带参数,在跳转后的页面,一刷新页面跳转来的参数就没有了,如何解决这个问题?如下代码:
this.$router.push({
name: 'pinglun',
params: {
param1: param1,
param2: param2
}
});
在路由中定义path时加上
path: '/pinglun/:param1/:param2',
//接收路由挑来的值
this.receivedParam1 = this.$route.params.param1;
this.receivedParam2 = this.$route.params.param2;
用户信息的修改
首先是头像的展示和修改,这个功能卡了很长时间
数据定义:
data() {
return {
form: {
userId:'',
userNickname: '',
userGender: '',
userMajor: '',
userLocation: '',
},
touImg:'',
fileList: [], // 保存上传的文件
};
},
利用ui中的功能将图片上传
afterRead(file) {
console.log(file.file);
changeTouxiang(this.userInfo.userId,file.file).then((res)=>{
console.log(res);
})
}
在数据库中获取图片展示出来
getMyDate(this.userInfo.userId)
.then(res=>{
this.touImg=res.data.userAvatar
this.fileList.push({ url: this.touImg,isImage: true })
console.log(this.touImg);
})
对于信息修改,有些信息需要修改,有些不需要修改,但不管修不修改,都要传到数据库中进行更新
// 更新用户信息
const updatedUserInfo = {
...this.userInfo,
userNickname: this.form.userNickname,
userGender: this.form.userGender,
userMajor: this.form.userMajor,
};
改进
1.分页数据的展示和管理
2.对于弹窗的应用,例如:弹窗修改某些数据
3.展示多张图片
vant-ui的使用
安装
通过 npm 安装
# Vue 3 项目,安装最新版 Vant:
npm i vant -S
# Vue 2 项目,安装 Vant 2:
npm i vant@latest-v2 -S
导入
全部导入
在main.js中
import Vant from 'vant';
import 'vant/lib/index.css';
// 把vant中所有的组件都导入了
Vue.use(Vant)
使用
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
按需导入
安装一个插件
yarn add babel-plugin-import -D
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
把引入组件的步骤抽离到单独的js文件中比如 utils/vant-ui.js
import { Button, Icon } from 'vant'
Vue.use(Button)
Vue.use(Icon)
main.js中进行导入
// 导入按需导入的配置文件
import '@/utils/vant-ui'
toast 轻提示
两种使用方式
- 导入调用 ( 组件内 或 非组件中均可 )
import { Toast } from 'vant';
Toast('提示内容');
- 通过this直接调用 ( **组件内 **)
main.js 注册绑定到原型
import { Toast } from 'vant';
Vue.use(Toast)
this.$toast('提示内容')
vw适配
yarn add postcss-px-to-viewport@1.1.1 -D
项目根目录, 新建postcss的配置文件postcss.config.js
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375,
},
},
};
路由配置
一级路由
但凡是单个页面,独立展示的,都是一级路由
- 登录页
- 首页架子
- 首页 - 二级
- 分类页 - 二级
- 购物车 - 二级
- 我的 - 二级
- 搜索页
- 搜索列表页
- 商品详情页
- 结算支付页
- 我的订单页
router/index.js
配置一级路由,新建对应的页面文件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Login from '@/views/login'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/',
component: Layout
},
{
path: '/search',
component: Search
},
{
path: '/searchlist',
component: SearchList
},
{
path: '/prodetail/:id',
component: ProDetail
},
{
path: '/pay',
component: Pay
},
{
path: '/myorder',
component: MyOrder
}
]
})
export default router
tabbar标签页
vant-ui.js
引入组件
import { Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)
layout.vue
<template>
<div>
<!-- 二级路由出口 -->
<van-tabbar active-color="#ee0a24" inactive-color="#000">
<van-tabbar-item icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item icon="apps-o">分类页</van-tabbar-item>
<van-tabbar-item icon="shopping-cart-o">购物车</van-tabbar-item>
<van-tabbar-item icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
二级路由
1.router/index.js
配置二级路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Login from '@/views/login'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
import Home from '@/views/layout/home'
import Category from '@/views/layout/category'
import Cart from '@/views/layout/cart'
import User from '@/views/layout/user'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/',
component: Layout,
redirect: '/home',
children: [
{
path: 'home',
component: Home
},
{
path: 'category',
component: Category
},
{
path: 'cart',
component: Cart
},
{
path: 'user',
component: User
}
]
},
{
path: '/search',
component: Search
},
{
path: '/searchlist',
component: SearchList
},
{
path: '/prodetail/:id',
component: ProDetail
},
{
path: '/pay',
component: Pay
},
{
path: '/myorder',
component: MyOrder
}
]
})
export default router
redirect重定向到主页
2.准备对应的组件文件
layout/home.vue
layout/category.vue
layout/cart.vue
- `layout/user.vue
3.layout.vue
配置路由出口, 配置 tabbar
<template>
<div>
<router-view></router-view>
<van-tabbar route active-color="#ee0a24" inactive-color="#000">
<van-tabbar-item to="/home" icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item to="/category" icon="apps-o">分类页</van-tabbar-item>
<van-tabbar-item to="/cart" icon="shopping-cart-o">购物车</van-tabbar-item>
<van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
接口配置
响应拦截器统一配置
响应拦截器是咱们拿到数据的 第一个 “数据流转站”,可以在里面统一处理错误,只要不是 200 默认给提示,抛出错误
utils/request.js
import { Toast } from 'vant'
...
// 添加响应拦截器
request.interceptors.response.use(function (response) {
const res = response.data
if (res.status !== 200) {
Toast(res.message)
return Promise.reject(res.message)
}
// 对响应数据做点什么
return res
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
axios封装
将 axios 请求方法,封装到 request 模块**
1.安装 axios
npm i axios
2.新建 utils/request.js
封装 axios 模块
利用 axios.create 创建一个自定义的 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
添加请求 loading 效果
1.请求时,打开 loading
// 添加请求拦截器
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
Toast.loading({
message: '请求中...',
forbidClick: true,
loadingType: 'spinner',
duration: 0
})
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
2.响应时,关闭 loading
// 添加响应拦截器
request.interceptors.response.use(function (response) {
const res = response.data
if (res.status !== 200) {
Toast(res.message)
return Promise.reject(res.message)
} else {
// 清除 loading 中的效果
Toast.clear()
}
// 对响应数据做点什么
return res
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
封装api接口
1.将请求封装成方法,统一存放到 api 模块,与页面分离
2.具体实现
新建 api/login.js
提供获取图形验证码 Api 函数
图形验证码的接口
import request from '@/utils/request'
// 获取图形验证码
export const getPicCode = () => {
return request.get('/captcha/image')
}
login/index.vue
页面中调用测试
async getPicCode () {
const { data: { base64, key } } = await getPicCode()
this.picUrl = base64
this.picKey = key
},
async created () {
this.getPicCode()
},
data () {
return {
picUrl: '',
picKey: ''
}
},
methods: {
// 获取图形验证码
async getPicCode () {
const { data: { base64, key } } = await request.get('/captcha/image')
this.picUrl = base64
this.picKey = key
}
}
create方法,在页面还未渲染成html前,调用函数,从后端获取数据,在实现对页面的数据进行显示
短信验证码的接口
export const getMsgCode = (captchaCode, captchaKey, mobile) => {
return request.post('/captcha/sendSmsCaptcha', {
form: {
captchaCode,
captchaKey,
mobile
}
})
}
await getMsgCode(this.picCode, this.picKey, this.mobile)
若是有参数的后端接口,在调用时这样传递参数
总结
在登录的组件中,data里存着需要用到的数据,方便使用
生命周期钩子,页面未渲染前就获得后端数据
定义方法,用异步
实现双向绑定,相当于input+value
路由相关总结
全局路由前置守卫
介绍
目标:基于全局前置守卫,进行页面访问拦截处理
对于登陆后才能访问的页面进行拦截
配置
路由导航守卫 - 全局前置守卫
1.所有的路由一旦被匹配到,都会先经过全局前置守卫
2.只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容
router.beforeEach((to, from, next) => {
// 1. to 往哪里去, 到哪去的路由信息对象
// 2. from 从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
// 如果next()调用,就是放行
// next(路径) 拦截到某个路径页面
})
const authUrl = ['/pay', '/myorder']
router.beforeEach((to, from, next) => {
const token = store.getters.token
if (!authUrl.includes(to.path)) {
next()
return
}
if (token) {
next()
} else {
next('/login')
}
})
动态渲染
主页渲染
封装接口
封装准备接口 api/home.js
import request from '@/utils/request'
// 获取首页数据
export const getHomeData = () => {
return request.get('/page/detail', {
params: {
pageId: 0
}
})
}
页面调用
import GoodsItem from '@/components/GoodsItem.vue'
import { getHomeData } from '@/api/home'
export default {
name: 'HomePage',
components: {
GoodsItem
},
data () {
return {
bannerList: [],
navList: [],
proList: []
}
},
async created () {
const { data: { pageData } } = await getHomeData()
this.bannerList = pageData.items[1].data
this.navList = pageData.items[3].data
this.proList = pageData.items[6].data
}
}
接口数据结构
数据
传到子组件
把数据传到子组件
<div class="goods-list">
<GoodsItem v-for="item in proList" :key="item.goods_id" :item="item"></GoodsItem>
</div>
接收
搜索列表渲染
根据关键字搜索
1.计算属性,基于query 解析路由参数
computed: {
querySearch () {
return this.$route.query.search
}
}
2.根据不同的情况,设置输入框的值
<van-search
...
:value="querySearch || '搜索商品'"
></van-search>
3.页面中基于 goodsName 发送请求,动态渲染
data () {
return {
page: 1,
proList: []
}
},
async created () {
const { data: { list } } = await getProList({
goodsName: this.querySearch,
page: this.page
})
this.proList = list.data
}
<div class="goods-list">
<GoodsItem v-for="item in proList" :key="item.goods_id" :item="item"></GoodsItem>
</div>
分类id搜索
1 封装接口 api/category.js
import request from '@/utils/request'
// 获取分类数据
export const getCategoryData = () => {
return request.get('/category/list')
}
2.跳转传递
3 搜索页,基于分类 ID 请求
async created () {
const { data: { list } } = await getProList({
categoryId: this.$route.query.categoryId,
goodsName: this.querySearch,
page: this.page
})
this.proList = list.data
}
vuex相关
登录页面
存入vuex
1.新建 vuex user 模块 store/modules/user.js
export default {
namespaced: true,//开启命名空间
state () {
return {
userInfo: {
token: '',
userId: ''
},
}
},
mutations: {},
actions: {}
}
2.挂载到 vuex 上
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user,
}
})
3.提供 mutations
mutations: {
setUserInfo (state, obj) {
state.userInfo = obj
},
},
4.页面中 commit 调用
// 登录按钮(校验 & 提交)
async login () {
if (!this.validFn()) {
return
}
...
const res = await codeLogin(this.mobile, this.msgCode)
this.$store.commit('user/setUserInfo', res.data)
this.$router.push('/')
this.$toast('登录成功')
}
持久化处理
1.新建 utils/storage.js
封装方法
const INFO_KEY = 'hm_shopping_info'
// 获取个人信息
export const getInfo = () => {
const result = localStorage.getItem(INFO_KEY)
return result ? JSON.parse(result) : {
token: '',
userId: ''
}
}
// 设置个人信息
export const setInfo = (info) => {
localStorage.setItem(INFO_KEY, JSON.stringify(info))
}
// 移除个人信息
export const removeInfo = () => {
localStorage.removeItem(INFO_KEY)
}
2.vuex user 模块持久化处理
import { getInfo, setInfo } from '@/utils/storage'
export default {
namespaced: true,
state () {
return {
userInfo: getInfo()
}
},
mutations: {
setUserInfo (state, obj) {
state.userInfo = obj
setInfo(obj)
}
},
actions: {}
}
购物车
1.新建 modules/cart.js
模块
export default {
namespaced: true,
state () {
return {
cartList: []
}
},
mutations: {
},
actions: {
},
getters: {
}
}
2.挂载到 store 上面
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import cart from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
getters: {
token: state => state.user.userInfo.token
},
modules: {
user,
cart
}
})
3.封装 action 和 mutation
mutations: {
setCartList (state, newList) {
state.cartList = newList
},
},
actions: {
async getCartAction (context) {
const { data } = await getCartList()
data.list.forEach(item => {
item.isChecked = true
})
// 后台返回的数据中,不包含复选框的选中状态,为了实现将来的功能
// 需要手动维护数据,给每一项,添加一个 isChecked 状态 (标记当前商品是否选中)
context.commit('setCartList', data.list)
}
},
5.页面中 dispatch 调用
computed: {
isLogin () {
return this.$store.getters.token
}
},
created () {
if (this.isLogin) {
this.$store.dispatch('cart/getCartAction')
}
},
mapState - 渲染购物车列表
1.将数据映射到页面
import { mapState } from 'vuex'
computed: {
...mapState('cart', ['cartList'])
}
2.动态渲染
<!-- 购物车列表 -->
<div class="cart-list">
<div class="cart-item" v-for="item in cartList" :key="item.goods_id">
<van-checkbox icon-size="18" :value="item.isChecked"></van-checkbox>
<div class="show" @click="$router.push(`/prodetail/${item.goods_id}`)">
<img :src="item.goods.goods_image" alt="">
</div>
<div class="info">
<span class="tit text-ellipsis-2">{{ item.goods.goods_name }}</span>
<span class="bottom">
<div class="price">¥ <span>{{ item.goods.goods_price_min }}</span></div>
<CountBox :value="item.goods_num"></CountBox>
</span>
</div>
</div>
</div>
封装 getters - 动态计算展示
1.封装 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)
}
}
全选反选功能
1.全选 getters
getters: {
isAllChecked (state) {
return state.cartList.every(item => item.isChecked)
}
}
...mapGetters('cart', ['isAllChecked']),
<div class="all-check">
<van-checkbox :value="isAllChecked" icon-size="18"></van-checkbox>
全选
</div>
2.点击小选,修改状态
<van-checkbox @click="toggleCheck(item.goods_id)" ...></van-checkbox>
toggleCheck (goodsId) {
this.$store.commit('cart/toggleCheck', goodsId)
},
mutations: {
toggleCheck (state, goodsId) {
const goods = state.cartList.find(item => item.goods_id === goodsId)
goods.isChecked = !goods.isChecked
},
}
3.点击全选,重置状态
<div @click="toggleAllCheck" class="all-check">
<van-checkbox :value="isAllChecked" icon-size="18"></van-checkbox>
全选
</div>
toggleAllCheck () {
this.$store.commit('cart/toggleAllCheck', !this.isAllChecked)
},
mutations: {
toggleAllCheck (state, flag) {
state.cartList.forEach(item => {
item.isChecked = flag
})
},
}
相关功能
搜索栏搜索
点击搜索,或者下面搜索历史按钮,都要进行搜索历史记录更新 (去重,新搜索的内容置顶)
<div @click="goSearch(search)">搜索</div>
<div class="list">
<div v-for="item in history" :key="item" @click="goSearch(item)" class="list-item">
{{ item }}
</div>
</div>
goSearch (key) {
const index = this.history.indexOf(key)
if (index !== -1) {
this.history.splice(index, 1)
} // 如果原先存在,先删掉
this.history.unshift(key) // 存入搜索历史
this.$router.push(`/searchlist?search=${key}`)
}
加入购物车 - 唤起弹窗
<van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'"> // 定义title和showPannel方便切换
111
</van-action-sheet>
data () {
return {
...
mode: 'cart' // 默认是购物车
showPannel: false
}
},
注册点击事件,点击时唤起弹窗
<div class="btn-add" @click="addFn">加入购物车</div>
<div class="btn-buy" @click="buyFn">立刻购买</div>
addFn () {
this.mode = 'cart'
this.showPannel = true
},
buyFn () {
this.mode = 'buyNow'
this.showPannel = true
}
配置token
请求拦截器中,统一携带 token
// 自定义配置 - 请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
...
const token = store.getters.token
if (token) {
config.headers['Access-Token'] = token
config.headers.platform = 'H5'
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
结算功能
封装通用 API 接口 api/order
import request from '@/utils/request'
export const checkOrder = (mode, obj) => {
return request.get('/checkout/order', {
params: {
mode,
delivery: 0,
couponId: 0,
isUsePoints: 0,
...obj
}
})
}
通过购物车结算
1 跳转时,传递查询参数
<div @click="goPay">结算({{ selCount }})</div>
goPay () {
if (this.selCount > 0) {
this.$router.push({
path: '/pay',
query: {
mode: 'cart',
cartIds: this.selCartList.map(item => item.id).join(',')
}
})
}
}
2 页面中接收参数, 调用接口,获取数据
data () {
return {
order: {},
personal: {}
}
},
computed: {
mode () {
return this.$route.query.mode
},
cartIds () {
return this.$route.query.cartIds
}
}
async created () {
this.getOrderList()
},
async getOrderList () {
if (this.mode === 'cart') {
const { data: { order, personal } } = await checkOrder(this.mode, { cartIds: this.cartIds })
this.order = order
this.personal = personal
}
}
立即购买结算
1 点击跳转传参
<div class="btn" v-if="mode === 'buyNow'" @click="goBuyNow">立刻购买</div>
goBuyNow () {
this.$router.push({
path: '/pay',
query: {
mode: 'buyNow',
goodsId: this.goodsId,
goodsSkuId: this.detail.skuList[0].goods_sku_id,
goodsNum: this.addCount
}
})
}
2 计算属性处理参数
computed: {
...
goodsId () {
return this.$route.query.goodsId
},
goodsSkuId () {
return this.$route.query.goodsSkuId
},
goodsNum () {
return this.$route.query.goodsNum
}
}
3 基于请求时携带参数发请求渲染
async getOrderList () {
...
if (this.mode === 'buyNow') {
const { data: { order, personal } } = await checkOrder(this.mode, {
goodsId: this.goodsId,
goodsSkuId: this.goodsSkuId,
goodsNum: this.goodsNum
})
this.order = order
this.personal = personal
}
}
mixins 复用
作用
多个页面需要一个功能时,用mixins进行复用,使代码更简洁
流程
1 新建一个 mixin 文件 mixins/loginConfirm.js
export default {
methods: {
// 是否需要弹登录确认框
// (1) 需要,返回 true,并直接弹出登录确认框
// (2) 不需要,返回 false
loginConfirm () {
if (!this.$store.getters.token) {
this.$dialog.confirm({
title: '温馨提示',
message: '此时需要先登录才能继续操作哦',
confirmButtonText: '去登陆',
cancelButtonText: '再逛逛'
})
.then(() => {
// 如果希望,跳转到登录 => 登录后能回跳回来,需要在跳转去携带参数 (当前的路径地址)
// this.$route.fullPath (会包含查询参数)
this.$router.replace({
path: '/login',
query: {
backUrl: this.$route.fullPath
}
//route.fullPath 包含从基本URL后开始的路径,假设基本URL(base URL)为 /app,并且当前路由为 /app/home?a=1,那么route.path的值将是 /home?a=1;
})
})
.catch(() => {})
return true
}
return false
}
}
}
2 页面中导入,混入方法
import loginConfirm from '@/mixins/loginConfirm'
export default {
name: 'ProDetail',
mixins: [loginConfirm],
...
}
3 页面中调用 混入的方法
async addCart () {
if (this.loginConfirm()) {
return
}
const { data } = await addCart(this.goodsId, this.addCount, this.detail.skuList[0].goods_sku_id)
this.cartTotal = data.cartTotal
this.$toast('加入购物车成功')
this.showPannel = false
console.log(this.cartTotal)
},
goBuyNow () {
if (this.loginConfirm()) {
return
}
this.$router.push({
path: '/pay',
query: {
mode: 'buyNow',
goodsId: this.goodsId,
goodsSkuId: this.detail.skuList[0].goods_sku_id,
goodsNum: this.addCount
}
})
}
项目打包
介绍
脚手架开发后的代码不能参与上线,要将代码进行打包,才能上线。
打包是什么?
- 将多个文件压缩合并成一个文件
- 语法降级
- less sass ts 语法解析, 解析成css
如何打包
vue脚手架工具已经提供了打包命令,直接使用即可。
npm run build
配置
module.exports = {
// 设置获取.js,.css文件时,是以相对地址为基准的。
// https://cli.vuejs.org/zh/config/#publicpath
publicPath: './'
}
配置好以后,在本地就可以直接打开项目
懒加载
路由懒加载 & 异步组件, 不会一上来就将所有的组件都加载,而是访问到对应的路由了,才加载解析这个路由对应的所有组件
将路由文件中的代码改成以下样式:
const ProDetail = () => import('@/views/prodetail')
const Pay = () => import('@/views/pay')
const MyOrder = () => import('@/views/myorder')