vue2自创项目——饭搭子项目总结


本篇主要总结出现的问题和一些解决方法

问题

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 轻提示

两种使用方式

  1. 导入调用 ( 组件内非组件中均可 )
import { Toast } from 'vant';
Toast('提示内容');
  1. 通过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')
  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值