Vue2积分商城PC端项目(六)

目录

一、导航守卫(导航拦截、路由拦截)

1.1、全局导航守卫

1.2、组件内部导航守卫

二、个人中心--购物车页面

2.1、个人中心左侧结构样式套用

2.2、重定向到购物车(二级路由)

2.3、购物车数据请求及渲染

三、404处理

四、全部商品中,滚动到底部加载更多

4.1、工具函数的使用

4.2、"节流"解决短时间内重复触发的问题

4.3、是否已经到底部

五、跨域配置

六、项目环境变量配置

七、项目总结

7.1、项目介绍

7.2、项目技术点


一、导航守卫(导航拦截、路由拦截)

文档地址: 导航守卫 | Vue Router

路由拦截(导航守卫:前置导航守卫和后置导航守卫)

前置导航守卫有三个参数

to 表示即将进入的路由

from 表示即将离开的路由

next() 表示执行进入这个路由

1.1、全局导航守卫

在router/index.js中:

// 全局导航守卫
router.beforeEach((to, from, next)=>{
  // 有token就表示已经登录
  // 想要进入个人中心页面,必须有登录标识token
  // console.log('to:', to)
  // console.log('from:', from)
  if(to.path=='/user'){
     	let token = localStorage.getItem('x-auth-token')
        // 此时必须要有token
        if(token){
          next(); // next()去到to所对应的路由界面
        }else{
          // 提示没有登录
          store.dispatch("showToast/asyncChanIsShowToast",{
              msg: "你还没有登录!",
              type: "danger",
          })
        }
        return; // 需要些return,防止执行完上面的next(),还继续执行下面的next()
  }
  // 如果不是去往个人中心的路由,则直接通过守卫,去到to所对应的路由界面
  next()
})
export default router

1.2、组件内部导航守卫

全局导航守卫是每一次改变路由都会触发的,然而,目前我们只需要到个人中心页面才触发,所以我们在组件内部书写导航守卫:

User.vue组件中

import store from "@/store"
...
beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    let token = localStorage.getItem("x-auth-token");
    if(token){
      next()
    }else{
      // 提示没有登录
      store.dispatch("toastStatus/asyncChanIsShowToast",{
      msg:"请先登录",
      type:"warning"
    })     
    }
},

二、个人中心--购物车页面

2.1、个人中心左侧结构样式套用

把今天其他文件夹中的User.vue组件替换到项目views目录中

处理头像和用户名的展示

import {mapState} from "vuex";
...
computed:{
    ...mapState({
    	userInfo:state=>state.userInfo.userInfo
    })
},

2.2、重定向到购物车(二级路由)

备注:二级路由的两种表示方式:(在以及路由的基础上)
        path:'cart',
        path:'/user/profiles',

  <!-- 二级路由的出口 -->
<article><router-view></router-view></article>

需求:一进入到个人中心页面,就到购物车界面,

分析:购物车是个人中心众多页面下的一个,我们会把它做成子路由

先准备购物车组件:在components下新建user文件夹,新建cart.vue组件

router/index.js下配置重定向到 /user 的子路由:

  {
    path: '/user',
    name: 'User',
    component: () => import('../views/UserView.vue'),
    // 二级路由的两种表示方式
    children:[
      {
        path:'cart',
        component:()=>import('../components/user/CartBar.vue')
      },
      {
        path:'/user/profiles',
        component:()=>import('../components/user/ProfileBar.vue')
      },
    ]
  },

套用购物车组件:直接把今天其他文件夹中的user文件夹替换到项目components目录中

2.3、购物车数据请求及渲染

api.js中:

// 请求购物车数据
export const CartDataApi = () => request.get(`/shop/carts`);

cart.vue组件中:

			<td>
            <section>
              <img
                width="84"
                :src="imgBaseUrl+item.coverImg"
                alt="列表图片"
              />
              <div class="info">
                <h5>{{item.title}}</h5>
                <p>{{item.versionDescription}}</p>
              </div>
            </section>
          </td>
          <td>{{item.coin}}鸡腿</td>
          <td>
            <div class="step">
              <span>-</span>
              <input type="text" disabled v-model="item.total" />
              <span>+</span>
            </div>
          </td>
          <td>{{item.coin * item.total}}鸡腿</td>
          <td>
            <span class="del">删除</span>
          </td>
