2019年京东PLUS会员前端开发之路

时光如梭,白驹过隙,2019年转瞬即逝。这一年对于 PLUS 会员项目前端同窗来讲是坎坷和充实的,如白岩松所说,痛并快乐着。回首望去,异业合做权益的陆续接入,6.18大促和双11活动的需求扎堆,中间穿插部分机型首屏白页等问题的困扰,在一阵慌乱以后,咱们逐渐稳住了阵脚。在完成平常需求的同时,基于原有框架,对项目的稳定性、加载、体验、开发效率等方面作了更多夯实。javascript

2019年,累计支持了近90多个大小需求。主要分为四类:产品升级、异业合做、促销活动、紧急需求。在这些需求中包含了经典卡新增用户权益的需求,如健康、读书、快递券、95折商品权益。也大大扩展了和其余异业的联合,如腾讯视频、携程旅游、酷狗音乐等。此外还有研发侧发起的性能优化、用户体验优化等。css

PLUS 会员项目横跨三个职场,研发和项目侧在北京(2处职场),产品在上海团队。沟通显得尤其重要,任何一个微小的问题,均可能在用户中被放大,每一次操做上线都需当心谨慎,如履薄冰。html

项目功能复杂,状态繁琐,业务逻辑经常内含玄机,一个改动有可能会影响数十个状态,牵一发而动全身。整页纸都容不下一个弹窗的交互逻辑,改一处而崩所有。此外,时常并行十来个需求,每周须要数次合并功能上线,还有那不得不说的恐怖魔咒:上线频繁通宵~~~前端

然而,破茧成蝶,涅槃重生,哪个不是须要痛到极致以后才能获得升华。在经历了疯狂的成长以后,PLUS 会员项目像个横冲直撞的孩子,咱们决心从新梳理,改变现状,让他健康成长。本文将从性能优化,开发效率,流程优化这三个方面阐述,如图所示:vue

icon

什么是PLUS会员

首先请容许我简单介绍下 PLUS,京东为向核心客户提供更优质的购物体验,特别推出京东 PLUS 会员,包含十几项权益:java

  • 购物10倍返京豆:PLUS 会员人均 1 年最高可返 220 元价值京豆,一年买几个大件电器会员费就回本了。
  • 自营免运费:每个月可得到 5 张运费券,一年就可得到 60 张运费券。另外还有整年 36 元快递券。
  • 会员价商品:可尊享 500 万件京东商城商品会员价,以更低的价格购买商品。
  • 上门退换货:会员享受自营商品售后服务(退货、换货、维修)免运费和免费上门取件“双免”服务。
  • 海量生活特权:吃甜品买一赠一,看电影打 5 折,唱KTV 2 小时免单,吃个火锅还免费赠你一盘羊肉卷,还有好多好多特权,统!统!不!要!钱!
  • ...

更多权益就不赘述了,更多 戳这里node

此外,购买京东 PLUS 会员联名卡,就拥有了两个以上的会员权益,嘘~ 低调,毕竟你们都是有会员身份的人~~webpack

孩子:妈妈,我想买京东 PLUS 会员~ 妈妈:买,买个联名卡,一个顶两个,够不够? 孩子:够了,谢谢妈妈,妈妈真好!git

接下来就带你们看一下 PLUS 会员部分页面的庐山真面目吧:github

icon

OK,广告结束,下面进入正题。

前端架构

PLUS 会员项目 18 年开始选用 Vue 技术栈,使用了团队自行开发的 Gaea 构建工具和 NutUI 组件库,此外引入了 Carefree,SMock,Vuex,TS,PWA等。

Gaea构建工具 ,是咱们团队自主开发的一套 Vue 技术栈构建工具,基于 Node.js、Webpack 模版工程等的 Vue 技术栈的整套解决方案,包含了开发、调试、打包上线完整的工做流程。极大的提升了工做效率,目前团队全部 Vue 业务都使用该脚手架。

NutUI组件库 ,是一套京东风格的轻量级移动端 Vue 组件库,由咱们团队历时数年打磨,目前有30+京东移动端项目使用,github上获得1.5k+的star。该 Vue 组件库提供大量的可复用的 Vue 基础组件,极大的便利了 PLUS 项目的开发。

Carefree ,一套不依赖 wifi 热点的移动 web 真机测试一站式解决方案,是咱们团队在平常开发中发现真机很依赖电脑发出热点才能进行调试的痛点,针对这一问题,旨在摆脱wifi热点束缚,让移动web真机测试自由自在而自主研发的一套解决方案。

SMock ,由团队自主研发,针对项目前期尚无数据的问题,分析须要 mock 的文档,输出相应的 mock 数据,并启动 node 服务,供前端开发时调试使用,提升前端开发效率,支持跨域访问。

此外,咱们引入了 Vuex 作状态管理,PWA 作数据缓存和离线应用,也尝试使用 TypeScript 语言开发。

icon

性能优化

提升页面渲染性能,改善用户体验,是团队一直重视的方向。首页已经使用了不少方法来优化性能,包括使用骨架屏减小用户等待体验、核心数据直接出以此减小请求接口时间,楼层内部滚动懒加载,使用 Webp 缩小图片大小等等。因此一开始咱们犹如站在巨人的肩膀上,一动不动,生怕一不当心失足掉下去~~~

提升页面渲染性能

