react组件化

react组件化

使用antd的yarn create react-app antd-demo创建的项目,使用组件配置按需加载

  • 安装react-app-rewired取代react-scripts(package.json),可以扩展webpack的配置 ,类似vue.config.js
  • 项目根目录创建 config-overrides.js,配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const { injectBabelPlugin } = require("react-app-rewired");
    module.exports = function override(config, env) {
    config = injectBabelPlugin(
    // 在默认配置基础上注入
    // 插件名,插件配置
    ["import", { libraryName: "antd", libraryDirectory: "es", style: "css" }],
    config
    );
    // 高级组件、可使用装饰器配置 ES7 -> ES5
    config = injectBabelPlugin(
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    config
    );

    return config;
    };
  • 使用Button实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import React, { Component } from 'react'
    // antd.css都加载
    // import Button from 'antd/lib/button'
    // import 'antd/dist/antd.css'
    // 按需加载
    import {Button} from 'antd'

    export default class AntdTest extends Component {
    render() {
    return (
    <div>
    <Button type="primary">按钮</Button>
    </div>
    )
    }
    }

容器组件、函数组件,react渲染数据的深浅比较

  • 容器组件负责数据获取,展示组件负责根据props显示信息
  • shouldComponentUpdate
  • PureComponent (定制了shouldComponentUpdate后的Component(浅比较))

    1
    class Comp extends React.PureComponent {}
  • memo高阶组件(React v16.6.0 之后的版本,可以使用 React.memo 让函数式的组件也有PureComponent的功能)

    1
    const Demo = React.memo(() => ( <div>{this.props.value || 'loading...' } </div> ));
  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    import React, { Component } from "react";
    import { func } from "prop-types";

    // 容器组件
    export default class CommentList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    comments: []
    };
    }
    componentDidMount() {
    // 不断轮询比较、耗费🆓
    // setInterval(() => { // 每次生成的是全新数组
    setTimeout(() => {
    this.setState({
    comments: [
    { body: "react is very good", author: "facebook" },
    { body: "vue is very good", author: "youyuxi" }
    ]
    });
    }, 1000);
    }
    render() {
    return (
    <div>
    {this.state.comments.map((c, i) => (
    <Comment key={i} data={c} />
    // <Comment key={i} body={c.body} author={c.author} />
    // <Comment key={i} {...c} />
    ))}
    </div>
    );
    }
    }
    // 展示组件
    // memo高阶组件(也是一个函数,接收一个组件、返回一个全新的组件),也是浅比较(实现原理和使用PureComponent一样,解决了以前函数组件不能使用PureComponent)
    // React v16.6.0 之后的版本,可以使用 React.memo 让函数式的组件也有PureComponent的功能
    // const Comment = React.memo(function(props) {
    // console.log("render Comment"); // 执行两次
    // return (
    // <div>
    // <p>{props.body}</p>
    // <p> --- {props.author}</p>
    // </div>
    // );
    // });

    // 使用PureComponent (15.3 版本) 浅比较,避免对象比较(引用类型),挖掘太深,也没有达到效果
    // 解决 数据没有变 render函数不执行
    // 传值类型、或者避免引用类型的地址不变(尽量一层)
    // class Comment extends React.PureComponent{
    // render() {
    // console.log("render comment");
    // return (
    // <div>
    // <p>{this.props.body}</p>
    // <p> --- {this.props.author}</p>
    // </div>
    // );
    // }
    // }

    // class Comment extends React.Component{
    // // 生命周期进行比较 新状态nextProps 与 data比较
    // // 弊端: 比较累赘
    // shouldComponentUpdate(nextProps){
    // if (nextProps.data.body === this.props.data.body &&
    // nextProps.data.author === this.props.data.author) {
    // return false;
    // }
    // return true;
    // }

    // render() {
    // console.log("render comment"); // 执行了2次
    // return (
    // <div>
    // <p>{this.props.data.body}</p>
    // <p> --- {this.props.data.author}</p>
    // </div>
    // );
    // }
    // }

    function Comment({data}){
    console.log("render comment"); // 执行了4次
    return (
    <div>
    <p>{data.body}</p>
    <p> --- {data.author}</p>
    </div>
    );
    }

高阶组件

  • 在React里就有了HOC(Higher-Order Components)的概念
  • 高阶组件也是一个组件,但是他返回另外一个组件,产生新的组件可以对属性进行包装,甚至重写部分生命周期
    1
    2
    3
    4
    5
    6
    7
    const demoComp = (Component) => { 
    const NewComponent = (props) => {
    return <Component {...props} name='demo组件' />;
    };
    return NewComponent;
    };
    // 上面demoComp组件,其实就是代理了Component,只是多传递了一个name参数

高阶链式调用

  • 高阶组件最巧妙的一点,是可以链式调用

高阶组件装饰器写法

  • ES7装饰器可用于简化高阶组件写法
  • npm install –save-dev babel-plugin-transform-decorators-legacy
  • 同上,项目根目录创建 config-overrides.js,配置
  • 参考源码 https://github.com/yongfeng-peng/my-react-demo/blob/master/src/components/Hoc.js
    1
    2
    3
    4
    5
    // 高级组件、可使用装饰器配置 ES7 -> ES5
    config = injectBabelPlugin(
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    config
    );

组件跨层级通信 - 上下文

  • 组件跨层级通信可使用Context
  • 这种模式下有两个角色,Provider和Consumer
  • Provider为外层组件,用来提供 数据;内部需要数据时用Consumer来读取
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const FormContext = React.createContext()
    const FormProvider = FormContext.Provider
    const FormConsumer = FormContext.Consumer
    let store = {
    name: '学习',
    sayHi() {
    console.log(this.name);
    }
    }
    let withForm = Component=> {
    const NewComponent = (props) => {
    return <FormProvider value={store}> <Component {...props} /> </FormProvider>
    };
    return NewComponent;
    }
    @withForm class App extends Component {
    render() {
    return <FormConsumer> {
    store=> {
    return <Button onClick={()=>store.sayHi()}> {store.name} </Button>
    }
    } </FormConsumer>
    }
    }

code