VUE3生鲜项目笔记

项目初始化

现在项目上新建几个包:

apis是接口文件

composables放组合函数

directives放全局指令文件

utils放工具文件

然后终端进行git init指令,将项目进行git管理:

右边变绿了说明已经进行git管理了

然后git add .是将我们新写的代码放在暂存区

git commit -m "写给本次代码内容取名"  是提交

element-pius安装

npm install element-plus --save指令,

具体参考element-pius官网:安装 | Element Plus

然后element-plus按需导入指令:npm install -D unplugin-vue-components unplugin-auto-import

这个项目是基于vite来写的所以我们参照:

按照上图缺啥补啥:

然后从官网组件里随便插一个按钮看看能不能用:

但是发现缺两个东西,自动提示了,我这里搜了一下安装之后就行了:

npm install --save @vue/shared   

npm install --save @vue/reactivity

然后就可以了

如此element-olus就可以用了

主题色设置

一般上了项目我也不会接触这些东西,看看就行和编码没啥关系

npm i sass -D指令

然后新建styles/element文件夹,然后写一个index.sass文件,内容是:

@forward 'element-plus/theme-chalk/src/common/var.scss' with(

    $colors:(

        'primary':(

            //主色

            'base':#27ba9b,

        ),

        'success':(

            //成功色

            'base': #1dc779,

        ),

        'warning':(

            //警告色

            'base': #ffb302,

        ),

        'danger':(

            //危险色

            'base': #e26237,

        ),

        'error':(

            //错误色

            'base': #cf4444,

        ),

    )

)

然后在vite.config.js里面改一下配置

安装axios

密令npm i axios

然后再utils里写http.js代码,那个baseURL就是基地址,在api里写后面的地址

然后在api里面写请求

然后测试:

效果:

看一下请求信息:

如此一个请求就发完了

路由配置

在route文件夹的index.js里面写:

首先写一个躯壳一开始的东西,随时可以用:

代码直接写这里往后可以直接复制粘贴:

//createRouter创建router实例对象

//createWebHistory创建history模式的路由

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({

  history: createWebHistory(import.meta.env.BASE_URL),

  //path和componment对应关系的位置

  routes: [

  ]

})

export default router

然后在views文件夹里面去写login的index

把他配到路由里面去

然后在App.vue里加一个<RouterView/>,这表示开启了一级路由,可以访问login和layout页面,但是layout还有下一级的页面,所以也需要在layout里面写一个<RouterView/>开启二级路由才能访问layout以下的页面

启动项目后在项目地址后面加个/login,就是打开一级的页面:

在刚刚路由的基础上,需要配二级路由:

在layout里也加上<RouterView/>然后我们访问/category:

scss文件自动导入

我们自定义的颜色写进一个scss文件里,然后在vite.config.js里加入他的路径

加入定制化样式

然后页面中style标签加入lang=scss属性

静态模版构建

先看成品:

这一个页面其实是这样做出来的:

动态展示一级导航

我们需要从数据库库里拿到这些数据然后动态地展示出来:

第一步

写访问方法

在aips文件夹下新建layout.js然后引入httpInstance(在安装axios标题里面写的)把要访问的url写上

组件里接收方法返回的数据

然后在header页里用list去接访问返回数据

把list动态渲染

最后以动态渲染的方式列出来

吸顶导航交互栏

当滑轮往下滑时,header要一直存在,当回到顶部时,再固定,其实就是写一个长得一模一样的组件:

我们要控制当下画的时候上面的显示,当回到顶部然后就只显示下面的,这就形成了导航栏一直都在的假象:

这里就要引入useScorll的方法,用y去接收当前屏幕距顶部的距离,然后当距离大于78时,死的那个就看不到了,这个时候活的这个div就要显示

顶部显示死的div:

当y>78时,显示活的div:

pinia请求优化

header和动态交互栏是两个组件,但是用的一个数据,我们的写法,当滑动时,会发两次请求,这样会性能低,因为数据是一样的所以我们可以通过pinia优化代码。

大致的思路是,我们把请求方法写在pinia里,然后把数据返回出来,用的时候直接拿,这样就避免了滑动时发两次请求了。以下是我画的思路图:

