堆积了两天一起发的,先祝大家节日快乐
后面任务很繁重,还有登录注册组件还有后台管理页面,真的繁重,我现在感觉每天全天时间都在学都不一定学得完,主要想在六月一号之前把整个项目过一遍。看看能不能创造奇迹
一.防抖和节流
抛出一个问题,就是我们的三级联动,正常情况你慢慢的去滑动是没有bug的,但是当你快速的从上往下滑一圈,你会发现只会触发几个标题,如果这个时候我们的回调也就是滑动的事件函数里面有大量的业务逻辑,他会执行完一个再去执行另一个,但是你已经滑完很久了,所以这个时候页面就会有卡顿现象
1.防抖
什么是防抖?防抖就是一个事件触发多次,最终只会有触发一次的结果。
我们这里防抖和节流采用一个 Lodash的插件来完成,但是自己也要懂防抖和节流的一个原理,首先防抖大概封装是这样的,定义一个函数,接受的参数为我们事件的回调和定时器的等待时间,里面先清除一个定时器,然后定义一个定时器,定时器里面调用我们的事件回调,当我们事件触发就去执行这个防抖函数,意思就是比如我们有一个键盘输入事件,当我们一输入就会清除定时器,在执行定时器,定时器里面的回调才会打印输出我们输入的内容,要等个500ms,那么在这500ms之内你再次输入,又不会打印输出了因为定时器被清除了,又会重新计时500ms,这就是防抖的一个思想。
注意这个clear只能写在函数的外面
2.节流
防止触发频率过快,在一个时间段内只执行一次。
一定要把防抖和节流做一个区别:防抖是可以触发n次,但是只会以你不触发后的最后一次为准,而节流是可以触发n次,但是限制了你的触发频率,所以n次内其实只触发了不到n次
自己封装就像这样,需要一个节流阀flag,当我点击一次去判断节流阀,判断成功进来先把节流阀关闭,在执行定时器,单位时间内执行完节流阀才会打开,所以这段时间,你怎么点击都执行不了回调,这里不需要清除定时器,因为定时器会自动到时间就结束
3.三级联动节流
首先要注意一点,vue脚手架默认是给你安装好了lodash
怎么来用具体可以参照他的中文文档,它有两种引入方式,以节流为例,在文档里面找到_.throttle,最主要的是引入如果是全部引入,因为他还有很多的方法,数组求和等等,直接以_命名他
然后用的时候就像官网文档一样用
但是我们这里如果就想用一下节流就没必要全部导入了,直接导入我们想要的节流,注意怎么导入的,在lodash找到throttle
然后用的时候也是直接用,还要注意一下这里要回归原始的es5的键值对的形式才可以使用这个节流
二.三级联动组件路由跳转
当我们呢点击三级联动的一二三级标题的时候,可以实现跳转到search页面,并且会把自己的id和标题当成参数传过去,所以这里就涉及到,路由的跳转,我们的这些标题又刚好是a标签用一个router-link就可以跳转过去,但是这样做会有一点不完善,因为我们一级标题还好后面二级三级那么多,每一个都转换为router-link,这个组件标签又是vue内部提供的,去点击一次还要进入vue内部处理一次,就会造成页面卡顿
那么就只有采用编程式跳转了,其实这里编程式跳转也不太好,为什么?我们每一个a标签都要来一个点击事件,是不是跟上面这种情况差不多了
这里最好的解决办法是,用编程式路由导航➕事件委托来做,我们给他们共同的父元素来一个点击事件,他们的父元素只有一个不就可以解决了吗
现在就要去解决一些问题了
-
第一个问题:我们下面这么多元素,怎么就能确保点击的是a标签?
我们可以把每一级标题添加一个自定义属性data-categoryName,当我们点击的时候再通过e.target.dataset.categoryName来判断点击的元素是否是a标签
-
第二个问题:怎么来区分点击的是一级、二级还是三级标题,因为我们传过去的参数有id1、id2、id3,这里做一下改造,将我们前面获取到的自定义属性采用解构赋值,注意一下,我们虽然自定义属性是驼峰命名,但是最终到页面会给你转化为小数 ,为一个变量
我们给每个标题同样通过自定义属性给他们的id给到,同时以解构赋值接受给变量
得到参数之后,因为我们是编程式导航嘛,所以最终肯定是要通过push方法传出去值的,判断为a标签后定义一个对象里面放的是要跳转的地址也就是search,然后依次判断是否是id1,是就来个query的对象里面正常写一般写在push里面query里面的参数形式,最后都判断完了,把query对象放到跳转地址这个对象里面,直接push即可
三.search模块商品分类
将三级联动全局组件直接拿过来,然后给三级联动来一个条件渲染,默认为true
这里要考虑一点,当我们进入home那么在search里面的组件就会被销毁,当我们进入search,在home里面的组件就会被销毁,所以当我们切换到search的时候,typenav的mounted又会被执行一次,那么我们就可以把逻辑写在这里,就是当$route.path!=home这个时候就把条件改为false
然后鼠标移入分类,又改为true
移出可以接着上次在共同父级设置的移出这里做,同样还是要判断一下是不是home页面,因为home不能隐藏
1.过渡动画
回顾一下过渡动画,要写vue的过渡动画必须有一个前提 那就是必须要有v-for或者v-show,由transition标签包裹,然后可以写自己的name,css部分name-enter,name-enter-to,离开是leave,最后时间以及要变化哪些都写在active里面
四.三级联动列表优化
现在还有什么优化的地方,有一个地方,我们刚才说过,切换一次路由,里面的所有组件都会被销毁,包括三级联动组件,所以频繁切换,三级联动也在频繁销毁频繁执行mounted,而我们的mounted里面写了一个东西,dispatch派发到vuex里面的actions,并且这里还有逻辑处理,就是发送了ajax请求,所以问题就出现在这里,我们切换一次路由组件,就会发送一次ajax请求,但针对于这里来说,这里三级联动的请求,只需要一次就够了,也没有改,所以就需要换位置,他的统计都不只是只执行一次,继续往父级走,那就是App组件,就写在这里,因为app组件只会执行一次,在这里派发到actons,typenav需要的时候就state来取就是
五.合并参数
就是将我们的query和我们的params参数合并起来。
这里主要是考虑两种场景,一个是点击搜索会有params的参数,这个时候我点击分类标题会将他的query参数合并上去。因为我们是后点的三级联动列表,所以逻辑应该在后点这里合并上去
然后还有一种场景,我已进入主页就由三级联动标题进入搜索页,然后我再根据搜索页去搜索更加详细的查找内容,这个时候后点的是header里面的搜索按钮,所以逻辑在header组件去做
六.mockjs模拟数据
因为我们现在首页组件当中除了三级联动后面的业务没有接口,包括轮播图等,这个时候就需要一个模拟数据,mockjs是一个插件,专门用来生成数据,拦截ajax请求
1.创建数据
使用方法:
-
首先src创建一个mock文件夹
-
第二步开始创建假数据,我们的数据都是json格式,所以直接在mock里面创建json文件,放入数据,注意格式化一下,不能留有空格,不然跑不起来
这是轮播图的模拟数据,注意点mock数据需要的图片要放到public文件夹下,因为只有public下面的文件打包的时候才会原封不动输送出去
-
第三步创建mockServer.js通过mockjs来模拟出数据
注意引入Mock名字必须是大写首字母,它是一个对象,后面引入两个json文件,为什么没有export,这里可以直接导入?因为webpack是默认对图片、json文件导出的,这些文件是默认导出的,不用export
-
然后就可以调用Mock里面的mock方法,两个参数,第一个是请求路径,第二个是请求数据
-
然后我们的mockServer.js文件需要在入口文件引入,表示至少需要执行一次,我们的模拟数据才会出现,这里不需要暴露,直接在入口文件引入这个js文件,表示直接执行
2.应用数据
这样一来我们的模拟数据就创建好了,接下来就是怎么来用他的问题了。
他虽然不会发起真正的ajax请求,会被浏览器拦截,但是可以把他当做真正的请求看,所以第一步我们先去接口统一管理里面,给他来一个请求和响应的拦截器,并且用axios创建一个他的实例,而且baseurl要为/mock
然后去接口统一管理处,创建一个获取banner的接口函数
这个时候回到我们需要轮播图的组件,给他的mounted派发actions,把我们的数据用vuex管理起来,同时在actions获取ajax请求
去actions发起ajax请求
可以在打印台看到,我们确实得到了数据,最重要的一点没有网络请求,这就是ajax的拦截
这个时候mutations将我们的数据给上,同时state定义一个数组装这个数据
在我们轮播图组件拿到这个数据,通过计算属性
3.轮播图(方案一)
我们用swiper实现轮播图功能
-
第一步下载导入,swiper需要导入两个,一个是css样式,由于我们下面的组件也会用到这个样式,所以直接导入在入口文件,还有一个是js哪里要用就导入在哪
-
实现swiper轮播图第二步就是要定义好结构,结构这里最重要的是用我们的mock模拟出来的数据,列表渲染出来
-
这个时候可以去用她的js逻辑了,但是要想一下这个定义在哪里,有一个很重要的前提,new Swiper首先必须页面上有这些结构,如果我们把new Swiper写在mounted,这个时候页面上确实有结构了,但是我们的列表循环里面的bannerList是通过ajax得来的,要知道ajax是典型的的异步函数,所以当我们开始new的时候,图片这些数据还没有开始遍历的,这个时候结构就不完整,轮播图的实现就有问题
这里有一个比较笨的解决方法,就是开一个定时器,将new swiper放在里面
4.轮播图(方案二)
这也是最佳的完美解决方案,我们首先可以通过watch来做,去观察bannerList的值,当他发生变化就已经代表了ajax请求回来了,所以这个时候再去new swiper
当然只是这样还是不够,为什么?这个时候我们的bannerList确实有值了,异步操作也已经完成了,但是这个时候要把这些数据拿去v-for列表循环又需要一定的等待时间,这个时候你直接去new了,其实页面上的结构还是没有的,这里真正完美的做法是watch+nextTick来做
nextTick:官网的解释在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
其意思就是当DOM完全呈现之后包括循环结束之后才会去执行nextTick的回调函数
七.floor组件模拟数据
又要给一个组件动态渲染数据了,由于前面已经通过mock模拟出来了数据,这个时候需要先将数据的接口写进接口统一管理
然后vuex三部曲走起,因为我们的接口请求都是在actions里面来做的
这个时候我们应该去去派发actions了,但是我们先观察一下这个数据的结构,它是一个数组里面有两个对象,而我们的floor这个组件当时也复用了两次,说明是不同的两个对象,如果我在floor组件里面去派发actions到时候来的数据就是一个数组有两个对象,你用这个也不行用那个也不行,都体现不了复用性,这里应该在他们父级也就是放他们组件标签处来接收数据,并且通过v-for遍历组件标签
接下来遍历我们的floor组件标签,同时父传子,通过props把属于他们自己的那个对象传过去
1.floor数据渲染
注意点:
- swiper5及以前的版本最大的容器叫做swiper-container不是现在官网上的swiper
-
还有一个点我这里直接在floor组件的mounted函数里面new swiper是可以实现的,为什么?
要知道当时不能实现是因为在mounted里面还发了ajax这个异步操作请求,所以那个时候接着来一个new swiper是没有完整结构的,但是在这里我们的ajax是在父级home这个组件完成的,并且都已经把数据通过props发过来了,你在floor里面也没有影响结构的异步了,所以直接在这里的mounted new swiper就没问题
八.共用组件carousel
当我们开发项目时,如果看到某一个组件在很多地方都使用,而且结构包括逻辑这些都一样的话,就可以拆分为全局组件,大家直接复用,就比如这里的上下两个轮播图。
当然大的轮播图是用watch来做的,我们也可以把小轮播图用watch来做,你会发现监视不了这个数据
因为我们这个数据是父组件派发然后传过来的,可以说过来的时候就已经是那个样子了,从没有变过,没有变动还怎么去监视,这里可以添加immediate让监视属性一开始就监视一次。
既然是一开始就执行,那时候肯定floor也为空,不然怎么会监视到数据变化,所以还是要配合nextTick让数据都有了到DOM上了再来new
这样我们就开始拆分组件了,全局组件放在components下面,关键步骤在于我们需要接受一个从外部传进来的轮播图数据,并且全局组件监听的就是他
然后在我们模板上遍历的也是他
注册全局组件
将我们的组件标签运用上,同时将数据传过来
九.Search模块
1.静态组件
完成一个组件开发,还是那个套路显示静态页面完成,然后拆分组件,发请求,vuex三部曲,组件获取数据并渲染出来
2.Search模块vuex操作
我们已经完成了静态组件以及拆分,下一步就是ajax的api请求,因为是api开头的所以可以直接用封装好的axios来获取请求,但是还是要去接口统一管理封装一下函数,因为这里我们search搜索商品的参数很多,所以是用一个对象传进来,而且正常的写法data后面本身就是一个对象,所以这里params传进来的参数至少都是一个空对象
然后就可以去vuex三部曲,通过接口文档可以看到,我们返回的结果是一个对象,所以这里直接数据定义为对象形式,但是这里还测试不了,用到了我们之前还没怎么用过的dispatch的第二个参数就是我们要传进来的值
3.数据动态展示
我们可以看到获取过来的数据有很多是数组形式的,这个时候就要用到 getters了,项目中的getters主要作用就是简化仓库中的数据,就是当我们state定义的数据里面还嵌套多层数据,这个时候就可以用gettes来简化,然后我们到时候用的话,就会简便的多
所以把上面这个数组通过gettes来获取,注意的是gettes可以接受一个参数,就是当前仓库中的state
同样也是写在计算属性里面来获取
然后我们就可以开始先把下面商品详情页的数据动态展示了,先获取getters
在我们getters里面有一点要注意,虽然一般不会出现这种情况,就是怕别人没有网了,这个时候我们没有goodsList等这些数组,那么用一个亦或语法让其成为空数组以防网页崩溃
4.根据不同参数获取数据展示
要根据不同参数获取数据展示,那我们就需要将mounted里面发起ajax请求的派发封装为一个函数
但是这里发起一个空对象去搜索有点扯,我们需要把发给服务器的参数整起来,看接口文档可知,要发给服务器的参数为一个对象,所以我们可以把这个参数定义在data里面,然后再mounted之前传进去参数,自然而然mounted挂载之后就会是我们从主页通过点三级联动及搜索页面的详情页或者是从主页通过搜索得来的详情页
当然我们不能就以这个数据发过去,所以在mounted,也就是dispatch之前将真正的参数传进来,注意这里用到一个es6新增对象的方法Object.assign,他可以合并多个对象,如果有相同的就以后面的值为准,没有的就添加上最后形成一个新对象
5.search模块子组件动态开发
既然我们都做了serach的搜索了,search里面还有一个子组件,来展示品牌和一些标签选择的,这些数据也是顺带返回回来的所以顺便就做了,因为这两个数据是数组返回,所以直接getters,然后组件接受getters
6.再次请求获取数据
为什么要再次请求,因为我们刚才做的版本只是在进入search这个组件由于重新挂载了,所以只能请求mounted那一次,如果我要继续搜索呢?
这个时候,有一个关键的技术点,当我们每一次搜索或者点击三级联动列表的时候,我们的url是不是在变
而且以前我们都知道我们的响应数据一般是data里面或者计算属性里面,通过vue管理者工具可以看到,我们的$route其实也是响应式数据
所以关键点就在于这里,我们可以监听$route,然后把参数来一次assign方法合并,再次dispatch即可做到重复搜索的功能