我用vue开发了一个动态网站--百宝阁 万字长文(spa电商,首页没有做动态,搜索页是动态)

news2024/11/18 3:33:57

一、前言

学习前端已有大半年了,虽然其中备考软件设计师考试花了两个月,但我还是收获颇丰,从最开始的html,到css,js,在到es6,promise,ajax,node.js、vue、webpack我已经有较为靠谱的编码习惯,亲身接触并实行语义化,模块化,注重复用性的项目,了解了节流,防抖,图片懒加载,路由的动态引入,npm,yarn的常用语句,linux常用命令,nginx的首次使用,整个网页从制作到上线腾讯云的linux服务器,在到域名的注册和ssl的申请,当然这次的网站我没有注册域名,就简单的配置了下nginx反向代理。这次网页的开发很好的增长我的编码经验。

二、项目里值得讲的一些点

一、路由的动态引入

在接触这个项目以前,我以前只知道常规的import 引入,相关的知识也就是什么默认暴露,分别暴露啊,引入的时候可以解构啊,在往远了讲,还会个commandjs里的require引入,什么动态引入,真没接触过。

普通写法:import  myorder from "@/pages/Center/myorder"
//不管现在要不要用直接引入
动态写法:()=>{
 return import("@/pages/Center/myorder")
 }
 好处:需要用到这个组件的时候在引入,极大节省了内存和空间,可以减少dist文件的大小,

进一步简写:
直接在路由里注册时引入的 
component:()=>import("@/pages/Center/myorder"),

二、js的执行顺序,异步和同步

js的执行顺序,先同步后异步, 异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列, Promise 里边的代码属于同步代码,.then() 中执行的代码才属于异步代码。
常见的宏任务:ajax、setTimeout、setIntervar、requestAnimationFrame、messageChannel、UI 渲染、setImmediate
常见的微任务:promise.then、queueMicrotask(基于 then)

项目里的bug

项目里有个给购物车设置数量的按钮,在我设置了正则表达式,以及blur事件限制他不能为负数后,这里设置change没用,我也不知道为啥,然后我当时上线项目之前根本没有发现问题,因为如果你正常频率的点击减1,购物车肯定不会出现负数,但这只防正人君子,在我首次上线后,便分享给我的同学,让他去试下支付接口,然后这家伙可能也是无聊,也算严谨,疯狂给我点击减少,然后竟然出现了负数,他截图给我发来的时候我都惊呆了!

在这里插入图片描述

也就是说,如果你点击的频率很快,这个数量会变成负数,然后我就郁闷了,
然后就试了各种方法,什么节流和防抖,但因为服务器本身反应慢,在加这玩意就很不怎么流畅,(在组件里加防抖是没有任何效果的,但在actions里面写防抖的确有效)当时我还特意重新研究了下我10多天前写的源码,不得不说,才仅仅10天前写的我就基本不怎么记得了!!!然后回忆大法之后,发现判断是否为负数的来源不严谨
来源:

...mapGetters(["carList"]),
    carInfoList() {
      return this.carList.cartInfoList || [];
    },

这里是通过actions里面向服务器发请求,拿到的现在购物车改商品的数据,然后存在state里,然后如果正常来讲我发一个减少1的请求,你更新一下,啊我就减1,但是如果你不是正人君子,疯狂点减少,然后会出现一种情况,你这边上一次的减1的请求还没有返回,跟更新到vuex里,数据还是上一次的,而你这边又请求-1,然后他拿着你本应该-1的数据去判断,而你没有-1,就会造成一种数据没有更新,请求又发了的糟糕情况,而我这里用一个timer去判断是不是上一次请求结束没有是可以的,但是因为定时器的加入会有点卡,于是我就想了个野路子,就是商品在减到只剩一件的时候,会有个弹框提示商品只剩一件,直接打断施法,等弹框取消后,vuex早就更新了,根据我的判断语句,为1的时候,你点-1,也是为加个0,就出现不了负数了。为此我还特意研究了什么js的执行顺序,哈哈由于服务器性能,就还是没有写防抖。

判断

if (disNum == -1) {
          disNum = cart.skuNum > 1 ? -1 : 0;
        }

三、服务器接口的使用(vuex和直接在组件里发请求)

第一阶段发axios请求数据或者mock数据

这里写了个拦截相应器,加了给进度条的插件

真正的发请求