先写pinia文件

新建一个专门给category写方法的js

父组件里调用pinia的方法

在index(需要调用list的父组件)里的加载期去调用getCategory方法

子组件直接拿数据

这样的话pinia里就存好了返回的list数据,然后组件们需要调用的时候就可以直接拿,不需要再发送请求了(两个组件一个用了解构的方式一个用了没解构的方式):

home页面结构图

整体结构和分类页面

这是大概的结构图

然后写分类页面,跟上一小节差不多,已经把data存到pinial里了,要做的就是把数据动态渲染到页面,先打印一下list:

把这些东西遍历出来就行了

动态渲染轮播图

需要请求新的数据,所以先写api

因为这个轮播图的只会加载一次,所以直接在页面组件里写就行:

效果图:

面板组件封装

其实就是有两个样式完全相同的区域,我们把他单独写成一个组件,导入到主页面,通过propos的方式给它传值而达到一个组件可以表达不同内容的方法。

先写独立的组件:

然后引入到父级:

这是效果:

渲染人气推荐和新鲜好物

人气推荐和新鲜好物是一样的,就是请求不一样

一先写api

组件内获取数据

渲染后的效果

自定义指令实现图片懒加载

懒加载的实现

图片懒加载的效果是当我们浏览到图片是才加在他并不是一上来全部都加载

实现方式就是,我们在main.js里面自定义一个指令‘img-lazy’,用img元素去绑定它,然后监听img元素,和img的url,然后利用useIntersectionObserver检测图片有没有展示到屏幕:

然后在回调函数里写赋值逻辑,当返回true时把url赋值给img的src,再把img里的src属性删掉

然后效果就是第一时间不会加载,当浏览到了的时候再加载

懒加载的优化

主要有两个问题

优化问题一

有逻辑的代码不要放在main.js里面,写一个外挂全局管理,顾名思义,本身全局管理的内容要放在main.js里面,但是有逻辑我么可以专门写个js把全局管理挂到外面,然后直接在main.js里面导名使结构更清晰简单。

这里涉及自定义指令,install(app)方法就是外挂全局管理的方法,自定义指令也有自己的类似于vue的钩子函数,并且vue2的vue3的钩子函数还不一样,比如说截图里的mounted(el,bind)(并且有关自定义指令绝大部分都是在mounted里写逻辑)就是钩子函数其中之一可以直接用。

在directives文件夹下面新建一个index.js,然后把逻辑搬过来:

然后再mian.js里面导名引入:

优化问题二

useIntersectionObserver视图监听方法不断被触发,影响性能:

图片加载过一次就不需要再次加载了,所以我们当这个图片为true时,我们把这个图片的监听关掉就行了,因为他已经加载过一次了:

注意看着是一个解构stop然后使用的过程,stop本身就是useIntersectionObserver里自我终结的方法,我们把他结构出来再用,就可以实现了。

路由和带值跳转(重要)

当我们点击居家的时候我们跳转居家页面,需要完成两个步骤

路由配置

在 <RouterLink >中配置跳转路径

注意to前面加:,然后··符号还有参数表示是${}

面包屑导航(params动态传参和接参)

第二个页面了所需要的数据不再是home得了,所以新建一个api文件category,和之前的不一样这次是需要传入参数id:

这个id是上一章说的那个routelink标签里传的id,在新的页面组件里要获取数据必须先拿到这个id,然后根据这个id跳转:

先引入useRoute,接到以后用route.params.id获取

然后剩下的和之前的一样

最后页面效果

按照传值访问不同类型的轮播图

之前的轮播图默认没有参数,图片是固定的,但是点击不同种类模块,轮播图就应该是不同标题的

动态传个distributionSite属性,当首页就显示distributionSite默认为1轮播图,分类的时候就访问distributionSite为2轮播图。

首先更改api:

然后再分类的组件里给distributionSite赋值,把代码模板复制过去。参考之前的轮播图

激活状态显示与分类列表渲染、

激活状态显示就是点到谁谁亮的这个效果:实现起来也简单,给<RouteLink>加active-class属性后面跟样式就行了:

分类列表渲染也能很简单,准备好静态模版然穿吧<GoodsItem>引进来就行了

效果:

解决路由缓存问题

因为首页是存在于home组件,而其他分类页都是同一个cetagory组件的页面,所以当从首页点击跳转到其他分类页的时候是跨组件跳转,生命周期会重新运行一遍,但是当分类页点到其它分类页时属于同组件跳转,由于同一组组件不会重新执行生命周期,所以无法跳转,尽管路由已经改变。

有两种解决办法:

1.<RouteView/>监听

<RouteView/>标签里加:key="$route.fullpath",这个属性的作用是监听路由变化,路由路径任何一个地方放生变化时会重新加载生命周期,但这种重新加载生命周期的方法太消耗性能,导致一些复用的组件也重新加载了,而且跳转的时候会闪一下体验也不好,所以不适合我们这种情况。

2.onBeforeRouteUpdate()监听

如此我们解决的办法可以用onBeforeRouteUpdate()方法去监听路由,当路由发生变化,再次调用api函数,因为跳转伴随着id的改变,所以我们需要将api函数加一个更新的id参数。

首先介绍一下onBeforeRouteUpdate(),他是一个专门监听路由的方法,我们可以试验一下当路由发生改变让他把to参数打印出来:

当我们点击跳转其他分类页时,打印出来:

所以我们可以通过to.params.id获取id,并将id传到api方法里:

封装业务方法

如果业务方法比较多显得组件页面很杂乱不利于维护,这个时候我们可以把方法封装到一个新文件里,在业务包category里新建一个包composables然后里面写js方法:

把原来繁琐的数据封装到新的方法js里,需要什么数据返回出来再在组件里取就行了

对路由布局的再次认识(重要)

页面的总布局是layout,layout分为这几个部分:

<RouteView/>是变化的,其他标签组件都是在布局上钉死的

通过点击header上的选项来显示<RouteView/>的页面,我们这里有分类页面和首页,两者是并列关系,<RouteView/>就是我们动态表演的区域,切首页就是显示首页组件,切其他的就显示其他分类组件。这里引申出一个名词叫一级导航,首页>居家的那个,可能有些无趣,他们并不是隶属关系而是并列关系,仅仅代表着我们浏览的顺序,因此,二级导航也同理,“首页”“一级导航”“二级导航”都在<RouteView/>的展现,这一点从路由文件中很容易就能看出:

二级导航展示

需要发送拿到二级导航数据的请求,所以先写api:


新建二级导航组件,将静态模版引入然后拿到二级导航数据:

动态获取一级二级导航的id和name:

效果:

二级导航内容渲染(Post请求)

首先先写api,需要传参数,参数就是post要传的数据。

调用api方法,封装到js文件

页面调用:

table切换筛选商品(重要)

需求是根据点击table页的不同按照table的条件筛选商品

对应参数就是data里的sortField,这里用到了v-model双向绑定,也就是说,元素的值就是v-model的值,他们两可以互相改变,v-model代表的值改变时元素table的值也相应改变,元素table的值改变时v-model代表的值也会改变,双双影响。

v-model的值会与标签元素的name绑定

后面绑定了一个@tab-change内置属性,代表着当table值改变时我们自定义的操作,首先我们可以看一下,当我们点击最高人气也就是对应的name=orderNum,打印出来的什么

果然改变:

这个时候我们就可以再次发送新的请求了,因为data里的排序值sortField已经改变,再次调用访问方法就行了:

列表无限滑动(infinite-scroll监听)

功能实现(...的使用)

解决的问题是,当我们下滑动浏览完第一页的数据时,我们要把第二页的数据也加载出来,如此一直往下滑的会不断填充后面的数据知道把数据全部加载完。

解决需求的原理是infinite-scroll监听,当监听的元素滑动到底时触发的一个自定义操作,我们只需要在这个自定义操作里写page++的请求就行了:

注意这个数组前的...是表示将数组展开,可用于拼接数组或者sum()计算等

BUG解决

