尚硅谷商品汇项目复习文档

项目资料

项目源码:https://gitee.com/HusePanghu/project-SP
项目地址:HusePanghu.SPH


前言

提示:本文档的编撰初衷是用于复习和回顾该项目,而非该项目的教程文档,弊处多多,敬请包涵。欢迎大家在评论区交流。


1、各种基础文件介绍     

node_modules:存储项目依赖文件
public:存放项目静态资源,注意:当项目打包时,webpack会把public文件夹原封不动的打包到dist文件中
src:程序员代码文件夹
assets:组件中的静态资源文件夹,一般存放组件公用的静态资源;在webpack打包的时候会将assets作为一个模块,打包到一个js文件夹中。
components:组件文件夹,存放vue中的各个组件。(一般是非路由组件)
App.vue:唯一的一个根组件
main.js:项目的入口文件,也是项目中最先执行的一个文件。

babel.config.js:与Babel相关的配置文件
package.js:相当于项目‘身份证’,记录着项目的名称,项目有哪些依赖,项目怎么运行。
package-lock.json:缓存性文件

2、项目的一些其他配置:

        1、项目启动 npm run serve 之后自动打开网页(localhost:8080)
        package.json

"scripts": {"serve": "vue-cli-service serve --open","build": "vue-cli-service build","lint": "vue-cli-service lint"}

         2、 关闭eslint校验提示
                在vue.config.js文件夹中 设置 lintOnSave:false

//关闭lint校验提示
lintOnSave:false,

        3、src文件夹设置别名 @ ,src文件夹创建别名,src/...简化为@/...

 "paths": {"@/*": ["src/*"]},

3、项目路由的分析:

        1、vue-router
                何为路由:kv键值对
                key:路由组件的路径
                value:路由组件

        2、项目中的路由组件:
                home组件、search组件、login组件、register组件
        3、项目中的非路由组件:
                header组件、footer组件(在home、search组件中显示,在login、regist中不展示)

4、项目流程

        项目以开发业务和逻辑为主,css和HTML开发为次
                1、编辑静态页面、确定页面样式
                2、拆分组件
                3、获取服务器的动态展示数据
                4、完成相应的动态业务逻辑
        注意事项:1、编辑静态页面时,组件的结构+组件的样式+图片资源,编辑不要有遗漏 2、本项目中样式使用的是less样式,在<style>中需标注lang='less',还需npm 安装less-loader 3、当样式编辑好之后,记得得清除默认样式,否则编辑的样式可能被覆盖而显示不出来。 public/index.html <link rel="stylesheet" href="./reset.css">

5、项目路由的设定 vue-router

        1、路由组件一般放置在pages||views文件夹中,components放置的为非路由组件
        2、部分路由组件:home、search、login、register
        3、设置路由器router,暴露并注册到main.js文件中;在路由器中注册路由组件
        4、路由配置文件一般放在router文件中
        5、当路由注册完成,不管是路由组件还是非路由组件,他们身上都有$router、$route属性
                $router:一般进行编程式路由导航跳转(push、replace)
                $route:一般获取路由信息(params、query、路径等信息)
        6、声明式导航<router-link>,编程式导航push\replace 声明式导航功能<编程式导航功能