import axios from "axios"
import nProgress from "nprogress"
//引入store
import store from "@/store"
import "nprogress/nprogress.css"
const request=axios.create({
    //设置路径后默认追加的路径
    baseURL:"/api",
    timeout:5000
})
//请求拦截器,发请求前可以做一些事情
request.interceptors.request.use((config)=>{
    //config中有header
    //进度条开始
    if(store.state.detail.uuid_token){
        config.headers.userTempId=store.state.detail.uuid_token
    }
    if(store.state.user.token){
        config.headers.token=store.state.user.token
    }
    nProgress.start()
    return config
})

//响应的拦截器
request.interceptors.response.use((res)=>{
  //成功的回调
  //进度条结束
  nProgress.done()
  return res.data
},(err)=>{
    return Promise.reject(new Error("faile"))
})
export default request

使用具体的接口

// 商品详情的数据
 export const reqGoodsInfo=(goodsId)=>{
  return request({url:`/item/${goodsId}`,method:"get"})
 }

 //商品添加到购物车
 export const reqAddOrupdataShopCar=(skuId,skuNum)=>{
  return request({url:`/cart/addToCart/${skuId}/${skuNum}`,method:"post"})
 }

 //购物车请求数据

 export const reqCarlist=()=>{
  return request({url:"/cart/cartList",method:"get"})
 }

第二阶段在vuex的action中使用写好的请求数据方法,通过mutations改变state里的值

async getGoodsInfo({commit},info){
        let result= await reqGoodsInfo(info)
        if(result.code==200){
            commit("GETGOODSINFO",result.data)
        }
    },
 GETGOODSINFO(state,info){
        state.goodsInfo=info
    }

第三阶段,通过mapstate来方法获取数据

...mapGetters(["carList"])

根据自己的业务逻辑使用数据,特别是分类和搜索页商品展现特别需要用到这个
在这里插入图片描述

在这里插入图片描述

四、微信二维码的生成(支付接口的使用)

首先得用到elementui的弹框组件
这里接需要安装element,以及按需引入
elementui的使用

import { MessageBox } from 'element-ui

二维码生成器 qrcode插件的使用

qrcode

支付二维码数据的请求,如三一致,组件通过dispatch发actions请求,actions通过引入的axios函数向服务器发请求,然后通过mutation改变state里的数据,然后在通过getters简化,最后通过mapgetters引入url,给插件。请添加图片描述

支付后立即跳到成功页面,然后看历史的订单上面显示的是支付成功。
为了方便,我还是在支付遇到问题点击后会出现一个兑换码输入框,输入123,就可以跳到成功页面,但历史记录显示未支付。

五、token和uuid的使用

说到登录和注册,以及支付就免不了这个用户信息的验证啊,
我这里其实用的也是插件,游客用的是uuid的插件,服务器就对游客不做登记,通过seesionstore来存储一些购物车信息,正式用户就把改的数据发给服务器存储,token是通过发axios请求来的。

六、图片的懒加载和放大镜的功能

图片的懒加载是插件

VueLazyload

放大镜:

功能源码

handler(event){
        let mask=this.$refs.mask;
        let big=this.$refs.big
        let left=event.offsetX-mask.offsetWidth/2
        let top=event.offsetY-mask.offsetHeight/2
        if(left<=0){
          left=0
        }
        else if(left>mask.offsetWidth){
          left=mask.offsetWidth
        }
        if(top<0){
          top=0
        }
        else if(top>mask.offsetHeight){
          top=mask.offsetHeight
        }
        mask.style.left=left+"px"
        mask.style.top=top+"px"
        //为什么是负的呢,是因为鼠标在小图上从左往右移动,大图就是从右往左移动
        big.style.left=-2*left+"px"
        big.style.top=-2*top+"px"
      }

在这里插入图片描述

七、手写的分页

分页器其实可以不用手写,element里有,但是为了练手我还是自己写了给组件,样式没放啊,核心就是保证有continues个连续的,看1,2,最后需不需要显示

<template>
     <div class="pagination" >
        <button :disabled="pageNo==1" @click="$emit('getPageNo',pageNo-1)" >上一页</button>
    <button v-if="start>1" @click="$emit('getPageNo',1)" :class="{active:1==pageNo}">1</button>
    <button v-if="start>2" @click="$emit('getPageNo',2)" :class="{active:2==pageNo}">2</button>
    <button v-if="(start-1)>2">···</button>

    
    <button v-for="(page,index) in end" v-show="page>=start" :key="index" @click="$emit('getPageNo',index+1)" :class="{active:(index+1)==pageNo}">{{ page }}</button>
    <button v-if="end+1<totalPages">···</button>
    <button v-if="end<totalPages" @click="$emit('getPageNo',totalPages)" :class="{active:totalPages==pageNo}"> {{ totalPages }}</button>
    <button :disabled="pageNo==end" @click="$emit('getPageNo',pageNo+1)"> 下一页</button>
   
    <button style="margin-left: 30px">总共{{total}}条数据</button>
  </div>