事情的原由,是在半年前,页面卡死白屏问题会时不时的冒出来,像个幽灵同样困扰着咱们。并且是随机偶然出现的,几千万用户中总会有几部手机被幸运之神选中————让其出现白屏、卡死、气的拿手机砸墙!但是一样的手机、一样的配置、一样的 APP 版本,在咱们这边却老是安然无恙。在此期间咱们尝试了不少方法排查,好比

睡觉、喝茶、聊天
搞笑,怎么可能?咱们回访用户咨询复现步骤,联合 WebView 研发排查缘由,根据用户 pin 查询崩溃信息,调查埋点上报等等,奈何是偶现问题收效甚微,而且仅仅经过一个白屏图片,很难定位问题究竟出如今哪里?即便修改怀疑影响的代码,也没法验证是否获得修复。

icon

因而茶不思饭不想,百思不得其解。这难道是量子力学中薛定谔的猫?事物最后变化的结果是根据人为观测而变化的?那解决后是否是能够得到下一届的诺贝尔物理学奖呢?想一想忽然还有点小激动呢...

第一阶段:问题初解决

好在皇天不负有心人,在切换了多种网速,模拟各类环境的尝试下,发如今页面卡死的状况下,每每会上报一个错误,就是依赖的变量没有定义致使的。

咱们知道基于 Webpack 脚手架和 Vue 框架开发的项目,会打包生成一个依赖 JS 文件,一个业务 JS 文件。为了便于上线,当时采用动态生成静态资源的连接。而浏览器每每会并行下载多个外部静态资源,若是业务 JS 文件一旦先于依赖 JS 文件执行,则找不到依赖变量,结局只有一个:那就是页面卡死!

好了,真凶既然已经锁定,接下来就是如何修改了,鉴于动态生成静态资源外链有可能致使 JS 执行顺序的不肯定,咱们又改回成了 Webpack 打包自动注入静态资源连接的形式。然而,事情真的就这么结束了吗?

第二阶段:新的问题

果真,以后白屏像幽灵同样,依然偶现!虽然比以前少了一些客诉,但仍然会不时的零星出现几例。而且随着时间的推移,需求愈来愈多,每次都要并行开发不少个需求。为了并行开发代码不冲突,以及避免每次上线时客户端缓存旧文件,因此每次上线都是使用新版本号的文件。可是这样致使即便改动一点代码,也要前端、后端一块儿上线,在快速迭代需求的状况下,每次改动都要后端研发修改十几个静态资源的版本号,前、后端研发不堪其扰。因而咱们决定使用公司的头尾系统,把静态资源的路径放在头尾系统中,每次上线新文件后,前端推送头尾,一键部署静态资源到各个服务器。然而好事多磨,因为维护多个页面,每一个页面对应两个静态文件:一个是 JS 文件,一个是 CSS 文件,分别放在<body>的尾部和<head>之中。致使每次改动头尾文件都要涉及到十几个文件的修改(哎,好想念当年后端同窗帮忙修改版本号的日子~),维护成本剧增,且随着页面的增多,这一现象有增无减;

此外,为了缓解用户在等待页面渲染中的焦急心情,早已经在返回的 HTML 中增长了骨架屏。然而遗憾的是,咱们发如今 IOS 系统中骨架屏并不会出现,而是直接呈现出最终渲染的页面。

基于解决以上问题,咱们没有像碰见危险就把头埋进沙子中的鸵鸟同样逃避,而是决定主动优化,去啃下这个硬骨头!

第三阶段:山穷水尽疑无路,柳暗花明又一村

常规的浏览器渲染机制,客户端从服务器请求回 HTML 以后,HTML 解析器就会从 HTML 文件的头部到尾部,一个个地遍历这些节点。当这些节点是普通节点的话,HTML 解析器就会将这些节点加入到 DOM 树中。当这些节点是 JS 代码的话,HTML 解析器就会将控制权交给 JS 解析器。若是这些节点是 CSS 代码的话,HTML 解析器就会将控制权交给 CSS 解析器。不过,当外联的 JS 代码和 CSS 代码还没从服务器传到浏览器的时候,这个时候若是 DOM 树上有可视元素的话,浏览器一般会选择在这个时候,将一些内容提早渲染到屏幕上来。可是问题就在于 IOS 系统中的 WebView 并无将首屏直出的这部分 HTML 页面显示出来。HTML 中直出的 DOM 结构会等待外部 CSS 和 JS 加载执行后才统一进行渲染。

为了验证这一猜测,咱们能够把 HTML 代码化简到最少的代码:只挂载元素中放置一个红色 div,在其外面放一个绿色 div。

<!DOCTYPE html>
<html lang="zh-cmn-Hans">
    <head>
        <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover" name="viewport">
        <title>PLUS会员</title>
        <link rel="stylesheet" type="text/css" href="//static.360buyimg.com/exploit/mplus/4.3.2/v4/css/index.css">
        <script>document.documentElement.style.fontSize = 320 / 7.5 + "px"</script>
    </head>
    <body>
    <div id="app">
        <div style="width:100%;height: 200px;background:red;"></div>
    </div>
    <div style="width: 100%;height: 200px;background:green;"></div>
    <script type="text/javascript" src="//static.360buyimg.com/exploit/mplus/lib/4.1/vendor.dll.js"></script>
    <script type="text/javascript" src="//static.360buyimg.com/exploit/mplus/4.3.2/v4/js/index.js"></script>
    </body>