再往下滑动的过程中会出现bug,那就是我们更改了page的值,如果我们店table其他页签也会用到这个已经更改的page,但是我们点击其他table肯定是从page=1开始浏览,所以我们在发请求的方法里在发送请求语句前洗衣歌data.value.page =1,相当于给他初始化一下。

切换分类或首页自动回到顶部

在router.js里面设置,当我们滑轮下滑浏览到中间时,点击其他模块还会停留在中间,我们只需要设置路由跳转时每次都回到顶部就可以。

*******详情页detail**********

详情页渲染(重要,内含可选链)

当页面点击具体某个商品时跳转的页面,是公共的,正在很多地方都可以挑,比如说首页里的那几个模块:

轮播图上的

新鲜好物上的

等等等等,做法是先写组件,新建Detail包下index.vue组件,把静态模版引入;然后写路由,

将上述的组件在的<RouterLink >中配置跳转路径,举例为:

然后可以写详情页的渲染,前面还是一样,写api:

然后根据路由里的id去获取商品goods的信息:

上述都很常规但是在详情页的返回数据可以看到:

那我们如果想动态渲染面包屑并且能让他点击跳转我们就得拿到goods.categories[1]数组的值,那么问题就来了,这个值可能还没拿到我们就调用了,这会导致出现undefined错误

这可能是由于数据量太大,我们发送请求又是异步操作,我们组件拿数据的时候返回数据还没准备好,导致接受返回数据的goods对象是空的{},因此会报undefined。这个时候有两个办法:

1使用可选链

我大概看了一下,确定应该只能访问goods对象的第一层数据,像“goods.salesCount”,“goods.commentCount”,“goods.categories”等等

但如果再往下“.”或者使用goods对象属性数组的具体哪个下标[]就不行比如

“goods.desc.length”,“goods.categories.[0]”等等,这个时候我们就可以用"?."或者“?”的形式来先确保一下他到底有没有,有则渲染没有则等。

2.if语句控制渲染

像可选链可以保证一个元素两个元素的使用,但是多了我们还要一个个地去写“?.”么,那太麻烦了,所以我们直接可以在盒子上写if判断,若内容属性存在那么这盒子再开始渲染,否则就等,等到内容确保接到以后再渲染(当然不可能等太久,都是微观时间长度我们肉眼几乎看不出来),如图:

这样就行了。总之,建议直接v-if,少的话可选链也行

小问题之文本太长裁剪显示

我点的时候发现面包屑的最后是描述商品,有的太长好几行,这肯定是不行的,所以我设定超过六个字就截取前六个字然后+...,如此我们可以用正则表达式:

这是效果:

热榜实现(重点,map匹配赋值,组件代码可复用性)

需求内容很简单,就是热榜,热榜又有两个一个是24小时热榜,一个是周热榜,传递的参数不同而不同:

实现逻辑

先写api,不过这个limit=3就相当于写死了,传参不需要传

写公用DetailHot组件,因为24小时榜和周榜是分开的要用两次,两次的框架又是一样:

重点来了,我们怎样写才简单呢,因为每次的请求调用的方法都一样,而且还要辨别两个标题,这个时候我么可以看到:

这个type可以辨别两次不同的请求,当然可以顺便辨别标题,所以我们只需要传入一个hotType等于1或2,既可以代表type的请求参数,又可以通过map赋值来进行标题辨别

map匹配赋值

这个写法要注意,因为第一次遇到

一个点是script中获取父传给子的内容获取(template中直接{{}}里面写),是:

const props = definedProps({

xxxxxxxxxx

})

然后调用

const title = computed(()=>{TYPEMAP[props.hotType]})

同时传入的hotType又可以当请求参数type:

剩下的只需页面引入这个公共组件,然后传入hotType就行了:

代码可复用性

一开始我直接写了两个方法,一个传type=1,一个传type=2,这是很愚蠢的,如果这样写,往后有变东可能有type=3456789,难道还要多写那么多方法么?肯定是不行的,因为参数变量是一样的,方法肯定只写一个。通过变量的方式动态获取参数值解决传不同参数值,方法只写一个。

**************登录业务****************

登录页面(router跳转)

