JDReact 开发指南

Posted 全栈探索

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDReact 开发指南相关的知识,希望对你有一定的参考价值。

笔者曾数次打开 RN 官方文档,看着自己的 Windows 电脑默默的又把页面关了。对于 RN 以及 JDReact ,仅停留在这是一个拥有独特标签的 React 库的印象上。

在着手 JDReact 开发后,发现它不仅仅只是一个 React 库那么简单,它的周边及组件库都有很多挑战。

笔者从项目开始就认真记录了所有值得注意的地方,有些可能是坑,而有些则是 JDReact 及 RN 有别于常规Web开发特有的问题。

真心希望这篇文章能让读者在开发 JDReact 项目自始至终都能用上。

项目启动

有别于 RN 生成一个独立的 APP ,JDReact 生成的项目运行在京东APP内,启动流程也因此复杂一些。

创建项目

个人没有权限创建 JDReact 工程,请联系 JDReact 开发团队创建 git 工程。

启动开发环境

在申请成功后,开发人员会加入一个已初始化成功的 JDReact 项目。这个项目是可直接运行的,启动项目成功后在手机上会看到 Hello,yourProjectName

 
   
   
 
  1.    render() {

  2.        return (

  3.            <View style={styles.container}>

  4.                <JDText>Hello, {yourProjectName}</JDText>

  5.            </View>

  6.        );

  7.    }

那么如何启动这个项目呢?

嗯,clone 到本地, npm install,然后在 package.json里找找哪条命令像启动命令,就是它了! npm start

实际上,你可能在npm install时就出错了,因为JDReact项目目前要依托京东的npm内部库。

京东npm和yarn

若是Windows开发, npm installornpm start时你可能会遇到 errno3221225477安装错误(似乎是 npm 在 Windows 上的一个 bug ),这时候用 yarn install来安装能够避免这个错误。

配好 registry 后用 yarn install安装依赖包,接着执行 npm start

项目启动后,入口在哪呢?怎么在手机上调试呢?可利用 npm start debug自动打开调试入口,如下图。

JDReact 开发指南

图1 - debug 配置界面

在这个界面,一般只需要修改 IP,保证和手机同网段。

点击生成二维码扫描。

访问开发工程

准备测试手机

安卓、 ios 测试机各备一个即可,笔者在开发过程中也用过多种机型的测试机,来验证某些难解决的 bug 是否是因为机型问题导致,但实际上,各个测试机错误都表现一致,99%的错误都出在自身代码上。

JDReact 也不像 Web 开发对屏幕分辨率那么敏感,它依照 750px 设计宽进行开发,调用 JDDevice.getRpx(750宽设计稿数值)自动生成设备相应宽度。

准备测试软件

从应用商店下载的京东APP是没法进行调试的,需要下载京东的 Debug 客户端。

安卓:

安卓客户端安装挺方便,下载到手机后点击安装即可,注意设置中开启信任未知来源。

安装成功后,在本地调试阶段,需要开启“账户设置--设置--打开jdreact调试”功能,在打包测试阶段,则需要关闭此功能。

iOS:

iOS 需要将 ipa 包下载到本地然后通过iTools进行本地安装。iOS “账户设置”里没有 jdreact调试选项,无需额外操作。

安装成功后,需要在iOS“设置-通用-描述文件与设备管理”中信任新装的APP。

在手机上看到“Hello, yourProjectName”

将手机和图1中的 IP 连在同一网段,用 Debug 版本的京东APP扫描“安卓/iOS跳转”下的二维码,激动人心的时刻到了,手机屏幕见红了!恭喜你,你用的是安卓机。

此时提示“Could not connect to development server”。

这个时候,摇一摇手机,弹出了一系列菜单选项,最下边有一行“Dev Settings”,点击进去,找到“Debug server host & port for device”,输入图1 IP 和端口,保存,点击Reload。

可以在 npm start所在的命令行窗口看到编译进度条。

成功!

常见开发问题及采坑

在开发过程中,不免遇到各种问题,我总结了整个开发过程中的一些问题、关键点,包含了 Native 以及转 H5 三端问题。

Windows兼容性

Windows 下存在 npm startnpm run web-start启动不起来的问题。

npm start启动不起来的问题,先执行 yarn clean cache,然后 rm-rf node_modules,再用 yarn install安装依赖包。

npm run web-start启动不了的问题,先执行 yarn clean cache,然后 rm-rf node_modules,再用 npm install安装依赖包。

就是这么奇妙。

常用包

 
   
   
 
  1. import React from 'react';

  2. import PropTypes from 'prop-types';

  3. import { View, StyleSheet, Platform, } from 'react-native';

  4. import { JDImage, JDJumping, JDText, JDDevice, JDTouchableWithoutFeedback } from '@jdreact/jdreact-core-lib';

@jdreact/jdreact-core-lib是 JDReact 核心包,所有组件都在这里面,源码就处于这个目录。