</html>复制代码

而后在IOS系统中效果以下图所示:

icon

能够看到,因为红色 div 位于挂载元素之中,直接被生成的 Vue 页面替换掉了;绿色 div 是和 Vue 页面一块儿渲染出来,因为完整的页面须要调用不少接口判断,因此显示的稍晚。可是不管哪个 div 都是在 Vue 最初的页面渲染前不会显示的。

那么 IOS 端的骨架屏面临着同样的境遇,若是放在挂载元素里面,根本不会显示;若是放在挂载元素外面,就会和 Vue 渲染页面同时出现,也就失去了骨架屏的意义。

为了不使用外部下载 CSS 和 JS,致使 IOS 系统影响首屏直出骨架屏的渲染,仍要采用待骨架屏加载完以后,再去动态引入 JS 和 CSS,因为首页骨架屏的代码量自己并不大,待其加载后再去动态引入静态资源,时间影响不大(后面有实验数据)。可是又回到了第一阶段中,动态生成静态资源致使的 JS 执行顺序问题。

因此,绕了一圈,无解,剧终~

等等,开玩笑,此可轻易言败!通过屡次的尝试,咱们采用了引入事件监听的方法,核心思想是待依赖文件都加载完以后,再去执行业务代码。 好处是:

  1. 保证了静态资源的并行下载,不影响原有下载方式;
  2. 保证了代码执行的顺序;

在此过程当中须要注意:

  1. HTML 中不要写 ES6 语法的代码,避免由于没有 babel 的编译致使低版本浏览器不兼容的问题;
  2. 生成多个 JS 标签,先挂载到虚拟的节点对象 再统一挂载到 body 上,减小页面的重绘;
var oFragment = document.createDocumentFragment();
    var indexScript1 = document.createElement('script');
    var indexScript2= document.createElement('script');
    oFragment.appendChild(indexScript1);
    oFragment.appendChild(indexScript2);
    body.appendChild(oFragment);复制代码
  1. 兼容本地开发,本地开发的时候仍然使用 Webpack 自动引入静态资源的形式,打包和线上的再用动态生成静态资源的方式:
<script type="text/javascript">
    var staticVersion = 'dev';
</script>
<script type="text/javascript">
    //头尾系统的版本号,从新定义 staticVersion
    var staticVersion = '1.0.0';
</script>
<script type="text/javascript">
    window.onload = function(){
        if(staticVersion != 'dev'){
            //动态生成代码
        }
    }
</script>复制代码

通过实验,在正常网络状况下,IOS系统中页面虽然渲染平均慢了 266ms,可是骨架屏再也不等待 CSS 和 JS 的加载,提早了1000ms左右出现。在弱网状况下,页面渲染时间相差无几,骨架屏依然能够在 HTML 返回后快速渲染出来。

icon

解决了如下问题:

  1. 优化静态资源执行顺序问题,避免了由于业务代码早于依赖文件执行而出现白屏卡死问题;
  2. IOS 系统和安卓系统均可以在 HTML 返回后当即展现骨架屏;
  3. 减小骨架屏渲染时间,无需等待外部静态资源的下载;
  4. 以下图所示,因为动态生成静态资源路径,以往每一个页面都要引入两个头尾文件(包括 JS 和 CSS 路径文件),优化以后只须要维护3个头尾文件便可,待以后版本进一步迁移以后,整个项目只须要维护1个头尾文件便可!

icon

优化楼层懒加载

页面楼层懒加载,可以颇有效的提升首页加载速度,减小没必要要的请求。在 PLUS 会员首页中,底部长长的商品楼层非常符合懒加载的条件,用户并不会在进来的时候看到商品楼层。在以前的项目中,每次滑动商品楼层只请求一页的数据,减小没必要要的请求和渲染。可是还有待优化空间,若是用户的手机首屏并无显示商品楼层,咱们就不须要在刚进来就加载该部分的 JS 代码。再来看这个文件的大小,线上通过压缩后的 JS 文件 11.7k,CSS 大小16.8k,加起来总共有 28.3k 了,这样看来已经不小了。

为了进一步减小首页没必要要的加载,咱们使用了 Vue-lazyload 插件,来实现组件的懒加载功能,而且使用预加载技术,让浏览器空闲时间先把该文件下载下来,避免用户滑动到商品楼测才去下载对应的静态资源。

基于 webpack 的 @babel/plugin-syntax-dynamic-import 和 Vue-lazyload 插件很容易实现异步加载代码的独立打包。

icon

尤为注意页面中处于该商品楼层上面的区域,若是没有固定高度的楼层,必定要保证渲染完成后,再去触发计算商品楼层的是否在可视区域内。最终懒加载组件曝光后,再去加载商品楼层。记得取消 network 面板中的 Disable cache,效果以下:

icon

能够看到效果,页面商品楼层才去加载floor-feeds.js 文件,而且已经有了预加载,因此从缓存中获取,时间为2ms。

icon

如上图所示,在小屏手机下,进入页面并不会加载 floor-feeds.js,只有在页面往下滑动时,才会从预加载 Prefetch 中加载该文件。 最后兼顾一下用户体验和性能,在大屏手机下的商品楼层,在首屏下面呼之欲出,此时设置懒加载功能意义不大,由于用户稍微滑动一下屏幕,就会露出商品楼层,而且渲染商品楼层也须要时间。而在一些小屏幕手机下,商品楼层距离首屏很远,商品楼层的懒加载就显得非常重要了。

