专栏名称: 京东设计中心JDC
专业,创造力,激情,设计。京东用户体验设计部门,致力于创造更美好的电子商务购物体验。
目录
相关文章推荐
解放军报  ·  起床号 ·  4 天前  
51好读  ›  专栏  ›  京东设计中心JDC

JDC丨京东设计中心 - 京东PLUS会员移动端改版实战

京东设计中心JDC  · 公众号  ·  · 2017-09-13 15:43

正文

帝都的三月春暖花开、惠风和畅。在这美好的时节,我们接了个“大项目”——京东PLUS会员M端频道改版。

京东为向核心客户提供更优质的购物体验,推出了京东PLUS会员,包含购物回馈、自营运费补贴、畅读电子书、退换无忧、专属客服和专享商品等权益,全方位提升和丰富网购特权。PLUS会员项目作为国内第一个电商付费会籍项目,由传统的以流量为核心向以用户为核心进行转变,通过付费服务锁定高净值核心用户,向其提供更优质的购物体验,实现用户价值最大化。 这是京东很重要的一个产品,刘总在各种不同场合向雷军、刘中国、杨元庆、周鸿祎等知名人士赠予过PLUS终身荣誉会员。京东APP 6.0计划在首屏增加PLUS会员频道的入口,预计将会带来大量的新用户访问,我们以此为契机对PLUS会员频道M端进行了整体改版,以提升用户体验和转化率。

▲ PLUS会员移动端首页


▲ PLUS会员移动端首页


这个项目还是蛮有挑战的:

  • 一方面要赶 APP 新版上线这个 deadline,另一方面需求提出的太晚,导致工期特别紧张。

  • 业务逻辑本身比较复杂,近10种用户账户状态、实名认证情况、小白信用达标状态、自动续费开通状态、会员级别、后台配置等维度都将直接影响页面的楼层顺序、样式和逻辑。而根据需求,频道首页特别重,复杂程度远超现有页面和其他频道页。

  • 我们前端和后端、产品团队身处异地,沟通成本较高。

  • 项目页面既要作为京东M站的一部分适配主流手机的各种浏览器,同时还需要内嵌在 iOS/Android 平台的京东、京东金融、微信、手Q等 App 中,兼容性要求高。在项目后期,产品方对个别机型上的低版本浏览器也提出了兼容要求。

  • 页面需要兼容一些其他团队提供的公共代码。

我们开工时,需求文档还未完全定稿,对需求了解的不充分和对业务逻辑复杂度认识不够,让我们一度非常被动,开发工作充满了挑战。为保证按时上线,我们顶着压力,加班加点,那段时间几乎连轴转。无论如何,我们拿下了这场硬仗,现在回过头来看,还是感慨无限。这里记录一下这个项目开发中遇到的一些问题。


▲ 尊享商品列表页


▲ 我的PLUS页


选型之坑

旧版采用的是前端开发静态页面,后端再套页面的开发模式,在这种模式下前端人员可发挥的空间受到了很大制约,也给后期的维护和二次开发工作带来了麻烦。于是,此次改版我们提出采用前后端分离模式开发,以明确前后端权责、提高开发效率,同时提高后期可维护性,这一方案得到了后端团队的支持。而难点在于这次是改版,并不是完全从零开始,所以一旦前后端分离,之前在view层面的很多业务逻辑就也要拿到前端来做,而我们团队作为一个公共部门并不熟悉他们的业务,这无疑进一步增加了我们的压力。

我们在前端采用了目前比较流行的 Vue.js 2.0 开发框架,脚手架工具和之前开发的很多项目一样,仍然选择了京东前端自己开发的 JDF ,而当时的 JDF 版本尚不支持解析 Vue 模板文件,所以我们使用了 Vue.js 的独立构建模式,也就是在页面上直接引入完整版的 Vue.js 文件(包含模板编译器),模板的编译在浏览器端完成,后来发现这给我们挖了个大坑。

