关于前端:NutUI-实战持续升级企业业务之福礼

53次阅读

共计 9834 个字符,预计需要花费 25 分钟才能阅读完成。

前言

自 2018 年 4 月福礼上线以来,通过疾速迭代和扩大功能模块,整个我的项目疾速倒退。2019 年,京东福礼的月均沉闷用户同比增长达到 265%,累计为超过 3500 家企业提供了数字化福利治理服务,有近 600 万人次通过京东福礼实现了个性化福利发放。回首过往,从只反对一种流动模式到现今反对六种流动模式,从只反对积分领取到反对混合领取、金额领取、单次领取,从只反对扫码展现 H5 到反对微信小程序内嵌、原生 App 内嵌 … 福礼正在以飞快的速度欠缺本人的性能体系,以期给客户更好的服务。随同着我的项目性能的疾速迭代和欠缺,是研发同学们在技术上的攻坚克难和一直冲破的过程,也是整个团队(产品,后盾,测试)一起成长,共同进步的过程。为此特整顿此文,记录过往的教训和思考,也冀望能给同路人带来启发和帮忙。

自 2019 年至今的次要性能迭代停顿图

什么是福礼

福礼是京东为优质企业客户打造的以年节福礼 / 季度劳保兑换为主的员工福利商城。该产品致力于晋升员工福利感知度、升高福利发放和支付老本,为企业客户提供京东品牌保障的海量副品、搭配企业专属优惠价格和极速物流配送服务,进而帮忙企业正当布局年度福利计划,实现一站式企业员工福利的治理及洽购。

接下来就带大家看一下福礼的庐山真面目吧:

好了,广告宣传局部到此结束,接下来咱们将从更好的福礼、开发效率优化、流程优化三个方面来聊聊咱们在福礼我的项目的继续降级中的播种和思考!

前端架构

福礼我的项目自 18 年立项以来始终应用 Vue 技术栈,应用了团队自行开发的 Gaea 构建工具和 NutUI 组件库,此外引入了 Carefree,SMock,Vuex 等。

Gaea 构建工具,是咱们团队自主开发的一套 Vue 技术栈构建工具,基于 Node.js、Webpack 模版工程等的 Vue 技术栈的整套解决方案,蕴含了开发、调试、打包上线残缺的工作流程。极大的进步了工作效率,目前团队所有 Vue 业务都应用该脚手架。

NutUI 组件库,是一套京东格调的轻量级挪动端 Vue 组件库,由咱们团队历时数年打磨,目前有 50+ 京东挪动端我的项目应用,github 上失去 1.9k+ 的 star,176 Used By(不蕴含公有仓库)、236 fork,NPM 下载量超过 14.5 K。该 Vue 组件库提供大量的可复用的 Vue 根底组件,极大的便当了福礼我的项目的开发。

Carefree,一套不依赖 wifi 热点的挪动 web 真机测试一站式解决方案,是咱们团队在日常开发中发现真机很依赖电脑收回热点能力进行调试的痛点,针对这一问题,旨在解脱 wifi 热点解放,让挪动 web 真机测试无拘无束而自主研发的一套解决方案。

SMock,由团队自主研发,针对项目前期尚无数据的问题,剖析须要 mock 的文档,输入相应的 mock 数据,并启动 node 服务,供前端开发时调试应用,进步前端开发效率,反对跨域拜访。

更好的福礼

站在伟人的肩膀 – 组件库助力开发

古语道:“小人性非异也,善假于物也“一套值得信赖的组件库,会使开发事倍功半。

福礼我的项目自立项开发以来,始终应用 NutUI 组件库。从 1.x 版本中局部的援用组件到 2.x 版本大范畴应用,NutUI 沉闷的社区互动和及时的响应速度,以及具备连续性的降级,逐步取得了咱们的信赖。

NutUI 组件库是一套京东格调的轻量级挪动端 Vue 组件库。通过 JDRD 前端团队 2 年多的迭代降级,目前有 50+ 京东挪动端我的项目应用,内部应用我的项目达 40+ 我的项目。GitHub 2k star、194 Used By(不蕴含公有仓库)、254 fork,NPM 下载量超过 16.8 K。

除了 NutUI 自身的我的项目影响力强劲外,最吸引我应用的还有以下几点:

业务组件

除了一般的罕用组件,NutUI 基于自身的根底组件,通过剖析我的项目中具备很多共性的业务逻辑,形象后再次封装,从而开发出了很多业务组件。它们能够省去使用者由根底组件组装并写反复业务代码的过程,真正的解决业务开发中工夫紧,工作重的痛点。