<script>
import {CartDataApi} from "@/request/api";
export default {
  data() {
    return {
      ...
      // 购物车数组
      cartArr:[]
    };
  },
  created(){
    CartDataApi().then(res=>{
        if(res.code===0){
            this.cartArr = res.data
        }
    })
  }
};
</script>

三、404处理

新建Error.vue组件:写入提示信息

在路由表中配置剩余地址对应Error组件:

,
  {
    path: '*',
    name: 'Error',
    component: () => import('../views/Error.vue')
  }

四、全部商品中,滚动到底部加载更多

Goods.vue中:

<List :arr="goodsListShow" :maxLength="28" />
      <p style="text-align:center;margin-top:20px">
        {{isReallyBottom?"没有数据了":"正在加载..."}}
      </p>

4.1、工具函数的使用

把三个函数(带兼容性的写法)写在utils文件夹的index.js作为工具函数引用: 用export暴露

在要使用的页面借助import引入,使用时在对应名字后加上()

import { getScrollTop, getClientHeight, getScrollHeight } from "../utils";

  // if(窗口高度+超出窗口的页面高度>=页面高度-20)

 if (getClientHeight() + getScrollTop() >= getScrollHeight() - 20) {。。。}

utils文件夹的index.js中(export暴露)

//获取滚动条当前的位置 
function getScrollTop() {
    var scrollTop = 0;
    if(document.documentElement && document.documentElement.scrollTop) {
        scrollTop = document.documentElement.scrollTop;
    } else if(document.body) {
        scrollTop = document.body.scrollTop;
    }
    return scrollTop;
  }

//获取当前可视范围的高度 
function getClientHeight() {
    var clientHeight = 0;
    if(document.body.clientHeight && document.documentElement.clientHeight) {
        clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight);
    } else {
        clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
    }
    return clientHeight;
}

//获取文档完整的高度 
function getScrollHeight() {
    return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
}
     

4.2、"节流"解决短时间内重复触发的问题

节流:利用一个变量,控制代码在一段时间内(setTimeout)不会重复触发执行

在此处的应用:商品列表滚动的监听。

一页8条数据,随着页面的滚动加载后面的数据,借助setTimeout设置后面页面数据出现频率

Goods.vue中:

 //加载更多...(第一页:展示8条数据【下标0~7】)

//第1次滚动到底部,要加载第2页的数据,i是从下标为8~15

//第2次滚动到底部,要加载第3页的数据,i是从下标为16~23......

<script>
import {getScrollTop,getClientHeight,getScrollHeight} from "@/utils"
...
data(){
    return{
        // 产品列表
      goodsList: [],
      // 用来展示的产品列表
      goodsListShow: [],
      // 默认展示第一页
      page:1,
      // 每页8条
      size:8,
      // false 表示没有正在加载
      isLoading: false,
      //是否已经到低
      isReachBottom:false,
     
    }
},
 ...  
 	getGoodList() {
      GoodListAPI({
        ...
      }).then((res) => {
        ...
		// 在请求到数据之后,先展示前8条
        this.goodsListShow=this.goodsList.filter((item,index)=>index<8)
      });
    },
	scrollFn() {
      // 滚动就执行这里的代码频繁触发事件
      console.log(getClientHeight() + getScrollTop() == getScrollHeight() + 1);
      // console.log("页面正在滚动");
      // 如果滚动到底部的时候,
      // if (到底部了) {
      // if (窗口高度+scrollTop>=页面文档高度-20) {
      if (getClientHeight() + getScrollTop() >= getScrollHeight() - 20) {
        
        // 需要this.isLoading为false才能进行加载
        if (!this.isLoading) {
          // this.isLoading避免了重复触发这个到底了加载数据事件
          this.page++;
          this.isLoading = true;
          setTimeout(() => {
            // 往goodsListShow这个数组去push下一页的数据
            // 从goodsList数组中去  this.page页的数据 push到goodsListShow
            for (var i = this.size * (this.page - 1);i < this.size * this.page;i++) {
              //this.goodsList[i]必须有这个值,才能push到展示的数组里面去
              this.goodsList[i]
                ? this.goodsListShow.push(this.goodsList[i])
                : "";
            }
            this.isLoading = false;
          }, 500);
        }
      }
    },
  },
  mounted() {
    // 监听滚动
    window.addEventListener("scroll", this.scrollFn);
  },
  beforeDestroy() {
    // 取消监听
    window.removeEventListener("scroll", this.scrollFn);
  },
 </script>                                                                      