▲ Vue.js的不同构建版本


项目测试阶段发现,iOS版京东APP 频道首页的打开速度明显慢于其他浏览器和 APP,每次进入页面都会首先渲染出 loading 浮层,之后3-4秒才渲染出页面的楼层。我们马上开始排查。在 APP 中调试还是比较困难的,而在调试方便的浏览器中又不存在这个问题,所以这个问题的排查,我们还真是下了一番功夫。


我们初步判断这不是常规的因为请求多、资源数据量大、JS逻辑等原因造成的问题,因为这些问题一旦存在必然影响不止一个浏览器,且通过浏览器性能监控并未发现明显异常情况。

我们和测试团队利用抓包工具对 iOS版京东APP 中这个页面的资源请求情况进行了监测(这个阶段我们还踩了iOS 10.3 版本 https 请求抓包的坑,下文细说),也没有发现明显异常,基本印证了我们的判断。

另一方面,我们发现同一部 iPhone 的 Safari 浏览器和京东金融、微信、手机QQ等 APP 中页面加载速度都明显快于 iOS版京东APP,所以我们把关注点放到了 iOS版京东APP 内置的浏览器组件上,经过深入调查及与APP团队沟通,我们了解到 iOS版京东APP 使用的是比较老的 UIWebView。

苹果公司在 iOS8 之后提供了替代 UIWebView 的 WKWebView,较 UIWebView 在性能、稳定性、功能方面有大幅提升,目前主流 iOS APP 大部分都在使用或已经迁移到 WKWebView。

▲ 苹果官方从 iOS8 开始就建议不再使用 UIWebView


所以我们判断频道首页在 iOS版京东APP 中加载速度慢的原因是:性能低下的 UIWebView 在编译逻辑复杂的频道首页 Vue.js 模板时耗时过多。那么,怎么解决这个问题呢?等待 APP 升级 WKWebview 明显解决不了燃眉之急。要解决当下的问题还得从页面自身入手。我们计划尝试把模板编译工作拿到本地来做,减轻浏览器负担。

我们基于 webpack 搭建了一套新的脚手架,使用 Vue.js 官方提供的 Vue-template-loader 在本地 nodejs 环境对页面 Vue 模板进行了预编译,把模板文件提前编译成了 JavaScript 渲染函数。频道首页在新的脚手架跑通之后,我们在 iOS版京东APP 中测试了一下,页面渲染速度大幅提升,随后我们把整个项目都迁移到了新的脚手架中,问题得以彻底解决。
来,感受一下。




回过头来看,我们团队可能不是第一个踩此坑的,也不会是最后一个。所以无论是从节约公司研发资源角度,还是从提升用户体验的角度,我们都希望 iOS 团队的兄弟们能尽快升级 WKWebView。我们在撰写这篇文章的时候去苹果官网看了一下,截至2017年7月,iOS 9.0 及以上版本已经占到97%。

▲ 苹果官方的 iOS 版本分布数据(数据截至2017年7月)


开发阶段的跨域问题

前端后端分离,又一次把跨域问题摆在了我们面前。页面上线之后与接口是在同一个域下的,只有在开发阶段才面临跨域问题,所以只要把我们自己开发环境的跨域问题解决即可。
早期解决开发阶段的跨域,通常采用给 Chrome 设置启动参数 “–disable-web-security” 禁用浏览器安全策略的办法,可这招在新版本 Chrome 上已经不灵了。再想想,移动端项目不用考虑低版本 PC端浏览器,完全可以采用 CORS(跨域资源共享)技术来实现跨域。对于 GET/POST 这类简单请求来说,只要服务器响应头的 Access-Control-Allow-Origin 字段值包含当前源(协议+域名+端口),支持 CORS 的浏览器便会放行此次请求。既然是开发阶段的问题,我们还是不要麻烦后端同学修改接口了,Chrome 浏览器有个名叫 “Allow-Control-Allow-Origin: *” 的插件,可以自动给服务器响应头加上 “Allow-Control-Allow-Origin: *” ,装上它, Chrome 就可以跨域了。那……其他浏览器呢,当然我们可以说我开发阶段只用 Chrome,可是手机浏览器呢?APP呢?它们可未必有这个插件。

