Hybrid移动应用在多页面大数据复杂业务背景下的优化实践方案

来源:互联网 发布:松江行知进修学校 编辑:程序博客网 时间:2024/05/16 19:21

这里写图片描述

前言

对于混合应用而言,性能问题一直被吐槽,虽然设备的内存的不断增大,很大程度上缓解了这个一问题,但是和原生应用来讲还是有很大区别,本人从Phonegap2.x开始,一直的探索和使用混合应用技术。

当时的2.x性能真是不怎么样,首次加载时间也比较长,后来phonegap被apache纳入旗下以后,更名为Cordova,可以说从此以后,性能问题得到了很大的改善,占用内存也越来越小,到如今使用的版本已经变为6.3.0,看得出cordova也一直在尝试缩小与native的差距。

不管一个平台如何,是好,是坏?很大程度上都取决开发人员的技术水平,所以如何开发一个高性能的混合应用成为了至关重要的问题。

对于大部分项目而言,都不是一锤子买卖,很多都是长期项目,长期项目就免不了版本的一次一次的更新迭代,可以业务逻辑变得越来越复杂,页面变的越来越多,动画越来越炫酷,数据量越来越大,面对种种问题,我们都需要解决,所以一个好的优化方案势在必得。

下面,就谈一下,这几年我再混合应用开发过程中尝试的优化方案。

部分内存数据移动至离线存储

影响速度的根本因素,几乎都在内存上,如果内存消耗过高,应用就会卡顿,甚至闪退现象。

所以建议内存方面应注意, 不要存大量的应用数据在内存中,我们可以选择离线数据库,比如pouchdb,当然部分必要数据还是要存的,看实际需求。

目前离线数据库框架都是基于websql,运行速度非常快, 所以必要情况下,可以选择数据离线存储,避免占用内存,因为混合应用的webview渲染本身已经消耗部分内存了。

加载速度优化

由于项目不断变大,势必会导致js bundle文件的不断扩充,文件大了,加载速度也就会变慢, 首要解决方案大概如下:

  1. JS压缩整合 (压缩所以js,整合一个文件)
  2. 图片压缩整合 (压缩图片,保证清晰度即可)
  3. CSS压缩整合 (压缩整合一个文件,减小大小)
  4. 巧用Splash 页面(设置3秒延迟,保证html加载完全)

如果不足以优化,就需要讲JS 文件整合成两个文件,从而使文件变小,不必要的的js bundle 可以使用h5 异步加载,提高效率。

例如:

HTML页面与原生View的无缝衔接

对于混合应用而言,很难达到htmL view和原生view的无缝对接,也就是说,使用起来恰似是同样的实现方式,在一个栈里边。

但事实, 原生view属于native的栈中,而html view属于另一栈中,两种形式的view分属于不同的世界, 而且在使用js调用原生应用,cordova存在线程阻塞问题。

如果下面这个需求,就不好实现了:

这里写图片描述

大概是,view1中点击按钮,调用扫描插件view2, 扫描出结果以后启动view3,当在view3点击back按钮,需要在返回扫描产检view2, 扫描插件view2点击back退回view1.

对于这个需求,无论是native 还是 html, 在一个栈中,都不是问题,非常简单,但是,不同栈里,就不太好实现了,当在view3点击back调用插件会阻塞掉线程,即便执行back,也无法回退的,最后,在view2点back 回退时候发现, view3闪一下,马上退回view1。

为了解决这个问题,需要用timeout简单实现一个异步的操作,问题就解决了:

