一、移动APP 开发技术选型进化
第一阶段 Native
1.性能优秀
2.开发效率低下,不可跨端
第二阶段 Native H5/Hybrid
1.Hybrid多端运行,解决了开发效率问题
2.性能存在瓶颈
现阶段 Native H5/Hybrid React Native
二、开发进阶-页面性能
页面(Page)切换卡顿问题
页面切换(push)和render同时进行,对复杂页面场景,会有卡顿问题
解决方案:1.调整页面切换动画时间
2.使用InteractionManager API页面切换完成,再render
三、开发进阶-统一Storage
统一Storage提供
1.Storage.load(params,callback)
2.Storage.remove(params)
3.Storage.save(parmas)
说明:
Native/RN/H5通用
Save API,params中domain区分不同业务
Save API,params中expires可控制过期时间
Save API,params中isSecret可控制是否加密存储
四、开发进阶-IconFont
Iconfont使用
1.文件大小,可以设置颜色,灵活性高
2.Font文件中每个图标对应一个Unicode码
使用步骤
1.http://iconfont/平台创建下载字体文件
2.导出字体文件,项目/文件命名规则crn_font_xxx,前缀固定;
3.注册字体文件CRNDev.registerIconFont()测试环境使用,生产环境会动态查找注册;
4.设置组件style,fontfamily = 项目/文件名 Note:iconfont项目/文件名不能手动修改
common.js
export default StyleSheet.create({
commonIcon: {
fontFamily: 'ct_font_common',
},
diyTourIcon: {
fontFamily: 'crn_font_diytour2'
},
diyTourIcon2: {
fontFamily: 'crn_font_diyTour'
}
})
页面上使用icon引用字体: icon.js
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {Text,Dimensions} from 'react-native'
import baseStyle,{color} from "../../styles/base/common"
export default class Icon extends Component {
render() {
let { size, color,value, style,type } = this.props
let fontFamily = baseStyle.diyTourIcon
if(type==='common'){
fontFamily=baseStyle.commonIcon
}else if(type==='dp2'){
fontFamily=baseStyle.diyTourIcon2
}
return (
<Text
style={[
fontFamily,
{
color,
fontSize: size
},
style
]}
>
{value}</Text>
)
}
}
Icon.propTypes = {
size: PropTypes.number,
color: PropTypes.string,
value: PropTypes.string,
style: Text.propTypes.style,
}
Icon.defaultProps = {
size: 20,
color: color.cBule,
value:'',
}
开发环境常见问题
1.icon不显示,一般是由于下载的文件名被修改过文件名
生产环境常见问题
1.图标非自己设计的icon,一般是被系统其它unicode占用,到iconfont平台生成新的code即可
五、开发常见问题
A.原因: Native Runtime 和 RN JS 代码API 不一致
解决方案:
1.删除node_modules
2.修改package.json中依赖的相关JS组件版本
3.重装node_modules
B.原因:依赖CRN版本和App版本不一致 0.51 runtime的App运行0.41的Js代码
解决方案:
1.删除node_modules
2.修改package.json中CRN的依赖分支
3.重装node_modules
C.原因:从node_modules查找模块出错
解决方案:
1.npm list |grep react* 查看是否有其他组件使用不同的react版本
2.删除node_modules
3.重启电脑,重新安装node_modules
六、CRN发布
发布流程:
1.创建项目(发布计划),设置代码仓库分支,入口文件,发布版本、频道
2.新增版本,FAT->UAT->生产 环境逐一发布
3.支持灰度发布、暂停发布
4.发布结果查看,支持分APP版本查看下载量、使用量
CRN-Web框架
crn-cli run-patch 替换RN依赖文件
crn-cli pack 本地打包
crn-cli openUrl 在App中打开url
crn-cli example 建立API示例工程
crn默认会依赖常用第三方库 crn-ext提供常用第三方库,业务按需依赖 eg: react-native-swiper
页面路由与跳转
page.push()
page.pop()
page.popToPage()
page.replace()
page.replaceAtIndex()
pop到容器最后一层后直接关闭CRN容器
Android物理键回退:onBackAndroid()
组件和Page生命周期:
componentWillMount PageWillAppear
componentDidMount PageDidAppear
componentWillUpdate PageWillDisappear
componentDidUpdate PageDidDisappear
componentWillUnMount
CRN页面加载流程
包安装 -> CRN框架代码执行(后台预加载) -> 业务JS -> 代码执行(a.页面渲染 b.初始页面渲染->网络请求->页面渲染)
router.js
import { App,lazyRequire } from '@ctrip/crn';
import pageConfig from './constants/pageConfig';
const DIYIndex = lazyRequire('./containers/diyIndex');
const DPCalendar = lazyRequire('./containers/dpCalendar');
const CityList = lazyRequire('./containers/cityList');
const DPIndex = lazyRequire('./containers/dpIndex');
const Confirm = lazyRequire('./containers/confirm');
const DPConfirm = lazyRequire('./containers/dpConfirm');
const Passenger = lazyRequire('./containers/passenger');
const FlightList = lazyRequire('./modules/flightList/container/flightList');
const HotelDetail = lazyRequire('./containers/hotelDetail/hotelDetail');
const pages = [
{
component: DIYIndex,
name: pageConfig.DIYINDEX.pageName,
// isInitialPage: true
},
{
component: DPCalendar,
name: pageConfig.DPCALENDAR.pageName,
// isInitialPage: true
}, {
component: CityList,
name: pageConfig.CITYLIST.pageName,
},
{
component: DPIndex,
name: pageConfig.DPINDEX.pageName
}, {
component: DPConfirm,
name: pageConfig.DP_CONFIRM,
// isInitialPage: true
}, {
component: Confirm,
name: pageConfig.SDP_CONFIRM.pageName
}, {
component: FlightList,
name: pageConfig.FLIGHT_LIST.pageName,
// isInitialPage: true
},
{
component: Passenger,
name: pageConfig.PASSENGER.pageName,
},
{
component: HotelDetail,
name: pageConfig.HOTEL_DETAIL.pageName,
isInitialPage: true
}
];
const navigationBarConfig = {
hide: true, // 默认为 false
backgroundColor: "#19A0F0", // 导航栏背景色
};
export default class Router extends App {
constructor(props) {
super(props);
this.init({
pages,
navigationBarConfig,
});
}
}
index.ios.js
/**
* Sample Ctrip React Native App
* http://crn.site.ctripcorp.com/
* @flow
*
* 此处有几点需要注意:
* 1、必须将AppRegistry.registerComponent写在该文件中, 并且将AppComponent从其他处require进来;
* 2、必须添加一个module.exports这一句, 发生产包必须这么做;
*/
'use strict';
import {
AppRegistry
} from 'react-native';
const DIYTOUR = require('./main');
AppRegistry.registerComponent('diytour', () => DIYTOUR);
//Attention: 此处module.exports必须保留
module.exports = DIYTOUR;
index.android.js
/**
* Sample Ctrip React Native App
* http://crn.site.ctripcorp.com/
* @flow
*/
'use strict';
import {
AppRegistry,
} from 'react-native';
const DIYTOUR = require('./main');
AppRegistry.registerComponent('diytour', () => DIYTOUR);
// Attention: 此处module.exports必须保留
module.exports = DIYTOUR;
main.js
'use strict';
import { CRNDev } from '@ctrip/crn';
import DIYTOUR from './src/app';
// global.console = {
// timeEnd: ()=>{},
// time: ()=>{},
// info: () => {},
// log: () => {},
// warn: () => {},
// debug: () => {},
// error: () => {},
// };
if (__DEV__) {
CRNDev.registryIconFont({
fontList:[
'http://10.32.228.8:5389/fonts/crn_font_diytour2.ttf',
'http://10.32.228.8:5389/fonts/crn_font_diyTour.ttf',
]
}, (result) => {
console.log('ttf文件安装' + (result ? '成功' : '失败'));
});
}
module.exports = DIYTOUR;
import打包后->require -> JS exe -> Cache Ret -> LazyRequire的使用(LazyRequire加载Page子类,只加载需要的Page)