就比方常常在商城中应用到的地址组件,一个组件蕴含了抉择自定义地址,抉择已有地址,自定义图标,自定义地址与已有地址切换等多种业务的须要。只有产品须要对应的性能,咱们就能够疾速引入组件并看到对应的成果,基于大量用户应用的业务组件,在性能上更加欠缺,避免了因业务逻辑没有思考全而带来返工的问题,也缩小了开发者从根底组件封装的工夫耗费。

地址组件在福礼中的应用

电商类组件覆盖率高

NutUI 是一套京东格调的轻量级挪动端 Vue 组件库,所以其中收录的组件能更好的笼罩电商类挪动利用的开发。
相较于其余经典开源组件库,福礼应用组件覆盖率比照如下:

性能 MintUI VantUI NutUI
上拉加载、下拉刷新 o × o
Dialog 对话框 o o o
Swiper 轮播图 o o o
Tab 选项卡 o o o
Toast 吐司 o o o
回到顶部 × × o
左滑删除 × o o
上传 × o o
Popup 弹出层 o o o
Stepper 步进器 × o o
图片懒加载 o × o
时间轴 × o o
搜寻栏 o o o
商品价格 × × o
徽标 o × o

对立的京东设计格调,使整个组件库的组件款式对立,交互合乎逻辑。开发者也能够通过主题定制的形式来满足业务多样化的视觉需要。想理解更多主题定制戳这里 主题定制。

理解你的用户 – 数据采集

如何使本人的我的项目更贴近用户,首先就须要理解用户。作为与用户交互的最火线,为了助力客户作出更优的洽购决策,推送给顾客更满意的商品,咱们只能迎接挑战,降级本人的数据采集能力。为此,咱们应用京东自主研发的数据采集服务 –“子午线”来实现交互埋点等用户操作信息的收集性能。一番批改下来,颇有播种,分享于此。

1. 动静引入 PV 埋点代码

本来的 PV 埋点代码是默认写死在 html 中的,但为了要在登陆之后,从接口中传入流动信息和员工信息,所以必须革新为登陆之后动静引入 PV 埋点代码。而又因为我的项目中有外接模块,从外接模块返回到利用的时候兴许流动数据有变动,所以还须要革除之前的埋点代码,再动静引入新的埋点代码。
基于此咱们的解决思路能够分以下两步:

1-1 调用

别离在根组件和首页中调用。
根组件调用:外接模块返回到本利用的时候,都会触发根组件的生命周期,这时能够从新获取流动自身的信息。
首页调用:在登陆前,后盾不会返回对应的流动和用户数据,所以须要在首次登陆的之后进入首页再调用 PV 埋点函数。

this.$JDUnify.JDPV(data.data);

1-2 在动静插入的时候先动静删除之前的插入的内容

 <!-- 动静插入外围办法 -->
 content(html) {let cont = document.getElementById('cont');
     cont.innerHTML = html;
     let oldScript = cont.getElementsByTagName('script')[0];
     cont.removeChild(oldScript);
     let newScript = document.createElement('script');
     newScript.type = 'text/javascript';
     newScript.className = "xxx";
     newScript.innerHTML = oldScript.innerHTML;
     document.body.appendChild(newScript);
 };
 JDPV(obj) {
     ...
     let dongtaiTag = document.body.querySelectorAll('.xxx');
     <!-- 在创立的时候先删除这个节点 -->
     if (dongtaiTag.length > 0) {document.body.removeChild(document.body.querySelectorAll('.xxx')[0]);
     }
     let japHtml = `<script type="text/javascript">
         var jap = {
           siteId: "xxx",
           autoLogPv: true,
           anchorpvflag: true,
           extParams:{xxx:'${obj.xxx}'
           }
         };
         <\/script>`;
     this.content(japHtml);
 };

2. 对立埋点办法

因为在点击触发埋点的时候有公共值和不同埋点要求的数据值的差异,并且公共值的内容与 PV 埋点值雷同,所以咱们构建了一个类来治理两个办法, 并将公共数据放在 this.obj 上共享。

point(eventId, enventInfo) {
    <!-- 传入参数校验 -->
    if (JSON.stringify(this.obj) == "{}") {return}
    <!-- 非凡字段加密 -->
    const xxx = MD5(xxx + '');
    try {let click = new MPing.inputs.Click(eventId);
        <!-- 对立传入的值 -->
        click.xxx = this.obj.xxx;
        <!-- enventInfo 不同埋点要求的数据值 -->
        if (enventInfo) {
            <!-- 点击埋点办法传入的值 -->
            click = Object.assign(click, enventInfo); // 上报扩大字段,字段名称和内容均可本人设置
        }
        click.updateEventSeries();
        new MPing().send(click);
    } catch (e) {}};