</template>

<script>
//  pageNo 当前的页数 当前第几个
//pageSize 一页展示多少数据
//  totalPages 总共有多少页
//continues 连续分页的连续页码
export default {
    name:"pagination",
    methods:{
      
    },
    computed:{
        totalPages(){
            return Math.ceil(this.total/this.pageSize)
        },
        locationPage(){
            let start=1,end=1
            if(this.continues>this.totalPages){
                start=1
                end=this.totalPages
            }
            else{
                if (this.pageNo+parseInt(this.continues/2)>this.totalPages) {
                    start=this.totalPages-this.continues+1
                    end=this.totalPages
                }
                else if(this.pageNo-parseInt(this.continues/2)>=1){
                    start=this.pageNo-parseInt(this.continues/2)
                    end=this.pageNo+parseInt(this.continues/2)
                }
                else if (this.pageNo-parseInt(this.continues/2)<1) {
                    start=1
                    end=this.continues
                }
                else if( this.pageNo+parseInt(this.continues/2)<=this.totalPages){
                    end=this.pageNo+parseInt(this.continues/2)
                }
                
            }
            return {start,end}
        },
        start(){
            return this.locationPage.start
        },
        end(){
            return this.locationPage.end
        }
    },
    props:["pageNo","pageSize","total","continues"]
}

八、路由守卫实现购物网站应该的业务逻辑

一个合格的网站是需要一定访问限制的,比如你一个游客最多拥有查看商品和加入购物车的权限,不可能有付款和查看支付记录的权限,你都没登录怎么付款?所以就需要用到路由守卫,这里的用到了全局前置,独享路由的守卫,组件内守卫。
上述的付款多是用独享
登录后,不能在进登录页面是全局
支付成功是组件内守卫
如果

//路由组件更新的时候,如果只是params等参数的变化,组件不变,此组件会被复用
      beforeRouteUpdate(to,form,next){
        next()
      }

全局

router.beforeEach(async(to,from,next)=>{
    let token=store.state.user.token
    let name=store.state.user.userInfo.name
   if(token){
    if(to.path=="/login"||to.path=="/register"){
        next("/home")
    }
    else{
        if(name){
            next()
        }
        else{
            try {
                //獲取用戶信息的
              await  store.dispatch("getUserInfo")
              next()
            } catch (error) {
                //token過期
               await store.dispatch("userLogOut")
               next('/login')
            }
        }
    }
   }
   //没有登录不能去交易和交易记录,支付成功
   else{
    let topath =to.path  
    if(topath.indexOf("/trade")!=-1||topath.indexOf("/pay")!=-1||topath.indexOf("/center")!=-1){
        next("/login?redirect="+topath)
    }
    else{
        next()
    }
   }
  
})

这里我还加了个滚动默认为Y:0

scrollBehavior(to,from,savePosition){
        return {y:0}
    }  

和通过meta属性来限制,foot组件,搜索框什么时候出现

九、linux使用的注意点

1、nginx方向配置和www文件存放的地方

使用xftp7的时候不要把www文件放到root下,放到var是个不错的选择、在这里插入图片描述

2、使用xshell修改nginx的配置

修改nginx.conf里面的
在这里插入图片描述

3、图片命名的要求

刚上线时,部分图片无法正常加载,可在本机客户端运行是可以的,于是我就研究哪些不能加载的图片的共性,结果发现他们命名中都有中文,我估计是编码错误,于是改成中文之后,果然可以。

十、重写vue的push和repalce方法

let originPush=VueRouter.prototype.push;
let orignreplace=VueRouter.prototype.replace;
VueRouter.prototype.replace=function(location,resolve,reject){
    if(resolve && reject){
        orignreplace.call(this,location,resolve,reject)
    }
    else{
        orignreplace.call(this,location,()=>{},()=>{})
    }
}
VueRouter.prototype.push=function(location,resolve,reject){
    if(resolve && reject){
        originPush.call(this,location,resolve,reject)
    }
    else{
        originPush.call(this,location,()=>{},()=>{})
    }
}

