三大组件App/Page/ViewPort
App - CRN app 入口
App 是一个组件,也是一个基类。
//App 类定义
import {Component} from 'react';
clas App extens Component {
}
每一个使用使用AppRegistry.registerComponent
注册的组件都应该是一个 App 子类。改模块加载时会 new 出一个实例,我把这个实例称之为 app。(下文中出现的 app 都是指该 app 而不是 Native App)
//
Class HelloWorld extends App {
}
AppRegistry.registerComponent('HelloWorld', () =
>
HelloWorld);
为什么业务方需要继承 App 组件?
App 作为一个入口组件主要提供了下面几个功能:
- 初始化页面与管理页面渲染
- 封装路由,管理路由配置
- 与 Native 交互,绑定 Native 暴露的变量与方法,这些变量和方法的作用域与生命周期都与 app 一致
- 处理侧滑返回,在 Native 侧滑返回(app 首页)与 RN 侧滑返回(非 app 首页)之间无缝自由切换
- 集成 RN 原生导航栏,抹平平台差异。
所以,基于这些功能,业务方的入口组件需要继承 App 组件,否则业务开发需要自行去做这些成本巨大的工作。
除了继承,还有其他额外工作吗?
当然,你需要传入页面配置信息:
// main.js
import page1 from './src/Page1.js';
import page2 from './src/Page2.js';
const pages = [
{
component:page1,
name:'page1',
isInitialPage:true,
},
{
component:page2,
name:'page2',
}
];
const navigationBarConfig = {
hide:true, // 默认为 false
backgroundColor:'rgb(1, 100, 200)', // 导航栏背景色
};
class HelloWorld extends App {
constructor(props) {
super(props);
this.init({pages, navigationBarConfig});
}
}
AppRegistry.registerComponent('HelloWorld', () =
>
HelloWorld);
上面的代码就是一个完整的入口页面。
入口类HelloWorld
仅需实现一下构造方法,并且调用一下init
实例方法,传入pages
和navigationBarConfig
参数,为了方便扩展,init
方法的参数被设计为一个对象。
pages
是一个数组,包含了当前 app 所有页面类配置信息,数组顺序并不是页面切换顺序。
pages
的元素也是一个对象,代表一个 Page 类的配置:
{
component:page1,
name:'page1',
isInitialPage:true,
},
component
是一个Page
类名,根据这个类名实例化具体页面对象;
name
页面名,页面切换时需要用到;
isInitialPage
指定 app 首页,也可以在 URL 中指定,优先级:URL > 配置指定 > pages 数组第一个元素。
app 上的常量
app 会自动绑定到每个 Page 实例上,所以在每个页面中都是通过this.props.app
访问到,app 上目前绑定了两个 Native 暴露的变量:
- Bundle URL
- Bundle URL Query (可以通过 URL 传入一些数据到 RN)
// 访问方式
this.props.app.URL;
this.props.app.URLQuery;
Page
app 对应整个AppRegistry.registerComponent
注册组件的生命周期。
那么 Page 则对应每个页面。
// Page.js
class Page extends Component {
}
同样,Page 也是个基类,需要继承使用:
// Page1.js
class Page1 extends Page {
}
这里的 Page1 就是上面 App 配置里面引用的 Page1。
Page 提供哪些功能?
Page 封装了以下功能:
- 页面切换(push/pop)
- PV自动埋点
- 页面生命周期自动管理
页面切换 push
继承了 Page 组件,你可以以最简单的方式区切换页面,完全不用关心导航组件。
// Page1.js
//切换到page2,这里的 page2 是之前配置里的页面
this.push('page2');
//也可以带参数到 page2
//page2 中通过 this.props.foo 访问
this.push('page2', {'foo':'bar'});
页面切换 pop
回退到上一个页面,如果使用了默认导航栏,切未自定义左侧返回按钮,那么会自动处理返回事件。如果需要手动调返回,那么只需要调用this.pop()
即可。
// Page2.js
this.pop();
pop 回传参数
如果你需要传递数据到上一个页面,我建议的方式是上一个页面push
过来时通过参数传第一个 callback 函数,手动 pop 之前通过回调上一个页面传过来的 callback 回传参数。
// Page2.js
this.props.callback(data);
this.pop();
PV自动埋点
按照产品统计标准,每次页面展示(包括从后一个页面回退回来)都会记录一次 PV。需要在 Page 子类中实现 pageId 函数:
// Page1.js
pageId() {
return '62330005';
}
如果未实现 pageId 函数,则会使用 page name 作为 pageId 进行 PV 埋点。
ViewPort
ViewPort 可以理解为 Web 上的视口,页面展示的内容应该使用 ViewPort 包起来,因为 ViewPort 可以帮你做:
- 根据导航栏的隐藏与否自动调整页面大小
- 优化页面切换卡顿问题
// Page1.js
class Page1 extends Page {
render() {
return (
<
ViewPort
>
<
View style={styles.container}
>
<
Text style={styles.welcome}
>
Welcome to Page1
<
/Text
>
<
Text style={styles.instructions}
>
Shake or press menu button for dev menu
<
/Text
>
<
/View
>
<
/ViewPort
>
);
}