React.js 新手快速入門 - 進階篇

著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。

react.js中的組件化

容器組件 VS 展現組件

在 react 中組件化是廣泛存在的,可是組件化也是分類型的,接下來將介紹容器組件與展現組件,這2者同爲組件,卻有着不一樣的功效,記住下面這段話,有助於幫你理解容器組件與展現組件:css

基本原則:容器組件負責數據獲取與處理;展現組件負責根據全局props展現信息vue

import React, { Component } from "react";
// 容器組件
export default class CommentVs extends Component {
    constructor(props) {
        super(props);
        this.state = {
            comments: []
        };
    }
    // 生命週期函數
    componentDidMount() {
        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} />
            ))}
            </div>
        );
    }
}

// 展現組件
function Comment({ data }) {
    return (
        <div>
        <p>{data.body}</p>
        <p> --- {data.author}</p>
        </div>
    );
}
複製代碼

PureComponent 組件

什麼是PureComponent呢,其實就是定製化後的shouldComponentUpdate的增強Component(內部實現淺比較)react

import React, { Component, PureComponent } from "react";

// shouldComponentUpdate的增強版
class PureComponentTest extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            comments: [
                { body: "react is very good", author: "facebook" },
                { body: "vue is very good", author: "youyuxi" }
            ]
        };
    }
    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");
        return (
            <div>
                <p>{this.props.body}</p>
                <p>------{this.props.author}</p>
            </div>
        );
    }
}

export default PureComponentTest;
複製代碼

React.PureComponent的實現原理: webpack

  1. 經過is函數對兩個參數進行比較,判斷是否相同,相同直接返回true:基本數據類型值相同,同一個引用對象都表示相同
  2. 若是兩個參數不相同,判斷兩個參數是否至少有一個不是引用類型,存在即返回false,若是兩個都是引用類型對象,則繼續下面的比較;
  3. 判斷兩個不一樣引用類型對象是否相同,先經過Object.keys獲取到兩個對象的全部屬性,具備相同屬性,且每一個屬性值相同即兩個對相同(相同也經過is函數完成)

所謂的淺比較,指的就是這行代碼!is(objA[keysA[i]], objB[keysA[i]])。能夠看到,在比較兩個對象中屬性的屬性值的時候,是直接採用Object.is的方式進行的比較,若是對應屬性值剛好是基本類型值固然沒有問題,可是若是,剛好對象中的該屬性的屬性值是引用類型的值,那麼比較的仍舊是引用,而不是對象的外形。因而可能出現這種狀況,當ObjA和objB的對象外形一致,按道理說不須要更新,可是因爲其中某個相同屬性的屬性值是引用類型,而他們雖然外形也是一致的,可是引用不一樣,那麼!is(objA[keysA[i]], objB[keysA[i]])仍舊會返回true,最終致使shallowEqual函數返回false(這樣shouldComponentUpdate方法會返回true),從而致使組件出現無心義的更新。web

那麼爲何這裏會採用「淺比較」呢?這其實也是出於對於性能的考量。咱們都知道,在js中,對引用類型外形的比較,其實是須要經過遞歸比較才能完成(深複製引用類型也須要經過遞歸完成)。而在組件更新判斷的生命週期中不斷執行遞歸操做去比較前後的props和state對象,毫無疑問會產生較大的性能開銷。因此這裏只能折中,採用淺比較的方式。固然反作用就是,仍可能出現沒有必要的從新渲染(也就是兩個對象的外形一致,但其中的某些屬性是引用類型,這樣即便引用類型屬性值的外形也是一致的,淺比較依舊斷定這兩個對象不一樣,從而致使多餘的從新渲染)。npm

React.memo 函數式組件

React.memo是 React v16.6.0 以後的版本,可使用 React.memo 讓函數式的組件也有PureComponent的功能json

const Joke = React.memo(() => (
    <div>
    {/* ||若是value爲空,則顯示 loding*/}
    {this.props.value || 'loading...' }
    </div>
));
複製代碼

ant-design組件庫的使用

首先給對應項目工程安裝依賴,執行命令 npm install antd --savebash

簡單示例,button按鈕的使用服務器

import React, { Component } from 'react'
// 導入antd 按鈕組件
import Button from 'antd/lib/button'
// 導入antd 樣式表
import "antd/dist/antd.css"
class ButtonTest extends Component {
    render() {
        return (<div className="App">
            {/*使用antd button 組件*/}
            <Button type="primary">Button</Button>
        </div>
        )
    }
}
export default ButtonTest
複製代碼

更多內容請參考ant design官方指南 babel

按需加載的配置

