ant design pro 代码学习(六) ----- 知识点总结2

1 、connect 多个model

  以下为redux的API中对connect方法的定义:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

  实际过程中使用最多的是mapStateToProps(另外两个参数暂时不做讨论),如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。

  以BasicLayout组件的connect为例,代码如下:

export default connect(({ user, global, loading }) => ({
  currentUser: user.currentUser,
  collapsed: global.collapsed,
  fetchingNotices: loading.effects['global/fetchNotices'],
  notices: global.notices,
})
)(BasicLayout);

  为便于理解,对以上代码稍作更改。如下所示:

function mapStateToProps(store) {
  console.log(store);
  const {user, global, loading} = store
  return {
    currentUser: user.currentUser,
    collapsed: global.collapsed,
    fetchingNotices: loading.effects['global/fetchNotices'],
    notices: global.notices,
  }
}

export default connect(mapStateToProps)(BasicLayout);

  此时控制台中,将会打印出所有已经注册过的model。(此处ant design pro代码有一个bug,下边会详细分析)。其中可以获取多个管理model的数据,即可以将多个model的数据(state)注册到当前组件的props中。。

2 、dva之loading状态

  dva-loading的使用非常简单,在index.js中加入:

import createLoading from 'dva-loading';
app.use(createLoading());

  每个页面中将loading状态作为属性(props)传入组件,在进行样式处理,比如转圈圈或者显示正在加载什么的,但是重点是,我们的app有多个页面,每个页面都这么做,很繁琐。如何只做一次状态处理,每次请求期间都会触发loading状态呢,其实也很简单啦,因为dva-loading提供了一个global属性。

  可能有个疑问:既然 global 可以展示异步加载是否完成,为什么还要 effects 属性,这是因为一个页面中可能同时有多个异步加载,只要有一个异步加载没有完成,global 都是 true,但是 effects中加载完成的异步方法都会变成false,只有没加载完成的异步方法也会是 true。

  loading的注入:

@connect(({login, loading}) => {
    return {
      login,
      submitting: loading.effects['login/login'],
    }
  }
)

  loading的属性:

loading: {
  global: false,
  models: {app: false},
  effects: {app: false}
}

  loading.effects是一个对象,对应的参数是:model下的effect,值是true或false,表示该effect是否被调用

参考:https://www.jianshu.com/p/61fe7a57fad4


3 、动态加载组件dynamicWrapper(ant design pro 的bug)

  dynamicWrapper 的定义如下,具体的功能分析,已经在 ant design pro 代码学习(五) ----- 知识点总结1分析过:


const dynamicWrapper = (app, models, component) => {
  // () => require('module')
  // transformed by babel-plugin-dynamic-import-node-sync
  if (component.toString().indexOf('.then(') < 0) {
    models.forEach(model => {
      if (modelNotExisted(app, model)) {
        // eslint-disable-next-line
        app.model(require(`../models/${model}`).default);
      }
    });

    return props => {
      if (!routerDataCache) {
        routerDataCache = getRouterData(app);
      }
      return createElement(component().default, {
        ...props,
        routerData: routerDataCache,
      });
    };
  }
  // () => import('module')
  return dynamic({
    app,
    models: () =>
      models.filter(model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`)),
    // add routerData prop
    component: () => {
      if (!routerDataCache) {
        routerDataCache = getRouterData(app);
      }
      return component().then(raw => {
        const Component = raw.default || raw;
        return props =>
          createElement(Component, {
            ...props,
            routerData: routerDataCache,
          });
      });
    },
  });
};

  引用的地方代码如下:

component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),

  在实际的运行过程中,并没有实现按需加载,所有的model都在路由生成时注册啦(执行if部分代码)。已下例为证明:稍微修改了一下BasicLayout的connect方法

function mapStateToProps(store) {
  console.log(store);
  const {user, global, loading} = store
  return {
    currentUser: user.currentUser,
    collapsed: global.collapsed,
    fetchingNotices: loading.effects['global/fetchNotices'],
    notices: global.notices,
  }
}

export default connect(mapStateToProps)(BasicLayout);

  此时console.log输出的所有model。如果按需加载的话,此时store中的model,应该只包含BasicLayout依赖的model— [‘user’, ‘login’]

  实际上所有的项目涉及的model都已经被注册。如图所示

  111

  因此建议dynamicWrapper方法修改如下:

const dynamicWrapper = (app, models, component) => {
  return dynamic({
    app,
    models: () =>
      models.filter(model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`)),
    // add routerData prop
    component: () => {
      if (!routerDataCache) {
        routerDataCache = getRouterData(app);
      }
      return props =>
        createElement(component().default, {
          ...props,
          routerData: routerDataCache,
        });
    },
  });
};

  修改的model加载如下:

  222

  该问题已经在github上提出issue。链接如下:https://github.com/ant-design/ant-design-pro/issues/1761