因为在push的时候是要返回一个promise的,所以重写push和replace方法。

这里感谢尚硅谷提供的所有api

三、项目业务逻辑和组件的规划

一、业务逻辑

在这里插入图片描述

二、组件的规划

vue开发者工具的展现
在这里插入图片描述
首页
请添加图片描述

搜索页和其他页面就是中间这个大组件的切换,和footer,搜索框是否显示

四、项目展示

一、运行截图

1、首页

请添加图片描述

2、注册

在这里插入图片描述

3、登录

在这里插入图片描述

4、搜索

支持按价格排序,搜索,和按页数
在这里插入图片描述

在这里插入图片描述

5、详情

在这里插入图片描述

6、购物车

在这里插入图片描述

7、确认收货人和地址

在这里插入图片描述

8、付款

在这里插入图片描述

9、付款成功

在这里插入图片描述
10、查看购物记录
在这里插入图片描述

点这里访问 百宝阁

五、自我推荐

大三到大四的暑假想找个互联网公司,网友有推荐的吗。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue实战第4章:主页设计之中部内容设计

前言 本篇在讲什么 接上篇文章&#xff0c;我们制作了一个自定义的网页导航栏&#xff0c;本篇文章我们简单制作一个内容页 仅介绍简单的应用&#xff0c;仅供参考 本篇适合什么 适合初学Vue的小白 适合想要自己搭建网站的新手 适合没有接触过vue-router的前端程序 本篇…

Vue常用指令及声明周期

文章目录知识点前端开发环境配置v-text && v-htmlv-if、v-else && v-showv-forv-onv-modelv-bind、v-cloak、v-pre&&v-once全局 API 是什么Vue.directive 自定义组件Vue.directive 是什么自定义组件回调函数参数自定义组件的生命周期Vue.set 全局操作为…

【Kafka】一.认识Kafka

kafka是一个分布式消息队列。由 Scala 开发的高性能跨语言分布式消息队列&#xff0c;单机吞吐量可以到达 10w 级&#xff0c;消息延迟在 ms 级。具有高性能、持久化、多副本备份、横向扩展能力。 生产者往队列里写消息&#xff0c;消费者从队列里取消息进行业务逻辑。 一般在…

Unity对接接口丨简单教学丨UnityWebRequest

新手制作接口对接前言使用过程Postman测试第一次测试第二次测试第三次测试第四次测试第五次测试第六次测试总结前言 提示&#xff1a;大体介绍今日功能介绍 介绍大概UnityWebRequest对接接口方式,博主也是作为刚开始对接口的使用&#xff0c;相当详细。 使用过程 这里为内容…

你知道IP属地是怎么来的?

在互联网高速发展的时代&#xff0c;登录网络使用网络的过程当中&#xff0c;会存在非常独特的IP属地&#xff0c;这个独特的概念就是在互联网不断发展过程当中&#xff0c;对于大家来说非常熟悉而又陌生的一个必要设备&#xff0c;在使用各种电子设备上网的时候&#xff0c;都…

Flutter For Web实践

1 什么是Flutter Flutter是Google开源的一套UI工具包&#xff0c;帮助开发者通过一套代码库高效构建多平台精美应用&#xff0c;支持移动APP、web、桌面和嵌入式平台。Flutter和其他的跨平台解决方案的实现方式上有比较大的差异。 我们以React Native&#xff08;下文简称RN&…

kubernetes教程 --组件详细介绍

组件详细介绍 NameSpace 在 Kubernetes 中&#xff0c;名字空间&#xff08;Namespace&#xff09; 提供一种机制&#xff0c;将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一&#xff0c;但跨名字空间时没有这个要求。 名字空间作用域仅针对带有名字…

STM32单片机初学8-SPI flash(W25Q128)数据读写

当使用单片机进行项目开发&#xff0c;涉及大量数据需要储存时&#xff08;例如使用了屏幕作为显示设备&#xff0c;常常需要存储图片、动画等数据&#xff09;&#xff0c;单靠单片机内部的Flash往往是不够用的。 如STM32F103系列&#xff0c;内部Flash最多只能达到512KByte&a…

git安装与学习1(CSDN_0010_202201014)

目录 1. Git下载 2. git安装 3. 生成并添加SSH公钥 3.1 生成公钥 3.2 添加公钥 4. 代码备份 4.1 创建远程仓库 4.2 创建本地仓库 1. Git下载 Git下载官网&#xff1a;Githttps://git-scm.com/ 详细的下载教程见&#xff1a; https://blog.csdn.net/weixin_47638941/ar…