最终整体的数据采集代码构造如下:

class JDUnify {constructor() {
    // 用于 PV 和点击埋点传递数据
        this.obj = {}}
    // 埋点办法
    point(eventId, enventInfo) { };
    // 文档动静插入方法
    content(html) { };
    // pv 办法
    JDPV(obj) {};}
export default {// 挂载到 vue 原型链上,将来能够通过 this.$JDUnify.point("xxx"); 应用
    install: function (vm) {vm.prototype.$JDUnify = new JDUnify()
    },
    // 我的项目中应用到的专用办法
    JDUnify: new JDUnify()}

多端接入

作为一个 H5 我的项目,福礼自身具备很强的跨多端能力。但所谓没有限度的自在就不是真正的自在,没有内接标准的解放,就很难有好的用户体验,甚至还会因为外接容器的不同而产生兼容性问题。基于上一个大节采集的用户数据,咱们发现,以后的内嵌诉求次要集中在微信小程序内嵌和原生 App 内嵌两个方面。

对于微信小程序,咱们应用了微信小程序原生的 webview 组件。其中从小程序关上内嵌我的项目页面应用的是 src 的形式:

从微信小程序到 H5

<web-view src="{{url}}" ></web-view>

所有须要传递给 H5 的参数都能够写在 url 的前面拼接过去,而后在 H5 中通过 url 参数获取的形式取得。

如果想从 H5 到小程序通信,咱们采纳的是 wx.miniProgram.xxx API,如示例中的 reLaunch API。

从 H5 到微信小程序

wx.miniProgram.reLaunch({url: `/pages/xxx?url=` + encodeURIComponent(url)
})

这里要留神跳转函数间 API 所代表的不同含意,同时须要留神微信小程序中路由层级的限度问题。详情请参考官网文档 web-view,本文就不再赘述。

对于原生 App 内嵌 H5,因为不像微信小程序有对立的标准,所以咱们制订了一套 JS API 规定,通过 postMessage 办法,实现 H5 与原生 App 的通信。整体思路如下:

  1. 原生 App 确认应用 JS API 标准,通过原生 App 设置 navigator.userAgent 为 fuli/andriod 或者 fuli/ios 来告诉 H5 应用标准。
  2. 前端调用不同的原生 API 与 原生 App 通信,其中 andriod 应用 window.callApp.postMessage(jsonstr);ios 应用 window.webkit.messageHandlers.callApp.postMessage(jsonstr);

两方约定好的规定应用 jsonstr 形容。

  1. 原生 App 承受 json 匹配做对应逻辑。

H5 同原生 App 通信

class NativeApp extends App {executed(name, data) {let params = { name, data};
        let str = JSON.stringify(params); // 调用 app 参数输入
        const _window= window;
        const _userAgent = navigator.userAgent; //app userAgent 输入
        if (_userAgent.indexOf('fuli/android') !== -1) { // 调用 android
            try {_window.callApp.postMessage(str) 
            } 
            catch (error) {alert('android error :' + JSON.stringify(error) + 'post android str:' + str) }
        } else if (_userAgent.indexOf('fuli/ios') !== -1) { // 调用 ios
            try {_window.webkit.messageHandlers.callApp.postMessage(str);
            } catch (error) {alert('ios error :' + JSON.stringify(error) + 'post ios str :' + str);
            }
        }
    }
    // 设置题目
    setTitle(title) {this.executed(setTitle, { title})
    }
    // 关上新窗口
    newWebView(data) {this.executed(newWebView, data)
    }
    // 敞开新窗口
    closeWebView(){this.executed(closeWebView)
    }
    // 关上登陆页
    openLogin(){this.executed(openLogin)
    }
    ...
}

开发效率

作为一个由多人开发以及波及到多端测试的我的项目,如何进步开发效率和合作效率,始终是咱们苦苦考虑的方向。

绕不过的坎 – 真机测试

像许多 H5 我的项目一样,真机调试是所有开发和测试都绕不过的流程。在开发中咱们的形式同大多数开发者一样应用的是手机连贯电脑热点的形式。

个别在本地应用 webpack-dev-server 启动我的项目,再在代理工具中配置上映射关系,测试手机连贯电脑收回的热点,就能够在手机上轻松的跑起来本地的测试代码了。但这样的配置给测试也带来了两个问题:

  1. 电脑收回的热点有时候会不稳固,有时候还会跑掉。
  2. 连贯不同的热点要配置不同电脑的 https 证书。

这两个问题,对于前端开发还好,然而对于要大量测试各个版本手机的测试们来说可就压力山大了。