你能夠理解爲是懶加載,就是在須要的時候才加載組件插件。配置步驟以下:

  1. 安裝react-app-rewired取代react-scripts,這是能夠擴展webpack的配置 ,相似vue.config.js
    npm install react-app-rewired@2.1.5 babel-plugin-import --save
    npm install customize-cra less less-loader --save
    複製代碼
  2. 新建config-overrides.js文件,內容爲
    const { override,
      fixBabelImports,addBabelPlugins
     } = require("customize-cra");
    
    module.exports = override(
      // antd按需加載
     fixBabelImports(
        "import", { libraryName: "antd", libraryDirectory: "es", style: "css" }
      ),
      addBabelPlugins(
        ['@babel/plugin-proposal-decorators', { legacy: true }],
      )
    );
    複製代碼
  3. 修改package.json內的scripts內容以下:
    "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-app-rewired eject"
      },
    複製代碼

同窗們能夠本身對着項目修改調試。

什麼是高階組件

React裏已經有了HOC(Higher-Order Components)的概念,也就是高階組件,高階組件實際上是返回另一個組件,產生的新的組件能夠對屬性進行包裝,甚至重寫部分生命週期。看個例子

const jpsite = (Component) => {
    const NewComponent = (props) => {
        return <Component {...props} name="開課吧高階組件" />;
    };
    return NewComponent;
};
複製代碼

上面 jpsite 組件,其實就是代理了一個Component,只是給原 Component 多傳遞了一個name參數

高階組件的鏈式調用

import React, { Component } from 'react'
import {Button} from 'antd'

const withName = (Component) => {
    const NewComponent = (props) => {
        return <Component {...props} name="開課吧高階組件" />;
    };
    return NewComponent;
};

const withLog = Component=>{
    class NewComponent extends React.Component{
        render(){
            return <Component {...this.props} />;
        }
        componentDidMount(){
            console.log('didMount',this.props)
        }
    }
    return NewComponent
}

class App extends Component {
    render() {
        return (
            <div className="App">
            <h2>hi,{this.props.name}</h2>
            <Button type="primary">Button</Button>
            </div>
        )
    }
}

export default withName(withLog(App))
複製代碼

withName(withLog(App))就是鏈式調用的方式

高階組件的裝飾器寫法

ES6裝飾器可用於簡化高階組件寫法,首先安裝插件
npm install --save-dev @babel/plugin-proposal-decorators
而後config-overrides.js添加以下內容

實例Demo以下:

import React, { Component } from "react";

// 高階組件
const withName = Comp => {
  // 甚至能夠重寫組件聲明週期
  class NewComponent extends Component {
    componentDidMount() {
      console.log("do something");
    }
    render() {
      return <Comp {...this.props} name="高階組件試用介紹" />;
    }
  }

  // 假設經過某種特殊手段獲取了本節課名字
  return NewComponent;
};

const withLog = Comp => {
  console.log(Comp.name + "渲染了");
  return props => <Comp {...props} />;
};

@withLog
@withName
@withLog
class Jpsite extends Component {
  render() {
    return (
      <div>
        {this.props.stage} - {this.props.name}
      </div>
    );
  }
}

export default Jpsite;

複製代碼

@withName @withLog三者連在一塊兒的寫法就是裝飾器寫法了,效果等同於withName(withLog(App))這種鏈式調用的方式

組件跨層級的上下文通訊

組件跨層級通訊可以使用Context 這種模式下的兩個角色,ProviderConsumer Provider爲外層組件,用來提供數據;內部須要數據時用Consumer來讀取

import React, { Component } from "react";

// 1. 建立上下文
const Context = React.createContext();

const store = {
  // 指定6611尾號用戶中獎
  name: "恭喜你中到了一等獎",
  sayHi() {
    console.log(this.name);
  }
};

const withProvider = Comp => props => (
  <Context.Provider value={store}>
    <Comp {...props} />
  </Context.Provider>
);

const withConsumer = Comp => props => (
  <Context.Consumer>
    {/* 必須內嵌一個函數 */}
    {value => <Comp {...props} value={value} />}
  </Context.Consumer>
);

@withConsumer
class Inner extends Component {
  render() {
    console.log('Inner');
    return <div>{this.props.value.name}</div>;
  }
}

@withProvider
class ContextSample extends Component {
  render() {
    console.log('ContextSample');
    return <div><Inner></Inner></div>;
  }
}

export default ContextSample
複製代碼

掃碼關注公衆號,回覆20191121獲取本文全部源碼

寫做不易,若是文章對你有幫助,能否留下腳印留個贊~

☞☞點擊這裏購買雲服務器☜體驗代碼效果☜

相關文章
相關標籤/搜索