CHAPTER 5 自动发现、自动注册、分布式监控、SNMP监控

自动发现与自动注册5.1 自动发现与自动注册5.1.1 简介5.1.2 两种模式5.2 自动发现--被动模式5.3 自动注册--主动模式5.4 分布式监控5.4.1 介绍5.4.2 配置zabbix proxy5.5 SNMP监控5.5.1 使用范围5.5.2 安装snmp程序5.5.3 配置snmp程序5.5.4 测试snmp5.5.5 在web界面进行配置5.1…

LeetCode 203. 移除链表元素

原题链接 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给你一个链表的头节点 headheadhead 和一个整数 valvalval &#xff0c;请你删除链表中所有满足 Node.valvalNode.val valNode.valval 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&a…

如何利用Power Virtual Agents机器人获取OneNote教学资源

今天我们继续来介绍如何利用PVA聊天机器人来获取OneNote教学资源。设计思路是在PVA聊天机器人的对话框中输入触发短语后&#xff0c;PVA聊天机器人会将指定的OneNote教学资源发送到学生的电子邮箱中。 首先&#xff0c;在PVA聊天机器人中创建主题。 创建一个“问题”后&#x…

【Spring AOP】Spring AOP快速使用

文章目录前言1、相关注解类注解方法注解2、execution 表达式例一【execution 表达式 的限制使用】例二【execution 表达式 的通配使用】3、相关pom依赖4、案例创建接口Aop使用 方式一【Before 、After 、AfterRuturning、AfterThrowing版】Aop使用 方式二【Around版】前言 Aop…

【笔记】通过labview调用halcon

使用原因&#xff1a;手眼标定使用halcon的九点标定&#xff0c;但是整体系统是用labview的&#xff0c;机器人得到二维坐标后&#xff0c;需经过halcon算子计算得到机器人坐标系下坐标&#xff0c;在进行运动。 下载路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s…

「计算机组成原理」数据的表示和运算(上)

文章目录一、进位计数制1.1 其他进制转十进制1.2 十进制转其他进制1.3 二进制、八进制和十六进制1.3 真值和机器数二、BCD码2.1 8421码2.2 余3码2.3 2421码三、整数的表示和运算3.1 无符号整数3.1.1 无符号整数的表示3.1.2 无符号整数的运算3.2 有符号整数3.2.1 有符号整数的表…

聚观早报 | 微信也要下场做“外卖”?;《羊了个羊》营收破亿

今日要闻&#xff1a;微信也要下场做“外卖”&#xff1f;;《羊了个羊》营收破亿&#xff1b;苹果将新XR头显上市推迟至6月&#xff1b;“交个朋友”被爆欠薪暴力裁员&#xff1f;&#xff1b;微软解释ChatGPT Bing AI推出速度缓慢的原因微信也要下场做“外卖”&#xff1f;腾讯…

C语言返回类型为指针的一些经典题目(上)

大家好&#xff0c;今天和大家分享一下C语言返回类型为指针的一些重要题目&#xff0c;看完你会恍然大悟。原来我对指针的了解还停留在指针只是一个地址的位置上&#xff0c;看完后你会对指针的用法进一步得到提升。目录一.关于指针类型的基础概念二.题目剖析一.关于指针类型的…

Java反序列化漏洞——CommonsCollections6链分析

一、前因因为在jdk8u71之后的版本中&#xff0c;sun.reflect.annotation.AnnotationInvocationHandler#readObject的逻辑发生了变化&#xff0c;导致CC1中的两个链条都不能使用&#xff0c;所有我们需要找一个在高版本中也可用的链条。/* Gadget chain: java.io.ObjectInputStr…

35岁危机

人们对社会的期望是不断变更的&#xff0c;无论拥有高技能的人还是普通的白领&#xff0c;这种期望都让人们不断地励磁进步&#xff0c;以期实现自己的理想。但是&#xff0c;当人们达到35岁时&#xff0c;多数人就会陷入一种状态&#xff0c;这被称之为“35岁危机”。 在35岁…

时间轮和时间堆管理定时器

高性能定时器 时间轮 由于排序链表定时器容器有这样一个问题&#xff1a;添加定时器的效率偏低。而即将介绍的时间轮则解决了这个问题。一种简单的时间轮如下所示。 如图所示的时间轮内&#xff0c;指针指向轮子上的一个slot&#xff08;槽&#xff09;&#xff0c; 它以恒定…