"onload"和"onUnload"都是uniapp生命周期函数的一部分,它们主要用于处理页面的加载和卸载事件。两者之间的区别如下:onLoad()函数是页面初始化时即被触发的函数,即页面加载事件的回调函数。它在页面初次加载时执行一次,后续不再执行,所以在onLoad()函数内部一般进行一些初始化工作或者发送网络请求等操作。onUnload()函数是页面卸载时即被触发的函数,即页面卸载事件的回调函数。当页面被关闭或者跳转到另一个页面时,onUnload()函数会被触发。一般在onUnload()函数内部释放一些页面占用的资源、取消预订的定时器或者websocket等。在uniapp中,onLoad()和onUnload()函数都是可选的,如果没有定义,则不会执行。要注意的是,当页面被销毁时,onUnload()函数的执行优先于beforeDestroy()函数。因此,一般在beforeDestroy()内部进行销毁前的最后一次操作。
需求:通过微信绑定手机号弹窗,获取用户信息,带到后台原因:sessionkey过期了为啥过期:与微信官方交互了两次,sessionkey更新导致sessionkey不一致最终解决思路:具体问题:使用框架使用的是uni,写的是微信小程序,ul框架用的:thorui产品要点击微信一键登录,通过获取手机号在获取用户信息存放到数据库中,刚开始我也实现了,但是每次登录的时候第一次都会失败,第二次点击就可以了,一开始我还以为是后台的问题,想要疯狂甩锅,代码实现是:在button按钮绑定事件,拿到用户信息,进行un,login,然后根据code获取sessionkey,调接口传encryptedData和sessionkey到后台进行对encryptedData解密,返回用户信息具体实现:对thorul的button进行二次封装,增加了获取手机号功能,返回的是一些encryptedData,cloudID用户加密之后的信息,要放到后台,通过sessionkey进行解密返回先说一下第二套方案吧,但是他是不行的,但是我就要说:发现了是两次sessionkey不一样了,于是在封装的button进行了处理,在button中进行的uni.loginbinduserphone(data){letuserData=datauni.login({provider:'weixin',success:function(rep){userData.userDataLoginCode=rep//rep中有code通过code获取sessionkey,此时与微信官方进行两次交互,但是code是最后的我以为可以解开用户信息_this.$emit('getphonenumber',userData);}});}在这里其实是不行的第一次与微信官方获取用户解密的信息生成了一个sessionkey第二次通过un.login登录获取sessionkey但是此时你的sessionkey钥匙打不开你第一次的锁,所以授权失败,当你第二次的登录的时候,你的sessionkey已经存在了,所以授权成功,**于是我有了思路,想着能不能在第一次获取加密信息的时候拿到sessionkey存起来,看了下没有相对数据,百度无果,浏览了相关公司的小程序发现的确可以,顺丰的,享道的摩拜的都可以,但是我发现他们都有一个共同的问题就是当点击授权的时候回延迟1秒左右,于是问题解决了解决方案:先进行uni.login获取sessionkey在获取encryptedData传给后台,问题就解决了,我在那个button按钮上面加了一个tab事件,进行获取sessionkey在进行encryptedData解密,要在封装的button中加一个settimeout就好了
失败原因好像是因为在 getphonenumber 回调函数中调取了 wx.login 接口解决:在其他地方获取 微信登录code(我是定时器4分钟获取一次,使用后重新获取)<template><view><buttonopen-type="getPhoneNumber"@getphonenumber="getphonenumber">获取手机号码</button></view></template><script>exportdefault{data(){return{timer:null,//定时获取登录code以防过期jsCode:'',//获取到的code只能使用一次五分钟不使用会过期}},onShow(){//监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面this.getLoginCode()//进入页面先获取一次code//定时获取code以防止过期this.timer=setInterval(()=>{this.getLoginCode()},1000*60*4)},onUnload(){//离开页面清除定时器if(this.timer){clearInterval(this.timer);this.timer=null;}},methods:{//@getLoginCode获取登录codegetLoginCode(){var_this=thisuni.login({success:function(res1){_this.jsCode=res1.code//赋值console.log(_this.jsCode);}})},//@getphonenumber获取手机号码getphonenumber(row1){if(row1.detail.errMsg&&row1.detail.errMsg=='getPhoneNumber:failuserdeny'){console.log('用户拒绝获取手机号码');return}vardata={jsCode:this.jsCode,//登录code只能使用一次五分钟内有效encryptedData:row1.detail.encryptedData,//需要解密的内容iv:row1.detail.iv//偏移量}console.log(data);//用户同意获取手机号码//调取后端接口处理业务this.getLoginCode()//重新获取code,一个code只能使用一次,五分钟后失效}}</script>
微信小程序分享使用方法:onLoad(){wx.showShareMenu({withShareTicket:true,//设置下方的Menus菜单,才能够让发送给朋友与分享到朋友圈两个按钮可以点击menus:["shareAppMessage","shareTimeline"]})},//发送给朋友onShareAppMessage(res){//此处的distSource为分享者的部分信息,需要传递给其他人letdistSource=uni.getStorageSync('distSource');if(distSource){return{title:'欢迎使用xxx商城',type:0,path:'/pages/index/index?id='+distSource,summary:"",imageUrl:"https://58d.oss-cn-hangzhou.aliyuncs.com/goods/ttg_1596073788000.png"}}},//分享到朋友圈onShareTimeline(res){letdistSource=uni.getStorageSync('distSource');if(distSource){return{title:'欢迎使用xxx商城',type:0,query:'id='+distSource,summary:"",imageUrl:"https://58d.oss-cn-hangzhou.aliyuncs.com/goods/ttg_1596073788000.png"}}},注意事项分享给朋友圈功能是2020年7月份新增的功能,目前仅支持安卓,IOS不支持目前是内测阶段,部分功能还是有问题的,例如分享到朋友圈功能,其中的query参数,这个参数在onLoad中是获取不到的,也就是说从此页面分享出去的,其他人进入此页面是拿不到分享者的信息的。这样就不能做分享者与用户之间的关联了。
uni-app分包优化、页面预加载、页面跳转封装等优化方式优化一、分包优化1.目录2.分包配置3.页面预加载二、页面跳转1.UNI跳转方法2.封装为vue全局方法优化因小程序有体积和资源加载限制,各家小程序平台提供了分包方式,优化小程序的下载和启动速度。在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示。一、分包优化注意:subPackages里的pages的路径是root下的相对路径,不是全路径。微信小程序每个分包的大小是2M,总体积一共不能超过16M。分包下支持独立的static目录,用来对静态资源进行分包。uni-app内支持对微信小程序、QQ小程序、百度小程序、支付宝小程序、字节小程序(HBuilderX3.0.3+)分包优化,即将静态资源或者js文件放入分包内不占用主包大小。针对vendor.js过大的情况可以使用运行时压缩代码HBuilderX创建的项目勾选运行–>运行到小程序模拟器–>运行时是否压缩代码cli创建的项目可以在package.json中添加参数–minimize,示例:“dev:mp-weixin”:“cross-envNODE_ENV=developmentUNI_PLATFORM=mp-weixinvue-cli-serviceuni-build--watch--minimize”1.目录pages是我们的主包,里面有登录页和index开头的3个底部tabBar选项卡页面。pagesOther里面是我们的分包,里面有个人中心的一些订单和消息页面。注意:tabBar页面以及引用的页面都需要放在主包,静态文件夹名为static┌─pages│├─index││└─index.vue│├─index2││└─index2.vue│├─index3││└─index3.vue│└─login│└─login.vue├─pagesOther│├─static│└─personal│└─order.vue│└─message.vue├─static├─main.js├─App.vue├─manifest.json└─pages.json2.分包配置修改mainfest.json配置,点击源码视图,在mp-weixin中加入以下代码修改pages.json配置,在pages同级下新增subPackages,分包中配置了page路径后,主包pages中就不需要了"pages":[//登录{"path":"pages/login/login","style":{"navigationBarTitleText":"登录"}},//选项卡{"path":"pages/idnex1/idnex1","style":{"navigationBarTitleText":"选项卡一"}},{"path":"pages/idnex2/idnex2","style":{"navigationBarTitleText":"选项卡二"}},{"path":"pages/idnex3/idnex3","style":{"navigationBarTitleText":"选项卡三"}}],"subPackages":[{"root":"pagesOther",//分包名称"pages":[//省略无数个页面路径...//个人中心{"path":"personal/order","style":{"navigationBarTitleText":"订单"}},{"path":"personal/message","style":{"navigationBarTitleText":"消息"}}]}],3.页面预加载配置preloadRule后,在进入小程序某个页面时,由框架自动预下载可能需要的分包,提升进入后续分包页面时的启动速度在刚刚pages.json中继续添加preloadRule预加载配置packages:表示进入pages/index/index1这个页面后加载pagesOther这个分包,值为__APP__时表示主包network:在指定网络下预下载,可选值为:all(不限网络)、wifi(仅wifi下预下载)"preloadRule":{"pages/index/index1":{"network":"all","packages":["pagesOther"]}},然后我们可以看下运行时的依赖分析二、页面跳转类似HTML中的<a>组件,但只能跳转本地页面。目标页面必须在pages.json中注册1.UNI跳转方法uni方法进行跳转uni.navigateTo保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面uni.redirectTo关闭当前页面,跳转到应用内的某个页面uni.reLaunch关闭所有页面,打开到应用内的某个页面uni.switchTab跳转到tabBar页面,并关闭其他所有非tabBar页面uni.navigateBack关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages()获取当前的页面栈,决定需要返回几层uni.preloadPage预加载页面,是一种性能优化技术。被预载的页面,在打开时速度更快2.封装为vue全局方法我们可以自己封装为Vue方法,来调用跳转与传参跳转vue2写法varutils={};//公共处理方法utils.install=function(Vue,option){//页面跳转Vue.prototype.jump=function(path){uni.navigateTo({url:path})};//返回Vue.prototype.back=function(obj){uni.navigateBack(obj);};//带参跳转Vue.prototype.togo=function(url,data){url+=(url.indexOf('?')<0?'?':'&')+param(data)uni.navigateTo({url})};functionparam(data){leturl=''for(varkindata){letvalue=data[k]!==undefined?data[k]:''url+='&'+k+'='+encodeURIComponent(value)}returnurl?url.substring(1):''};//获取登录用户Vue.prototype.getUserInfo=function(){letres=uni.getStorageSync('userInfo');if(res){returnres}else{return{}}};};exportdefaultutils;在main.js中use一下就可以在页面用了importutilsfrom'./common/util.js'Vue.use(utils)携带userid跳转到order页面togoOrderList(userid){this.togo('/pagesOther/personal/order',{userId:userid})}vue3写法import{App}from'vue'importhttpfrom'./http'constsetupUtils=(app:App)=>{//请求app.config.globalProperties.$http=http//页面跳转app.config.globalProperties.jump=(path:string)=>{uni.navigateTo({url:path,})}//返回app.config.globalProperties.back=(obj:Object)=>{uni.navigateBack(obj)}//跳转传参app.config.globalProperties.togo=(url:string,data:Array<any>)=>{url+=(url.indexOf('?')<0?'?':'&')+param(data)uni.navigateTo({url,})}constparam=(data:Array<any>)=>{leturl=''for(letkindata){letvalue=data[k]!==undefined?data[k]:''url+='&'+k+'='+encodeURIComponent(value)}returnurl?url.substring(1):''}//获取文件APIapp.config.globalProperties.getFileApi=()=>{return'http://localhost:8089/api/file/'}//日期处理等等...}exportdefaultsetupUtils代码暂时放在了gitee上uni-mc,方便大家查看目前uni-mc项目使用cli方式运行,使用了阿里云函数
页面中使用<show-modal></show-modal>this.$showModal({title:'',content:'当',cancelText:"取消",confirmText:"生成赛果",success(res){if(res.confirm){console.log('用户点击确定')}elseif(res.cancel){console.log('用户点击取消')}}})在main.js中引入show-modal组件importinitModalfrom"@/components/show-modal/initModal.js"importshowModalfrom"@/components/show-modal/show-modal.vue"initModal(Vue);Vue.component('show-modal',showModal);创建组件show-modal组件名称show-modal.vue<template><viewclass="_showModal"v-show="show"><viewclass="_shade"></view><viewclass="_modalBox"><viewclass="_modal"><slotname="title"><viewclass="title"v-show="title">{{title}}</view></slot><slotname="content"><viewclass="content">{{content}}</view></slot><slotname="btn"><viewclass="btnbox"><viewclass="btn":style="{color:cancelColor,background:cancelBackgroundColor}"@click.stop="clickBtn('cancel')">{{cancelText}}</view><viewclass="btn":style="{color:confirmColor,background:confirmBackgroundColor}"@click.stop="clickBtn('confirm')">{{confirmText}}</view></view></slot></view></view></view></template><script>exportdefault{name:"show-modal",computed:{show(){returnthis.$modalStore.state.show;},title(){returnthis.$modalStore.state.title;},content(){returnthis.$modalStore.state.content;},showCancel(){returnthis.$modalStore.state.showCancel;},cancelText(){returnthis.$modalStore.state.cancelText;},cancelColor(){returnthis.$modalStore.state.cancelColor;},cancelBackgroundColor(){returnthis.$modalStore.state.cancelBackgroundColor;},confirmText(){returnthis.$modalStore.state.confirmText;},confirmColor(){returnthis.$modalStore.state.confirmColor;},confirmBackgroundColor(){returnthis.$modalStore.state.confirmBackgroundColor;}},methods:{closeModal(){this.$modalStore.commit('hideModal')},clickBtn(res){this.$modalStore.commit('hideModal')this.$modalStore.commit('success',res)}},beforeDestroy(){this.$modalStore.commit('hideModal')},data(){return{};}}</script><stylelang="scss"scoped>._showModal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;._shade{width:100%;height:100%;position:absolute;top:0;left:0;background:#000;opacity:.6;z-index:11000;}._modalBox{width:100%;height:100%;position:absolute;top:0;left:0;z-index:12000;display:flex;justify-content:center;align-items:center;._modal{flex:none;width:250px;min-height:170px;background:#fff;border-radius:16px;.title{text-align:center;font-size:16px;font-family:SourceHanSansCN;font-weight:bold;color:#333333;margin-top:20px;}.content{min-height:80px;width:86%;margin:20pxauto;font-size:20px;font-family:SourceHanSansCN;font-weight:500;color:#333333;display:flex;justify-content:center;align-items:center;text-align:center;}.btnbox{display:flex;width:88%;margin:auto;margin-bottom:20px;flex-direction:row;justify-content:space-between;.btn{width:100px;height:32px;border-radius:16px;display:flex;justify-content:center;align-items:center;font-family:SourceHanSansCN;font-weight:400;}}}}}</style>创建initModal.jsimportVuexfrom'vuex'exportdefaultfunctioninitModal(v){//挂在store到全局Vue原型上v.prototype.$modalStore=newVuex.Store({state:{show:false,title:"标题",content:'内容',showCancel:true,cancelText:"取消",cancelColor:"#ED4F4E",cancelBackgroundColor:"#FFE5E5",confirmText:"确定",confirmColor:"#FFFEFA",confirmBackgroundColor:"linear-gradient(0deg,#F655550%,#E54848100%)",success:null,},mutations:{hideModal(state){//小程序导航条页面控制//#ifndefH5if(state.hideTabBar){wx.showTabBar();}//#endifstate.show=false},showModal(state,data){state=Object.assign(state,data)console.log(state);state.show=true},success(state,res){letcb=state.successletresObj={cancel:false,confirm:false}res=="confirm"?resObj.confirm=true:resObj.cancel=truecb&&cb(resObj)}}})v.prototype.$showModal=function(option){if(typeofoption==='object'){//#ifndefH5if(option.hideTabBar){wx.hideTabBar();}//#endifv.prototype.$modalStore.commit('showModal',option)}else{throw"配置项必须为对象传入的值为:"+typeofoption;}}}
uniapp作为一款开源软件,可以做到一端多用,不过也有局限,在开发中有时候需要动态的去修改标题,例如多页面逻辑合成一个页面的时候uniapp打包分为H5 小程序 和APP(一般主要的是这三个)下面贴上的代码,做些解释 “#ifdefH5”和“#ifdefMP”之类的是uniapp官方提供的终端区分编译条件ifdef条件详见=> uniapp条件编译/***@name修改微信title*@param{*}title*/exportfunctionweixinTitle(title){//#ifdefH5setTimeout(()=>{document.title=title;varmobile=navigator.userAgent.toLowerCase();if(/iphone|ipad|ipod/.test(mobile)){variframe=document.createElement("iframe");iframe.style.visibility="hidden";iframe.setAttribute("src","loading.png");variframeCallback=()=>{setTimeout(()=>{iframe.removeEventListener("load",iframeCallback);document.body.removeChild(iframe);},0);};iframe.addEventListener("load",iframeCallback);document.body.appendChild(iframe);}},0);//#endif//#ifdefMPuni.setNavigationBarTitle({title:title})//#endif}有的可能会直接去使用wx.setNavigationBarTitle({title:title}),是可以使用,但是你的代码要是需要在支付宝小程序,百度小程序之类的还是需要改回uni.setNavigationBarTitle。
您直接复制粘贴即可使用不需要做特殊的处理。如您满意请给莫成尘点个Fabulous1:纯文字提示框uni.showToast({title:'只有文字弹窗',icon:'none',//如果要纯文本,不要icon,将值设为'none'duration:2000//持续时间为2秒})2:带有成功图标提示框uni.showToast({title:'成功提示弹窗',icon:'success',//将值设置为success或者''duration:2000//持续时间为2秒})3:带有取消确认的提示框uni.showModal({title:'有确认取消的弹窗',content:'确认要删除该项吗?',success:function(res){if(res.confirm){console.log('点击了确认')}else{console.log('点击了取消')}}})4:带有图片提示框uni.showToast({title:'自定义图标弹窗',//图片优先级更高但您应该使用本地的而非线上的图片链接image:'../../static/logo.png',duration:2000})5:加载中提示框//showLoading需要用hideLoading来结束,一般网络请求封装中常用uni.showLoading({title:'加载中...'});setTimeout(()=>{uni.hideLoading()},2000)6:带有遮罩蒙版提示框//如果有透明蒙层,相当于全屏不能做其他操作如点击事件uni.showToast({title:'遮罩层的弹窗',duration:2000,mask:true//是否有透明蒙层,默认为false})7:有列表(上拉列表)提示框uni.showActionSheet({itemList:['A','B','C'],success(res){console.log(res.tapIndex)},fail(res){console.log(res.errMsg)}})
在项目开发中,没有使用Vuex进行全局缓存,而是使用uniapp自带的类似于js的storage来进行跨页面数据共享。因而在使用时,会出现很多相同的代码去操作缓存数据。故而全局方法可以帮忙减少代码量,让代码看起来更加简洁。1.uniapp添加全局方法和变量在main.js进行挂载。例如挂载全局请求的url。Vue.prototype.apiUrl="http://api.***.cn";在其他vue文件里面进行使用时,直接this即可。leturl=this.apiUrl全局方法的挂载现在main.js里面声明一个变量,值为一个函数。constsetCache=(key,value,seconds)=>{console.log("setCache")};然后使用Vue.prototype.变量的形式将方法挂载上去,如下。Vue.prototype.$Cache={setCache};这样setCache方法就被挂载在一个全局变量$Cache上,在使用时直接调用就行了。声明全局变量时,一般会用$符号开头console.log(this.$Cache.setCache("lll","111",100));2.二次封装uniapp的缓存方法uniapp的异步缓存uni.setStorage({key:'storage_key',data:'hello',success:function(){console.log('success');}});uniapp的异步获取缓存try{constvalue=uni.getStorageSync('storage_key');if(value){console.log(value);}}catch(e){//error}代码量虽然不多,但是当使用的次数多了之后你仍然会发现这些代码很冗余,就很难受。不过uniapp处理得比较好的是,缓存时key为字符串,而值可以为一个json对象,或者Boolean值,这样就很嗨皮了呀。反正我做缓存就只会使用这两种类型,别问我为什么。接着封装一下咯设置缓存的同步封装:这里我们加上了缓存时间,uniapp默认缓存有效时间为28天。但是日常使用中,我觉得太长了。所以改为支撑自定义。怎么改,uniapp没有封装这个方法呀。于是缓存一次的,变为了缓存两次。一次存数据,一次存时间。就像下面这这样,看不懂的可以留言。constsetCache=(key,value,seconds)=>{try{if(key&&value){console.log(value);uni.setStorageSync(key,value);}if(seconds){letexpire=newDate().getTime()+parseInt(seconds);uni.setStorageSync(key+'expire',expire);}console.log('设置缓存成功:'+key);}catch(e){console.log('设置缓存失败:'+e.message);}};判断缓存是否过期:这份代码,结合着上面的代码看效果会更加。constisCacheAlive=(key)=>{try{constexpireTime=uni.getStorageSync(key+'expire');constnowTime=newDate().getTime();if(expireTime<nowTime){returntrue;}else{returnfalse;}}catch(e){console.log('判断缓存是否过期出错:'+e.message);}};就贴这两段代码得了,其他的那些封装都很简单,没啥好说的。当然vue支持很多全局挂载的方法,但是我在uniapp项目中测试的时候只有上面这种方法生效。不知道有没有大佬指条明路,在main.js写公共的方法,代码量多了我很难受呀。
在main.js文件中定义一个Vue实例并将其挂载在Vue原型上:importVuefrom'vue'constapp=newVue()Vue.prototype.$app=app在任何组件中都可以通过this.$app访问该Vue实例,并在该实例上定义需要的全局变量://组件A中设置变量this.$app.globalData={userInfo:{name:'张三',age:18},token:'xxxxxxxxxxxxxxxxxxxx'}//组件B中获取变量console.log(this.$app.globalData.userInfo.name)//输出:张三console.log(this.$app.globalData.token)//输出:xxxxxxxxxxxxxxxxxxxx这样就可以在任何组件中都共享同一个全局变量了。需要注意的是,为了避免冲突,建议使用独特的变量名来表示全局变量。