与JDReact的第一次亲密接触 ——加油卡项目总结
Posted 手机京东技术团队
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了与JDReact的第一次亲密接触 ——加油卡项目总结相关的知识,希望对你有一定的参考价值。
甄玉磊
京东UED前端攻城狮,热爱尝试各种新颖技术
“起初我只是想做一个网页,却一不小心掉入代码的海洋里!”
JDReact平台是在Facebook ReactNative开源框架基础上,进行了深度二次开发和功能扩展。不仅打通了android/ios/Web三端平台,而且对京东移动端基础业务能力进行了SDK级别的封装,提供了统一、易于开发的API。基于以上种种优势,加油卡壳牌业务决定使用JDReact技术进行开发。
1.1 项目背景
加油卡壳牌业务是京东APP中的一个项目,可以从首页的「充值缴费」入口进去,如下视频演示:
1.2 项目环境配置
1.2.1 开发分支以及文件结构
初始项目中只包含一个dev分支,最后打包APP时会从该分支中抽取代码,所以我们新建了一个trunk分支,平时在该分支上进行开发,最后打包APP的时候再切换到dev分支,合并代码。
对于文件结构,如上图所示,其中Navigation.js设置页面路由信息,以及打开项目时进入的默认页面;pages文件夹下存放项目的每个页面代码;
images存放项目开发中使用到的图片;components文件夹下存放页面中用到的各个组件。
1.2.2 安装依赖环境
按照JDReact团队给出的安装依赖环境的步骤,当执行npm start命令之后,理想状态下会出现下面的提示:
很不幸的是,有时候会出现如下警告:
根据提示,可以看到打开.babel.json文件的时候出错,其实如果忽略这些警告,也可以打开京东APP进行调试的,但是如果想解决这个问题可以参考下面的步骤:
找到C:Users用户名.babel.json文件右击——属性——安全——SYSTEM的权限全部改为允许(如果已经改过权限,可以来回切换一下),重新执行npm start就可以了。
1.2.3 安装React语法高亮插件
项目是在sublime中开发的,但是在编写JDReact代码时,整个代码的高亮是混乱的,如下图所示:
因此,需要安装Babel Snippets插件来解决高亮问题,该插件可以使React.js、JSX语法代码高亮。安装方式:在sublime中,ctrl+shift+p打开面板选择Install Package,输入babel Snippets点击安装,安装完毕后再次ctrl+shift+p,选择set syntax javascript(babel),则React语法代码可以高亮,方便我们接下来的开发。执行完毕后效果如下图所示:
1.3 在无线持续构建平台打包项目
手机安装debug版本的京东APP之后,可以在手机端实时看到开发的代码效果,那么如何生成APP呢?因为构建平台会抽取项目中dev分支的代码,所以如果代码有所改动,先把trunk分支代码合并到dev分支。整体思路是把业务代码以插件形式打包进JDReact,然后再打包包含该插件的APP。打包流程如下:
对于Android版本,首先打开无线构建平台网址,选择一键打包——JDReact,依次选择插件名称——平台选择(android)——dev 分支——点击开始构建。
打包好JDReact后,再打包京东APP:
选择京东商城——android——其中debug版本可以晃动手机进行调试,release版本不可以调试,近似于真实环境,所以如果平时开发调试的话选择debug版本——选择dev分支——开始构建。
一般来说Android版本的打包过程还算顺利,但是IOS版本的打包过程就让人心碎了,下面我们来聊聊IOS版本:
首先仍旧是插件的打包,类似Android版本的打包过程,对于IOS不同的是版本选择IOS,分支选择dev点击开始构建。
IOS的插件构建完成之后,类似的在京东商城选择IOS——这里的IOS JRdebug版本是用来调试的版本,遗憾的是我们多次下载JRdebug版本在手机端总是打开之后闪退,还好releaseP版本也可以进行调试,虽然不是很方便,但也可以解燃眉之急:
1.4 手机安装APP
按照上述过程生成APP,Android版本的APP和IOS版本的APP接下来的步骤有所不同,对于Android版本,直接扫描构建平台——历史版本生成的二维码,安装京东APP,注意下面的步骤:
打开JDReact调试模式;
如果使用的是真实环境,还需要在服务器设置中取消选择“前两项”,测试环境需勾选“前两项”;
每次安装完新的APP之后,一定要记得晃动手机,dev Seetings——设置 Debug server host & port for device这个选项端口8081,如果报错could not connect to developmentserver,需要检查一下防火墙是否打开了8081这个端口:控制面板——windows防火墙——高级设置——入站规则——新建规则——端口——特定本地端口:8081——选择允许联机连接——一直点击下一步。
对于IOS的releaseP版本:需要在构建平台——历史版本中找到打包好的ipa 后缀文件,使用itoos4软件安装到iphone手机,该过程需要注意的是:
电脑端开启npm start服务(同样保证防火墙对端口8081的不限制);
手机连接电脑的wifi,手机浏览器登录跳转协议页面
如果是真实环境,打开手机APP时,选择api.jd.com的环境,否则选择测试环境;
每次改动代码之后,需要退出后台的京东APP,然后先打开APP,选择host 环境,最后在跳转协议列表页面重新用“React打开”按钮打开项目。
在项目开发过程中遇到了很多问题,这里总结了几个具有代表性的问题:
2.1 JDImage加载图片问题
使用JDImage组件加载本地图片和线上图片的方式是不一样的,如果不加注意很容易把两者混淆:
线上的图片直接使用uri:
<JDImage source={{uri: 'http://xxx.jpg'}} style={styles.img0} />
加载本地的图片使用的是require:
<JDImage source={require('./images/xx1.jpg')} style={styles.img1} />
并且每次新增本地图片,控制台都会报错:...Unexpected character '◆'(1:0) at ...
这是因为新增加的图片必须重新npm start。
2.2 通过路由完成页面之间的跳转
由于文档不全,路由之间如何传递参数,如何获取参数没有说明,导致在这里也花费了不少力气。对于页面之间的跳转,我们刚开始使用 JDRouter 中的 resetTo 方式进入某个路由:
1 2 3 4 |
this.context.router.reset([ { routeName: 'index', params: { title: 'apis' } }, { routeName: 'disCountPage', params: { datas: this.props.discountData } } ], 1); |
则下一个页面获取路由参数的方式是:this.context.router.getCurrentRoutes()[1].params.title;
随着项目开发,发现这样有个问题,例如从A页面跳转到B页面,如果从B页面回退到A页面,A页面之前的操作状态会全部清空,因此,为了保留A页面的状态,改为使用push方式进入某个路由,用popToWithProps返回上一个路由,这样的方法可以保留原页面的状态,但是如何获取路由携带的参数,文档中则没有介绍,经过多次尝试,我们发现需要这样设置:
A页面通过push跳转到B页面并传参:
1 2 |
this.context.router.push({ routeName: 'B', props: { datas: this.props.discountData }}); |
B页面获取参数方式:this.props.datas.data,B页面返回上一页面使用 popToWithProps,并传参:this.context.router.popToWithProps('A',{trandatas:null});。A页面获取参数方式:this.context.router.getCurrentRoutes()[0].params.trandatas
2.3 修改底层组件
诚然JDReact底层封装的组件可以满足项目开发中的大部分需求,但是有些特殊需求,暂时不能满足,比如加油卡项目中设置密码的弹窗,设置按钮的字体在不同状态下要有对应的颜色
该密码弹窗组件是在JDConfirmDialog组件的基础上开发的,但是该组件并没有提供确定按钮位置样式的变化,于是我们找到底层封装JDConfirmDialog组件的目录node_modules/@jdreact/Libraries/JDConfirmDialog/index.js文件,为了不影响底层组件,我们复制出该文件,在
1 2 3 |
<JDText style={styles.confirmText}> {this.props.confirmText} </JDText> |
的位置增加可以传入设置文本的样式textStyle:
1 2 3 |
<JDText style={[styles.confirmText,this.props.textStyle]}> {this.props.confirmText} </JDText> |
这样就可以在组件外面设置按钮的样式了。
2.4 JDNetwork使用方法
在调取后台数据的时候走了很多弯路,首先是无法获取到后台发出的数据,debug版本的京东APP默认的host 是 api.m.jd.com.care,后端使用JSF杰夫服务平台提供数据,前端使用JDNetwork组件给出的API:fetchWithoutHost却无法调取后台数据,在此过程中,为了打通接口多次和不同团队进行了沟通咨询,最后发现,JSF提供的数据前端无法直接使用,后端研发还需要再进行解析封装。
本以为数据接口只要调通了一个,其他的接口就问题不大了,然而却发现有的接口可以调通,有的接口却总是抛出错误,经过排查发现,只要后端返回的字段code不等于0,就会抛出异常。这是怎么回事呢?经过询问才知道code作为保留字段,必须为0,而不能作为业务含义(比如code表示用户的状态等等),否则一旦不等于0,就表示API网关返回异常。
项目在测试环境中测试的差不多了,需要放在预发环境上,然而如何切换到预发环境上呢?需要配置host吗?需要修改代码吗?答案是不需要修改代码,从底层封装的组件可以看出我们使用的fetchWithoutHost,其封装的host是api.m.jd.com,和beta-api.m.jd.com,分别对应着正式环境和测试环境,JDNetwork写一套代码就可以。
对于Android版本的APP:在debug的客户端中选择设置——debug配置——服务器设置——如果勾选前两项就是测试环境api.m.jd.care(经过咨询得知beta-api.m.jd.com和api.m.jd.care都对应着测试环境,区别只在于beta-api.m.jd.com支持https,而api.m.jd.care这个网关是不支持 https),如果去掉勾选前两项,host就是api.m.jd.com;
对于IOS的releaseP版本则需要在点击“ React 打开”按钮启动APP时选择需要的host环境。
2.5 JDtext换行问题
JDreact组件开发的样式布局和常用的H5开发样式布局,多少还是有所不同。例如,项目中用户勾选协议部分如下图所示:
因为黑色字体使用 <JDText> 组件包裹,红色字体点击需要出现弹窗,所以红色字体部分需要使用点击组件 <JDTouchable> 标签包裹,查看<JDTouchable> 底层代码可以看到里面是用 <View> 标签搭建的组件,所以 <JDText> 生成的黑色字体(行内元素)和 <JDTouchable>包裹的红色字体(块级元素)无法像上图一样,只能分成多行显示:
这显然无法满足样式的需求,解决方法是红色的字体部分也使用 <JDText> ,点击事件放在 <JDText> 上面,并且用上一级的 <JDText> 嵌套,代码如下所示:
1 2 3 4 5 |
<JDText style={{fontSize:JDDevice.getFontSize(24)}}>请您确认京东将收集您上述信息实现本服务,本服务约定请详见<JDText onPess={this._lookdeal} style={{fontSize:JDDevice.getFontSize(24),color:'red'}}> 《壳牌加油卡充值协议》 </JDText> </JDText> |
这样就达到了我们想要的样式。
2.6 组件之间的层级问题
2.7 Context的使用
在项目中,每个组件都需要获取用户登录信息,如果每个组件都调用获取用户的登录信息,显然是冗余的,那么有什么方法可以优化吗?我们可以在父组件中获取一次用户登录信息,然后分发到每个组件中,这样无疑减少了很多重复,但是从父组件传递到后代组件,数据层层下传也会造成很多的麻烦。如果有方法可以把数据从父组件直接下传到后代组件就好了,这时Context就派上用场了。例如需要把顶层组件的color数据传递到底层组件Button中,结构示意图如下:
j
Context犹如在顶层组件和后代组件Button之间打开了一条直通大道,可以从父组件中把数据直接传递到后代组件而不需通过中间组件传递,其使用方法简化如下:
(1)首先在顶层组件ParentInfo中定义color的类型:
1 2 3 |
ParentInfo.childContextTypes = { color: React.PropTypes.string }; |
定义顶层组件所拥有的子类Context对象——该顶层组件所拥有的子类Context对象为color,且必须为字符串类型。
然后通过getChildText方法,来给子Context对象的属性赋值:
1 2 3 |
getChildContext() { return {color: "red"}; } |
这样就完成了顶层组件中,Context对象的赋值。
(2)越级传递,因为color属性只在最底层使用,在一级子组件(图中的中间子组件)中并没有直接用到,因此我们可以直接传递到最底层(越级),在Button组件中使用。
首先Button组件中,再次声明了所接受到的Context的子组件color类型,声明必须为字符串:
1 2 3 |
Button.contextTypes = { color: React.PropTypes.string }; |
然后可以通过this.context.color这种方式调用:
<button style={{background: this.context.color}}>子组件</button>
综上所述,我们通过Context就能实现值的越级传递。从而简化了逻辑和代码复杂度。
关于手机APP调试代码的具体步骤,详见 1.4。使用debug版本的APP,晃动手机可以出现调试的选项,这里我们解释一下常用的几个功能:
reload手动重新加载;
debug js remotely可以在手机连接的电脑上打开浏览器,按F12可以打开调试模式,方便看到log出来的数据;
Enable live reload和enable hot reloading虽然可以在修改代码后保持热更新,但是由于我用到的测试机反应太慢了,这个功能没怎么用到,不得不一直晃动手机手动reload;
上线流程其实按照JDReact团队给出的发布规范走就可以了,作为前端的工作也就是要压缩图片,保证生成的升级包不要大于1M。升级包由JDReact团队上线,后端研发部署H5页面,上线完毕后,测试人员在无线持续集成平台的历史版本中下载安装相对应测试版本的Android 和IOS版本进行测试。在这个过程中要注意的是:
安装好客户端之后,需要把系统时间往后调整30分钟才能看到效果,一般上线往往延迟到凌晨左右,如果跨夜上线的话,不但要往后修改30分钟,一定要注意还要往后修改一天日期。例如系统时间为11月4日23:40,需要将时间改为11月5日00:10。
集成平台上的安卓版本目前分为dev分支、master分支、发版分支。其中dev分支和master分支都是从git库中的dev分支上抽取代码的,而每次客户端升级的发版分支是从master分支上抽取代码的,所以上线测试完成之后,还需要在集成平台上打包master分支,保证该分支是最新的代码,这样下次客户端升级才会包含本次上线的业务代码。
值得注意的是每次客户端升级之后对应的git库都要新拉出一个对应版本号的分支,如果有问题,可以在该分支下修改代码,然后在集成平台上对应的发版分支上抽取代码,因为此时你再修改git库中的dev分支无法影响发版分支;换句话说,分为两种情况:
在集成平台上发版分支创建之前,master分支已经包含了你的业务代码,发版分支就会从master中抽取代码,这样就包含了你的业务;
在集成平台上发版分支创建之后,如果在发版客户端中发现你的业务出了问题,需要在git库中对应的发版分支下修改问题,然后直接打包到发版分支上去,因为此时发版分支已经不再从master分支上抽取代码了,所以修改完git库中的发版分支代码之后,还需再次打包master分支,这样才能保证下一次发版分支从master分支中提取到你的业务代码。
综上所述,你需要做的如下所示:
对于之前已经上线的客户端,你需要生成升级包,由JDReact团队上线;
然后更新集成平台上的master分支,保证之后版本的客户端包含你的业务;
每次客户端更新之后,你需要在git库中新拉一个对应版本的分支,修改完毕后,将其打包到正在灰度的发版分支上;
最后再次更新git库中dev分支,更新集成平台中的master分支;
IOS系统和Android系统类似,IOS系统目前打包到dev分支(之后可能会有改动),这样才能保证以后每次的新版本发布,带上此次开发的项目。
敢不敢点开阅读原文啊?
以上是关于与JDReact的第一次亲密接触 ——加油卡项目总结的主要内容,如果未能解决你的问题,请参考以下文章
秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别