从新梳理这两个问题,咱们发现问题的外围就在于不同电脑的多个热点上。于是转化思路咱们尝试在服务器上安排代理软件,让服务器代替发送热点的电脑。这样一来只须要手机配置拜访指定的端口并装置这台服务器上的 https 认证证书,就能够在手机上轻松的拜访前端公布在测试服务器上的代码了。

但好景不长,随着我的项目外接的 app 增多,以及微信小程序的外接,导致在自身手机代理配置没有问题的状况下,呈现了手机就是代理不上的状况。通过浏览代理工具的文档发现了这段话 在 android 6.0 之后的一些 app 在胜利装置证书后依然无奈对 https 连贯进行手抓包,有可能是该 app 没有增加信赖用户自定义证书的权限。改原生 app 的配置,臣妾做不到呀。

重整思路,再次登程,咱们发现测试的指标不是为了抓包,而在非凡手机兼容性的测试上。换句话说,咱们能不能不配置代理,来测试前端的代码。通过剖析,咱们发现之前配置代理是因为为了不便打包上线,即上线的动态资源拜访门路不变,通过代理工具,将动态资源映射到指定 IP 服务器的模式,如下所示:

192.168.XXX.XX(测试服务器端口) static.360buyimg.com(线上机群域名)

所以只有将动态页面上的动态资源链接以及打包的链接改为对应的 IP 的域名,就能够不必配置代理了。

为了更好的治理和辨别,最终咱们抉择基于京东商城对象存储来实现非凡手机兼容性测试的工作。想要理解更多内容,请参考 京东商城对象存储文档。

这样,对于非凡的手机版本以及内嵌的非凡 App,咱们都能够通过公布到京东商城对象存储平台上来实现兼容性的测试。

薛定谔的猫 – 宜人的缓存

在开发中常常会遇到这样的状况,本人批改完测试提出的问题,满足的公布到测试服务器,后果测试反馈没有看到成果,并向你甩出了截屏。这样的抵触场景,大多数是因为测试没有拜访到新公布的动态资源导致的。那怎么会没有拜访到新公布的动态资源呢?这就要咱们先看看浏览器获取资源数据的程序:

  1. 先在内存(from memory cache)中查找,如果有,间接加载;
  2. 如果内存中不存在,则在硬盘(from disk cache)中查找,如果有间接加载;
  3. 如果硬盘中也没有,那么就进行网络申请;
  4. 进行网络申请时,强缓存是优先于协商缓存的,是先进行强缓存(expires 和 cache-control),如果失效则间接应用缓存数据,否则进行协商缓存(Etag/if-none-match),由服务器来决定是否应用缓存数据;
  5. 申请获取的资源缓存到硬盘和内存。

所以,尽管提交了改过后的代码到测试服务器,但因为测试拜访频繁,测试看到的是浏览器缓存下来的动态资源。对于实在版本上线,简略的说,前端会公布不同的版本号,同时后端也会公布与之对应的同样版本号的页面。从而在用户拜访资源的时候会间接拜访到新版本号的资源,不会呈现缓存旧版本资源的状况。但因为在测试阶段,公布到测试环境的频次比拟高,如果每次公布都改版本号的话须要节约大量打包的工夫,所以最好的办法是测试在发现有缓存时自行革除下终端的缓存。

为了不让测试在一波回测操作之后才发现没有拜访到最新代码,咱们会在测试环境下打包出工夫戳,并在第一次拜访我的项目的时候提醒进去测试的应用版本,测试能够根据提醒内容来判断是不是拜访到了提测的版本。

提醒测试版本

要实现这个提醒,次要分以下几步:

1. 在打包的时候,配置工夫变量,并命名为 buildTime

new webpack.DefinePlugin({
 'process.env': {buildTime: JSON.stringify(new Date().toString())
 }
}),

2. 独自抽离环境文件 env.js,用于在不同 webpack 环境下,调用不同的展现

let config = {
buildTime: process.env.buildTime,
isPrd: true // 是否为线上
}
switch (process.env.NODE_ENV) {
case 'development':
    config.isPrd = true;
    break;
case 'upload':
    config.isPrd = false;
    break;
case 'production':
    config.isPrd = true;
    break;
}
export default config;

3. 在 App.vue 中弹出提醒关上的前端版本

import config from "./config/env";
if (!config.isPrd) {
  this.$toast.text(
      "以后版本:预发 > 版本公布工夫" + config.buildTime,
      {duration: 3000}
  );
}

高效后悔药 –Git 规范化

作为多人疾速开发的我的项目,进行代码提交审核和治理是十分重要的。为了更无效的回溯代码,不便查找定位,以及沟通,咱们独特制订了如下的提交标准。

