lixingdecai / react-native-note Goto Github PK
View Code? Open in Web Editor NEWRecord some of the problems encountered when i use react-native in our native app
Record some of the problems encountered when i use react-native in our native app
在android上实现页面跳转的防重复提交。
在android 的单页面应用中,跳转到另一个页面后,当前页面的脚本就停止运行。所以无法使用回调来触发触控区域的可点击性,只能使用简单粗暴的定时器来解决。用状态state 来设置 TouchableOpacity的 disabled属性。disabled如果如果设为true,则禁止此组件的一切交互。只用去控制这个state可以实现防重复提交。
在android下,性能会相对比较差,点击跳转会相对比较慢。一开始我设置的延迟时间是1000ms,甚至后面的1500ms,在机子比较卡顿的时候,还是相对比较容易触发onPress() 事件。
对于这种重复提交的使用比较频繁,复用高,将 TouchableHighlight 和 TouchableOpacity 进行封装使用;具体封装代码如下:
/**
* MTouchableOpacity
* @desc TouchableOpacity 封装了防重复提交事件 默认禁止重复提交
* @param disableDoubleClick 为true时 开启防重复提交
* @param disableDuration 防重复提交事件,默认2000ms
* @author [email protected]
*/
import React, {Component, PropTypes} from 'react';
import {
StyleSheet,
View,
Text,
Image,
TouchableOpacity
} from 'react-native';
export default class MTouchableOpacity extends Component {
constructor(props) {
super(props);
this.state = {
disableDoubleClick: false
};
}
static defaultProps = {
disableDoubleClick: true, // 默认禁止重复提交
disableDuration: 1000,
};
static propTypes = {
disableDoubleClick : PropTypes.bool,
disableDuration: PropTypes.number
};
/**
* 禁止重复提交
*
* @memberof Index
*/
async _disableDoubleClick() {
await this.setState({
disableDoubleClick: true
})
// this.state.disableDoubleClick = true;
this.timer = setTimeout(async () => {
// this.state.disableDoubleClick = false;
await this.setState({
disableDoubleClick: false
})
}, this.props.disableDuration);
}
componentWillUnMount() {
this.timer && clearTimeout(this.timer)
}
async _onPress() {
const {onPress} = this.props;
console.log('点击1 : ' + this.state.disableDoubleClick);
onPress && onPress();
this.props.disableDoubleClick && await this._disableDoubleClick(); // 如果不禁用重复提交
}
render() {
const {onPress, disabled, ...attributes} = this.props;
const isDisable = disabled === undefined ? this.state.disableDoubleClick : disabled;
return (
<TouchableOpacity
onPress={this._onPress.bind(this)}
disabled={isDisable}
{...attributes}
>
{this.props.children}
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
});
#场景,我在点击一个item时,希望做数据上报,但我担心阻塞主进程,所以在执行数据上报的时候,我对其进行了异步操作。
// 一个点击事件
_goChapter() {
this._gaShowCollect(); // 一个setTimeout 100ms 异步执行的操作
Service .runAction('/novel/chapter', params).then(res => {console.log(res);}); // 单页面跳转
}
但是在android下,页面跳转后,这个页面的进程就停止了,setTimeout,将方法的线程排在了事件队列的最后,但是只有等上个页面回退,回到当前页面的上下文,才会继续执行。
简单两步,打包的时候删除bundle中console相关代码
npm i babel-plugin-transform-remove-console --save
{
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
两步 让你在生产环境中的bundle文件不存在 console相关代码,减少了一定的代码量,和隐藏相关备注信息。
========
此方法,并不能去除所有console
originData.replace(/console[.]log[(].+[)]/g, '')
正则表达式 匹配删除console
originData.replace(/console[.]log[(].+[)]/g, '')
但是也只适合在没有压缩的代码里使用。在压缩的代码中使用,里面有各种字符,没办法匹配结束
https://stapp.space/how-to-remove-javascript-console-log-pros-2/
重写console 方法
if(!DEV) {
console = {};
console.log = () => {};
console.error = () => {};
}
1、add babel-plugin-react-native-web [过程](https://www.npmjs.com/package/babel-plugin-react-native-web)
2
这是新手的一个必趟误区。
认为setState()结束后会渲染页面。但是并不是这样。
看例子
componentDidMount(){
this.setState({
val: this.state.count + 1
});
//第一次输出
console.log(this.state.count);
this.setState({
val: this.state.count + 1
});
//第二次输出
console.log(this.state.count);
setTimeout(()=>{
this.setState({val: this.state.count + 1});
//第三次输出
console.log(this.state.count);
this.setState({
val: this.state.val + 1
});
//第四次输出
console.log(this.state.count);
}, 0);
}
输出结果 0 0 2 3
四次打印,分为两组,这两组的调用栈都是不同的
看看setState的调用流程
this.setState() == 传入state/ 还有回调 ==> enqueueSetState() / enqueueCallback ==> 是否处于批量更新模式 ==> updateComponent == > 更新pending state or props
在RN中,我会根据某字段来显示和隐藏某个部分。而在很多异步返回的字段,有可能为空。在js中,我们默认一个空的对象 === false;
正常的写法是:
{
isShowTheView && <View><Text>有数据</Text></View>
}
可是当异步返回的数据,也就是this.setState({isShowTheView: ''})的时候会报RawText "" must be wrapped in an explicit component的红屏错误
解决方法很简单,将 isShowTheView 改为 !!isShowTheView, 相反再相反强行转为true 或者 false
{
!!isShowTheView && <View><Text>有数据</Text></View>
}
版本升级的带来的成本还是挺大的,特别是iOS 和 android上。纯前端大部分是根据你页面数量,来手动修改,春劳力成本输出,大部分的版本兼容性问题,都会在手机 或者流浪器的console中进行提示,根据提示还是很好进行定位。
这次版本升级,前前后后耗费将近一周的时间把。
对于版本升级,现在0.40以后大部分都是修补漏洞,对于没有太大需求,是没必要进行无谓升级,当然,如果你刚开始使用 RN , 还是推荐实现当前最新版本。版本升级,当你觉得有必要的时候再进行,不影响工程项目的前提先,没必要修复,那些升级修复的功能,你可能没用到,也可能永远不会用到。
$ npm install -g react-native-git-upgrade
react-native-git-upgrade提供了豪华的一条龙自动合并更新流程,主要包含两个服务:
+ 首先它会利用Git工具计算新旧版本文件间的差异并生成补丁
+ 然后在用户的项目文件上应用补丁
$ react-native-git-upgrade
这样会直接把react native升级到最新版本
或者是:
$ react-native-git-upgrade X.Y.Z
这样把react native升级到指定的X.Y.Z版本
package.json 文件又不会修改你的依赖版本,你还得手动更改,不然你重新下载依赖又照样报错了
npm install --save prop-types
React.PropTypes
// After (15.5)
import React from 'react';
import PropTypes from 'prop-types';
class Component extends React.Component {
render() {
return <div>{this.props.text}</div>;
}
}
Component.propTypes = {
text: PropTypes.string.isRequired,
};
React.createClass
// After (15.5)
var createReactClass = require('create-react-class');
var Component = createReactClass({
mixins: [MixinA],
render() {
return <Child />;
}
});
网络监听的事件名称修改: change -- > connectionChange
NetInfo
.isConnected
.addEventListener('connectionChange', this._handleConnectionInfoChange.bind(this));
报错 Error while updating property 'selectionColor' of a view managed by: AndroidTextInput
一开始以为是 react-native-elements 版本问题,升级到最新后,还是不行
看了这个issue后,直接将selectionColor去掉了。有其他办法的,可以帮忙贴出来
目前的前端算比较是大热。react、vue、ng三辆马车在前端领域的推动。基于三辆马车的热更新的前端框架RN、Weex、NS应运而生。本来一开始在选型是选的weex,但是后面坐了两个页面就换RN了,因为社区和文档的不成熟等在开发的效率和代价上很大影响。所以本文热更新出现的相关问题,都是基于RN进行描述。
公司属于国内的二线互联网公司,主要产品就是移动APP,我们在RN上的使用,是以嵌入式的方式,集成到已有的 Native APP 中。在RN中的使用定位是,替代一些PV较高的H5,和一些新的业务的功能模块。展现的形式有,独立完整的页面 和 页面中的某个卡片,最终都会以RNView的形式展示,这个就很大了提高了RN展示的自由度。
提高用户体验
不管是快速解决线上bug,还是解决
版本审核这个在iOS上的特有的环节,
竟然可以热更新了,那就可以自由
的发布发版本。这里版本涉及到APP版本、RN 生产的bundle版本、RN版本;
APP版本:APP对RN的支持,是根据你的需求所需要的功能,一步步的接入,所以APP版本也是要考虑
RN bundle版本:这个是业务相关的RN的代码了,其实我们根据生产的zip包下发;
RN 版本:RN的官方版本,目前使用的是0.43版本,RN不向上兼容,所以升级的代价还是挺大的。
所谓的热更新,就是可以实时更新相对应的功能,那怎么更新呢,这需要一个下发机制。APP有个版本,RN有个版本,当这两个版本匹配的时候,会下发你所需要的更新;
这个问题在RN技术刚引入的时候,表现的非常明显。比如你要引入一个监听页面重新回到屏幕的方法,就得客户端支持,这个就受限于App版本,只能等下个版本APP支持了这个功能以后,才能做相对应实现的热更新。毕竟没有办法一口吃成胖子,一方面客户端没有办法一下子全都实现,另一方面你在没遇到相关需求问题的时候,没有办法预料到需要哪些功能实现。
所以在初期,会给产品经理和其他同事一感觉,这也并不是如吹捧的可以不跟版本的热更新。
对于RN相关开发人来说,工作量不是变少,而是变多了。对于之前Native开发,iOS程序员开发iOS,android程序员开发android,而RN,虽然是一套代码开发两端代码(理想),可是总还是会有区别的;
一个是适配造成差异性。
一种是业务上的差别,举个栗子,付费功能在iOS上是需要很严格的审核的,而android不用,所以在android会先上付费功能,而iOS得审核通过才能上,夸张的是有的公司十几次版本的申请都没过。
在开发的过程中,还要测试,边开发边测试。这也是相当大的工作量。
所以单纯拿业务页面的工作量来对比RN和Native,是大于1小于2;
对于iOS 和 android 两种系统,同一套代码,不能保证两个实现都一样,只要验证一种系统的手机就行了。就RN而言,他对iOS的支持度好于android。
还有一个问题是,测试人员可能对RN不大了解,而你又是个前端的开发人员,可能他们会潜意识把RN当做H5,然后就会觉得测试比H5麻烦多的多。她们心里的预设就会觉得RN测试成本好大好大。
作为嵌入Native App 的 RN,RN需要沟通的对象就有iOS业务、iOS架构、android业务、android架构、产品、测试、H5、服务端、统计等。俨然就是个产品,跟所有部门都得打交道,在沟通和对接的环节就变得多了。而且在初期大家对RN的都不太熟,增加了很大的沟通成本。
在引入RN到现在,每个环境都如履薄冰,如果没有一个强有力的人员主导这件事情,可能在任何一个环节就夭折掉或者限制了RN的在公司里面的发展。因为RN增加的工作量,让大家会开始慢慢抵触,
Native 的更新默认都是根据版本,都在新版本中生效。这个对于热更新来说,没什么问题。
但是H5 和 RN之间就存在问题了;H5的页面是发布在互联网上的一个地址,只要不设置缓存,所以可以理解为是即时生效的,而RN的功能,是根据后台配置的,他生效时机依托于他的触发时机。而触发热更新的时机,或者说热更新生效的时机,一般都是在APP重新启动的时候。
所以问题来了,在H5生效和RN生效之间会有个时间差,这个时间差的长短,依托于用户重启app的时间。所以很可能H5 和RN 同时上了新版本,但H5生效了,RN还未生效的情况。
这是个很容易出现严重问题,但容易被忽略,或者考虑不到的问题
编码规范:每个人的编码风格和习惯不同,例如有些人习惯句尾写封号,但是如果这些代码风格不进行一个
统一的规范,在多人的项目的代码管理中,就会造成很多不必要的代价。目前市场主流的编码规范Airbnb的编码风格,我们也是基于这套 -->
Airbnb React/JSX 编码规范
规范性的好处很多,但是他的优先级并不是最高,它是在你项目发展到一定层度,项目相对稳定可以引进的。它是可以让你项目开发管理上变得更好的东西,但并不是必须配。
编辑器选择微软的VS code
vscode 使用 代码统一风格工具.editorconfig
使用vscode + eslint + prettier 修改后的效果图:
左侧41个文件是eslint fix 自动修复的文件数;中间是前后两个文件的代码差异比对,红色区域是eslint 自动修复的语法规则。右侧带有下滑线是不符合设定的eslint语法规则,需要手动修改的代码
npm install --save-dev eslint
npm install --save-dev babel-eslint eslint-plugin-react-native eslint-plugin-jsx-a11y eslint-plugin-import
(或者将下面的package.json拷到你的项目直接安装)
babel-eslint 是 eslint 的解析器
eslint-plugin-jsx-a11y 是 jsx 的插件
eslint-plugin-react-native 是 react native 插件
eslint-plugin-import 是支持es6 import/export 的插件
eslint-config-airbnb 基于airbnb的eslint代码规则
附一份package.json的依赖配置作参考
"dependencies": {
"antd-mobile": "^2.0.0-beta.2",
"babel-plugin-import": "^1.5.0",
"create-react-class": "^15.6.2",
"prop-types": "^15.6.0",
"react": "16.0.0-alpha.12",
"react-native": "^0.48.3",
"react-native-animatable": "^1.1.1",
"react-native-blur": "^3.1.2",
"react-native-elements": "^0.11.2",
"react-native-picker": "^4.0.18",
"react-native-picker-android": "^1.0.3",
"react-native-storage": "^0.2.1",
"react-native-vector-icons": "^4.0.1",
"react-navigation": "^1.0.0-beta.11"
},
"devDependencies": {
"babel-eslint": "^8.0.1",
"babel-jest": "19.0.0",
"babel-preset-react": "^6.24.1",
"babel-preset-react-native": "1.9.1",
"eslint": "^4.9.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.4.0",
"eslint-plugin-react-native": "^3.1.0",
"jest": "19.0.2",
"q": "^1.5.0",
"react-native-webpack-server": "^0.9.3",
"react-test-renderer": "~15.4.0",
"shelljs": "^0.7.7"
},
当然相对于Airbnb,我们RN工程的一些差异性,我们还是做了小的改动,下面是我的.eslintrc 中eslint的一些规则:
module.exports = {
"extends": ["airbnb"],
"parser": "babel-eslint",
"plugins": [
"react",
"react-native",
"jsx-a11y",
"import"
],
rules: {
// 0 = off, 1 = warn, 2 = error
// FB配置参考:
// https://github.com/facebook/react-native/blob/8baaad9b0fbda2b02bb1834452aa63cac7910dc5/.eslintrc
"arrow-parens": ["error", "as-needed"],
"global-require": 0,
"no-use-before-define": 0, // disallow use of variables before they are defined
"max-len": 0, // specify the maximum length of a line in your program (off by default)
"no-console": 0, // disallow use of console (off by default in the node environment)
"no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block
"no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true }], // allow short circuit allow ternary
// "no-empty": [2, { methods: true }],
"no-unused-vars": 0,
"no-underscore-dangle": 0,
"no-param-reassign": ["error", { "props": false }],
"radix": 0,
"block-scoped-var": 0, // treat var statements as if they were block scoped (off by default)
"complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default)
"consistent-return": 0, // require return statements to either always or never specify values
"class-methods-use-this": 0,
// allow async-await
'generator-star-spacing': 0,
'curly': 0,
"object-curly-spacing": 0,
"one-var": 0,
"no-return-assign": 1, // disallow use of assignment in return statement
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react/self-closing-comp": 1,
"react/jsx-closing-bracket-location": 0,
"react/prop-types": 0, // 避免redux等注入属性的情况
"react/forbid-prop-types": 0
},
// 这里设置可能用到的全局变量
"globals": {
"window": true,
"__DEV__": true,
"__APP__": true,
"__ANDROID__": true,
"__IOS__": true
}
};
eslint --ext .js ./src --fix
,几个简单页面的demo 报了上百个错误,就这些报错,估计语法错误,估计得改好久时间。https://www.npmjs.com/package/prettierrc
1.1. npm install prettier --save-d / yarn add prettier --dev
1.2. 安装插件
prettier 在每个对应的IDE都有对应的插件,我使用的是VS code。就以vs code举例。搜索prettier插件的第一条就是,如下图所示
{
"name": "projectname",
"version": "1.0.0",
"description": "test",
"dependencies": {
"prettier": "latest"
},
"prettier": {
"singleQuote": true, // 规则
……
}
}
2.2. 配置文件.prettier
配置内容如下
{
"useTabs": false, // Indent lines with tabs instead of spaces.
"printWidth": 80, // Specify the length of line that the printer will wrap on.
"tabWidth": 2, // Specify the number of spaces per indentation-level.
"singleQuote": false, // Use single quotes instead of double quotes.
/**
* Print trailing commas wherever possible.
* Valid options:
* - "none" - no trailing commas
* - "es5" - trailing commas where valid in ES5 (objects, arrays, etc)
* - "all" - trailing commas wherever possible (function arguments)
*/
"trailingComma": "none",
/**
* Do not print spaces between brackets.
* If true, puts the > of a multi-line jsx element at the end of the last line instead of being
* alone on the next line
*/
"jsxBracketSameLine": false,
/**
* Specify which parse to use.
* Valid options:
* - "flow"
* - "babylon"
*/
"parser": "babylon",
/**
* Do not print semicolons, except at the beginning of lines which may need them.
* Valid options:
* - true - add a semicolon at the end of every line
* - false - only add semicolons at the beginning of lines that may introduce ASI failures
*/
"noSemi": true,
/**
* Add additional logging from prettierrc (not prettier itself).
* Defaults to false
* Valid options:
* - true - enable additional logging
* - false - disable additional logging
*/
"rcVerbose": true
}
坑: 使用此配置文件的时候,请将里面的注释删除,不然可能不生效
其实prettier 的格式化不完全和 eslint的规则相符,还是会有冲突的地方。从上面的配置文件可以看出,prettier 可配置的选项还是比较简单的。在有些地方prettier 还是会把一些正确的格式成eslint不允许的。这种时候只能去修改eslint的配置文件.eslintrc
解决:
在.eslintrc中添加 "arrow-parens": ["error", "as-needed"]
1、gitlab 安装gitlab-runner
2、jenkins 安装插件 Gitlab Hook Plugin + Build Authorization Token Root Plugin
3、配置代码源,和构建分支
5、将上面的URL,配置到你的gitlab的intergrations里面;
如果403 就要加用户名和密码,不然可以省略
404:得将上面的URL变成下面的URL,我的工程是根目录,直接把上面的project的省略掉
URL = http://用户名:密码@CI地址:端口号/你工程的路径/buildByToken/build?job=工程名&token=序列号
6、保存,然后就是点击测试
如果现实蓝色201,则恭喜你,基本成功了。
第一种方法存在一个很大的问题,就是,任何一个分支有push event 都会触发 jenkins的触发器。这是一个很大的问题,我们的目的是只要触发指定的分支,才触发jenken的触发器。所以就有了下面这种解决办法。
但要注意的点,gitlab 配置好地址和token 后 点击push test 是无效的。你只能在分支上进行真实的push event 进行测试。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.