4.3、是否已经到底部

结构完善:isReachBottom

<p style="text-align: center; margin-top: 20px">
    {{ isReachBottom ? "已经没有数据了" : "正在加载... ..." }}
</p>
<script>
...
// 定义是不是已经没有数据了
isReachBottom: false,
    ...
    if (getClientHeight() + getScrollTop() >= getScrollHeight() - 20) {
            if (this.goodsListShow.length >= this.goodsList.length) {
              // 没有数据了
              this.isReachBottom = true;
              return;
            }
</script>

最后解决选项切换时候的bug

async goodsSearch(){
    	... ...
    	// 初始化数据
		this.isReachBottom = false;
      	this.page = 1;
        // 判断是不是已经没有数据了
        if (this.goodsListShow.length >= this.goodsList.length) {
          // 每次请求到数据,把数据把页数和是否到底部初始化一下
          this.isReachBottom = true;
        }
}

五、跨域配置

我们对 vue.config.js 进行配置:

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: "http://kumanxuan1.f3322.net:8881/cms",
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}

request.js中:

const instance = axios.create({
    baseURL: "/api",
    timeout: 5000
})

记得配置完需要重启服务器!!

六、项目环境变量配置

项目目录下新建两个文件,分别是开发环境和生产环境下的两个不同配置

.env.dev

NODE_ENV=development
VUE_APP_BASE_URL=http://192.168.113.249:8081/cms
VUE_APP_STATE_URL=http://127.0.0.1:8080

.env.prod

NODE_ENV = production
VUE_APP_BASE_URL = http://kumanxuan1.f3322.net:8881/cms
VUE_APP_STATE_URL="后端给的地址"

在package.json中修改启动命令:

"serve": "vue-cli-service serve --open --mode dev",
"servepro": "vue-cli-service serve --open --mode prod",

在vue.config.js中换成:

'/api': {
    target: process.env.VUE_APP_BASE_URL,
    pathRewrite: {
        '^/api': ''
    }
}

七、项目总结

7.1、项目介绍

  这是一个由vue-cli搭建的PC端SPA商城,该商城主要涉及登录注册、商品列表、商品详情、个人中心、购物车及商品检索等主体功能。

7.2、项目技术点

  1. 使用vue-cli搭建项目,并结合蓝湖+PS进行页面切图,实现对设计稿的高保真还原;

  2. 使用axios进行数据请求,并对其进行请求拦截器响应拦截器封装;

  3. 封装所有请求的api,统一管理项目所有的请求路径

  4. 鉴权,认证机制采用手机+验证码、手机+密码及微信扫码登录认证,其中微信扫码登录结合环境变量,调用后端接口实现平台切换验证;

  5. 使用localStoragetoken进行存储;

  6. 使用原生JS在组件mounted中监听滚动,并实现向下滚动加载更多;

  7. 使用组内导航守卫对每个进入个人中心页的路由进行拦截,判断路径后保证有token方能进入该路由;

  8. 使用路由监听解决路由跳转而页面不跳转的问题;

  9. 给组件绑定key属性,通过修改key值来进行组件重载

备注:源码gie地址:https://gitee.com/wu-xi456258/points-mall.git

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值