引入PWA

PWA 全称 Progressive Web App,即渐进式 WEB 应用。是近两年来比较火的一个技术,经常使用来实现离线缓存功能,用户能够较快的访问已经访问过的页面,PLUS 会员首次使用了该技术,具体的代码就不说了,线上示例一搜一麻袋,这里聊一聊较少说起的坑吧。

  1. 尽可能把 sw.js 要放在和页面 HTML 同一个目录下,不然注册 serviceWorker 无效; 最初模板代码发在了 src/template/index.html,我把 sw.js 放在了根目录,也就是和 src 一个路径下,再去配置 scope ,老是提示注册 serviceWorker 无效;

  2. 终于注册成功以后,代码也不报错,但是查看控制台中的 Network,全部的请求并无走 serviceWorker,配置文件改了又改,缓存删了又删,Network 中的 Disable cache 也取消选择,最后才发现环境的配置问题!以下图所示:

icon

打开浏览器的控制台,选择 Application 页签,左侧选择 Service Workers 页签,必定要注意这三个选项:

  1. Offline:选中后关闭网络,此时能够模拟离线状况;
  2. Update on reload:选中以后表示每次刷新页面,从新加载 sw.js 文件;
  3. Bypass for netWork:选中以后请求只走网络,而不从缓存中加载;

我最开始的 Bypass for netWork 默认选中,因此致使页面请求一直走的是网络请求。

icon

注意的是,咱们这次只是在更新迭代较少的异常状态页面加入了 PWA 做为尝试,其余状态按照每周至少两次上线,同时并行十来个需求的节奏,每一个页面对应一个 sw.js 文件的状况来讲,若是每次都要后端去更新 sw.js 的文件,我感受后端童鞋又要扛着五米长的大刀找我来玩耍了~~~

引入 TypeScript

提到TypeScript你们确定不陌生,它是 JavaScript 的一个超集,主要提供了类型系统和对ES6的支持。它的出现让开发人员在编译阶段就能检测大部分错误,增长了代码的可读性和可维护性。可是面对上千万用户的 PLUS 会员项目,咱们迟迟不敢动手,考虑到 PLUS 会员项目影响范围较广,业务逻辑复杂,且不断有新的业务需求加入,小小的变化可能会产生严重的、意想不到的后果。

本着当心求证再扩大战果的思想。咱们尝试在新页面的需求中,把 TypeScript 引入进来,在这个全新的页面上进行一场 Vue + Webpack + Vuex + TypeScript 的小实验。基于安全这一原则,在不改动原有项目代码的基础上,咱们当心翼翼的踏上了外界传言 vue + typescript 深坑的探雷之路:

icon

通过以上步骤,在原来的 JavaScript 项目基础上进行 TypeScript 改造,以后就能够使用 TypeScript 语法进行开发了。开发过程跟 Vue 项目开发没有什么区别,但有几点是咱们开发时遇到的问题,抛砖引玉:

一、背景图片路径问题

若是你的项目中有这样引入图片:

.weixin {
  background: url("../img/weixin-pay.png") no-repeat center;
  background-size: 0.46rem 0.41rem;
}复制代码

而且在 ts 文件中是经过 import 引入的 css 文件

<style lang="scss">@import '../asset/css/pay-method.scss';</style>复制代码

那么在启动服务的时候可能会访问不到背景图片,这个问题初步判断是路径解析错误的问题。在尝试了把样式直接写到页面中或者写到 common.css 里仍是路径用@来引入,都不能解决,最终在入口文件中引入 css 文件就能解决这个问题。

二、引入组件

原有项目中引入组件咱们可能会是这样:

import Dialog from "@/component/dialog/index.js";
Dialog.install(Vue);复制代码

但若是引用了 TS 后,咱们须要这样改写下不然将会报错:

import Dialog from '@/component/dialog/index.js';
(Dialog as any).install(Vue);
复制代码

三、this 的使用

在 TS 中咱们想使用 this 引入全局组件,但它所指向的上下文并非全局,因此要使用全局的组件就要对 this 进行下定义:

(this as any).$toast.text('正在处理中,请稍后再试~');
复制代码

四、全局变量的声明

在引入 TS 以后,若是在项目中想用 window等全局变量,都须要在代码最前边声明一下

declare var wx;
declare var MPing;
declare var window;复制代码

以上是目前在开发项目时遇到的问题,以及咱们如今的处理方法,可能不是最优的解决方案,欢迎留言一块儿讨论学习~~

从 CodeReview 中优化性能

随着迭代需求的日益增加,代码冗余度和繁琐度也越发严重,久而久之可维护性就会变差。此外,开发人员书写代码水平、风格不一,有可能留有隐患,对此,通常在项目上线前,最好要进行 CodeReview,发现问题后便于修改,趁着安排好的测试资源检查无误后上线。。。

然而,对于 PLUS 团队成员来讲,纷至沓来的需求压力以及各个需求卡死的上线日期,每每可以实现需求按时上线已经实属不易。因此只能在项目上线后,见缝插针式的抽时间进行 CodeReview,这就致使了修改后没有项目排期,没有测试资源的尴尬境地。可是为了推进从 CodeReview 中发现的问题,可以顺利上线,咱们在每次 CodeReview 以后,作了如下事情:

  1. 梳理 CodeReview 后发现的问题,分析哪些问题须要进行修改和优化;
  2. 整理好每一个修改点可能会致使的问题;
  3. 向项目经理、产品经理、测试等研发发送申请项目优化的排期邮件;
  4. 待排期肯定后,再安排研发修改问题,避免提早修改,由于中间穿插的平常需求开发,有可能会致使后期合并代码带来的隐患;
  5. 通过测试检查无误后,再上线;