4 、装饰器

  修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。

  实际开发中,React 与 Redux 库结合使用时,常常需要写成下面这样。

class MyReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

  有了装饰器,就可以改写上面的代码。

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

  1. 修饰器不仅可以修饰类,还可以修饰类的属性。

  2. 如果同一个方法有多个修饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。

  3. 除了注释,修饰器还能用来类型检查。所以,对于类来说,这项功能相当有用。从长期来看,它将是 JavaScript 代码静态分析的重要工具。

  4. 修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升

参考:http://es6.ruanyifeng.com/#docs/decorator


5、Fragment

  React 中一个常见模式是为一个组件返回多个元素。 片段(fragments) 可以让你将子元素列表添加到一个分组中,并且不会在DOM中增加额外节点。
某些HTML元素只能接受特定的元素作为子节点,例如tr只能接受td ,如果将这些特定的子节点封装成组件的话,必须加入div包裹,那么最终生成的 HTML 将是无效的。

  React.Fragment将这些特定元素包裹,不会产生额外影响,改功能类似于Vue中的template。

  参考:http://www.css88.com/react/docs/fragments.html

6、context

  以下内容摘抄自:聊一聊我对 React Context 的理解以及应用

In Some Cases, you want to pass data through the component tree without having to pass the props down manuallys at every level. you can do this directly in React with the powerful “context” API.

  当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。

  使用props或者state传递数据,数据自顶下流。

  image

  使用Context,可以跨越组件进行数据传递。

  image

  如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者(Consumer),通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式

  对于父组件,也就是Context生产者,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object) 。对于子组件,也就是Context使用者,需要通过一个静态属性contextTypes声明后,才能访问父组件Context对象的属性,否则,即使属性名没写错,拿到的对象也是undefined。

  通过childContextTypes和contextTypes这两个静态属性的约束,可以在一定程度保障,只有组件自身,或者是与组件相关的其他子组件才可以随心所欲的访问Context的属性,无论是数据还是函数。因为只有组件自身或者相关的子组件可以清楚它能访问Context哪些属性,而相对于那些与组件无关的其他组件,无论是内部或者外部的 ,由于不清楚父组件链上各父组件的childContextTypes“声明”了哪些Context属性,所以没法通过contextTypes“申请”相关的属性。所以我理解为,给组件的作用域Context“带权限”,可以在一定程度上确保Context的可控性和影响范围。

  对于无状态子组件(Stateless Component),可以通过如下方式访问父组件的Context

import React from 'react'
import PropTypes from 'prop-types'

const ChildComponent = (props, context) => {
 ......
}
  
ChildComponent.contextProps = {
  ......  
}

  以Login组件为例:父组件中什么childContextTypes对象,并通过getChildContext()返回一个对象。代码如下:

class Login extends Component {
        
    static childContextTypes = {
        tabUtil: PropTypes.object,
        form: PropTypes.object,
        updateActive: PropTypes.func,
      };
      
    getChildContext() {
        return {
          tabUtil: {.....},
          form: this.props.form,
          updateActive: activeItem => {......},
        };
  }
}

  在子组件中使用,首先声明contextTypes,代码如下:

export default class LoginTab extends Component {
    static contextTypes = {
        tabUtil: PropTypes.object,
    };
    
    componentWillMount() {
        if (this.context.tabUtil) {
          this.context.tabUtil.addTab(this.uniqueId);
        }
    }
}

上一篇:ant design pro 代码学习(五) ----- 知识点总结1
下一篇:ant design pro 代码学习(七) ----- 组件封装(登录模块)


参考:https://segmentfault.com/a/1190000013365874?utm_source=tag-newest

   http://react.css88.com/docs/context.html

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值