搞清楚 CORS 原理之后会发现这个问题不难解决,没有插件我们可以使用抓包工具来给 response 加上 “Allow-Control-Allow-Origin: *”,比如 fiddler。在 fiddler 菜单中找到 Rules → Customize Rules…,弹出 fiddler 脚本编辑器(第一次按提示安装即可),如下图所示,在 OnBeforeResponse 函数中加上红框中的代码,保存成功之后,fiddler 便会自动给经过它的 response 增加 “Allow-Control-Allow-Origin: *” 了,我们只需要设置好浏览器和手机的代理,让请求和响应经过 fiddler 即可实现跨域。

Flexbox布局问题

本次改版的页面布局,大量使用了 CSS3 Flexbox 弹性盒模型。Flexbox 不是什么特别新的技术,但是由于相关规范经历多次迭代,写法不尽相同,而不同时期的浏览器实现了不同版本的规范,这就造成了一些同学对 Flexbox 布局写法和浏览器兼容性的疑惑,迟迟不敢在项目中大规模应用。


1

2

3

4

5

6

7

8

.box {

display :-webkit-box;

display :-moz-box;

display :-webkit-flex;

display :-moz-flex;

display :-ms-flexbox;

display :flex;

}


其实,算上所有版本规范,移动端对 Flexbox 的支持还是比较好的,不信你看 http://caniuse.com/#search=flexbox 。我们完全可以只按照最新的标准来写,然后借助 autoprefixer 等工具来自动补全旧版代码兼容低版本浏览器,主流脚手架、IDE都支持。

不可否认,在个别老旧机型上,Flexbox 确实会遇到些问题(主要是旧版的问题),比如旧版 inline 元素需要设置 display:block 才能正常显示,不支持 flex-wrap 等,但这些问题都是可以解决的。我们不能因噎废食,因为这些小问题存在就完全故步自封,抛弃 Flexbox。关于 Flexbox 布局总结几点:

  • 需要明确 Flexbox 规范有过多个版本,不同时期的浏览器可能实现了不同版本规范。

  • 按照最新版规范( https://www.w3.org/TR/css-flexbox-1/ )编码,通过工具针对我们设置的浏览器范围自动补全代码。

  • 总结个别老旧机型的兼容问题,编码时注意绕过。

  • Flexbox 也不是万能的,有它适用的布局,也有不适用的,勿滥用。

iOS https抓包问题

开发阶段在真机上调试网页,我们通常需要配置手机的代理服务器为开发电脑的 fiddler 等抓包工具,然后即可调试开发电脑上的页面。https请求抓包相对麻烦,需要安装根证书,在手机浏览器里通过ip+端口访问电脑上的 fiddler,会提示下载证书,下载安装即可。可是在这个项目,我们遇到点麻烦,Android 手机正常调试没毛病,手头的 iPhone 的 https 请求却抓不到包,不管装不装证书都不行。后来又找了几台 iPhone,发现安装证书之后,有的可以访问,有的不可以,于是我们怀疑是iOS版本问题,遂查了一下 iOS 近期版本更新内容,发现果然是新发布的10.3版本有变化,需要在“设置”>“通用”>“关于本机”>“证书信任设置”中对已安装的证书进行信任设置,之后才能抓包 https!!这个问题耽误了不少时间,最后知道真相的我们眼泪掉下来~以后得多长个心,多关注主流手机系统、浏览器的更新内容。

ES6兼容问题

对于手机端项目,最让人头疼的莫过于兼容问题了。项目完成后,被测试人员告知在华为个别机型自带的浏览器中,商品列表页面显示异常,商品一直处于加载状态,如下图所示:

我们尝试在这些手机的京东APP、UC等浏览器中访问,没有发现异常。于是判断是原生的浏览器的兼容问题,但是具体是哪里不兼容呢?我们首先猜测华为手机原生浏览器的内核版本过低,无法支持 Vue.js 2.0 框架。然而,在之前上线的一些项目就是使用 Vue.js 2.0 开发的,在这些手机原生浏览器下也能打开,因此排除了这个问题。最后发现原因是项目中使用了 ES6 的 API,而华为部分机型原生浏览器内核版本过低,不支持 ES6 的 API。其实对于 ES6 的兼容问题我们之前是有所注意的,在脚手架中使用了 Babel 来转化 ES6 代码。Babel 是一个广泛使用的转码器,几乎可以编译所有新潮的 JavaScript 语法,但对于 API 来说却并非如此。比方说,下列含有箭头函数的 ES6 代码:


1

2

3

function addAll () {

return Array . from ( arguments ). reduce ((a, b) => a + b);

}

经过 Babel 插件转化为:


1

2

3

4

5

function addAll () {

return Array . from ( arguments ). reduce ( function (a, b) {

return a + b;

});

}

它仍然并非随处适用,因为并非所有的 JavaScript 环境都支持 Array.from 这个全局对象。Babel 默认只转换新的 JavaScript 句法,而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)。而 polyfill 可以在当前运行环境中用来模拟性复制尚不存在的原生 API 的代码,能让我们提前使用还不可用的 API。所以为了完整使用 ES6 的 API,我们安装了 babel-polyfill。在此过程中,需要注意的是尽量使用 NPM 中提供的最新版的 babel-polyfill,因为尝试使用过一些较老的版本,个别华为机型仍然不能打开页面。