至此,咱们从加强代码的健壮性、优化用户体验、提升页面渲染性能,优化代码结构、提升开发人员效率几个方面着重入手,排查了一些待优化的问题,并对这些问题进行梳理,申请项目排期和宝贵的测试资源,推进优化需求上线。

icon

开发效率

如何提升开发工做效率?在提高性能和用户体验的同时,也是咱们苦苦思索的方向。磨刀不误砍柴工,只有解决开发过程当中对研发人员的痛点,才能让研发人员更加快速的进入业务的开发之中。

自动导入文件

Vue框架开发项目,组件化思想已经深刻人心,可是随着项目复杂度的深刻,咱们发现每次新建一个组件,都要在首页进行引入文件路径,而且在 components 中添加该组件:

icon

若是有十几个组件,这里就要 import 十几个组件,相似的还有单页面应用,每次新增路由时,也要引入大量的路由。面对这种状况,为了提升开发人员的工做效率,减小开发组件工序,咱们使用自动导入文件夹中的组件的方法,避免页面引入过多的 import 和机械单调的引入步骤;

  1. 首先把全部要引入的文件按照文件夹分类,新建导出文件 myIndex.js
const files = require.context('./', false, /\.vue$/); //引入当前路径下全部的vue文件
let configRouters = {};
const modules = {};
files.keys().forEach(key => {
    const handleKey = key.replace('.vue', '').replace('./', '');//提取vue文件名字
    const newName = handleKey.split('-');  //将floor-coupon.vue 改成 ['floor','name','defal']
    let newString = '';
    newName.forEach((item, index) => {
        let newStr = '';
        if ( index !== 0) {
            newStr = item.substring(0, 1).first.toLocaleUpperCase() + item.substring(1);
        } else {
            newStr = item;
        }
        newString += newStr;
    })
    configRouters[newString] = files(key).default;//将files文件和对应的配置文件 configRouters 联系起来

})
export default configRouters; //导出配置文件
复制代码
  1. 在业务代码 index.vue 文件中,导入上面的入口文件:
import configRouters from '@/component/myIndex.js';
export default {
  components: configRouters, //把组件配置文件赋值给 components 便可。
}复制代码

这样经过上述方式,每次新增的组件,在文件夹中直接新建便可,实现了自动引入组件的功能。 经过以上优化的好处是:

  1. 减小业务代码中引入的组件数量;
  2. 优化添加组件的操做过程,只须要在相关文件夹内开发组件代码,便可自动引入该组件。

开启本地服务

工欲善其事,必先利其器。一款拥有着高效率的开发流程是每一位研发人所追求的。为了便捷快速的开发,通常在本地使用 webpack-dev-server 启动项目。然而一旦进入后续迭代,因为存在接口跨域和登录信息限制问题,没法在本地打开页面进行开发。以往大都使用 whistle 或者 fildder 采起配置线上代理文件看调试效果,以下所示进行配置:

plus.m.jd.com/index               https://localhost:8080/index.html
plus.m.jd.com/js/                 https://localhost:8080/js/
plus.m.jd.com/css/                https://localhost:8080/css/
plus.m.jd.com/img/                https://localhost:8080/img/
plus.m.jd.com/vendordev.dll.js    https://localhost:8080/vendordev.dll.js复制代码

由于每一个页面都对应一套配置代理文件,多个页面则有不一样的配置代理文件。切换配置代理和操做起来,配置过程甚是繁琐。 为了解决以上问题,如何在本地开启的环境上也能调用接口?就须要解决请求跨域登录信息两只拦路虎。

  1. 如何解决跨域问题?

目前解决跨域经常使用的方法是采用 webpack-dev-server 结合 proxy 接口代理或者使用 Nginx 都可配置跨域的代理。因为在本地配置好 webpack-dev-server 以后,全部项目成员均可以使用,一劳永逸。咱们采用了配置 webpack-dev-server 中 proxy 的接口代理,在 webpack.config.js 文件中对 webpack-dev-server 配置以下:

proxy: [
    {
        context: ['/user','/apis'], //使用context属性,能够把多个代理到同一个target下
        target: 'https://rsp.jd.com/', //把用 user 和 apis 开头的接口代理到 https://rsp.jd.com/域名下
        secure: false, //默认不支持运行在https上,且使用了无效证书的后端服务器,这里设置为true,能够支持
        changeOrigin: true,//若是接口跨域,须要进行这个参数配置
        pathRewrite: {'^/apis': ''},//因为apis开头的路径,是人为添加方便区分哪些接口要代理的,因此这里去掉apis
        headers: { //设置请求头
            origin: 'https://plus.m.jd.com', //请求接口限制来源,因此要改动请求源
            host: 'rsp.jd.com',//设置请求头的host
            referer: 'https://plus.m.jd.com/index'//设置请求头的referer,由于后端接口会有限制
        }
    }  
]复制代码

