react组件化

  1. 组件

    • 适用ant-design组件
      • 起步安装

        npm install antd --save
        复制代码
      • 试用button组件

        import React, { Component } from 'react';
        import Button from 'antd/lib/button'
        import 'antd/dist/antd.css'
        class App extends Component {
          render() {
            return (
              <div className="App">
                <p>hello world</p>
                <Button type="primary">Button</Button>
              </div>
            );
          }
        }
        export default App;
        复制代码
      • 配置按需加载:上边组件如果使用就需要全部引用antd.css ,引用别的组件还得import

        安装react-app-rewired取代react-script,可以对webpack的配置进行扩展,类似于vue.config.js,

        npm install react-app-rewired@2.0.2-next.0 babel-plugin-import --save
        复制代码

        更改package.json ,使用react-app-rewired

        "scripts": {
            "start": "react-app-rewired start",
            "build": "react-app-rewired build",
            "test": "react-app-rewired test",
            "eject": "react-app-rewired eject"
          },
        复制代码

        新增config-overrids.js文件

        const {injectBabelPlugin} = require('react-app-rewired');
        module.exports = function override(config,env) {
            config =injectBabelPlugin(
                ['import',{libraryName: 'antd', libraryDirectory: 'es', style: 'css'}],
                config
            );
            return config
        };
        复制代码
      • 参考官网使用

    • 容器组件Vs展示组件
      • 基本原则:容器组件负责数据的获取,展示组件负责根据props显示信息

        class CommentList extends Component{
            constructor(props){
                super(props);
                this.state={
                    comments:[]
                }
            }
            render() {
                return <ul>
                    {this.props.comments.map(value => {
                        return <li>{value}</li>
                    })}
                </ul>
            }
            componentDidMount() { // 模拟一下ajax数据获取
               setTimeout(()=>{
                   this.setState({
                       comments:["哈哈","嘻嘻","嘿嘿"]
                   })
               },1000)
            }
        }
        复制代码
      • 进行分离,分离成容器组件和展示组件

        class CommentList extends Component{
            constructor(props){
                super(props);
                this.state={
                    comments:[]
                }
            }
            render() {
                return <CommentDetail comments={this.state.comments}/>
            }
            componentDidMount() { // 模拟一下ajax数据获取
               setTimeout(()=>{
                   this.setState({
                       comments:["哈哈","嘻嘻","嘿嘿"]
                   })
               },1000)
            }
        }
        // 将上边的组件进行拆分 显示组件和动作组件也就是获取数据的组件
        class CommentDetail extends Component{
            render() {
                return <ul>
                    {this.props.comments.map(value => {
                        return <li>{value}</li>
                    })}
                </ul>
            }
        }
        
        复制代码
      • 将上边显示组件 写成function的组件形式

        function CommentDetail({comments}) {
            return <ul>
                {comments.map(value => {
                    return <li>{value}</li>
                })}
            </ul>
        }
        复制代码
      • 进行分离的优势:

        1. 能清楚的看到如何工作和如何展示
        2. 重用性高
        3. 更高的可用性
        4. 更易于测试
    • PureComponent

      定制了shouldComponentUpdate后的Component(进行浅比较)

      • 形式 :直接使用

        class Comp extends PureComponent{ ....}
        
        复制代码
      • 原理:定制shouldComponentUpdate生命周期,如果props没变化,阻止渲染

        shouldComponentUpdate(nextProps, nextState) {
            // 如果nextProps的内容等于this.porps,nextState的内容等于this.state
            return false
        }
        
        复制代码
      • 实现 : 如果不写shouldComponentUpdate这个生命周期的对比,则不管title变不变都重新渲染,但是使用了PureComponent或者加上下边生命周期 ,title不变就不会重新渲染,其PureComponent就是通过shouldComponentUpdate定制了一个组件

        class TitleDisplay extends Component{
            shouldComponentUpdate(nextProps, nextState, nextContext) {  // 通过对比
                if (nextProps.title ===this.props.title){
                    return false
                }
            }
            render() {
                console.log('title重新渲染了');
                return <h2>
                    {this.props.title}
                </h2>
            }
        }
        
        复制代码
      • 缺点 :只有class 形式的组件才能使用PureComponent这个功能

      • React16 新增一个新功能React.memo 来完美实现React组件,让函数式的组件也有PureComponent的功能

        const TitleDisplay =React.memo(props=>{  // 函数式组件的
            return <h2>{props.title}</h2>
        })
        
        复制代码
    • HOC高阶组件:高阶组件一般都是以with开头进行命名

      提高组件的复用率,首先想到就是抽离相同的逻辑,在React里就有了Hoc的概念,高阶组件也是一个组件,但是它返回另外一个组件。产生的新的组件可以对属性进行包装也可以重写部分生命周期

      • 简单的高阶组件实现:高阶组件增加属性

        import React from 'react';
        import ReactDOM from 'react-dom';
        import './index.css';
        import App from './App';
        import * as serviceWorker from './serviceWorker';
        
        ReactDOM.render(
            <App  age="18" />,  //这个age =18的属性后边的{...props}就可以接收到
            document.getElementById('root')
        );
        
        复制代码
        const withKaikeba = (Component)=>{
            const newComp = props=>{    // 这个newComp是一个函数式的组件
                console.log(props);
                return <Component {...props} name="开课吧高阶组件测试"  />  
        // 将全部的props展开添加进属性中 比如在index.js中使用了App这个组件那么他的props就会传递进来
            };
            return newComp
        };
        class App extends Component{
            render() {
                return <div>
                    <h1>XXX</h1>
                    <p>{this.props.name}</p>
                </div>
            }
        }
        App = withKaikeba(App);  //使用这个组件
        export default App;
        
        复制代码
      • 简单的高阶组件实现:定制组件的生命周期

        const withLog = Component=>{
            class NewComponent extends Component{
                componentWillMount() {
                    console.time('ComponentRender');
                    console.log(`组件${Component.name}准备完毕了`)
                }
                render() {
                    return <Component {...this.props} />  //这也是展开props属性
                }
                componentDidMount() {
                    console.timeEnd('ComponentRender')
                    console.log(`组件${Component.name}渲染完毕了`)
                }
        
            }
            return NewComponent;
        };
        App = withKaikeba(withLog(App)); //  高阶组价支持链式调用
        
        复制代码
    • 高阶组件的装饰器写法

      上边的链式操作比较繁琐 ,逻辑比较绕,ES7中有一个优秀的语法——装饰器,专门处理这种问题

      • 安装babel插件,用于解析Babel 5中的旧装饰器行为

        npm install --save-dev babel-plugin-transform-decorators-legacy
        
        复制代码
      • 在config-overrides.js进行扩展,才能编译

        const {injectBabelPlugin} = require('react-app-rewired');
        
        module.exports = function override(config,env) {
            config =injectBabelPlugin( // 这是为能够按需加载的扩展
                ['import',{libraryName: 'antd', libraryDirectory: 'es', style: 'css'}],
                config
            );
            config=injectBabelPlugin(   // 这是为支持装饰写法的扩展
                ['@babel/plugin-proposal-decorators',{legacy:true}],
                config
            );
            return config
        };
        
        复制代码
      • 之后直接使用

        @withKaikeba
        @withLog   
        class App extends Component{
            render() {
                return <div>
                    <h1>XXX</h1>
                    <p>{this.props.name}</p>
                </div>
            }
        }
        // App = withKaikeba(withLog(App));
        export default App;
        
        复制代码
    • 组件间通信
      • 父组件向子组件通信

        class Child extends Component{  //  class 组件
            render() {
                return <div>
                    {this.props.name}
                </div>
            }
        }
        function Child(props) {    // 函数式组件
            return <div>
                {props.name}
            </div>
        }
        class App extends Component{
            render() {
                return <div>
                    <Child name={"向子组件传递值"}/>
                </div>
            }
        }
        
        复制代码
      • 子组件向父组件通信 ——利用回调函数

        这块有必要说一下 ,在 cb={this.callback.bind(this)} ,由于cb是子组件传递过来的,msg当然应该绑定cb函数本身,不能绑定App组件的this也就是用箭头函数不能获取到传递的值

        彻底理解可以参考

        function Child(props) {    // 子组件
            return <div>
               <div>
                   {props.name}
               </div>
                <Button type={"primary"} onClick={()=>props.cb('子组件传递的值')}>点击我传递</Button>
            </div>
        }
        class App extends Component{  //父组件
            callback (msg) {
                console.log(msg);
            }
            render() {
                return <div>
                    <Child name={"向子组件传递值"}  cb={this.callback.bind(this)} />
                </div>
            }
        }
        
        复制代码
      • 跨级组件之间通信:使用 context 对象上下文(Context) 提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性

        React.createContext: const {Provider, Consumer} = React.createContext(defaultValue);
        Provider :提供者<Provider value={/* some value */}>
        Consumer:订阅者 < Consumer> {value => {…}} < /Consumer>

        vuejs中可以使用privide&inject模式来进行组件之间的通信,这个模式的来源–context

        react中也有两个角色,Provider和Consumer,Provider在外层组件,内部需要数据,就使用Consumer来读取

        import {store} from './store'
        
        const AppContext = React.createContext();  //新建上下文 ,Provider和Consumer可以拆分出去
        const {Provider,Consumer} =AppContext;  
        class Demo extends Component{
            render() {  //  下边使用Consumer来进行接收
                return <Consumer>   
                    {
                        //  下边这块其实就是一个函数式的组件
                        store=>{
                            return <Button
                                type={"primary"}
                                onClick={()=>store.sayHi()}
                            >
                                {store.name}
                                </Button>
                        }
                    }
                </Consumer>
            }
        }
        class App  extends Component{
            render() {
                // 直接进行嵌套 ,value是固定写法
                return <Provider value={store}>
                     <div>
                         <h1>哈喽啊!小年依旧学习React</h1>
                         <Demo/>
                     </div>
                </Provider>
            }
        }
        
        复制代码
    • 其他多数据的组件通信就可以使用Redux进行
    • 实现createContext ? createContext就是一个高阶组件,结合用法去理解
      //  createContext初始化 简单的实现原理
      import React ,{Component} from 'react'
      function createContext() {
          let instance = {   // 1. 先声明一个对象初始化value
              value:null
          };
          class Provider extends Component{
              constructor(props){
                  super(props);
                  instance.vlaue = props.value   // 2. 赋给instance的value
              }
              render() {
                  return this.props.children     // 3.这块类似于vuejs的<slot></slot>  扩展
              }
          }
          class Consumer extends Component{
              constructor(props){
                  super(props);
                  this.state={
                      value:instance.vlaue
                  }
              }
              render() {
                  return this.props.children(this.state.value)    // 4.这块就是一个函数,将instance.value 传递进去
              }
          }
          return {Provider,Consumer}
      
      }
      
      复制代码
  2. React未来

    • Fiber:异步渲染 ,底层在实现虚拟Dom中实现了类似CPU调度的功能,
    • Suspense :用同步的代码实现异步的操作
    • Hooks:开发者可以完全抛弃class ,拥抱函数式编程
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享