1

npm install --save- dev babel-polyfill


安装后,只需要在文件顶部导入 babel-polyfill 就可以了。但是这种方案显然有些缺陷,babel-polyfill 构建压缩后体积为80~90k,这对于移动端还是有点大的。为了一些非主流的浏览器来引入 polyfill 而影响主流浏览器的性能,值不值得呢?所以如何取舍?要不要使用 babel-polyfill?还是需要评估和权衡的。

Vue数组问题

在 PLUS会员频道M端中有个页面为商品列表页,该页面如下图所示:

从图中可以看出,该页面顶部有导航栏,点击导航栏可以切换下面商品列表部分,而每一个商品列表页内部都分为多页,具体可以参考下面的示意图:

从上图可以看出,每个商品列表页都会根据用户在其内部向下滑动的程度来触发请求下一页的数据,所以每个商品列表页对应的商品区域都要有独立的“当前页码”、“当前提示是否有更多商品状态”、“当前向下滑动位置”、“当前是否出现回到顶部的按钮”等信息,因此我们使用了对象来保存每一商品列表页的相关数据,而这些对象又组成了数组。于是,不可避免地要使用 Vue 来监听数组的变化,从而动态渲染页面。但是这里遇到一个问题,例如我们使用数组 arrtab[i] 来保存导航栏第i个标签的信息:


1

2

3

4

5

6

arrtab[i] = {

'flag' : 0 ,

'page' : 1 ,

'haveGoods' : 2 ,

'reFlag' : 0

}

当该商品列表页对应的商品列表向下滑时,page 发生变化,但是对应的页面没有变化,也就是 Vue 没有检测到该数组 arrtab 发生了变化,但是我明明改变了数组 arrtab 了呀?于是重新查看Vue文档,发现 Vue 对数组的监听还有“特殊”的处理,文档指出:

  1. 由于 JavaScript 的限制,Vue 无法检测到以下数组变动:

  • 当你使用索引直接设置一项时,例如


  • 1

    items[indexOfItem]







请到「今天看啥」查看全文