通过上述配置请求接口,而且设置项目中全部的接口前缀是 user 和 apis 开头,若是是本地开发才新增的前缀,就须要根据开发环境切换接口前缀,在上线环境上删除该前缀。

这样,就能够在本地开发时解决跨域问题,若是出现状态码错误,就要问一下后端是否是作了什么限制,通常修改请求头 headers 对应的信息便可。

  1. 如何解决接口传递登录信息?

解决了跨域问题,接口还能不能访问,由于请求接口还须要携带 cookie 信息来判断用户是否登录。咱们知道用户登录后,浏览器会把当前用户信息对应的 id 值保存到 cookie 中,以后每次请求都会携带该域名下的 cookie 来判断用户登录状态。而本地开发的项目,是没法携带 plus 域名下的 cookie,因此本地访问的接口都会返回用户未登录,从而没法获取相关数据。

浏览器说了:一切不给登录 cookie 就想要数据的行为都是耍流氓!好吧,这种行为能够理解,毕竟不能空手套白狼,咱们只好曲线救国。因为服务器为了防止 XSS 攻击,对 cookie 中用户信息的 id 值设置了 httpOnly 属性,避免使用 JS 获取。但这样致使咱们也没有办法使用 JS 获取用户信息的 cookie,因此咱们只好先在浏览器中访问线上的 plus 页面。而后在dev-tool面板上查找到用户信息的 id,而后把该值放在本地的 cookie 中。这样启动本地 webpack-dev-server 服务,在以后的请求中把该 cookie 携带过去:

icon

经过以上两个核心步骤,开发人员能够实如今本地一键命令便可启动项目,极大的给开发人员提供了便利。如需模拟不一样状态用户,只须要在js中替换对应的cookie变量便可。

icon

自动格式化代码

世界由于多样性而变得如此精彩,可是用在代码格式上,倒是一场灾难!在团队协做开发当中,相信小伙伴们大部分会碰见如下尴尬的场景:

  1. 不一样的代码风格在 lint 工具检查出问题以后只能人肉修改,而一般这样的错误都是分号、tab缩进、空格、引号之类的;
  2. 当你须要在其余同事的分支基础上进行二次开发的时候,发现他的代码风格和本身的不同,阅读和开发起来很是不习惯;

为了不这种状况,咱们使用了超高人气的自动格式化工具 Prettier,它能够自动的按配置好的规范帮你完成代码的格式化,这不只省时省力,还能提升你的代码阅读性,简直就是团队协做开发的福音!

Prettier 的优势

  • 它能够在编辑器中使用,也能够在命令行中使用,配置很是简单。
  • 它既能在保存代码的时候进行格式化当前文件,也能一键格式化全部的文件;
  • 它支持 HTML/JS/JSX/TS/JSON/CSS/SCSS/LESS/VUE 等主流文件格式;

安装使用 Prettier 的步骤以下所示:

icon

这样每次修改代码后,保存文件的时候就会自动格式化。若是想一键把项目中全部文件都进行格式化,还能够使用命令行的方式,这里推荐在 package.json 中配置scripts命令:

"prettier:check": "prettier -l src/**/*.{js,vue,scss}",
//"prettier:check" 是检查项目src目录下的任何js、vue、scss文件是否存在格式问题;
"prettier:fix": "prettier --write src/**/*.{js,vue,scss}"
//"prettier:fix" 是一键格式化项目中src目录下全部js、vue、scss文件;复制代码

这样咱们就能够经过执行命令方式,来检查代码格式和自动修复格式,减小团队不一样开发人员代码风格不同,以及单独修改代码格式的弊端,一劳永逸。

提升 Git 规范化

PLUS项目快速的迭代需求和多个需求并行开发的节奏,致使每次都要新拉分支进行开发,可是每每狂欢以后一地鸡毛,贪图一时的便利进行代码提交,每每是到最后也没法想起最初的修改目的。git版本管理系统为代码的有序和稳定作出了突出贡献,可是便利的同时也须要规范和优化,才能使得git展示出更多的价值。

规范 git 提交

全部的提交建议使用 标识:内容 的形式,说明这次提交的目的,使每次提交都有价值

标识 说明
feat 新功能(feature)
fix 修补bug
docs 文档(documentation)
style 格式(不影响代码运行的变更)
refactor 重构(即不是新增功能,也不是修改bug的代码变更)
test 增长测试
chore 构建过程或辅助工具的变更

提交信息优化

icon

上面是 PLUS 项目中的一段提交信息,一连几个相同的提交,像这样提交价值就会减弱。通常来讲,commit message 应该清晰明了,说明本次提交的目的。对于这种状况的发生,通常有如下几种场景:

  1. 当前分支开发到了一半须要紧急切换其余分支,若是不提交,可能不容许切换分支或者出现把当前的修改带到了新的分支的状况,全部先提交一个临时的,回头再次开发;
  2. 当前分支开功能开发完了,提交一版本,一会发现有问题,可能只是一行代码,在提交一次。来来回回提交了好几回代码;

针对这两种状况:

  1. 能够使用 git stash,去暂时保存,但不提交代码,等切换回分支的时候,再读取出来开发 git stash apply

  2. 针对第二种状况已经提交了,这时候能够用 git commit --amend,撤销上一次提交到暂存区,并从新提交内容;

注意:该方法必定要在未提交到远程的commit进行操做,千万不要提交到远程以后在执行此命令。

流程优化

