===========================这是华丽丽的分割线~~=========================
vue传参方式有:query、params+动态路由传参。
说下两者的区别:
1.query通过path切换路由,params通过name切换路由
//query通过this.$route.query接收参数created(){constid=this.$route.query.id;}//params通过this.$route.params来接收参数created(){constid=this.$route.params.id;}
params+动态路由的url方式:/detail/123
4.params动态路由传参,一定要在路由中定义参数,然后在路由跳转的时候必须要加上参数,否则就是空白页面:
{path:'/detail/:id',name:'Detail',component:Detail},注意,params传参时,如果没有在路由中定义参数,也是可以传过去的,同时也能接收到,但是一旦刷新页面,这个参数就不存在了。这对于需要依赖参数进行某些操作的行为是行不通的,因为你总不可能要求用户不能刷新页面吧。例如:
上面的这个报错大家都不会陌生,报错是说没有访问权限(跨域问题)。本地开发项目请求服务器接口的时候,因为客户端的同源策略,导致了跨域的问题。
下面先演示一个没有配置允许本地跨域的的情况:
可以看到,此时我们点击获取数据,浏览器提示我们跨域了。所以我们访问不到数据。
那么接下来我们演示设置允许跨域后的数据获取情况:
注意:配置好后一定要关闭原来的server,重新npmrundev启动项目。不然无效。
好了,最后附上proxyTable的代码:
axios的封装,主要是用来帮我们进行请求的拦截和响应的拦截。
在请求的拦截中我们可以携带userToken,post请求头、qs对post提交数据的序列化等。
在响应的拦截中,我们可以进行根据状态码来进行错误的统一处理等等。
axios接口的统一管理,是做项目时必须的流程。这样可以方便我们管理我们的接口,在接口更新时我们不必再返回到我们的业务代码中去修改接口。
为什么要使用按需加载的方式而不是一次性全部引入,原因就不多说了。这里以vant的按需加载为例,演示vue中ui库怎样进行按需加载:
libraryDirectory{"plugins":[//这里是原来的代码部分//…………//这里是要我们配置的代码["import",{"libraryName":"vant","libraryDirectory":"es","style":true}]]}在main.js中按需加载你需要的插件:
//按需引入vant组件import{DatetimePicker,Button,List}from'vant';使用组件:
//使用vant组件Vue.use(DatetimePicker).use(Button).use(List);最后在在页面中使用:
我们正常写的所有样式,都会被加上[data-v-23d425f8]这个属性(如1所示),但是第三方组件内部的标签并没有编译为附带[data-v-23d425f8]这个属性。所以,我们想修改组件的样式,就没辙了。怎么办呢,有些小伙伴给第三方组件写个class,然后在一个公共的css文件中或者在当前页面再写一个没有socped属性的style标签,然后直接在里面修改第三方组件的样式。这样不失为一个方法,但是存在全局污染和命名冲突的问题。约定特定的命名方式,可以避免命名冲突。但是还是不够优雅。
作为一名优(强)秀(迫)的(症)前(患)端(者),怎么能允许这种情况出现呢?好了,下面说下优雅的解决方式:
通过深度选择器解决。例如修改上图中组件里的van-ellipsis类的样式,可以这样做:
.van-tabs/deep/.van-ellipsis{color:blue};编译后的结果就是:
这样就不会给van-ellipsis也添加[data-v-23d425f8]属性了。至此你可以愉快的修改第三方组件的样式了。
当然了这里的深度选择器/deep/是因为我用的less语言,如果你没有使用less/sass等,可以用>>>符号。
我在a页面写一个定时,让他每秒钟打印一个1,然后跳转到b页面,此时可以看到,定时器依然在执行。这样是非常消耗性能的。如下图所示:
解决方法1:
首先我在data函数里面进行定义定时器名称:
data(){return{timer:null//定时器名称}},然后这样使用定时器:
this.timer=(()=>{//某些操作},1000)
最后在beforeDestroy()生命周期内清除定时器:
beforeDestroy(){clearInterval(this.timer);this.timer=null;}方案1有两点不好的地方,引用尤大的话来说就是:它需要在这个组件实例中保存这个timer,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。解决方案2:
该方法是通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。以下是完整代码:
我们在做手机端时,适配是必须要处理的一个问题。例如,我们处理适配的方案就是通过写一个rem.js,原理很简单,就是根据网页尺寸计算html的font-size大小,基本上小伙伴们都知道,这里直接附上代码,不多做介绍。
;(function(c,d){vare=document.documentElement||document.body,a="orientationchange"inwindow"orientationchange":"resize",b=function(){varf=e.clientWidth;e.style.fontSize=(f>=750)"100px":100*(f/750)+"px"};b();c.addEventListener(a,b,false)})(window);这里说下怎么引入的问题,很简单。在main.js中,直接import'./config/rem'导入即可。import的路径根据你的文件路径去填写。
这里我说一下vue-awesome-swiper这个轮播组件,真的非常强大,基本可以满足我们的轮播需求。swiper相信很多人都用过,很好用,也很方便我们二次开发,定制我们需要的轮播效果。vue-awesome-swiper组件实质上基于swiper的,或者说就是能在vue中跑的swiper。下面说下怎么使用:
项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。而生成的.map后缀的文件,就可以像未加密的代码一样,准确的输出是哪一行哪一列有错可以通过设置来不生成该类文件。但是我们在生成环境是不需要.map文件的,所以可以在打包时不生成这些文件:
在config/index.js文件中,设置productionSourceMap:false,就可以不生成.map文件
开发移动端项目,点击事件会有300ms延迟的问题。至于为什么会有这个问题,请自行百度即可。这里只说下常见的解决思路,不管vue项目还是jq项目,都可以使用fastClick解决。
安装fastClick:
cnpminstallfastclick-S在main.js中引入fastClick和初始化:
importFastClickfrom'fastclick';//引入插件FastClick.attach(document.body);//使用fastclick
为什么选项要有统一的书写顺序呢?很简单,就是要将选择和认知成本最小化。
副作用(触发组件外的影响)
全局感知(要求组件以外的知识)
组件类型(更改组件的类型)
模板修改器(改变模板的编译方式)
模板依赖(模板内使用的资源)
组合(向选项里合并属性)
接口(组件的接口)
本地状态(本地的响应式属性)
事件(通过响应式事件触发的回调)
非响应式的属性(不依赖响应系统的实例属性)
如果你是vue-cli初始化的项目,会默认安装webpack-bundle-analyzer插件,该插件可以帮助我们查看项目的体积结构对比和项目中用到的所有依赖。也可以直观看到各个模块体积在整个项目中的占比。很霸道有木有~~
路由懒加载可以帮我们在进入首屏时不用加载过度的资源,从而减少首屏加载速度。
路由文件中,
非懒加载写法:
importIndexfrom'@/page/index/index';exportdefaultnewRouter({routes:[{path:'/',name:'Index',component:Index}]})路由懒加载写法:
exportdefaultnewRouter({routes:[{path:'/',name:'Index',component:resolve=>require(['@/view/index/index'],resolve)}]})
spa这种单页应用,首屏由于一次性加载所有资源,所有首屏加载速度很慢。解决这个问题非常有效的手段之一就是前后端开启gizp(其他还有缓存、路由懒加载等等)。gizp其实就是帮我们减少文件体积,能压缩到30%左右,即100k的文件gizp后大约只有30k。
vue-cli初始化的项目中,是默认有此配置的,只需要开启即可。但是需要先安装插件:
//2.0的版本设置不一样,本文写作时为v1版本。v2需配合vue-cli3cnpmicompression-webpack-plugin@1.1.11然后在config/index.js中开启即可:
build:{//其他代码…………productionGzip:true,//false不开启gizp,true开启//其他代码}现在打包的时候,除了会生成之前的文件,还是生成.gz结束的gzip过后的文件。具体实现就是如果客户端支持gzip,那么后台后返回gzip后的文件,如果不支持就返回正常没有gzip的文件。
**注意:这里前端进行的打包时的gzip,但是还需要后台服务器的配置。配置是比较简单的,配置几行代码就可以了,一般这个操作可以叫运维小哥哥小姐姐去搞一下,没有运维的让后台去帮忙配置。
这样一个场景:有三个页面,首页/或者搜索页,商品分类页面,商品详情页。我们希望从首页进入分类页面时,分类页面要刷新数据,从分类进入详情页再返回到分类页面时,我们不希望刷新,我们希望此时的分类页面能够缓存已加载的数据和自动保存用户上次浏览的位置。之前在百度搜索的基本都是keep-alive处理的,但是总有那么一些不完善,所以自己在总结了之后进行了如下的实践。
编译前:
如果你希望scoped样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用>>>操作符:
.parent[data-v-f3f3eg9].child{/*...*/}而对于less或者sass等预编译,是不支持>>>操作符的,可以使用/deep/来替换>>>操作符,例如:.parent/deep/.child{/*...*/}
==================================
后面会继续更新:
------------华丽丽的分割线-------------------------华丽丽的分割线-------------------------华丽丽的分割线-------------------------华丽丽的分割线-------------------------华丽丽的分割线-------------------------华丽丽的分割线-------------------------华丽丽的分割线-------------
在我们的编辑器终端中全局安装:
cnpminstallhiper-g使用:终端输入命令:hiper测试的网址
平时我们查看性能的方式,是在performance和network中看数据,记录下几个关键的性能指标,然后刷新几次再看这些性能指标。有时候我们发现,由于样本太少,受当前「网络」、「CPU」、「内存」的繁忙程度的影响很重,有时优化后的项目反而比优化前更慢。
如果有一个工具,一次性地请求N次网页,然后把各个性能指标取出来求平均值,我们就能非常准确地知道这个优化是「正优化」还是「负优化」。
hiper就是解决这个痛点的。
在vue中获取数据有两种方式,引入尤大大的话就是:
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
从技术角度讲,两种方式都不错——就看你想要的用户体验是哪种。那么我们来实践一下这两种获取数据的方式,以及用户体验优化的一点思考。
一、首先是第一种:导航完成之后获取,这种方式是我们大部分都在使用的,(因为可能一开始我们只知道这种方式^V^)。使用这种方式时,我们会马上导航和渲染组件,然后在组件的created钩子中获取数据。这让我们有机会在数据获取期间展示一个loading状态,还可以在不同视图间展示不同的loading状态。获取数据大家都会,这里说下用户体验的一些东西:
那么,我们的页面是要有这基本的三个部分的,放代码:
二、第二种方式:导航完成之前获取
这种方式是在页面的beforeRouteEnter钩子中请求数据,只有在数据获取成功之后才会跳转导航页面。
beforeRouteEnter(to,from,next){api.article.articleDetail(to.query.id).then(res=>{next(vm=>{vm.info=res.data;vm.loadFinish=true})})},1.大家都知道钩子中beforeRouteEnter钩子中this还不能使用,所以要想进行赋值操作或者调用方法,我们只能通过在next()方法的回调函数中处理,这个回调函数的第一个参数就代表了this,他会在组件初始化成功后进行操作。
2.我想,很多时候我们的api或者axios方法都是挂载到vue的原型上的,由于这里使用不了this,所以只能在页面组件内引入api或者我们的axios。
3.赋值操作也可以写在method方法中,但是调用这个赋值方法还是vm.yourFunction()的方式。
4.为空提示、断网处理等都和第一种方式一样,但是,由于是先获取到数据之后再跳转加载组件的,所以我们不需要在预期的页面内展示骨架屏或者loading组件。可以,我们需要在当前页面进入之前,即在上一个页面的时候有一个加载的提示,比如页面顶部的进度条。这样用户体验就比较友好了,而不至于因为请求的s速度慢一些导致半天没反应而用户又不知道的结果。全局的页面顶部进度条,可以在main.js中通过router.beforeEach(to,from,next){}来设置,当页面路由变化时,显示页面顶部的进度条,进入新路由后隐藏掉进度条。
其实说到了这里,那么骨架屏的事情也就顺带已经解决了,一般页面骨架屏也就是一张页面骨架的图片,但是要注意这张图片要尽可能的小。
说到父子组件的通信,大家一定都不陌生了:父组件通过props向子组件传值,子组件通过emit触发父组件自定义事件。但是这里要说的是父子组件使用v-model实现的通信。相信大家在使用别人的组件库的时候,经常是通过v-model来控制一个组件显示隐藏的效果等,例如弹窗。下面就一步一步解开v-model的神秘面纱。抓~~稳~~喽~~,老司机弯道要踩油门了~~~
提到v-model首先想到的就是我们对于表单用户数据的双向数据绑定,操作起来很简洁很粗暴,例如:
以上原理实现方法,写法1:
父组件用法:
以上这种方式实现的父子组件的v-model通信,虽可行,但限制了我们必须popos接收的属性名为value和emit触发的必须为input,这样就容易有冲突,特别是在表单里面。所以,为了更优雅的使用v-model通信而解决冲突的问题,我们可以通过在子组件中使用model选项,下面演示写法2:
父组件写法:
最后,实现双向数据绑定的方式其实还有.sync,这个属性一开始是有的,后来由于被认为或破坏单向数据流被删除了,但最后证明他还是有存在意义的,所以在2.3版本又加回来了。
例如:父组件:
这里再提一下:
监听$emit触发的自定义事件,上面已经有过用法了,监听钩子函数,在上面的定时器那块也有演示到。监听钩子函数的场景使用的不多,但是还是要知道的。
例如:父组件:
这里说的路由拆分指的是将路由的文件,按照模块拆分,这样方便路由的管理,更主要的是方便多人开发。具体要不要拆分,那就要视你的项目情况来定了,如果项目较小的话,也就一二十个路由,那么是拆分是非常没必要的。但倘若你开发一些功能点较多的商城项目,路由可以会有一百甚至几百个,那么此时将路由文件进行拆分是很有必要的。不然,你看着index.js文件中一大长串串串串串串的路由,也是很糟糕的。
首先我们在router文件夹中创建一个index.js作为路由的入口文件,然后新建一个modules文件夹,里面存放各个模块的路由文件。例如这里储存了一个vote.js投票模块的路由文件和一个公共模块的路由文件。下面直接上index.js吧,而后在简单介绍:
importVuefrom'vue'importRouterfrom'vue-router'//公共页面的路由文件importPUBLICfrom'./modules/public'//投票模块的路由文件importVOTEfrom'./modules/vote'Vue.use(Router)//定义路由constrouter=newRouter({mode:'history',routes:[...PUBLIC,...VOTE,]})//路由变化时router.beforeEach((to,from,next)=>{if(document.title!==to.meta.title){document.title=to.meta.title;}next()})//导出exportdefaultrouter首先引入vue和router最后导出,这就不多说了,基本的操作。
这里把router.beforeEach的操作写了router的index.js文件中,有些人可能会写在main.js中,这也没有错,只不过,个人而言,既然是路由的操作,还是放在路由文件中管理更好些。这里就顺便演示了,如何在页面切换时,自动修改页面标题的操作。
而后引入你根据路由模块划分的各个js文件,然后在实例化路由的时候,在routes数组中,将导入的各个文件通过结构赋值的方法取出来。最终的结果和正常的写法是一样的。
然后看下我们导入的vote.js吧:
/***投票模块的router列表*/exportdefault[//投票模块首页{path:'/vote/index',name:'VoteIndex',component:resolve=>require(['@/view/vote/index'],resolve),meta:{title:'投票'}},//详情页{path:'/vote/detail',name:'VoteDetail',component:resolve=>require(['@/view/vote/detail'],resolve),meta:{title:'投票详情'}}]这里就是将投票模块的路由放在一个数组中导出去。整个路由拆分的操作,不是vue的知识,就是一个es6导入导出和结构的语法。具体要不要拆分,还是因项目和环境而异吧。
这里的路由用到了懒加载路由的方式,如果不清楚,文字上面有介绍到。
还有这里的meta元字段中,定义了一个title信息,用来存储当前页面的页面标题,即document.title。
兄弟们,抄家伙!上mixins!!!
import{u_fixed}from'./tool'constmixins={filters:{//保留两位小数mixin_fixed2(val){returnu_fixed(val)},//数字转汉字,16000=>1.60万mixin_num2chinese(val){returnval>9999u_fixed(val/10000)+'万':val;}}}exportdefaultmixins新建一个mixins.js,把我们需要混入的内容都写在里面,例如这里混入了filters,把常用的几个操作写在了里面,大家可以自行扩展。
例如,我现在可以直接在页面内使用我们的过滤操作{{1000|mixin_fixed2}}
先看下项目的config文件夹下的index.js文件,这个配置选项就好使我们打包后的资源公共路径,默认的值为‘/’,即根路径,所以打包后的资源路径为根目录下的static。由此问题来了,如果你打包后的资源没有放在服务器的根目录,而是在根目录下的mobile等文件夹的话,那么打包后的路径和你代码中的路径就会有冲突了,导致资源找不到。
所以,为了解决这个问题,你可以在打包的时候把上面这个路径由‘/’的根目录,改为‘./’的相对路径。
找到这个位置,添加一上配置,就是上图红框内的代码,就可以把它的公共路径修改为往上返回两级。这样再打包看下,就ok了!
最后再郑重说一点,如果你的路由模式是history的,那么打包放在服务器,必须要后台服务器的配合,具体的可以看官方文档,这点很重要。不然你会发现白屏啊等各种莫名其妙的问题。牢记!!!