艺龙网孙东 Slarkjs-前端框架的优化与实践
前言
进入2015,移动互联网继续迅猛发展。以我们公司艺龙为例,第一季度艺龙核心业务酒店的移动订单量在总酒店客房间夜中所占比例达到65%,这一数字在半年前还只是48%,移动业务的快速增长,对公司也越来越重要,对于我们前端框架有了更高的要求。移动前端相比pc前端有很大的不同,面临很多新的问题和挑战。
【作者】
孙东·
【以下为正文】
挑战
移动时代对前端有了更高的要求。移动的前端,用户比pc网站更在意流量,响应速度,触摸体验,用户体验的友好程度又直接影响到公司的业务量,所以,优化这些体验问题,将是移动前端的关键。
如何优化这些用户的痛点,也是我们关注的重点。传统的网站开发,还处于为了实现功能而进行的拿来主义,需要什么功能,网上搜索一段代码贴过来实现,缺点很明显
- 代码大小问题。插件所具有的功能不是你的业务全部需要的,但如果全部加载进来,在pc上无所谓,移动端因为网络环境不同可能会因为几十kb影响0.1s甚至更多的加载速度。
- 动画的高效性问题。一般pc的动画使用js实现,优点是简单,拿来就用,兼容性好,而手机上一般需要借助css3进行硬件加速来提高渲染的动画性能
- 触摸体验。触摸屏也是手机区别pc的一大特点,app已经培养了很多用户使用习惯,比如下拉刷新,左右滑切换标签,这些操作提高了用户的直观体验,与pc相比h5网站面临的挑战和机遇更多。
- 技术积累。没有固定的开发模式,根据功能来进行,导致每一个业务都要重新寻找,重新开发。
经过这几年的移动前端的探索和发展,以上的问题都已经有了基本成熟的共识和技术实现。即各家公司根据自身业务特点开发统一的框架并进行js模块化,css模块化,开发集成化,打包工程化,选用并改进性能最佳的lib来持续的优化框架,满足自身业务的需求和业务的发展。
百度轻应用做过一次调查问卷,对于移动h5站,还有哪些的需求最迫切,统计前五如下
- 性能问题(45%)
- 有限的硬件接口(37%)
- 难以集成本地元素(29%)
- 无法创建尖端的页面与交互(23%)
- 缺乏成熟的框架(20%)
之前这些问题在纯web上是无解的, cordova等工具另辟一条打包nativeapp的方式扩展了接口,真正的体验问题并没有解决,facebook发布的只支持ios的react-native的beta版,提升了页面流畅度,目前来看,ios不是webapp的性能瓶颈,对于安卓上的性能问题,我们只能期待。
机遇
尽管挑战很多,转机已经开始出现。ios8 开始支持safari的扩展,通过这个接口,前端可以直接开发和调用本地能力,而最新的安卓L 也终于抛弃了饱受诟病的android webkit而使用了兼容性和性能更佳的chronium webkit,这些都预示着h5网站可以有更好的调用本地接口能力和更好的体验,同时,一些适用于移动web开发的的技术方法和框架不断的涌现,使得开发高性能app越来越容易和简单。其中有一些很优秀的框架,比如用于优化页面转场效果的clouda+(blendui),响应式css设计ratchet,解决数据绑定的angularjs。
html5的开发限制越来越少,由于拥有可以快速开发,快速上线,搜索导流等功能,能够运行在安卓,ios等多个终端内的优势,可以预见会成为客户端的越来越重要的补充。
优化实践
本章节将详细介绍为了解决以上痛点,我们的前端框架如何进行优化和实践的。
优化首屏
传统h5网站框架通过模板和数据在前端组装,虽然优化了前端逻辑,但需要很长的首页白屏等待时间,同时也不会被搜索引擎抓取到,最佳的方案是首屏使用server端渲染,次屏开始通过接口和前端模板渲染,大大减少次屏数据量和相应时间,利于缓存静态资源,加快首屏,次屏的访问时间, 如果使用nodejs作为web Server,前后端的模板统一的实施会非常的容易和顺畅,而在艺龙,我们的webserver框架是java开发的,模板引擎使用的是freemark。为了统一两种模板,我们进行了一些变通,先把freemark模板进行预编译,编译成前端的js模板。
听起来很复杂,实际做起来并不难,以艺龙火车票h5网站为例。
- 首屏时使用java web框架执行页面,同时加载我们的main.js/main.css/template.js
- 当需要跳转页面时,使用core.addPager()的加载方法,就会更新成自动发送请求json数据的url(java web框架),如果server不支持传json就把渲染好的页面进行拆剪放到页面dom中,同时在route.js中找到url对应的前端模板,取到后就可以实现次屏的渲染。
- 动画通过css3在框架内实现
--- route.js define(['/src/js/core.js'],function(core){ //key 匹配 id // tpl 匹配 未来的模板 // url 匹配 url // js 匹配 js core.router.add({ "list":{ lindex:2, tpl:"list", url:"/huoche/list", }, "order":{ lindex:3, tpl:"order", url:"/huoche/detail", }, "entry":{ lindex:1, tpl:"entry", url:"/huoche", } }); });
约定规则
约定规则是框架的重要一环。slark使用twitter的ratchet规则定义前端模板规则,移动h5网站的每一个元素均使用推荐的的页面标签。好处是一方面我们拥有了众多可扩展的样式库,另外一方面可以根据约定进行扩展,实现我们的页面转场优化和下拉刷新的css约定。同时,借助我们的框架js,可以实现卡头卡尾的兼容性问题,减少类似错误的出现。
优化转场
转场的动画可以缩短用户操作点击等待。目前的做法除了使用css3动画外,市面上H5网站的转场方案是先loading预先加载好页面,在转场进入页面,其原因是由于css3动画中如果进行页面渲染有一定几率破坏动画或造成空白页的bug,经过调研我们发现仍然有向native体验靠拢的可能。
这点我们的实践是,当存在页面模板缓存时,先渲染模板,然后转场,0等待;当不存在页面模板缓存,需要通过网络请求时,先渲染带标题后退头的页面,进行转场,同样0等待,同时对渲染页面的时机进行了干预,当网络请求早于css3转场动画结束时,等待css3动画完成后再进行页面渲染,避免破坏动画的bug出现,为了更有效的提高转场的性能,减少dom树的层级和大小也是必不可少的,我们把所以当前页面的插件做了封装管理,与框架结合起来,在转场之前,把隐藏的dom移除dom树,而在动画结束,悄悄的把dom添加回来等待调用。
模块化&工程化
Requirejs是一个基于amd(异步模块定义)规范的一个js库,能够通过依赖前置管理js的模块依赖。
Sass 是一个新的css编程语言,嵌套语法和需要编译使它天然适合模块化,特别的,最新的libsass 3.2支持了绝大多数的sass语法,sass可以不在依赖于ruby环境,使用和安装编译更加简单。
经过模块化与分层,不仅可以提高代码复用的逻辑,而且可以管理依赖,打包出最小的代码集合,同时,entry,core,lib 三个模块可以不仅可以内部扩展,也可以交叉进行扩展,即lib写的不好的,core可以吸收和改进,core通用性强的部分也可以抽离出lib,业务代码同理。
管理好这些依赖和模块化的代码,简化编译过程,使用gruntjs来配置任务,可以自动进行代码的常规错误检查,打包,压缩等操作。
IOS 9
Ios版本的每次发布,都会对html5的支持度大幅提升,ios9也不例外,对于前端框架来说,关注新的ios版本,响应式的在支持更多特性的ios版本上给予更多的体验,slark框架将在以下的特性上进行ios的体验优化:
- 支持http/2.0 协议,通过http/2.0 可以减少更多的网络延迟,特别是https的网络传输额外字节量大幅减少,可以预见,现在业界使用的前端rsa加密可能会逐渐被支持http/2.0的https协议取代。
- 框架级css3布局语法支持增多。Ios9带来了丰富的css3 api,比如Scroll snap points,Feature Queries,mediaquery支持interaction 等等,使得在新的ios版本中,动画更流畅,操作反馈更丰富。
业务线例子
---main.js require(['src/js/core', 'entry/huoche/js/route', 'entry/huoche/js/index', 'entry/huoche/js/list', 'entry/huoche/js/order'], function(core){ }); ---index.js define(['/src/js/calendar.js', '/src/js/common.js', '/src/js/city.js','/src/js/core.js'], function(calendar, common, city,core) { //通过define管理依赖,内容略 });
---main.css // Variables @import "../../src/sass/variables.scss"; // Mixins @import "../../src/sass/mixins.scss"; // Normalize & Base CSS @import "../../src/sass/normalize.scss"; @import "../../src/sass/page.scss"; @import "../../src/sass/base.scss"; // Components @import "../../src/sass/buttons.scss"; // Javascript components @import "../../src/sass/calendar.scss"; @import "../../src/sass/city.scss"; //my css @import "sass/index.scss"; @import "sass/list.scss";
后记