快应用开发优化技巧

移动开发 移动应用
优化的目的是为了提高代码的可维护性以及应用性能,可以说,正是多种多样的优化手段,让逻辑性极强的代码变的充满艺术性。为了越发优雅地完成编码,我相信这个话题会一直探讨下去。

2018年3月,华为、小米、OPPO等九大手机厂商共同发布了快应用。快应用标准由主流手机厂商组成的快应用联盟共同制定,其拥有传统app的应用体验,同时又具备无需安装、即点即用的特点。其实,早在2013年,百度曾推出了轻应用,2017年,腾讯又推出了小程序,之后,阿里也推出了支付宝的小程序。业务细节上,它们各有不同,但大体上定位是类似的,如同当年CS架构转向BS架构一样,这种无需安装、即点即用的应用形式,正在成为app市场新的趋势。

闲言少叙,书归正传。本文的目的,是为了让大家了解到快应用开发常用的优化手段,提升对应用代码整理的组织能力,合理拆分功能模块,从而使项目更易维护,提高工作效率。至于如何搭建开发环境,开发流程以及系统api的描述,大家均可在快应用的官方文档中找到。本文将介绍快应用开发的优化技巧。

快应用开发采用前端技术栈,其优化方式主要可从以下四方面进行:

  1. 数据共享
  2. 性能优化
  3. 错误处理
  4. 结构优化

数据共享

在快应用开发中,开发者需要了解页面与APP之间,页面与页面之间的数据共享方式。其实不止快应用,任何前端工程都需要考虑这个问题,如react开发中,引入redux可方便统一管理数据源,在快应用开发中,可以通过使用框架API或使用全局变量global两种方式实现数据共享。

1. 框架API

开发者可以在页面ViewModel中,通过this.$app.$def获取APP上定义的数据及方法。

但这种方式的缺点在于需要依赖ViewModel实例,然而,很多全局方法与生命周期无关。全局变量global作为独立于应用生命周期的引用,理应成为开发者的***。 

2. 全局变量global

在app.ux、${anyPage}.ux中,开发者都可以使用变量global。然而,页面与APP之间,页面与页面之间的变量global并不相同,引用的值指向不同的对象;虽然不相同,但它们的原型都指向同一个全局的对象。因此,开发者可以在这个全局对象上定义变量,这样在任何JS中均可访问。同样,这种方式也存在缺点,比如存在污染全局环境,在复杂场景下容易引发难以复现的BUG等问题。

性能优化

1. 更合理的Dom结构

Dom节点是构成页面最基本的元素,尽量使用符合语义的标签,同时减少Dom层级结构会对页面的可读性及性能都会有明显提升。

2. 更有效的选择器

快应用的开发框架支持后代选择器,这对开发者提供了很大的便利,但与此同时,后代选择器的性能损耗是相对较大的,如使用不得当,会对页面性能造成较多损耗。

我们首先要知道,CSS选择器是由右向左解释的。看以下选择器

#container > a {font-weight:blod;}

对于很多刚开始了解css的朋友来说,通常会认为,该选择器先找到id为container的元素,然后将字体加粗效果应用到直系子元素中的 a 元素上,应该是个高效的选择器。然而事实并非如此,浏览器首先会便利页面中所有的a元素,然后筛选出父元素id为container的节点。所以这个看似高效的选择器,其实也是花费了不少的开销。

了解到这个规则后,对于后代选择器的使用,我们建议做到以下几点优化。

  1. 避免使用组件名称作为后代选择的***一项匹配规则,越是基础的组件,复用度就越多,越是要避免。如: .container #doc text { ... };否则每个text组件渲染时都会遍历匹配一次
  2. 减少后代选择的层级数量,层级越深,单次匹配耗时则成指数式增长。
  3. 后代选择中***一条匹配规则的定义名称尽量唯一,如:.container #doc .doc-item .doc-name-zh{ ... }

3. 图片优化

在前端开发中,图片通常占据了较大的空间。较多的图片资源也意味着较多的http请求,在相同带宽条件下,下载一个200k的图片,一定比下载两次100k的图片速度更快。建议可以做几下几点优化。

  1. 使用CSS Sprites,其实就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background-repeat”,“background-position”的组合进行背景定位,这种方式***的优点就是减少了http请求,大大提高了网页性能。话说回来,这种方式也有缺点,比如开发起来会相对麻烦。
  2. 对于页面上简单的图标文件,可尽量使用css3实现,或矢量图代替,这样也能明显减少图片占用的空间,提高性能。
  3. 避免图片压缩,如果页面中用不到较大的图片,就完全没必要通过css去压缩图片以适应需求。直接使用小图或其他可替代方式更加明智。

4. 简化ViewModel的data属性

数据驱动是当下流行的前端开发形式,在快应用中也是如此。在ViewModel的定义中,data属性主要承担数据驱动的数据定义,会对赋值的data中每个属性进行递归式的定义。因此,属性定义语义结构越清晰且数量越少,则质量越高。

例如,当我们发出fetch请求,返回结果中包含了很多数据,而前端需要显示的数据只是其中很少一部分,则在该页面的data中,就只需要定义前端需要显示的数据即可,如下面的示例代码片段。

// fetch请求返回的数据,数据量大,而data中只需要其中部分数据

const tradeInfoList = [

{

"_id" : "5c31aa2a565e9938214da13b",

"currentPrice" : 1,

"tradePrice" : 1,

"userId" : "admin",

"tradeAmount" : 600,

"stockInfo" : {

"code" : "000008",

"name" : "股票8",

"price" : 1,

"des" : "描述8"

}

},

{

// ...

}

]