没什么好说的,引入静态模板,注意的是在首页里的“请先登录”选项,点击跳转到登录页:

这里用到了router跳转

校验(非常重要)

表单输入校验

对表单提交的输入框数据进行校验,首先写一个数组与form进行v-model动态绑定:

然后写校验规则rules:

rules与表单绑定:

自定义校验

使用validator属性,这里的value是指的pops绑定的item的值,如果是文本则返回文本,如果是旋钮则返回true和false

表单统一校验

点登录按钮的绑定事件。事件会重新执行所有的校验:

登录业务实现

写api,

将表单数据拿到打包发送后端请求:

比较重要的两个点:

1.route和router的区别:使用useRoute是取参数,使用useRouter是调函数。

2.提示词组件以及css:

import { ElMessage } from 'element-plus'

import 'element-plus/theme-chalk/el-message.css'

如果后端通了但返回失败,那么可以从拦截器里写回调函数,并拒绝继续执行

Pinia用户状态管理(极其重要!!!!!!!!)

状态管理实现

用户登录后,要根据用户的信息展示用户的账号内容,比如说每个账号的购物车不同,当然要展示登录用户的购物车,这样的话那就有许多组件都会依赖于用户的登录信息,这种多组件共同享用的公共数据必须要用全局状态管理的方式。

在pinia里面写一个user的状态管理js,里面写登录业务以及接收用户信息userInfo:

然后登录页面去调这个getUserInfo方法:

登陆成功后,到了主页,顶部的位置要显示用户名,这个时候就要引入状态管理js,拿出userInfo看一下有没有值:

所以登录后会发现,名字已经显示:

持久化!!!!!!

当用户登录后,刷新页面userInfo数据就没了,因为这是从登录页的带进去的,但是我们正常使用时,肯定在一定有效期内刷新仍然保留用户信息,这个时候就要做持久化了

首先下载pinia-plugin-persistedstate持久化设置

指令:cnpm i pinia-plugin-persistedstate -S

main里的配置

此时登录后,local storage中会显示缓存内容

如果这个被清空,那么刷新页面就会没有userInfo数据

反之则有数据,所以加载页面时会先访问local storage中的值。

请求前设置携带token

请求前在请求头中设置携带token,发给后端鉴别一下token有没有失效,必须有了有效的token才能进行请求和访问,做法就是在拦截器里设置:

这样的话才请求前,会将token写在请求头里面,后端会进行识别:

退出登录(登录状态清零)

很好理解也很简单,就是确认退出按钮点击后,将userInfo的内容清零,然后跳转登录页

首先在pinia里写清零方法:

退出登录后调用clearUserInfo方法并且跳转登录页

token失效处理

通过token失效了,那么在拦截器里接收失败响应,然后把用户信息清空,然后跳转登录页

这里要注意在这里不能用useRouter和useRoute,要用router:

*********购物车*********

购物车的话,分为两种情况,第一种情况是用户不登录添加到本地购物车,第二种情况是,用户登录直接添加到购物车,第一种情况完成后如果用户登录,则自动合并到线上购物车。

本地购物车的实现

首先本地购物车要依赖于状态管理,在pinia中写一个购物车状态管理:

然后在细节页面,点击数量与count绑定,然后点击购物车会先看一下有没有选规格或尺码,这里早就实现了,当没选规格和尺码的时候返回空数组,只有选全了才会返回整个商品信息,然后可以判断下数组有没有id,有的话就拼数据发送,没有的话报警告:

然后pinia接收,根据sku规格和尺码的不同skuId都会不同,所以不同尺码和不同规格的同种商品也会新增购物车条数。

最后就是持久化缓存。

@click传两个参数的方式

对象只保留某几个属性的方法

根据id删除对象某个数据

*************结算页面*************

弹出框

v-model控制是否出现

点击确定或取消绑定的方法控制v-model变量是否为true

绑定选择地址的常亮

盒子被点击时,绑定方法传当前item的参数,在方法里把这个参数赋给我们定义的值,然后控制常亮当item的值与我们定义的值id相等则展示常亮样式:

其中那个activeAdd就是我么提前定义的数组

效果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值