回想起2019年,别有一番滋味在心头~~经历过通宵达旦的上线,眼神迷离的看见次日前来上班的同事。也遇到过周末早上忽然被电话惊醒,支持紧急需求的状况。有的时候刚刚下班,却被通知有需求要马上评审。也有过压力大、陷入迷茫困惑、情绪波动的时候。虽然说咱们无怨无悔,可是一个规范化的流程,本不该该让人如此精疲力尽。工做就是一个不断遇到问题而且解决问题过程,主动推动项目的规范化,提升人效,才是正面问题的态度。因此咱们主动在如下方向进行了推动:

icon

制定开发规范

团队的人员变更和快速迭代的需求,一度让新加入的成员对 PLUS 项目不知如何下手。同时,一个合格的项目也须要制定开发规范,既可让开发成员快速的了解进入项目,还能让老成员明确规范化开发流程。所以,咱们团队主要从如下几个方面进行汇总:

  1. 梳理各个分支对应状态分布
  2. 制定项目开发流程
  3. 汇总项目调试方法
  4. 规范上线流程
  5. 汇总plus会员相关资料连接

包括但并不限于以上方向,经过制定总结项目规范,汇总开发时遇到的问题,有力的推动团队开发人员对项目的熟悉,避免了新加入的成员经过本身摸着石头过河的现状,有利于快速的融入 PLUS 项目,顺利的承担起开发工做。

优化评审机制

PLUS业务发展快,频繁的需求评审,随时随地咚咚会议,困扰着整个 PLUS 项目团队成员。原计划安排的事情每每被迫中断,手头的工做不得不加班消化。久而久之,不但会让团队成员疲于应付,也打乱整个项目的排期进度,严重的引发蝴蝶效应,以后的需求有可能都会受到影响,所以解决这一问题迫在眉睫。

在项目复盘会议上,呼吁创建更加合理的评审机制,将需求评审会议规范化。好在各个团队的大力配合下,经过了每两周进行一次需求集体评审的制度。对于紧急需求,根据其紧急程度评估是否单独组织评审。

经过肯定需求评审机制以后,除非遇上6.18大促、双11活动等节日有着密集的评审,其余时间有了很大的改善,合理的评审机制,让你们能够更好的安排手头的工做,让人头疼的需求混乱状况有了较大的缓解。

推动新技术学习

学习如逆水行舟,不进则退。在前端技术日益更新的大环境下,若是只考虑实现业务需求,团队成员不但会慢慢的进入温馨区,也会逐渐会在技术上逐渐掉队。可是每周都要支持多个并行需求,加上前端主动发起的项目优化工做,再让团队成员去学习一些新的知识,时间上每每会显得捉襟见肘。因此咱们不能漫无目的的去学习新知识。必须结合 PLUS 会员项目有的放矢。学习到的新技术能够在 PLUS 项目中获得实践,不管是推动代码更加完善仍是优化用户体验,都可以学以至用才行。

因而基于以上目标,咱们开始有意识的去探索。因为项目使用 Vue 开发,一直在关注 Vue 3 的源码公布,其中使用的 TypeScript 技术映入咱们眼帘。发现 TypeScript 有不少优势,尤为是对多人合做开发的项目,增长了代码的可读性和可维护性。无规矩不成方圆,因而咱们团队制定好学习计划,每一个人利用空余时间进行学习,在项目需求不紧张的时候,组织部分红员对该项技术作了技术分享和知识讨论。此外,在新页面中进行了引入 TypeScript 的尝试工做,因而才了上面对 TypeScript 的实践部分。

与视觉侧的协做

因为 PLUS 会员迭代需求不少,大部分需求都涉及到视觉稿的更新,和设计师的密切沟通是常态。在这个过程当中咱们发现两个比较突出的问题。

  1. 设计师使用公司的产品协做工具 RELAY (类蓝湖),可是有些图片没有给到切图,或者到咱们开发的时候发现有些切图没有符合研发预期,因而联系视觉设计师去要对应的切图的时候,因为你们都很忙,可能不会及时回复。问题就会阻塞,遇到严重的状况,好比重构页面时缺失不少切图,或者相关字号、颜色等信息没法获取时,就会致使项目进度卡住。无形中增长了沟通成本。

  2. 需求上线须要通过视觉设计师的样式走查,走查问题须要再修改。然而种种缘由,不少次都是项目晚上上线,下午才视觉走查。致使研发没有时间改动,何况一旦改动也容易致使其余的样式问题。

经历过几回以后,咱们和视觉设计师们坐下来一块儿探讨了如何解决这些问题。

  • 针对问题1: 增长研发确认视觉稿的环节,视觉设计师定稿后,或者研发准备开发前,把本次须要的视觉稿先走查一遍,将过程当中须要用到的切图反馈给视觉设计师,统一提供切图,此外,时尚的设计师们还贴心的补充了PS稿件,方便咱们在联系不到人时,进行查漏补缺。
  • 针对问题2: 研发侧每次发提测邮件的时候,都要抄送相关@视觉设计师,推动在提测阶段先视觉走查。此外测试同窗提早给产品告知大概何时须要视觉走查,产品侧提早申请视觉资源,申请走查和走查结果一概走邮件,多重提醒方案,将样式走查流程提早。

经过以上方法,最大程度的保证了研发在开发项目时拿到本身但愿的视觉信息,避免了临时抱佛脚,到开发时才去沟通的问题,减小了沟通成本。此外提早通知视觉设计师进行样式走查,避免了上线前的改动,容易带来的隐患问题。