所有的提交倡议应用 标识:内容 的模式,让每次提交都有价值

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

在践行规范化的过程中,咱们发现了一个对于提交 commit 的小窍门。

你可能遇到这样的纠结场景:当在一个分支上正在工作,忽然被打断须要紧急修复一个线上的问题,bug 可能在以后分支,也或者在另一个分支,然而手上的代码还没有到可能提交的境地,但又不想将曾经写的内容作废,这时你能够应用 git stash xxx 命令。它示意将以后分支上的未提交的代码保存起来作为一个暂存区并命名为 xxx,这个暂存区是能够作用于所有分支,是一块独自的区域,后续能够通过 git apply stash xxx 将暂存的内容复原到任意分支。

整齐划一 – 主动格式化代码

作为团队合作开发的我的项目,通常会呈现以下的难堪的场景:

  1. 在批改了局部代码,并保留后,因为格式化工具的不同,导致整个代码全副格局被批改,很难突出的看到本次提交的内容。
  2. 因为格局不对立导致 git 抵触问题。
  3. 因为代码不标准导致可能的兼容性问题。

为了防止以上状况,咱们应用了适宜 vue 我的项目的整套代码标准工具链:vscode+vetur+prettier+eslint

整体的装置流程和配置流程如上图所示,其中在配置 vuter 的时候要特地留神,为了让 vuter 在格式化的时候,参考 eslint 的规定,而不是 prettier 的规定。你须要如下配置:

1. 在 settings.json 中将 vetur 中 js 的 formatter 设置为 prettier-eslint

{
   ...
   "vetur.format.defaultFormatter.js": "prettier-eslint"
   ...
 }

2. 手动装置 prettier-eslint 包

npm i -D prettier-eslint

对于一开始没有应用代码格式化开发的我的项目,举荐能够在我的项目的 package.json 中配置全局的 scripts 命令将整个我的项目的代码依照标准整体格式化。

//"prettier:fix" 是一键格式化我的项目中 src 目录下所有 js、vue、scss 文件;"prettier:fix": "prettier --write src/**/*.{js,vue,scss}"

更多的命令请参考 prettier-eslint-cli。

合作优化

作为一个残缺的商城类我的项目,波及到方方面面共事的通力合作。如何实现合作效率最大化的同时还能站在全局的角度思考问题?咱们次要从这两步进行摸索。

流程化 – 开发流程

兴许你也遇到过通宵达旦的上线,眼神迷离的看见第二天前来下班的共事,周末早上忽然被电话惊醒,反对紧急需要的状况,刚刚上班,却被告诉有需要要立即评审……

作为公共资源部门,大部分工夫都会有两个以上的需要,甚至还可能是不同技术栈的不同业务线的需要。所以定义一个以工夫线为轴的开发流程,是咱们和其余部门负责单干的根底,也是订立合作的根底。

体系化的看问题 – 复盘会

每当一个大的需要上线,项目经理都会组织复盘会,依照我的项目开发的整个流程,从各个合作方的角度复盘这次需要实现的播种和有余,并造成了前端与产研合作机制标准的文档。

就以排期的规范化为例,所有的紧急需要、新插入的需要、线上呈现的问题都要再通过产品的梳理、测试排查之后视需要状况而定分为以下状况:

  1. 紧急需要安顿较为闲暇研发反对;
  2. 不是特地紧急的需要,优化安顿下次排期;
  3. 对于线上问题,如果可能很快修复,个别追随下一个上线需要一起上线;如果不能疾速修复的,从新走排期。

同时,合作多方一起解决我的项目开发中波及多方的痛点问题:

  1. 真机测试艰难;
  2. 内嵌原生 App 的标准;
  3. 测试环境的跨域问题。

通过屡次的我的项目复盘,整体合作的效率以及彼此的互信都有了极大的晋升。

结语

回首过往,福礼在两年多的工夫里披襟斩棘,疾速倒退,实现了 30 + 以上的功能模块增加,并实现了月均沉闷用户同比增长达到 265 % 的骄人增长。但正如题目所言,继续降级是永无止境的,将来福礼的前端团队成员仍然会在保障实现络绎不绝的迭代需要前提下,从优化用户体验、晋升开发效率、推动流程优化等方面动手,继续降级福礼我的项目。
“ 长风破浪会有时,直挂云帆济桑田 ”。让咱们与福礼一起成长,期待一个更好的福礼呈现在将来,也祝福大客户业务组的业绩方兴未艾,加油!!

PS:如何大家对文章中的某个点感兴趣,欢送咚咚打搅 ~~

正文完
 0