export default {

data () {

return {

list: []

}

},

onInit () {

// 返回页面中需要的对象属性,过滤其他属性

this.list = tradeInfoList.map(item => {

userId: item.userId,

tradePrice: item.tradePrice

})

}

}

5. 懒加载

懒加载是一种通用的优化手段,传统H5页面中的懒加载,指的是页面即将进入屏幕可视区域时,才加载资源,渲染页面。这样,给用户直观的感受就是,页面加载速度变快了。在快应用开发中,可使用指令或事件触发来实现懒加载。

比如很常见的场景,一个包含list组件的页面,我们开始只渲染前十条数据,当页面下滑至某位置时,触发加载更多来完成渲染。

错误处理

前端开发中,一旦程序执行出错,就会报出JS异常弹框。

1. 访问null或undefined的属性

以上的这种错误可能是最常见的一种了,在稍微复杂的业务逻辑代码中,多加判空条件,是避免这种错误的最简易方式。即便某些数据在定义时是必须存在的,但我们无法完全保证,存在各种原因导致这些数据为空或undefined。保证代码的严谨性,是应对不确定性异常的根本方式。当然有些稍微复杂的情况下,需要特殊处理,如后两种场景。

2. JSON.parse解析出错

这种错误也十分常见。当我们转换一个JSON字符串时,如果这个字符串的格式并不是一个标准的JSON格式,那转换肯定失败。可以在JSON.parse()时,使用try-catch进行包裹,以便对错误信息进行分析,如

当然,在每处JSON.parse()的地方都执行try-catch会有些麻烦,更推荐的方式是,在app.ux中提前代理JSON.parse(),使用try-catch包围,如

export function parseProxy() {

const rawParse = JSON.parse JSON.parse = function (str, defaults) {

try {

return rawParse(str)

} catch (err) {

console.error( ` JSON解析失败:$ {str}, $ {err.stack}` )return defaults

}

}

}

3、ViewModel回调函数异常场景

用户打开PageA,然后在该页面中执行接口方法(如fetch请求),然后立即跳转到PageB;此时接口的回调函数返回,但PageA已经出栈销毁,此时,执行开发者传递的回调函数报错。

这是由于,回调函数中访问了一些data数据等,而这些ViewModel的数据属性已经伴随着页面销毁而删除了,所以引起报错。对于这种异常,通常可采用以下方式解决。

A. 在回调函数执行之前,通过ViewModel对象的$valid、$visible判断页面状态

B. 在Function.prototype上定义方法,关联到每个回调函数绑定ViewModel实例。

/**

* 在Function原型上定义bindPage方法:将回调函数绑定到页面对象,页面不可见或者销毁时,不执行回调函数

*/

export function bindPageLC () {

Function.prototype.bindPage = function (vmInst) {

const fn = this

return function () {

if (!vmInst) {

throw new Error(`使用错误:请传递VM对象`)

}

if (vmInst.$valid && vmInst.$visible) {

return fn(...arguments)

}

else {

console.info(`页面不可见或者销毁时,不执行回调函数`)

}

}

}

}

在${anyPage}.ux中,通过fn.bindPage(this),在回调函数上绑定ViewModel实例

export default {

data () {

return {}

},

request () {

// 调用bindPage(this)返回:绑定了页面对象的回调函数,当页面不可见或者销毁时,不执行回调函数

fetch.fetch({

success: function(ret) {

// 数据操作等

}.bindPage(this)

})

}

}

C. 通常在页面发送请求时,页面需要添加loading处理,以防止用户在此时进行其他操作,当然这种方式是从业务角度规避了这个异常。不过确是一种很常用的方式。可结合方式B以保证代码严谨性。

结构优化

结构优化的目的是减小页面以及整体rpk包的体积,减少冗余代码

常用的手段有以下几项:

A. 在app.ux中引入常用的JS库,并暴露给每个页面使用;可以避免每个页面在打包时对JS的重复定义

B. 项目内部的代码抽象封装,如封装常用的工具类函数,封装统一的Fetch请求方法,这些封装可作为公共方法提供给各个页面,便于维护的同时,也有效降低了代码量。

结尾

优化的目的是为了提高代码的可维护性以及应用性能,可以说,正是多种多样的优化手段,让逻辑性极强的代码变的充满艺术性。为了越发优雅地完成编码,我相信这个话题会一直探讨下去。

 

责任编辑:张燕妮 来源: 51CTO
相关推荐

2021-04-08 21:32:38

华为快应用Serverless

2019-03-20 23:39:16

快应用开发者

2017-12-23 14:38:41

Android编程开发优化

2011-08-08 14:57:46

iPhone Autoreleas Property

2018-04-13 10:36:44

Web应用优化

2019-03-21 14:18:38

iOS开发优化原因

2012-04-26 13:26:58

iPhone应用技巧

2023-03-09 15:10:49

应用开发鸿蒙

2010-05-14 15:58:26

HTML优化

2020-12-17 07:52:38

JavaScript

2011-08-02 09:04:02

移动应用开发

2017-04-18 09:00:19

Linux技巧系统备份

2019-08-14 15:40:05

Web图片优化前端

2010-01-12 18:00:50

VB.NET界面

2018-08-24 10:57:10

2020-01-07 10:14:38

Python程序员脚本语言

2011-03-25 13:43:54

Cacti优化

2011-07-19 09:51:32

性能优化Designing FAndroid

2023-03-09 16:42:00

程序性能优化Java技巧

2011-07-01 16:05:22

SEO
点赞
收藏

51CTO技术栈公众号