样式

  • 利用 StyleSheet创建样式对象,尽情使用 flex 布局。

  • 大部分情况下,用 JDDevice.getRpx()给宽高边距赋值。目的类似写sass的px2rem,都为方便转换成各手机相应的像素值。

  • 在设计稿标注1-4个像素的情况下,使用 JDDevice.getDpx(),因为 getRpx函数会对计算出来的值进行取整操作,因此,计算出0.5像素会被取整到0,导致细线显示不出来。

  • 所有节点默认带有 position:'relative'属性,因此直接使用 left, top 属性是可以的。需要绝对定位改成 position:'absolute'

  • 抛弃 CSS 样式中的继承、父子选择器思路,哪个样式属性作用于哪个组件就要写在这个组件的 style 对象里。

  • Text/JDText 组件能生效的样式不多,请不要将布局样式应用在上面,只用字体字重字号颜色之类的修饰样式。

路由

在入口页,引入 JDRouter定义路由页面。

 
   
   
 
  1. import { JDRouter, } from '@jdreact/jdreact-core-lib';

  2. const { Router, Route, } = JDRouter;

  3. import A from './A';

  4. import B from './B';

  5. export default class JDReactHello extends React.Component {

  6.  render() {

  7.    return (

  8.      <Router initRoute={{ key: 'acomp' }}>

  9.        <Route key="acomp" type="resetTo" component={A}/>

  10.        <Route key="bcomp" component={B}/>

  11.      </Router>

  12.    );

  13.  }

  14. }

在点击A页面按钮跳转B页面时,怎么调用跳转方法呢?

首先在A页面定义 contextTypes静态变量,用于获取 React 的 context 上下文。

 
   
   
 
  1. export default class A extends React.Component {

  2.  static contextTypes = {

  3.    router: PropTypes.object

  4.  }

  5. }

在A页面,调用 this.context.router可获取到router实例,调用this.context.router to${UpperFirstWrod(key)}进行跳转。比如跳转到B页面,则写成 this.context.router.toBcomp({})

所有在 组件 的后代元素,若定义了 contextTypes,就能获取到 router 实例。

在定义路由 key 时,将 key 统一定义,方便在其他组件中调用,也方便修改。不推荐直接使用字符串定义。

 
   
   
 
  1. // 不推荐

  2. <Route key="page" component={Page}/>

  3. // 推荐

  4. import { ROUTES } from '../util';

  5. <Route key={ROUTES.PAGE} component={Page}/>

网络请求

如果看过 RN 文档,可能就会用 fetch 方法来进行网络请求,甚至会用 axios。千万别着急开发,JDReact 里,最好的请求方式是用 JDNetwork进行网络请求。

我们的开发目标是兼容三端,fetch 和 axios 只适应 H5。

京东客户端要求所有请求都走京东网关接口,客户端也提供了专门的 SDK,如果现在的后端接口还是常规 H5 接口(ajax or jsonp),及时联系后端提供网关接口。

网关接口response里有个很重要的参数 code:0,如果返回json根没有 code:0,会被 SDK 直接视为错误。

网关也支持 jsonp,需要将 jsonp 的名称改为 jsonp:'jsonp'

根据环境不同京东登录态分四种:APP登录,H5登录,PC登录,微信手Q登录。需要根据环境来传递登录态标识。

建议包裹 JDNetwork,调用自己包裹的请求方法,方便扩展。

动画

笔者写了一个 loading 转动的动画,测试的时候在安卓机上发现这个 loading 动画组件会阻塞他父级组件的动画,具体原因笔者没有深究,最后采用 gif 替代,因此,写动画的原则是:能用 gif 就别用动画,动画能不嵌套就不嵌套。

Dialog

一般点击弹出对话框的按钮处于一个较深且小的子组件中,如果是客户端,将 Dialog 写在子组件中是没什么问题的,APP 的原生机制能保证整个蒙层覆盖屏幕。

但是当转为 H5 时,就会遇到 fixed 的一个机制:当父元素带有 transform 属性时,子元素fixed定位会相对于该父元素。这个时候,子元素里的Dialog就只蒙住父元素,而不是整个屏幕,导致样式出错,因此,对于这种全局性质的组件,最好放在根节点下,利用 redux 或其他方式进行数据传递。

第三方组件转 H5

对于那些不支持 H5 形式的三方组件,如果用 Platform.OS进行平台区分,转 H5 可能会有编译错误,可以利用 component.web.js,component.ios.js,component.android.js 来避免。

APP运行状态

RN 将 APP 运行状态抽象成 active, background, inactive三种状态,在 iOS 下,当处于非 active状态下, setTimeout/setInterval会休眠,因此,在处理定时任务时,需要关注 APP 运行状态。

实际上,浏览器也有判断页面是否处于激活状态的 document.hidden,Web页面的动画也存在休眠的情况。

JDScrollView作为 FlatList的滚动渲染组件

做商品列表,或者各种列表时会遇到下拉刷新的需求,下拉刷新统一采用京东下拉样式。因此采用 JDScrollView作为 FlatList的滚动渲染组件。

