当前位置: 首页 > 科技观察

快应用开发优化Tips

时间:2023-03-21 19:43:03 科技观察

2018年3月,华为、小米、OPPO等9家手机厂商联合发布了快应用。快应用标准由主流手机厂商组成的快应用联盟共同制定。具有传统APP的应用体验,同时具有免安装即用的特点。其实早在2013年,百度就推出了轻应用。2017年,腾讯推出小程序。之后,阿里还推出了支付宝小程序。在业务细节上,它们是不同的,但大体上它们的定位是相似的。就像CS架构转向BS架构一样,这种免安装即点即用的应用形式正在成为应用市场的新趋势。闲话少叙,全书回归正文。本文旨在让大家了解快速应用开发的常用优化方法,提高应用代码整理的组织能力,合理拆分功能模块,使项目更易于维护,提高工作效率。至于如何搭建开发环境、开发流程和系统API的说明,可以在快应用官方文档中找到。本文介绍快速应用开发的优化技术。快应用开发采用前端技术栈,其优化方法主要可以在以下四个方面进行:数据共享性能优化数据共享错误处理结构优化方式。其实不光是快应用,任何前端项目都需要考虑这个问题。比如在react的开发中,引入redux可以方便数据源的统一管理。在快速应用开发中,可以通过框架API或者使用全局变量global来实现数据共享。1、FrameworkAPI开发者可以通过页面ViewModel中的this.$app.$def获取APP上定义的数据和方法。但是这种方法的缺点是需要依赖ViewModel实例。但是,很多全局方法与生命周期无关。全局变量global作为独立于应用程序生命周期的引用,应该是开发者的首选。2、全局变量global在app.ux和${anyPage}.ux中可供开发者使用。但是变量global在页面和APP之间是不同的,引用的值指向不同的对象;尽管它们不同,但它们的原型都指向同一个全局对象。因此,开发人员可以在这个全局对象上定义变量,可以从任何JS访问。同样,这种方式也有缺点,比如污染全局环境,容易造成复杂场景下难以重现的bug。性能优化1.更合理的Dom结构Dom节点是页面最基本的元素,尽量使用语义标签,减少Dom层次结构将显着提高页面的可读性和性能。2、更有效的选择器快应用的开发框架支持后代选择器,为开发者提供了极大的便利,但同时后代选择器的性能损耗也比较大。如果使用不当,会对页面性能造成更大的损失。我们首先需要知道CSS选择器是从右向左解释的。看下面这个选择器#container>a{font-weight:blod;}对于很多刚接触CSS的朋友来说,通常认为这个选择器是先找到id为container的元素,然后给它应用字体加粗效果应该是直接子元素中a元素的有效选择器。然而,这种情况并非如此。浏览器首先对页面上的所有a元素进行便利化,然后过滤掉父元素id为container的节点。所以这个看似高效的选择器实际上是消耗了大量的开销。了解这个规则后,我们建议对后代选择器的使用进行以下优化。避免使用组件名称作为后代选择的最后匹配规则。组件越基础,越可重用,所以应该避免。例如:.container#doctext{...};否则,每个文本组件在渲染时都会遍历匹配一次。减少后代选择的层数。关卡越深,单场比赛耗时会成倍增加。后代选择中最后一个匹配规则的定义名称尽量唯一,如:.container#doc.doc-item.doc-name-zh{...}3.图片优化在前端开发中,图片通常占据比较大的空间。更多的图片资源也意味着更多的http请求。在同等带宽条件下,下载一张200k的图片一定比下载一张100k的图片快两次。建议做一些优化。使用CSSSprites其实就是将网页中的一些背景图片整合成一个单一的图片文件,然后结合CSS“background-image”、“background-repeat”、“background-position”进行背景定位。最大的好处是减少了http请求,大大提高了网页性能。话虽如此,这种方式也有缺点,比如开发相对麻烦。对于页面上简单的图标文件,可以尝试使用css3来实现,或者换成矢量图形,这样也可以明显减少图片占用的空间,提高性能。避免图像压缩。如果页面中没有使用较大的图片,则不需要通过css压缩图片来满足需求。直接使用小图像或其他替代品更为明智。4、简化ViewModel的数据属性数据驱动是前端开发的一种流行形式,在快应用中也是如此。在ViewModel的定义中,data属性主要负责数据驱动的数据定义,会递归定义赋值数据中的各个属性。因此,属性定义语义结构越清晰越少,质量越高。比如我们发送一个fetch请求,返回的结果中包含了很多数据,而前端需要展示的数据只是其中的一小部分。在页面的数据中,我们只需要定义前端需要展示的数据即可,示例代码片段如下。//fetch请求返回的数据数据量较大,data中只需要部分数据consttradeInfoList=[{"_id":"5c31aa2a565e9938214da13b","currentPrice":1,"tradePrice":1"userId":"admin","tradeAmount":600,"stockInfo":{"code":"000008","name":"Stock8","price":1,"des":"Description8"}},{//...}]exportdefault{data(){return{list:[]}},onInit(){//返回页面中需要的对象属性,过滤其他属性this.list=tradeInfoList.map(item=>{userId:item.userId,tradePrice:item.tradePrice})}}5.延迟加载延迟加载是一种通用的优化方法。传统H5页面中的懒加载是指页面即将进入屏幕可见区域时。加载资源并呈现页面。这样,给用户的直观感受就是页面加载速度变快了。在快速应用开发中,可以使用指令或者事件触发器来实现延迟加载。比如一个很常见的场景,对于一个包含列表组件的页面,我们一开始只渲染前十条数据,当页面滑动到某个位置时,触发加载更多的数据完成渲染。错误处理在前端开发中,一旦程序执行出错,就会报JS异常弹窗。1.访问null或未定义的属性可能是最常见的错误类型。在稍微复杂的业务逻辑代码中,增加更多的null条件是避免这个错误最简单的方法。即使某些数据在定义时一定存在,但我们不能完全保证这些数据为空或未定义的原因有多种。保证代码的严谨性是处理不确定异常的根本途径。当然,有些稍微复杂的情况需要特殊处理,比如后两种场景。2.JSON.parse解析错误也很常见。我们在转换一个JSON字符串的时候,如果字符串的格式不是标准的JSON格式,那么转换肯定会失败。您可以使用try-catch包装JSON.parse()以便分析错误消息。当然,每次JSON.parse()都要执行try-catch会很麻烦。比较推荐的方式是,在app.ux中提前代理JSON.parse(),周围用try-catch,比如exportfunctionparseProxy(){constrawParse=JSON.parseJSON.parse=function(str,defaults){try{returnrawParse(str)}catch(err){console.error(`JSONparsingfailed:${str},${err.stack}`)returndefaults}}}3.ViewModel回调函数异常。执行接口方法(如fetch请求),然后立即跳转到PageB;此时接口回调函数返回,但是PageA已经出栈销毁。这个时候开发者传过来的回调函数执行的时候就报错了。这是因为在回调函数中访问了一些data数据等,而这些ViewModel的data属性已经随着页面销毁被删除,所以报错。对于这种异常,通常可以采用以下方法解决。A、回调函数执行前,通过ViewModel对象的$valid和$visible判断页面的状态。B.在Function.prototype上定义一个方法,并将每个回调函数与一个ViewModel实例相关联。/***在Function原型上定义bindPage方法:将回调函数绑定到页面对象上。当页面不可见或销毁时,回调函数不会被执行*/exportfunctionbindPageLC(){Function.prototype.bindPage=function(vmInst){constfn=thisreturnfunction(){if(!vmInst){thrownewError(`使用错误:请传递一个VM对象`)}if(vmInst.$valid&&vmInst.$visible){returnfn(...arguments)}else{console.info(`当页面不可见或被销毁时,回调函数不执行`)}}}}在${anyPage}.ux中,通过fn.bindPage(this)将ViewModel绑定到回调函数示例exportdefault{data(){return{}},request(){//调用bindPage(this)返回:页面对象绑定的回调函数,当页面不可见或销毁时,回调函数fetch.fetch不会被执行({success:function(ret){//data操作等}.bindPage(this)})}}C.通常当页面发送请求时,页面需要加入loading处理,防止此时用户进行其他操作,当然这种方式从业务角度避免了这种异常。但这是一种很常见的方式。可以结合方法B来确保代码的严谨性。结构优化结构优化的目的是减小页面大小和整体rpk包。常用的减少冗余代码的方法有:A.在app.ux中引入常用的JS库,暴露给各个页面;可以避免每个页面打包时重复定义JS。B.项目内部的代码抽象封装,比如常用的工具类函数的封装,统一的Fetch请求方法的封装,这些封装可以作为公共方法提供给各个页面,方便维护同时,也有效减少了代码量。端优化的目的是提高代码的可维护性和应用性能。可以说,正是多种优化手段,让逻辑性很强的代码充满了艺术性。为了更优雅的完成编码,相信这个话题还会继续讨论下去。