setTimeout(function(){    // to execute the native plugin action. eg. call view2},100)

动画的使用

动画效果,是用户交互体验的根本,如果动画处理不好,就会使很大的问题。

比如进入一个页面后,需要执行一个大概三秒的动画效果,在这三秒内,可能无法触发任何click事件。

比如transform动画。

所以,当你的app中在进入页面两秒内发现点击事件不生效,你就要考虑一下这个页面是否有没有执行完的动画了。

巧用Cache缓存

对于angular这样的框架而言是没有页面缓存技术的, 但是部分应用场景中,页面缓存又在所难免,比如像ios tab view这样的底部导航view,是存在缓存的,如果我们可以取消,会非常不符合ios的设计规范。

从用户角度而言,通常情况这中tab view navigation是不需要重新加载数据的,这样的主页面,几乎时刻存在,从新加载会浪费过多的时间。

这里附上一个ionic的缓存方案:

$stateProvider.state('demo', {            cache:true,            url: '/demo',            template: '<div></div>',        })

上面代码中,通过路由功能对这个页面做了一个cache功能,同样可以禁止掉。

注意:ionic默认是有页面缓存的哦。

避免过多使用监听

监听操作,非常的好用,开发中带来了很多便利,但是于此同时也带来了一些弊端,即为内存的消耗,如果过多的创建监听,势必会占用很多内存。

所以在某些情况下我们可以避免使用监听操作:

比如:我们需要监听一个input输入框数值的变化:

通常来讲会是如下方案:

$scope.$watch('', function(new , old){})

但是这样就创建了一个监听操作。

我们也可以这样做:

<input type="text" ng-blur="action" />或者<input type="text" ng-change="action" />

合理使用HTTP

对于部分App, 在程序启动后,可能不止请求一个服务,可能会两个或者三个以上,如果需要等所有服务都请求完在进行页面跳转,实现起来就比较麻烦了。

如果一个请求完在请求另一个,时间势必会延长,影响用户体验。

我们可以采用如下解决方案,来自angular:

$q.all([async1, async2, async3...], function(res2, res2, res3 ....){})

http请求即为异步请求,所以以上方案就是异步并发请求,而且是等所有返回结果结束在进行统一回调。

这大大提升了请求速度,提升用户体验。

合理使用Native功能弥补JS的短板

JS是万能,但又不是万能的,混合应用的有些功能如果用js实现性能上会存在很多问题,比如:

  1. 拍照压缩
  2. 文件上传下载
  3. 网络状态监听

为了能够有更好的体验,我们就需要调用原生api实现此功能,并合理创建多线程处理,从而提高运行速度。

采用轻量级组件化框架技术

这几年下来,我使用的框架,还是比较多的,比如vue.js, ionic, avalon, SAPUI5, backbone, durandal, angular还有最新的react系列。

目前框架都在主推,高效轻量级的框架,效率高,性能好,加载速度快,易上手。

这些特性对于移动端同样适用。

所以,我们在进行混合应用开发时候,要选择轻量级的框架,切稳定,社区活跃的,保证遇到问题,有章可循。

这里建议的有: angular, ionic, backbone。

http://blog.csdn.net/jiangbo_phd/article/details/51761565

实现本地View的加载渲染

目前混合主要实现方式有两种:

  1. webview 加载本地html css, js , 图片文件,也就是说所有文件存在于手机app本地。
  2. 还有一种即为,html, css, js 图片文件部署在远程服务器上,通过url形式被webview加载。

两种方式各有优缺点,第一种加载速度快,适用于离线app. 第二种加载速度慢,使用与web app。

看你选择了。

Infinate动态加载数据

对于前端无穷渲染list数据是存在一定问题的,angular最大的问题就在这个地方,angular的ng-repeat,无法渲染大量数据,否则的页面会出现假死的状态。

对此,同样存在解决方案,也就是Infinate无穷加载,当滑动到底部,会动态监测,然后load更多。

附上一个简单的实现方案,采用ionic技术框架:

<ion-content ng-controller="MyController">  <ion-list>  ....  ....  </ion-list>  <ion-infinite-scroll    on-infinite="loadMore()"    distance="1%">  </ion-infinite-scroll></ion-content>function MyController($scope, $http) {  $scope.items = [];  $scope.loadMore = function() {    $http.get('/more-items').success(function(items) {      useItems(items);      $scope.$broadcast('scroll.infiniteScrollComplete');    });  };  $scope.$on('$stateChangeSuccess', function() {    $scope.loadMore();  });}

请求数据并渲染DOM优化方案

现在几乎所有的系统都是前后端分离的,也就说需要通过ajax请求数据后在渲染到页面上。

目前主流的框架都会支持很多的绑定方式,比如:

  1. 双向数据绑定:
  2. 单向数据绑定
  3. 一次性数据绑定

如何使用合适的绑定方式决定了我们内存消耗的多少。

上面三种方式消耗状态: 双向> 单向> 一次性

那么什么时候该使用什么样的绑定方式呢?

举个例子:

  1. 用户登陆 (双向,因为要获取用户名和密码在controller)
  2. 新闻详细信息显示(一次性,因为不需要再更新数据)
  3. 表单验证结果显示信息(单向,因为需要根据controll信息动态更新检验结果状态)

避免创建过多的window全局对象

学过JS的同学都知道window即为全局对象,是任何一个地方都可以调用到的,也会有老程序员告诉新人说:“你不要污染了全局空间”。

例如Angular这样的框架,同样有个全局属性“$rootScope”, 也是我们在任何地方都可以使用的。

如果项目过多的使用了这样变量:

一. 是会导致全局变量的混乱不堪,无法维护。
二. 不使用时候,无法回收这个变量,垃圾变量占用内存。

尽量避免使用JS控制Dom样式

最近两年,Angular是非常火热的框架,所以本人使用angular也会比较多一点,在这个框架中,有一个属性是可以动态控制dom样式的,叫ng-class, 可以说非常的好用,刚开始时候变肆无忌惮的使用起来。

后来发现运行起来多个ng-class控制时,或者在ng-class中使用个属性,并使用===, 三目运算符进行判断,最终导致页面闪动的状态,也就是效果不佳。

附上一个简单的例子:

<div ng-class="{{'customStyle':flag ==== customFlag}}"></div>

避免过度使用阴影和梯度和3D渲染

最近尝试了一下three.js这个框架,主要是想做3D图像,运行在浏览器端没有任何问题,但是在手机上,发现运行明显下降,运行一段时间后,手机发热,所以我们在项目中,尽量不要过多的使用3D这些特效,如果使用,要及时销毁,以免占用过多系统内存。

对于简单的CSS 3D旋转效果, 没有大问题, 大部分手机足以应付得了。

比如下面这段代码:

div{transform: rotateX(120deg);-webkit-transform: rotateX(120deg); /* Safari 和 Chrome */-moz-transform: rotateX(120deg);    /* Firefox */}

避免使用setInterval对插件进行频繁调用

对于cordova插件运行机制,很多新人并不是很清楚,也并没有很多人去编写自定义插件, 一个插件的调用流程大概如下图所示:

这里写图片描述

所以,一次调用过程,会花费较长的时间, 这个过程中,由于JS的单线程异步操作,此时通过JS调取,会阻塞线程的继续运行。

如果此时,频繁的调用插件,应用内存会骤然上升,后果会crash掉。

及时升级优化Cordova Platform

我们都知道移动系统的更细速度异常的快,andorid每年一个大版本, IOS也是如此,每年都会推出一个大版本,每一个版本的改变可大可小,但是对我们混合应用还是有一定影响的。

其中webkit的更新最为明显,毕竟混合应用使用的是webkit做UI渲染嘛。

最近就遇到一个问题,当IOS 更新到10, android更新到6以后,总会有一些莫名的问题。显示的多少会有些差异。

解决方案是,对Cordova Platform 做一个升级。

ios-platform —> 4.2.0

android-platform —-> 5.2.0

于此同时也对JS库做了一个版本升级,以便支持最新的webkit.

尽量不去渲染不需要显示的DOM

有一部分app在设计过程中,view的部分信息是不需要或者不经常显示的,也就是这部分信息根据用户行为是可有可无的,但是这个部分信息在此页面可能dom元素又非常之多,层次也会比较深。

如果我们默认就绘制dom元素,肯定会花费一些时间。

这种情况下,我们往往不去渲染,等需要的时候在渲染,所以这种实现方式很大程度上提升了渲染的速度。

下面说一下我再angular工程中的实现方案, 采用了ng-if解决这个问题,简单容易:

<div>    this is need to show    <div ng-if="false">this is not need to show</div></div>

使用canvas优化图片加载速度

通常使用Img在移动端渲染图片,速度往往不是很理想,会显示空白,这种情况我们通常会使用canvas进行优化,具体代码如下:

使用过程要注意,只有图片加载完成后才能显示出来,否则会无效,这里用到了回调onload方法。

<head>      <title></title>      <script type="text/javascript" src="jquery-1.7.1.min.js"></script>      <script type="text/javascript">          $(function () {              var c = document.getElementById("myCanvas");              var cxt = c.getContext("2d");              var img = new Image();              img.src = "./images/richard.jpg";              img.onload = function () {                  cxt.drawImage(img, 0, 0);              }          });      </script>  </head>  <body>      <canvas id="myCanvas" width="1024" height="768" style="border: 1px solid #f00;">Your browser does not support the canvas element.      </canvas>  </body>  

清除不需要的历史View

如果你的app页面越来越多,运行起来对内存的占用多少会有一定的影响,对于移动app,就需要我们尽量清除已经不再需要的view,比如注册流程所有的页面,比如slide导航,注册信息页,一旦注册完成,就需要删除所有的view历史。

再比如登陆,如果登陆成功也需要remove.

还有需要定时显示的banner等页面,显示过了,可能就不再需要了,这时候也需要清除掉。

下面说一下,我用ionic做过工程的相关方案:

if(this.$ionicHistory.backView()){ //获取上一个是哪个页面    this.$ionicHistory.clearHistory();  //clear}

动态刷新页面,为开发增添乐趣

对于多页面,复杂一些的项目工程,往往我们编译会花费较多的时间,记得我做过一个android native的项目,由于项目比较大,每次使用android studio编译都要花2分钟左右的时间,非熬人,对混合项目也不例外。

开发过程中,我们往往需要快速查看效果,实时浏览编译状态,所以就需要动态刷新了,说一下我采用的技术。

主要采用gulp + browserSync+ watchify来监听动态编译,实时加载,速度还是很快的,主要代码如下:

'use strict';var config      = require('../config');var browserSync = require('browser-sync');var gulp        = require('gulp');var url         = require('url');var proxy       = require('proxy-middleware');gulp.task('browserSync', function() {    var proxyOpts = url.parse(config.backendUrl);    proxyOpts.route = '/api';    var browserSyncConfig = {        port: config.serverport,        server: {            baseDir: config.dist,            middleware: [proxy(proxyOpts)]        },        logPrefix: 'MyDemo',        browser: ["google chrome"],        minify: false,        notify: false    };    browserSync(browserSyncConfig);});browserSync.reload({ stream: true, once: true })gulp.watch('path',   [task]);

前端代码工程化

前端代码工程化,今年被唱的很热,都在提这个概念,目前来看,一人独大的时代已经过去了,更多的团队协作开发,所以一个工程化代码是非常有必要的。

主要包括以下几个方面:

  1. 有意义的变量命名,遵从驼峰命名法或者团队内部命名法则,统一规范。
  2. 使用JSLint对代码进行检查,所以人统一一个标准。
  3. 采用构建工具对工程进行自动化部署,减少操作步骤,比如使用npm command, gulp ,grunt webpack等等。
  4. 合理编写注释代码,方便阅读及他人接手,编译后切记要remove掉,因为对于产品环境,这些注释没有任何意义。
  5. 使用标准的代码仓库,比如git, bitbuket等等, 提交代码进行comment, 合理创建子分之,保证多人开发协作无冲突。
  6. 代码组件化或者模块化,争取做到一人一模块。一模块一个文件包或者一个文件,这样可以保证彼此没有冲突。
  7. 保证团队每个人对代码工程使用,烂熟于心,方便管理及升级。
  8. 任命技术负责人,负责代码统一管理,版本合并,产品发布。

采用实时云更新技术对app升级

对于部分项目需求,用户不行频繁升级app, 的确这样也比较麻烦,这里我采用CodePush对混合应用代码进行云部署,不需要更新app, 既可以部署业务逻辑到用户的手机上,也可以根据用户需要定义所需的功能。

更多信息大家可以参考这篇文章:

http://blog.csdn.net/jiangbo_phd/article/details/52692320

使用Android Studio 和 Xcode性能查看工具

如果查看app内存消耗,是很多人关心的问题,这里我采用Android Studio 和Xcode性能工具进行查看测试,可以随时查看项目运行状态,并作出调整。

注意:项目需要的debug的运行状态。

Android:

这里写图片描述

IOS:

这里写图片描述

WebStorm工具使用优化

前端开发工具这几年最火爆的,无疑是Sublime, WebStorm, Atom, visual studio code, 其中Atom, Visual Studio code作为一款开源的IDE, 是当先最为流行的, 支持很多酷炫的样式和各种的自定义插件。

但是,说起来,这几年用的最多的,非常喜欢的一款工具就是WebStorm, 它可以说是一款集成的工具,支持很多H5框架,但确实收费的。

伴随着项目工程的越来越大, 项目更是用了npm bower这类库管理工具,到整个工程文件愈发庞大,有时候写着代码,工具就死掉了,非常不happy, 探索一番,找到如下解决方案。

打开我们的工程, 找到WebStorm的preference属性后,搜索Directories, 看到下面这个截图,右键Exclude一些文件包,比如node_modules, bower_components, platforms, www等等。

然后点击apply ⇒ ok,就好了,主要原理就是不要加载过大的工程文件,否则工具会吃不消的。

这里写图片描述

总结

混合应用的开发之路,并不是很容易,个人认为它的难点和复杂度要远远高于native和web,因为我们要掌握的东西实在太多了。

0 0
原创粉丝点击