推动旧版本迁移

因为历史缘由,PLUS 会员项目有三个分支,分布着不一样状态的 9 个页面。第一批开发的页面时间在两年前,当时采用的一些技术和开发代码不是很规范,里面混杂着 jdf + zepto 技术栈的一些代码。 在后续的开发过程当中逐渐延伸出共三个开发主分支:V二、V三、V4。

  • V2/V3,17年上半年开发,不是基于 Webpack 构建,受 jdf 影响大,不够合理。首页计算器和个人PLUS计算器未复用,使用老一版接口
  • V4,19年上半年新版本,基于 Gaea4 修改而来的一个Vue多页面页面应用,包含诸多新特性,性能有大幅度提高

V4 新特性包括

  • Webpack 升级到 4.0,Vue-loader 升级到15
  • 集成 carefree NutUI2.0 按需加载插件
  • Babel 升级到 7.0,实现 polyfill 的按需加载
  • 结合代码及楼层数据懒加载、Tree-shaking、Scope Hoisting、DllPlugin剔除未用到的 css 等技术手段削减代码体积
  • 京东APP内部分 H5 连接经过 openapp 协议拉起新的 webview 打开,提高用户体验和埋点上报成功率
  • 综合应用 webp 和 dpg 图片格式,削减图片体积,提高图片渲染速度
  • 引入Vuex进行数据集中管理、沉浸式、骨架屏

project.png

  • 老版本的加载速度瓶颈是串行请求:页面脚本→用户信息接口→楼层信息接口→各楼层数据→渲染。
  • V4版本经过后端把用户信息接口和楼层信息接口数据直接打在页面上,避免了接口的串行等待,提高了首页渲染速度:页面脚本→各楼层数据→渲染

下图 Chrome 内置的自动化测试评估工具 Audits 对 V4 新页面性能评分较高

图片2.png

骨架屏的引入缩短白屏等待时间,如图测试在200ms-600ms白屏期间展现骨架屏,白屏时间缩短2/3,有助于提高页面的主观加载速度,进而提高用户体验。

图片3.png

所以迁移到V4从性能和开发效率方面考虑显得尤其重要。尤为当多个并行需求过来,涉及到基于从三个主分支新拉出一系列子分支进行开发,维护多个版本的弊端一览无遗。为此咱们主动发声,以提效为出发点讲道理摆事实。首先争取到产品侧的理解和支持(特别感谢)得以立项提需,后续获得项目经理及测试的资源支持。推进产品侧关注代码版本迁移的状况,在以后的需求迭代中,逐渐将老版本的代码迁移到最新的 V4 分支上。

目前为止,V2 分支上的季卡过时首页、年卡过时首页、尊享商品页面[后改成省钱攻略页]、个人PLUS页面(正式过时、试用中状态)、试用过时30天内均迁移到 V4 上。还剩下 V2 改动不多的异常首页及 V3 中的试用中及试用过时30天内代码。这一进程还没有结束,最终的目标是把全部用户状态和页面代码所有迁移到最新技术栈架构分支上。

icon

排期规范化

随着对 PLUS 项目的重视,咱们安排了单独研发来跟进全部的需求和排期,避免了你们要一边支持开发,一边时间被打断参加各类会议。全部的需求通过评审、梳理以后,肯定哪些能够并行开发,哪些须要串行开发。再和项目经理配合,制定出前端团队的工做排期。这样每一个人下周的工做都是肯定了的,分工明确,不会被临时需求和紧急需求打乱。

此外,全部的紧急需求、新插入的需求、线上出现的问题都要再通过对接人的梳理、排查以后视需求状况而定:

  1. 紧急需求安排较为空闲研发支持;
  2. 不是特别紧急的需求安排下次排期;
  3. 对于线上问题,若是可以很快修复,通常跟随下一个上线需求一块儿上线;若是不能快速修复的,从新走排期。

这样,整个团队的时间有了更好的安排,不会被项目需求牵着鼻子走,每周的工做都是安排好的,每一个人的工做都是明确的,能够适当的调整本身工做的节奏,避免了各类紧急需求的插入。

通过以上措施,团队成员可以快速的进入项目开发,分工明确、每一个节点作什么事情也变的清晰。同时也提升了和其余团队的沟通协做,减小推诿。孟子曾曰:不以规矩,不能成方圆;不以六律,不能正五音。深觉得然!

结语

2019 年双十一,PLUS 正式会员数突破 1500w,迎来新的历史时刻。一位 PLUS 会员单笔消费 46.7 万元,成为京东单笔消费最高的一单。咱们在感觉着团队成绩的同时,也倍感责任重大。回首 2019 年,对于 PLUS 前端团队成员来讲,是不平凡的一年。在保证完成纷至沓来的迭代需求前提下,从优化用户体验、提升开发效率、推动完善制度流程等方面入手,使得 PLUS 会员项目日趋完善。

此刻,窗外寒冬中呼啸着凛冽的寒风,吹散了往日的雾霾,吹来了艳阳高照、晴空万里。悄悄的抓住 2019 的尾巴,对即将到来的 2020 年充满希翼。正如京东 PLUS 会员项目遇到的挑战同样,终究会跨过去迎来新的征程,2019再见,2020 加油!

相关文章
相关标签/搜索