但是这个组件存在一个问题,当列表高度不足容器高度时, JDScrollView的 loading 动画区域会露出来。

JDReact 开发指南

为了解决这个问题,方法之一是通过传给 FlatList的 data 数量,计算每个 data 的 renderItem 高度,来动态计算剩余空间,然后生成一个 空View 作为 renderItem 填充进行。

但这种方法遇到 Item 高度不定的情形计算起来非常复杂,笔者利用另一种思路来解决这个问题。

笔者解决这个问题的核心代码是:

 
   
   
 
  1. <FlatList

  2.  renderScrollComponent={(props) => (<JDScrollView {...props} />)}

  3.  contentContainerStyle={{minHeight: this.state.tabContentHeight}}

  4. />

利用 minHeight 强制保证 FlatList的内容区高度和容器高度一致。现在问题转化为,如何保证 FlatList渲染时得到容器高度 tabContentHeight

在 RN 中,从根节点到当前节点,如果在这条路径上的所有节点都具有 flex:1属性,那么,RN 就能在渲染内容区之前确定本身的高度。

利用这个信息,创建一个包裹组件,这个组件能获得他自身的高度:

 
   
   
 
  1. <ScrollHackWrap getAsyncHeight={(height) => { this.setState({tabContentHeight: height})}}>

  2.  <FlatList

  3.    renderScrollComponent={(props) => (<JDScrollView {...props} />)}

  4.    contentContainerStyle={{minHeight: this.state.tabContentHeight}}

  5.  />

  6. </ScrollHackWrap>

获取自身高度的原理是利用 RN 的 onLayout 属性。

 
   
   
 
  1. // ScrollHackWrap.js

  2.  getHeight(event) {

  3.    let { height } = event.nativeEvent.layout;

  4.    this.setState({

  5.      height: height

  6.    });

  7.    this.props.getAsyncHeight(height + this.indicatorHeight);

  8.  }

  9.  render() {

  10.    return (

  11.      <View onLayout={(event)=>{this.getHeight(event)}} style={{flex: 1}}>

  12.        {this.state.height > 0 && this.props.children}

  13.      </View>

  14.    );

  15.  }

这样,无论 renderItem 最后渲染的条目总高度是否低于容器高度,都能保证正确的显示。

转 H5

转H5时遇到了不少样式问题,但回过头看,实际上做的改动非常少。

若是 webkit 内核的浏览器,若发现有样式缺失,将具体样式补充在 contentContainerStyle之类里即可,这部分工作本应该是由 JDReact 团队来做的。

对于UC浏览器,核心是解决这两个问题。

  • 非 block 元素flex失效,也即inline, inline-block元素上用 display:flex是无效的,而 RN 将 Text 之类转成了 inline-block。因此,将Text改成display:block即可。

  • space-around不可用,这个就很没道理,但UC确实不支持这个属性,若可行,改成 space-between。 解决这两个问题,UC上的样式也就解决差不多了。

微信手Q登录

微信手Q是最令人头疼的,开代理无法进行 https 跳转,缓存页面缓存 cookie 等问题。

首先说说登录的事,前面讲到过京东登录分为4个状态,APP登录,H5登录,PC登录,微信手Q登录。

移动 Web 端主要就考虑H5登录和微信手Q登录了。

本段如果没看懂可直接跳过看下一段。如果你用了上述两种登录方式,那么就需要后端配合对这两种方式做校验。但实际上很多后端都只会校验 H5 登录,这时就会出现很奇怪的事,明明我用的是微信手Q登录方式,为什么也能获取到数据呢?原因在于之前访问过京东相关页面,并用H5方式登录过,cookie 里带有 H5 登录态相关字段,因此能校验通过。如果此时清除微信缓存,就会发现虽然登录但是数据获取不到了的奇怪现象。总之登录这一块比较复杂,不建议用多登录方式。

微信和 QQ 里是接受 H5 方式登录的,因此,只统一采用 H5 登录方式就行,微信手Q登录不用实现。

清除微信缓存的方法是退出账号重登。

顶部导航头

如果实现了自己的导航头,在京东APP的 WebView 内,就会出现双导航头的情况。

解决这个问题的思路是 Platform.OS==='web'&&uaType()==='jdapp',根据UA来判断是否处于京东APP内。

编译打包和上线

嗯~,这块就留给JDReact的官方文档吧。

小结

本项目没有表单相关的操作,因此,表单的问题总结不在此列,欢迎留言在开发RN/JDReact时遇到的难题和踩过的坑。同时感谢JDReact提供的良好平台。



以上是关于JDReact 开发指南的主要内容,如果未能解决你的问题,请参考以下文章

VSCode插件开发全攻略代码片段设置自定义欢迎页

IOS开发-OC学习-常用功能代码片段整理

Android开发常用代码片段

微信小程序代码片段

前端开发常用代码片段(中篇)

高效Web开发的10个jQuery代码片段