6、Footer的显示与隐藏,mate路由元信息

 routes:[{path:''',components:,mate:{show:false}}]

        v-show="$route.meta.show"

7、路由传参

        传参的类型:params传参、query传参
                params参数:属于路径中的一部分,注意在路由中配置路径是需要占位,占位符 ‘:‘
                query参数:不属于路径的一部分,不需要占位,类似于ajax中的queryString,/home?k=v&kv=
        三种方式:字符串传参、模板字符串、对象传参

        //字符串传参this.$router.push('/search/'+this.keyword+'?k='+this.keyword.toUpperCase())//模板字符串传参this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`)//对象传参(最常用)this.$router.push({name:'search',params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}})

8、拆分 home组件

        把预先准备好的静态页面拆分成七个路由组件

        css+HTML+图片资源,缺一不可,细心!

        分别注册引入到home组件中

<template><div><TypeNav></TypeNav><ListContainer></ListContainer><TodayRecomment></TodayRecomment><Rank></Rank><Like></Like><Floor v-for="(floor,index) in floorList" :key="floor.id" :floor="floor"></Floor><Brand></Brand></div>
</template>


   
9、测试api接口(postman)

        使用postman工具测试尚硅谷提供的接口没有问题
        接口返回的code字段为200,则表示接口访问成功,状态正常
        整个项目接口前缀都有/api字样               

10、axios二次封装,二次封装的目的

        1、配置请求拦截器和响应拦截器
                请求拦截器:在发起请求访问之前,进行一些业务处理。比如添加请求头关键字、设置请求进度条
                响应拦截器:在获得返回数据之后,可以进行一些业务处理。
        2、api文件夹
                用来存放api接口相关的文件和配置文件
                接口文档中都有/api,baseURL:'/api'

                '

//利用axios里的create方法创建axios实例
const requests=axios.create({//设置基础路径baseURL:'/api',//设置访问超时时间timeout:5000
})

11、跨域问题

        1、什么是跨域?从本地访问协议、端口、域名不同的地址叫做跨域访问,区别于本地访问
                HTTP://localhost:8080/#/home 本地服务器
                http://gmall-h5-api.atguigu.cn 后台服务器,非本地服务器
        2、跨域的解决办法:JOSNP、CORS、代理proxy

12、引入进度条nprogress

        npm i nprogress@0.2.0
        在请求拦截器中开始,在响应拦截器中结束

        引入nprogress的样式,(/node_modules/nprogress/nprogress.css),样式可修改 蓝色 #29d

//设置请求拦截器
requests.interceptors.request.use((config)=>{//进度条开始nprogress.start();return config;
})
//设置响应拦截器
requests.interceptors.response.use((res)=>{//成功的回调函数:当访问数据返回后,可以响应拦截,做一些业务处理//进度条结束nprogress.done();return res.data;
},(error)=>{return Promise.reject(new Error('fail'))
})


13、vuex

        1、多个组件间状态共享的集中式状态管理工具,用于组件间通讯
                大项目可用来维护数据,使数据维护轻量化、便捷化,小项目没必要使用vuex
                属性:state,actions,mutations,getter,modules
        2、vuex模块化开发
                把所有的状态(数据)放到一个store中,当项目数据过多时,就会显得store过于臃肿,
                数据的易维护特点便也失去了意义,所以就有了vuex模块化开发
                modules:{}

14、完成TypeNav的三级联动数据展示

        1、全局组件TyepNav,使用vuex发起api接口访问,获得数据

mounted() {//vue一挂载就立马发起请求CategoryList,存储于仓库中this.$store.dispatch('categoryList')}


        2、mounted(){}挂载里编辑回调函数,vue一挂载便开始请求api获取数据
            store.home actions->mutations->state使用mapstate展示数据   

<div class="sort" v-show="show"  @mouseleave="showLeave"><div class="all-sort-list2" @click="goSearch"><div class="item" v-for="(c1,index) in categoryList" :key="c1.categoryId":class="{cur:index==currentIndex}"><h3 ><!-- 渲染后 元素的属性名会自动转为小写 --><a @mouseenter="indexChange(index)" :data-categoryName="c1.categoryName" :data-category1Id="c1.categoryId" >{{ c1.categoryName }}</a></h3><!--二三级展示列表 --><div class="item-list clearfix" :style="{display:index==currentIndex?'block':'none'}"><div class="subitem" v-for="(c2,index) in c1.categoryChild" :key="c2.categoryId"><dl class="fore"><dt><a :data-categoryName="c2.categoryName":data-category2Id="c2.categoryId">{{ c2.categoryName }}</a></dt><dd><em v-for="(c3,index) in c2.categoryChild" :key="c3.categoryId"><a :data-categoryName="c3.categoryName":data-category3Id="c3.categoryId">{{ c3.categoryName }}</a></em></dd></dl></div></div></div></div></div>

15、向一级分类动态添加背景色

        1、第一种解决方案,采用样式
                .item:hover{
                background: skyblue;
                }
        2、第二种解决方法,TypeScript
                创建参数currentindex获取category列表的index值,当index==currentindex时 触发样式
                :class="{cur:index==currentindex}"
                样式cur .cur{blackground:skyblue}
                在methods中写触发事件函数,@mouseenter=“”,@mouseleave=“”

16、通过js控制二三级样式的隐藏与展示

        原生css中的style为:display:block|none

//js控制样式:
:style="{display:index==currentIndex?'block':'none'} 
//vue v-show: 
v-show="index==currentIndex"

17、卡顿现象 

        当事件触发非常频繁,每次事件触发都会进行一次回调,频繁执行回调则有可能导致浏览器卡顿。
        节流:在规定的时间间隔内不会触发事件回调,只有当时间间隔大于设定值回调才会被执行,使频繁触发变为少量触发
        防抖:前面的触发执行都被取消,只有最后一次在规定时间间隔里的触发才被执行。频繁的时间触发只执行一次。debounce

18、完成三级联动的节流操作

        //节流器写法,频繁触发->少量触发,节流触发次数

import throttle from "lodash/throttle" 
indexChange:throttle(function (index){ this.currentIndex=index; },50),

19、三级联动的路由跳转->search.html

        策略:a标签+编程式导航+事件委派 event.target定位标签+自定义属性
        缘由:页面一渲染,声明式导航<router-link>就会在循环体里会自动生成很多导航标签,内存使用率高,效率低。编程式导航同样存在内存占用高,页面卡顿的情况,所以均不采用

goSearch(event) {//获取event事件的点击位置let element = event.target// console.log(element)let {//前端标签里的属性名自动转为小写,取值的时候得注意categoryname,category1id,category2id,category3id} = element.datasetconsole.log(categoryname, category1id, category2id, category3id)//整理路由跳转的参数if (categoryname) {let location = {name: 'search'}let query = {categoryName: categoryname}if (category1id) {query.category1Id = category1id} else if (category2id) {query.category2Id = category2id} else {query.category3Id = category3id}if(this.$route.params){location.query = querylocation.params=this.$route.params// console.log(location);this.$router.push(location);}}},

20、对search页面进行categoryList显示控制

        1、鼠标悬停展示,鼠标移走关闭显示
        2、均在TypeNav.vue 编辑js操作,使用this.$route.path 获取当前页面路径
        3、添加过渡效果 <transition name="xxx"></transition>
    组件必须要有v-if/v-show属性才能使过渡效果生效
    样式: .xxx-enter{} .xxx-enter-to{} .xxx-enter-active{transition: all 0.2s linear;}

21、search页面的性能优化问题

        每跳转一次search页面就会发起一次categoryList的get请求,而在第一次获取到请求结果时,store中已经存储了请求数据 所以只需请求一次即可 将请求代码 this.$store.dispatch('categoryList')写在App.vue的mounted中,数据全局可使用。

22、合并参数query和params参数

            在search的input框中执行回调函数时,向push中加入query参数
            在三级联动的路由跳转回调中,加入params参数

    location.query=this.$route.query;location.params=this.$route.params;

23、swiper 轮播图插件

        import ‘/swiper/css/swiper.css’
        swiper轮播图生效的前提是必须先渲染前端结构
        mock 模仿 模拟发起访问请求,获取数据。数据是假数据,前提写好的。

mockServe.js   import Mock from 'mock.js'//导入的Mock为一个函数import banner from './banner.json'Mock.mock(url:'/mock/banner',{code:200,data:banner})

24、轮播图效果生效时间问题。

        由于swiper生效的前提是页面dom必须渲染完成,而swiper实例创建在哪里就成了问题
        1、swiper实例创建在mounted中,执行swiper实例创建时,数据也还没拿到,页面dom就没完成渲染,轮播效果不生成。
        2、在mounted中写一个计时器setTimeout(),实例创建放在计时器中,实例创建完成时,dom创建了,页面也有了,轮播效果也有,但有延时,不完美!
        nextTick:在下一次更新dom 循环结束之后,开始执行延时回调函数 异步操作, nextTick保证了执行延时回调时,dom已经更新。
        3、在mounted中使用nextTick,实例创建放在nextTick中,轮播有效果,但有延时,因为mounted执行完成就会开始执行nextTick,但此时bannerList获取数据这个流程却还没有完成,页面依旧没有渲染(异步操作的原因)。
        4、watch+nextTick解题,监视mounted中的bannerList是否有完成数据获取,当bannerList中获取了数据,再开始执行swiper实例创建
        此方法完美解决轮播生效问题。

watch:{bannerList:{handler(newValue,oldValue){this.$nextTick(()=>{const mySwiper = new Swiper ('.swiper-container', {loop: true, // 循环模式选项// 如果需要分页器pagination: {el: '.swiper-pagination',clickable:true,},// 如果需要前进后退按钮navigation: {nextEl: '.swiper-button-next',prevEl: '.swiper-button-prev',},// 如果需要滚动条scrollbar: {el: '.swiper-scrollbar',},
})})}}}

25、组件中通讯的方式

        props通讯 父传子
        自定义绑定事件:@on,@emit 子传父
        消息全局总线:$bus 全能
        消息订阅与发布:pubsub 全能
        插槽
        vuex 全能

26、Carsouel全局组件

        将swiper轮播图组件全局注册,实现全局复用

        并利用props传参。:list="list" 组件props接收

//注册 Carsouel轮播组件为全局组件import Carsouel from '@/components/Carsouel/index'Vue.use(Carsouel.name,Carsouel)<!--轮播图组件Carsouel--><Carsouel :list="floor.carouselList"></Carsouel>

27、Search路由组件

        1、search路由静态页面拆分和资源部署
                css+html+images,并注册路由组件
        2、获取search页面resSearchInfo数据
            1、api/index.js const reqresSearchInfo=(parmas)=>{
                return requests({rul:'/list',method:'post',data:parmas})
                }
            2、在search mounted(){} this.$store.dispatch();
                store 三连载 actions->mutations->getters->mapGetters
            3、在search页面更新动态数据
        3、发起请求访问时,需要携带参数
            this.$store.dispatch("resSearchInfo",this.searchParmas);
            params=Object.assign(this.searchParmas,this.$route.parmas,this.$route.query)//合并相同的参数

28、search页面存在的一个问题

        在搜索框中输入keyword点击搜索之后页面没反应,只有刷新页面才会展示搜索内容
        是因为在点击搜索之后,并没有触发访问请求api,dispatch
        所以可以对keyword进行watch监视,监视页面路由route变化,一旦路由发生变化就发起访问请求   

watch: {$route(newValue, oldValue) {//再次更新请求参数,//Object.assign()合并具有相同参数的对象,并更新参数Object.assign(this.searchParams, this.$route.params, this.$route.query)this.getData();//请求结束后需要清空路由参数,防止下一请求参数叠加混乱this.searchParams.category1Id = "";this.searchParams.category2Id = "";this.searchParams.category3Id = "";}}

29、面包屑

        页面上细小的属性块,面包屑展示的是路由信息,可删除
        1、search页面的面包屑,展示了当前搜索的关键信息,categoryName、keyword、trademark、AttrValue


        2、每当添加、删除一个面包屑,搜索页的关键信息改变,也就是路由路径改变,都需要重新发起一次访问请求来更新search页面。绑定自定义点击事件

<li class="with-x" v-if="searchParams.categoryName">{{ searchParams.categoryName }}<i@click="removeCategoryName">×</i></li>
removeCategoryName() {//点击面包屑‘×’标志,请求路径中的属性被清除,并且重新发起访问请求并刷新页面this.searchParams.categoryName = undefined;this.searchParams.category1Id = undefined;this.searchParams.category2Id = undefined;this.searchParams.category3Id = undefined;//路径请求属性清除之后,再次发起访问请求,刷新页面// this.getData();//由于路径改变,watch监视路径能监视到变化,所以会触发watch里的请求,而这里就不需要重复请求一次了。//但是清除面包屑属性并不会修改搜索框params属性,所以params属性在请求路径中需要保留if (this.$route.params) {// console.log(this.$route.params)this.$router.push({name: 'search', params: this.$route.params})}},

 30、售卖属性的操作 升序与降序

        1、属性:综合和价格
        点击”综合“展示综合属性的商品列表,默认降序展示,再点一次”综合”升序展示,同时样式改变↓->↑

changeSort(flag) {//获取之前的flag和sort信息let originFlag = this.searchParams.order.split(":")[0];let originSort = this.searchParams.order.split(":")[1];//定义一个新orderlet newOrder = "";if (flag == originFlag) {//当页面的flay没有发生改变,则改变排序方式newOrder = `${flag}:${originSort == 'desc' ? 'asc' : 'desc'}`;} else {//如果改变了,则为newOrder输入新flay和默认排序方式newOrder = `${flag}:desc`;//${"desc"}}//更新order属性,发起访问请求,刷新页面this.searchParams.order = newOrder;this.getData();},

        2、引入阿里巴巴图标库
                1、在public的index.html中添加引入的图标样式链接

    <!--    iconfont阿里图片链接--><link rel="stylesheet" href="https://at.alicdn.com/t/c/font_3566864_wlpeakzjqio.css">

                2、引入样式 class="iconfont" 绑定样式 :class="{'icon-long-arrow-up'}"

<span v-show="isOne" class="iconfont":class="{'icon-long-arrow-up':isUp,'icon-long-arrow-down':isDown}"></span>

31、手写分页器 重难点

        1、分页器的结构一般分为6部分
                 «上一页  第一页  ...  中  ...  最后一页  下一页»  共10条数据
                中间部分的连续展示页continues一般为奇数长度(5、7、9页),居中效果美观。
        2、难点:如何在不同情况下确定连续展示页的起始页和结束页(start,end)

startNumAndendNum() {let start = 0;let end = 0;const {pageNo, totalPage, continues} = this;if (totalPage < continues) {//当总页数小于连续页长度时start = 1;end = totalPage;} else {                    //正常情况下确定起始页和结束页start = pageNo - Math.floor(continues / 2);end = pageNo + Math.floor(continues / 2);if (start < 1) {           //起始页小于1时重新确定起始页、结束页start = 1;end = continues;}if (end > totalPage) {      //结束页大于总页数时start = totalPage - continues + 1;end = totalPage;}}return {start, end}}

        3、页面编辑分页器结构

<!-- 上--><button :disabled="pageNo==1" @click="goPageNo(pageNo-1)">上一页</button><!-- 第一页--><button v-if="pageNo>Math.ceil(continues/2)" @click="goPageNo(1)" :class="{active:pageNo==1}">1</button><button v-if="pageNo>1+Math.ceil(continues/2)">···</button><!-- 中--><button v-for="(n,index) in startNumAndendNum.end-startNumAndendNum.start+1":key="index" @click="goPageNo(startNumAndendNum.start+n-1)":class="{active:pageNo==startNumAndendNum.start+n-1 }">{{ startNumAndendNum.start + n - 1 }}</button><!-- 下--><button v-if="pageNo<totalPage-Math.ceil(continues/2)">···</button><!-- 最后一页--><button v-if="pageNo<totalPage-Math.ceil(continues/2)+1"@click="goPageNo(totalPage)":class="{active:pageNo==totalPage}">{{ totalPage }}</button><button :disabled="pageNo==totalPage" @click="goPageNo(pageNo+1)">下一页</button><!--数据列表的数据条数--><button style="margin-left: 30px">共 {{total }} 条</button>

        4、访问指定页 点击分页器中的某一页跳转到该页

                 绑定点击事件@click 获取当前页 pageNo 更新pageNo,发起请求访问

searchPage(page) {this.searchParams.pageNo = page;this.getData();}

        5、注册分页器为全局组件,全局复用

//注册Pagination分页器组件为全局组件
import Pagination from '@/components/Pagination/index'
Vue.use(Pagination.name,Pagination)
<Pagination :pageNo="searchParams.pageNo":pageSize="searchParams.pageSize":total="total":continues="5"@goPageNo="searchPage"></Pagination>

32、商品详情页面

        1、编辑页面: HTML、css、静态资源,在router中注册路由组件
        2、动态展示商品详情页信息
                1、准备vuex,新建一个子库,detail.js
                2、detail.js state、actives、mutations、getters 在主库中注册
        3、向后端发起请求,获取数据

const reqGoodList=(skuId)=>{ return requests({url:`/detail/${skuId}`,method:"get"})}mounted(){ this.#store.dispatch("GoodInfo",this.$route.params.skuId)}vuex actives->mutations mapState({goodInfo:(state)=>{return state.detail.goodInfo.categoryView}   })

         注意:由于存在页面已经开始渲染了但绑定的属性却还是undefined的情况,控制台会报错,
                避免报错,采用mapGetters获取数据可以避免该问题,因为在detail的store中可
                可以getters预先处理数据,若还未获取到数据赋空{}|[]
                categoryView(){
                    return state.goodInfo.categoryView||{};
                },
            ...mapGetters(['categoryView'])
        4、在页面用插值语法{{}}展示数据

33、详情页面的放大镜

        根据鼠标事件获取展示图片的像素坐标(event.offsetX,event.offsetY),以像素坐标为中心点展示一个放大的图片mask
        难点:1、获取的像素坐标的约束范围(left,top)

//属性约束if(left<=0) left=0;if(left>=mask.offsetWidth) left=mask.offsetWidth;if(top<=0) top=0;if(top>=mask.offsetHeight) top=mask.offsetHeight;

                2、放大倍数的方向问题
                放大之后,放大的倍数好确定,但得注意,像素坐标得反方向放大,展示的才为像素坐标为中心的图片

 //放大展示 反方向big.style.left=-2*left+'px';big.style.top=-2*top+'px'

34、商品可选属性的排他操作

        商品的颜色、配置、内存容量等属性可供用户选择,但选择一个高亮展示,其他的同类型属性就得关闭高亮展示,高亮只能有一个,排他操作。
        v-for遍历每个属性,在属性中配置一个点击事件,并传递当前属性和全部属性

@click="isCheck(spuSaleAttr,spuSaleAttrValue)"spuSaleAttr.forEach((item)=>{//关闭高亮效果item.isChecked=0;)spuSaleAttrValue.isChecked=1;

        forEach(()={})遍历每一对象

35、将商品添加到购物车

        1、<a @click="addShopCart">加入购物车</a>
        2、派发actions,三连环:dispatch->actions->reqAddOrUpdataCart
        问题:商品添加到谁的购物车里?由于此时未登录,认定添加到了一个游客的购物车里。一般购物网站点击到了这一步会要求登录才能进行下一步
        问题:怎么添加到游客的购物车里?访问请求的接口reqAddOrUpdataCart会根据请求头headers里的关键字段uuid_token,将商品信息添加到该游客的购物车里。
    3、设置uuid_token

import {getUUID} from '@/utils/uuid_token.js'在store.detail中 uuid_token=getUUID();import {v4 as uuidv4} from 'uuid'export const getUUID=()={let uuid_token=localstoreg.getItem('UUIDTOKEN')  if(!uuid_token){uuid_token=uuidv4();//将uuid_token存储到本地存储中,持久化存储localStorage.setItem.setItem('UUIDTOKEN',uuid_token)}return uuid_token;}//将uuid_token添加到requests.js的请求拦截器的headers中//返回config配置对象,里边有一个属性很重要,header请求头//给请求头添加一个userTempId临时id,userTempId字段为后端接收的字段名,不可随意编辑,否则后端显示无该参数if(store.state.detail.uuid_token){config.headers.userTempId=store.state.detail.uuid_token;}

36、成功添加购物车

        若let result=await reqAddOrUpdataCart() result.code==200,则购物车添加成功

//添加或更新购物车,传递参数skuId,skuNumexport const reqAddOrUpdataCart=(skuId,skuNum)=>{return requests({url:`/cart/addToCart/${skuId}/${skuNum}`,method:'post'})}

37、购物车页面

        1、HTML、style、css、静态资源,在router中注册路由组件
        2、创建shopCart的vuex
                1、/store shopCart.js state->actions->mutations->getters
        3、获取购物车商品列表 getCart();请求接口会根据headers 的userTempId获取该游客(未登录)的cartList
                mounted(){this.getCart()}
                getCart()->actions->reqShopCartList->mutations->getters return state.cartList[0]|| [];//当数据还未获取到时,返回一个空数据,避免报错的保险操作
                computed:{...getters(['cartList'])}//获取数据,页面展示 v-for
        4、删除购物车某一商品
                <a class="sindelet" @click="DeleteCartGood(good.skuId)">删除</a>
                DeleteCartGood(skuId)->dispatch->actions->reqDeleteCarListById->回到DeleteCartGood,再次调用getCart()重新获取购物车页面商品信息
        5、修改某一个商品的勾选状态
                <input @change="updateChecked(good,$event)">
                updateChecked->dispatch->actions->reqUpdateCheckedById(skuId,isChecked)->回到updateChecked,再次调用getCart()重新获取购物车页面商品信息

38、删除全部勾选的商品

        <a @click="deleteAllChecked">删除选中的商品</a>
        deleteAllChecked->dispatch->actions->在actions中派发dispatch,dispatch('deleteCartList',item.skuId)->getCart();
        代码实现:forEach逐一删除

deleteAllCheckedList({dispatch,getters}){let PromiseAll=[]getters.cartList.cartInfoList.forEach(item=>{let promise=item.isChecked==1?dispatch('deleteCartList',item.skuId):''PromiseAll.push(promise)})//只要全部的p1|p2....都成功,返回结果即为成功//如果有一个失败,返回即为失败结果return Promise.all(PromiseAll);}

39、勾选全选框控制所有商品的勾选

updateAllChecked({dispatch,getters},isChecked){let PromiseAll=[]getters.cartList.cartInfoList.forEach(item=>{let promise=dispatch('updateCheckedById',{skuId:item.skuId,isChecked})PromiseAll.push(promise)})return Promise.all(PromiseAll);}//computed:isAllCheck(){//every是forEach的break用法,//遍历数组里面原理,只要全部元素isChecked属性都为1===>真 true//只要有一个不是1======>假falselet checked= this.cartInfoList.every((good)=>good.isChecked==1)return checked;},

40、登录注册

        1、HTML、css、style、静态资源,在router中注册路由组件
        2、创建user store库 vuex
                用于存储用户的相关消息
                user.js (state actions mutations getters)->store index.js->向主库注册子库

41、注册组件

        1、四个input框,手机号、验证码、密码、再次输入密码
                data(){return{phone:"",code:'',password:'',password1:''}}
                全部v-model=''绑定
        2、获取验证码
                由于向手机号发送验证码,有成本,所以就直接从后端获取验证码之后直接自动输入到input框中
                <button  @click="getCode" >获取验证码</button>
                getCode->dispatch->actions->reqGetCode->mutations->state.code
                this.code=this.$store.state.user.code,v-model双向绑定,input自动填入
        3、注册用户信息
                由于验证码为自动填入,则没有验证验证码是否正确的一步
                所以直接验证,phone、code是否填入,password==password1
                //注意!访问请求的参数一定不能写错,参数名保持一致,顺序保持一致
                this.$store.dispatch('userRegister',{phone,password,code})->actions->reqUserRegister
                code=200 后端成功注册用户信息->注册成功跳转到登录页面 this.$router.push('/login')

42、用户登录

        1、在用户注册之后,后台会为用户生成一个用户令牌token。
        2、用户登录时输入的邮箱/用户名/手机号、password仅用于验证登录,用户信息均已后台获取得到的为准,获得用户信息需要token
        3、用户登录成功之后,会返回一个token,将token存储到localStorage中,持久化存储,页面刷新用户也不会丢失。

//给请求头添加tokenif(store.state.user.token){config.headers.token=store.state.user.token;}export const=setToken(token){localStorage.setItem('TOKEN',token)}//引入setTokenimport {setToken} from '@/utils/token.js'this.$store.dispatch('userLogin',{password,phone})->actions setToken(result.data.token)->mutations->state.token或者 state.token:getToken();//获取tokenexport const getToken=()=>{return localStorage.getItem('TOKEN')}

        4、获取用户信息
                1、home页面根据用户token,获取用户信息 this.$store.dispatch('getUserInfoByToken'),在home的header中展示个人信息
                2、获取到userInfo信息之后存储到user state.userInfo中,在header页面展示用户信息||退出登录,如果userInfo.name为空则展示登录||注册

43、退出登录

        1、发起退出登录的请求,并清空用户信息(userInfo,token)
                logout->actions->reqLogout->mutations

//mutations 操作state,退出登录,清除用户信息LOGOUT(state){state.token=''state.userInfo=''clearToken()}import {clearToken} from '@/utils/token.js'//清除本地存储数据export const clearToken=()=>{localStorage.removeItem('TOKEN');}

44、路由守卫

        1、全局守卫
                对项目中全局的路由跳转都进行监控和管制,可通过全局前置守卫(router.beforeEach())
        和后置守卫(router.afterEach())进行路由跳转管制。
        2、独享守卫
                某一个路由所独享的一个守卫,路由代码写在routes的组件中,独享守卫没有后置守卫,但可以和全局后置守卫配合使用。router.beforeEach((to,from,next)={})。
        3、组件内守卫
                路由规则写在路由组件中。beforeRouterEnter(){}进入守卫,进入组件时路由规则被调用
                        beforeRouterLeave(){}离开守卫,路由规则在离开该组件时被调用
                        beforeRouterUpdate(){}更新守卫,路由更新时路由规则被调用。
        4、vue的路由访问形式/模式:hash和history。有#的为hash路由,#值后边的内容称之为hash值。

45、使用路由导航控制用户的登录注册行为

        当用户未登录时,无法访问交易页面、订单页面、支付页面、个人主页并自动跳转到登录页面。
        当用户登录之后,home页面的登录/注册将会被替换为个人信息和退出登录。当用户在网址导航栏中跳转时,将会被自动跳转到主页。

//@/router/index.js
Router.beforeEach((to,from,next)=>{const toPath=to.path;const fromPath=from.path;const token=this.$store.state.user.token;if(!token){//用户未登录,无tokenif(toPath.indexOf('trade')!=-1||toPath.indexOf('pay')!=-1||toPath.indexOf('center')!=-1){next('/login');//路由守卫管制,跳转到login页面               }else{next();//除了上述地址,其余可以访问,放行}}else{//用户已登录if(toPath=='/login'||toPath=='/register'){next('/home');//自动跳转到主页。}else{if(name){next();}else{//当用户登录之后,用户名获取不到,则重新获取try{//重新获取userInfoawait store.dispatch('getUserInfoByToken')next();}catch (error) {//如果userInfo获取失败,则表明token失效,获取不到userInfo//token失效,重新登录await store.dispatch('logout')next('/login')}}   }
}
})

46、订单提交页面(trade)

        1、html、css、images、注册路由
        2、动态绑定数据
                //获取用户地址信息
                        this.$store.dispatch('getUserAddressInfo')
                //获取订单信息
                        this.$store.dispatch('getOrderInfo')
                store三连环:dispatch->actions->mutations->state
                ...mapState({userAddressInfo:(state)=>{return state.trade.userAddressInfo},
                orderInfo:(state)=>{return state.trade.orderInfo}})

        3、绑定路由
                在ShopCart页面声明式导航,跳转到'/trade'
                编辑组件内路由守卫路由规则,只能从ShopCart页面跳转到Trade页面
    在trade页面:

 //组件内守卫//只能从购物车跳转到交易页面beforeRouteEnter(to,from,next){if(from.path=='/shopcart'){// next('/trade');next();}else {next(false);}},

        4、提交订单,并跳转到支付页面
                1、编程式导航
        <a class="subBtn" @click="submitOider">提交订单</a>
        this.$router.push(`/pay?orderId=${result.data}`)
                2、向后端接口发起请求,提交订单信息

async submitOider(){//提交订单const {orderInfo}=this;this.orderId=orderInfo.tradeNo;this.orderDetailList=orderInfo.detailArrayList;let data={consignee: orderInfo.consignee,consigneeTel: orderInfo.phoneNum,deliveryAddress:orderInfo.fullAddress,paymentWay: "ONLINE",orderComment: this.msg,orderDetailList:this.orderDetailList}let result=await this.$API.reqSubmitOrder(this.orderId,data)console.log(result)//订单提交成功,跳转到pay页面if(result.code==200){this.$router.push(`/pay?orderId=${result.data}`)}else {return result.message;}
}

47、支付页面Pay

        1、html、css、images
        2、获取支付页面信息
                1、不再使用state三联环获取数据,使用全局API发起请求,返回结果直接存储在data()中。原来是在actions中发起请求,在mutations中处理数据,存储在state中,现在尝试一种新方式,直接在组件中操作api并存储返回结果。
                        1、将api接口js文件全局暴露,并全局引入。
                                在mian.js文件中 import * as API from '@/api/index'
                                beforeCreate中 Vue.prototype.$API=API 全局引入完成。
                        2、发起api请求    

data(){return{payMentInfo: {}}}
const result=this.$API.reqPaymentInfo(this.orderId);//注意异步请求
if(result.code==200){this.payMentInfo=result.data||{}}

        3、绑定动态数据,订单号、订单总金额

48、订单支付功能 使用element-ui messageBox支付二维码弹窗

        1、element-ui按需引入,全局引入会使得项目打包之后的文件非常庞大,占用空间。

//按需引入需要安装插件babel-plugin-component,详情查询官网//按需引入element-uiimport {Button, MessageBox} from 'element-ui';Vue.use(Button);Vue.use(MessageBox)//或者组件方式引入// Vue.component(Button.name, Button);// Vue.component(Select.name, Select);//使用<Button/>

        2、引入element 消息盒子和消息弹窗

import MessageBox from 'element-ui';Vue.prototype.$msgbox = MessageBox;Vue.prototype.$alert = MessageBox.alert;

        3、QRcode 链接转二维码插件,npm i qrcode 下载
                import QRCode from 'qrcode'
                let url=await QRCode.toDataURL(this.payMentInfo.codeUrl)
        4、使用element组件 创建定时器setInterval,不断查看支付状态,当获取并保存支付状态,关闭定时器clearInterval(this.timer),this.timer=null;  

async open() {let url=await QRCode.toDataURL(this.payMentInfo.codeUrl)this.$alert(`<img src= ${url} />`, '微信支付', {dangerouslyUseHTMLString:true,center:true,confirmButtonText: '已完成支付',showCancelButton:true,cancelButtonText:'支付遇到问题',beforeClose:(action,instance,done)=>{if(action=='cancel'){alert('请稍后再试')//关闭定时器clearInterval(this.timer);this.timer=null;done();}else {if(this.payMentCode==205){// clearInterval(this.timer)// this.timer=null;done();this.$router.push('/paysuccess');}}}});//在支付框里发起请求,查看支付状态if(!this.timer){this.timer=setInterval(async ()=>{let result=await this.$API.reqPaymentStatus(this.payMentInfo.orderId);//保存订单状态码console.log(result)this.payMentCode=result.code;if(result.code==205){clearInterval(this.timer);this.timer=null;console.log('定时器已关闭')// this.$msgbox.close();// this.$router.push('/paysuccess')}},2000)}},

        5、支付完成跳转paysuccess页面
                 <router-link to="/paysuccess">支付完成</router-link>

49、我的订单页面Center(个人中心)

        1、HTML、css、images
        2、注册路由

{name:'center',component:Center,path:'/center',mate:{show:true           }},

        3、订单页面为二级路由页面
        注册二级路由

{name:'center',component:Center,path:'/center',mate:{show:true           },children:[{name:'myorder',component:MyOrder,path:"myorder",//二级路由下,路径直接写路径名,不用带'/'.},{name:'grouporder',component:GroupOrder,path:'grouporder',},{//重定向,自动展示二级路由下的myorderpath:'/center',redirect:'/center/myorder'}]      },<router-view />

50、编辑myorder页面

        1、HTML、css、images
        2、请求访问全局api获取数据
                const result=this.$API.reqAllPaymentInfo(this.page, this.limit);
                data(){return orders:{}}
                if(result.code==200){this.orders=result.data;}
        3、页面绑定动态数据
        4、注册到center路由下的子路由中,完成路由绑定

51、图片懒加载 lazyload

import VueLazyLoad from 'vue-lazyload'Vue.use(VueLazyLoad,{loading:'@/assets/images/img.gif'})

        在页面使用v-lazy='' 代替img=''就实现了图片懒加载的运用

52、路由懒加载

        component:()=>{return import('@/pages/search/index.vue')}
        只有当调用路由时才加载路由,而非项目一挂载就全部加载,提高了路由的高效性

const foo=()=>{ruturn import('@/pages/search/index.vue')}{name:'search',path:'/search',component:foo//调用路由}//简写形式:{name:'search',path:'/search',component:()=>import('@/pages/search/index.vue'),}

53、vee-validate表单验证

        1、plugins思维,当使用一款插件时,插件的引入、调用、属性编辑、编辑规则我们可以全部放在一个js文件里,在main.js文件里全局引入。这样就不用全部堆在main.js文件里显得臃肿。实现分块管理。比如,element-ui的引入和Vue.use()使用全部放在一个js文件里,在main.js文件里导入该文件。实现element-ui的按需引用。
        2、在@/plugins/validate.js文件里编辑相关信息

//vee-validate 插件导入
import Vue from 'vue'
import VeeValidate from 'vee-validate'
//引入中文验证
import zh_CN from 'vee-validate/dist/locale/zh_CN'Vue.use(VeeValidate);//编辑属性
VeeValidate.Validator.localize('zh_CN',{messages:{...zh_CN.messages,is:(fail)=>{return `${fail}必须与密码相同`;}},attributes:{phone:'手机号',code:"验证码",password:"密码",password1:"确认密码",agree:"协议"},
})//自定义校验规则
VeeValidate.Validator.extend('agree',{validate:(value)=>{return value;},getMessage(field) {return field+'必须同意'}
})

        3、在页面绑定校验信息

<input type="text" placeholder="请输入你的手机号"v-model="phone"name="phone" v-validate="{required:true,regex:/^1\d{10}$/}"//编辑输入框的输入规则:class="{invalid:errors.has('phone')}"
>
//绑定样式
<span class="error-msg">{{ errors.first('phone') }}</span>//显示提示信息

54、项目打包

        1、在项目文件夹下npm run build,生成dist文件

        2、项目上线参考:(2条消息) ubuntu20.04安装nginx并配置反向代理_HusePanghu的博客-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

千橡CEO陈一舟5日聊微软入股Facebook和校内网

千橡CEO陈一舟5日聊微软入股Facebook和校内网 http://www.sina.com.cn 2007年11月01日 17:50 新浪科技 千橡集团董事长兼CEO陈一舟(新浪科技配图) 新浪科技讯 11月1日消息&#xff0c;千橡集团董事长兼CEO陈一舟将于11月5日10:30做客新浪科技白银时代&#xff0c;届时他将就微…

今天面了个阿里秒杀项目组的,见识到了基础天花板被震撼到了

在京东工作了8年&#xff0c;工作压力大&#xff0c;节奏快&#xff0c;但是从技术上确实得到了成长&#xff0c;尤其是当你维护与大促相关的系统的时候&#xff0c;熬到P7也费了不少心思&#xff0c;小编也是个爱学习的人&#xff0c;把这几年的工作经验整理成了一份完整的笔记…

互联网暴力裁员,唯有神技面试八股文手册,能助各位码农突破囧境

很多人都说今年对于IT行业根本没有所谓的“金三银四”“金九银十”。在各大招聘网站或者软件上不管是大厂还是中小公司大多都是挂个招聘需求&#xff0c;实际并不招人&#xff1b;在行业内的程序员基本都已经感受到了任老前段时间口中所谓的“寒气”。 虽然事实确实是如此&…

27位技术实战派负责人齐聚 深聊降本增效 你一定不想错过!

倒计时6天&#xff0c;11月4日 杭州云栖大会-探索增长动能峰会即将启动 阿里巴巴集团CTO线&#xff06;阿里云智能互联网行业线& ​阿里云智能基础产品事业部联合出品 27位技术实战派负责人齐聚 共同深入讨论企业降本增效的话题 欢迎一起来 看点 1&#xff1a;27位实战…

商汤科技推出“日日新SenseNova”,大模型体系赋能人工智能新未来

2023年4月10日&#xff0c;商汤科技SenseTime技术交流日活动在上海举行&#xff0c;分享了以“大模型大算力”推进AGI&#xff08;通用人工智能&#xff09;发展的战略布局&#xff0c;并公布了商汤在该战略下的“日日新SenseNova”大模型体系。 公开信息显示&#xff0c;商汤科…

AI抢饭碗成真!近500家美国企业用ChatGPT取代员工,有公司省下超10万美元

Alex 发自 凹非寺量子位 | 公众号 QbitAI 自从ChatGPT掀起浪潮&#xff0c;不少人都在担心AI快要抢人类饭碗了。 然鹅&#xff0c;现实可能更残酷QAQ...... 据就业服务平台Resume Builder调查统计&#xff0c;在1000多家受访美国企业中&#xff0c;用ChatGPT取代部分员工的&…

阿里CEO下令所有产品接入大模型!钉钉对标微软Office,已解锁近10项新功能

丰色 发自 凹非寺量子位 | 公众号 QbitAI “阿里版GPT”通义千问邀测没过几天—— 今天&#xff0c;在2023阿里云峰会现场&#xff0c;CEO张勇就宣布&#xff1a; 阿里巴巴所有产品未来都将接入“通义千问”大模型&#xff0c;进行全面改造。 与此同时&#xff0c;通义千问的能…

GPT-3核心成员出走打造ChatGPT最强竞品!12项任务8项更强,最新估值50亿美元

衡宇 萧箫 发自 凹非寺量子位 | 公众号 QbitAI 因不满老东家成为微软附庸&#xff0c;11名OpenAI前员工怒而出走。 如今带着“ChatGPT最强竞品”杀回战场&#xff0c;新公司估值50亿美元&#xff0c;一出手就获得3亿美元融资。 这家公司名叫Anthropic&#xff0c;新推出的聊天机…

一页PPT自动生成短视频的研究

希望通过一些技术&#xff0c;将以前自己讲过的PPT转换成有解说的短视频&#xff0c;从而进行一些分发 旁白到语音 从文字转换成语音我们首先想到的就是TTS&#xff0c;这其中我也是用了各式各样的TTS&#xff0c;发现发音电子音非常强&#xff0c;听听起来很不舒服。后来发现…

Edge浏览器安装油猴插件以及好用的插件推荐

目录 一、介绍 二、下载步骤 一、介绍 油猴插件是非常好用的&#xff0c;可以帮助我们解放双手眼睛这些。帮助学习&#xff0c;通过这些 二、下载步骤 1.打开edge浏览器&#xff0c;右上角点击三小点&#xff0c;选择扩展 2.点击这个 3.点击下载油猴插件 下载失败的看&a…

如虎添翼!6款备受欢迎的Edge浏览器插件

Microsoft Edge以其低资源消耗、强大的安全性和丰富的创新功能&#xff0c;使其备受欢迎。Edge浏览器正在逐渐替代Chrome成为很多用户的首选浏览器。 而作为桌面浏览器必不可少的一部分&#xff0c;扩展程序是很多用户选择一款浏览器重要的衡量标准。 扩展程序可以增强你的浏…

最强Microsoft Edge插件安装

一、Global Speed: 视频速度控制 Global Speed与几乎所有视频和音频流媒体站点兼容&#xff0c;包括Youtube&#xff0c;Netflix&#xff0c;哔哩哔哩&#xff0c;腾讯视频&#xff0c;百度网盘&#xff0c; 爱奇艺等。 当我们打开某个视频网站时&#xff0c;点击Global Spee…

数据分析 波士顿房价预测

一、导入所需要的数据包 二、读入数据 三、数据探索 查看数据的类型&#xff0c;完整性

用Python分析波士顿的房价,酸爽!!!

这个是Kaggle专栏的第二篇&#xff0c;赛题名是&#xff1a;House Prices - Advanced Regression Techniques。在本文中你将会学习到&#xff1a; 单、多变量分析相关性分析缺失值和异常值处理哑变量转换 原notebook地址&#xff1a; https://www.kaggle.com/pmarcelino/compre…

Python数据分析-房价的影响因素图解

摘 要 这两年OF一直在关注房价&#xff0c;抛开政策、炒房等一些外部因素&#xff0c;对于房屋本身来说&#xff0c;哪些是影响房价的主要因素&#xff1f;OF在众多因素中挑选了几个进行分析&#xff1a;房屋年龄、面积、层数&#xff08;1/1.5/2/2.5/...&#xff09;、卧室数…

python做波士顿房价预测

#1. 导入相关包 import numpy as np #导入numpy科学计算包 import pandas as pd #导入pandas数据分析包 from pandas import Series, DataFrame #Series是类似于一维数组的对象 import matplotlib.pyplot as plt #导入绘图的包 import sklearn.datasets as datasets #直接从…

python实现波士顿房价预测---(1)

波士顿房价预测 目标 这是一个经典的机器学习回归场景&#xff0c;我们利用Python和numpy来实现神经网络。该数据集统计了房价受到13个特征因素的影响&#xff0c;如图1所示。 对于预测问题&#xff0c;可以根据预测输出的类型是连续的实数值&#xff0c;还是离散值&#xff…

加州房价篇 (一) : 了解数据

让我们先从加利福尼亚州的房价说起 如果没有接触过机器学习&#xff0c;需要先进行环境配置 在每次开始之前都需要先运行以下代码,不报错方可继续 # Python 的版本需要大于3.5 import sys assert sys.version_info > (3, 5)# Scikit-Learn的版本需要大于0.20 import sklear…

我用python分析买房数据

首先说明&#xff0c;这是一篇技术文章。 明年打算买房&#xff0c;媳妇这段时间总去看房子&#xff0c;这种状态持续了两个月&#xff0c;最近终于消停了。现在整个市场不明朗&#xff0c;我们也不确定换到哪里。不如先整理点数据&#xff0c;至少能监控一些区域价格&#xf…

波士顿房价数据集怎么不见了?

波士顿数据下载 消失的波士顿 OoO 做线性回归的同学大概率会用到一个数据集&#xff0c;即波士顿房价数据集&#xff0c;然而当你从sklearn下载该数据集时&#xff0c;你会惊讶地发现居然下载不了了&#xff01;&#xff01;&#xff01;起初我以为是什么别的原因导致数据集可能…