页面组件与生命周期

一个应用是由许多页面组成,这些页面的文件名都是index.js, 它们都必须放在pages/xxx目录中。xxx为一个文件夹,里面通常有index.js, index.scss或index.less.

其次, 页面组件必须是一个有状态的 React 组件, 第一行必须为import React from '@react'

再次, 页面组件必须有render方法, 并且JSX只能出现在render方法中,不能出现在其他方法里面。JSX也不能用React.createElement()代替。因此render方法的JSX会被抽取出来,编译成wxml, axml, swan等文件。有关 JSX 的注意事项可以看这里

  1. //pages/train/index.js
  2. import React from '@react';
  3. class P extends React.Component {
  4. constructor() {
  5. super();
  6. this.state = {
  7. iconSize: [20, 30, 40, 50, 60, 70],
  8. text: 'this is first line\nthis is second line'
  9. };
  10. }
  11. static config = {
  12. "navigationBarBackgroundColor": "#ffffff",
  13. "navigationBarTextStyle": "black",
  14. "navigationBarTitleText": "微信接口功能演示",
  15. "backgroundColor": "#eeeeee",
  16. "backgroundTextStyle": "light"
  17. };
  18. add() {
  19. this.setState({
  20. text: this.state.text + '\nthis is new line'
  21. });
  22. }
  23. remove() {
  24. var textAry = this.state.text.split('\n');
  25. if (!textAry.length) return;
  26. textAry.pop();
  27. this.setState({
  28. text: textAry.join('\n')
  29. });
  30. }
  31. componentWillMount() {
  32. console.log('base componentWillMount');
  33. }
  34. componentDidMount() {
  35. console.log('base componentDidMount');
  36. }
  37. render() {
  38. return (
  39. <div class="container">
  40. <div class="item-list">
  41. <h2 class="item-list-hd">Icon</h2>
  42. <div class="group">
  43. {this.state.iconSize.map(function(item) {
  44. return <icon type="success" size={item} />;
  45. })}
  46. </div>
  47. </div>
  48. </div>
  49. );
  50. }
  51. }
  52. export default P;

页面组件的配置对象

其实就是来源于微信小程序官网

属性类型默认值描述
navigationBarBackgroundColorHexColor#000000导航栏背景颜色,如 #ff0000
navigationBarTextStyleStringwhite导航栏标题颜色,仅支持 black / white
navigationBarTitleTextString 导航栏标题文字内容
backgroundColorHexColor#ffffff窗口的背景色
enablePullDownRefreshBooleanfalse是否全局开启下拉刷新
tabBarObjectnull如果tabBar已定义,并且里面有list数组

页面组件的生命周期

由于页面组件也是一个React有状态组件, 因此它拥有React15/16的所有钩子。

当我们打开一个页面,页面组件会依次触发如下生命周期钩子

  1. componentWillMountgetDerivedStateFromProps ->
  2. onGlobalLoad -> onShow -> onGlobalShow ->
  3. componentDidMount -> onGlobalReady
  4. # 只有页面组件才有onShow, onHide钩子,普通组件没有这两个钩子

如果对页面组件进行setState,会依次触发如下生命周期钩子

  1. shouldComponentUpdate(如果return false, 后面的不会触发) ->
  2. componentWillUpdategetDerivedStateFromProps ->
  3. componentDidUpdate
  4. # componentWillMount/Update/ReceiveProps这三个钩子是 React15的旧钩子,如果定义了它们
  5. # 就不会触发React 16的新钩子getDerivedStateFromProps

如果用户从A页面跳转到B页面,是不会触页面组件的componentWillUnmount(即onUnload),而是触发页面的onHide钩子与app.js上的onGlobalHide钩子。然后再依次触发B的componentWillMount,onGlobalLoad,onShow。。。

页面组件与生命周期 - 图1

当然,除了页面的生命周期及页面上所有子组件的生命周期,应用本身还有生命周期,实际上我们看到的生命周期触发顺序是这样的。

页面组件与生命周期 - 图2

注: 页面组件必须使用 es6 风格来引入依赖与导出自己。

它的静态属性 config 会抽取出来生成对应的 JSON 配置对象,有关配置项可以看这里

页面事件

除了生命周期钩子, 有一些页面事件,都是以onXXX命名。如果用户没有定义这些页面事件, 框架还会尝试访问app.js中的以onGlobalXXX命名的方法,作为它的后备方案。

页面事件名全局事件名说明关系
onShowonGlobalShow没有参数总是触发全局事件
onHideonGlobalHide没有参数总是触发全局事件
页面初次被打开onGlobalLoad没有参数总是触发全局事件
页面初次渲染完onGlobalReady没有参数总是触发全局事件
页面被销毁onGlobalUnload没有参数总是触发全局事件
onResizeX有参数, {size }没有全局事件
onShare/onShareAppMessageonGlobalShare有参数, 有返回值,{from,target,webViewUrl}有页面就没有全局
onPageScrollX有参数,{scrollTop}没有全局事件
onReachBottomX没有参数没有全局事件
onPullDownRefreshX没有参数没有全局事件
onTabItemTapX有参数{index,pagePath,text}没有全局事件

页面组件的JS文件名必须为index.js的原因

主要是为了兼容快应用。快应用有一个manifest.json文件, 里面有一个router对象,包含所有页面

  1. "router": {
  2. "entry": "pages/index",
  3. "pages": {
  4. "pages/index": {
  5. "component": "index"
  6. },
  7. "pages/demo/syntax/index": {
  8. "component": "index"
  9. },
  10. "pages/demo/syntax/api": {
  11. "component": "index"
  12. },
  13. "pages/demo/syntax/await": {
  14. "component": "index"
  15. },
  16. "pages/demo/syntax/children": {
  17. "component": "index"
  18. },
  19. "pages/demo/syntax/extend": {
  20. "component": "index"
  21. },
  22. "pages/demo/syntax/if": {
  23. "component": "index"
  24. }
  25. }
  26. }

然后我们页面切换是通过React.api.redirectTo实现

  1. function createRouter(name) {
  2. return function (obj) {
  3. var router = require('@system.router');
  4. var params = {};
  5. var uri = obj.url.slice(obj.url.indexOf('/pages') + 1);
  6. uri = uri.replace(/\?(.*)/, function (a, b) {
  7. b.split('=').forEach(function (k, v) {
  8. params[k] = v;
  9. });
  10. return '';
  11. }).replace(/\/index$/, '');
  12. console.log(uri, "createRouter")
  13. router[name]({
  14. uri:"/"+ uri,
  15. params: params
  16. });
  17. };
  18. }