react進階系列:高階組件詳解(一)

不少人寫文章喜歡把問題複雜化,所以當我學習高階組件的時候,查閱到的不少文章都給人一種高階組件高深莫測的感受。可是事實上卻未必。html

有一個詞叫作「封裝」。相信寫代碼這麼久了,你們對這個詞所表達的含義都不會陌生。咱們一般會將功能相同或者類似的代碼提取出來封裝成爲一個可共用的函數或者對象,這也是咱們從初學者慢慢進階的必經之路。而高階組件就是一個封裝行爲。react

可是高階組件的封裝與咱們一般所使用的不太同樣,若是徹底同樣也就不是那麼難理解了。好在咱們有一個經常使用的口頭語「包一層「恰好能夠用來簡單解釋高階組件的不一樣。在普通組件外面包一層邏輯,就是高階組件。app

關於」包一層「,能夠經過一個很是簡單的例子來理解。函數

import React, { Component } from 'react';

class Div extends Component {
    componentDidMount() {
        console.log('這是新增的能力');
    }
    render () {
        return (
            <div>{ this.props.children }</div>
        )
    }
}

export default Div;

在上面的例子中,咱們把html的DIV標籤做爲基礎元件。對他新增了一個輸出一條提示信息的能力。而新的Div組件,就能夠理解爲div標籤的高階組件。到這裏但願你們已經理解了包一層的具體含義。學習

爲了更加透徹的理解「包一層」的概念,咱們須要來回顧一下new與構造函數之間的關係。在前面我有文章提到過爲何構造函數中this在運行時會指向new出來的實例,不知道還有沒有人記得。我將那段代碼複製過來。this

// 先一本正經的建立一個構造函數,其實該函數與普通函數並沒有區別
var Person = function(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        return this.name;
    }
}

// 將構造函數以參數形式傳入
function New(func) {

    // 聲明一箇中間對象,該對象爲最終返回的實例
    var res = {};
    if (func.prototype !== null) {

        // 將實例的原型指向構造函數的原型
        res.__proto__ = func.prototype;
    }

    // ret爲構造函數執行的結果,這裏經過apply,將構造函數內部的this指向修改成指向res,即爲實例對象
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));

    // 當咱們在構造函數中明確指定了返回對象時,那麼new的執行結果就是該返回對象
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }

    // 若是沒有明確指定返回對象,則默認返回res,這個res就是實例對象
    return res;
}

// 經過new聲明建立實例,這裏的p1,實際接收的正是new中返回的res
var p1 = New(Person, 'tom', 20);
console.log(p1.getName());

// 固然,這裏也能夠判斷出實例的類型了
console.log(p1 instanceof Person); // true

在上面的例子中,首先咱們定義了一個本質上與普通函數沒區別的構造函數,而後將該構造函數做爲參數傳入New函數中。我在New函數中進行了一些的邏輯處理,讓New函數的返回值爲一個實例,正由於New的內部邏輯,讓構造函數中的this可以指向返回的實例。這個例子就是一個「包一層」的案例。若是由於基礎不夠紮實致使你對上面的例子確實理解不了,咱們還能夠簡單粗暴的把上面的例子分紅三個步驟來記憶。spa

  1. 建立一個普通函數(由於new的存在因此變成構造函數)
  2. 建立一個new方法prototype

    • 在new方法中,建立一箇中間實例res
    • 對中間實例res通過邏輯處理以後返回res
  3. 使用new方法建立實例

而剛好,高階組件的建立邏輯與使用,與這裏的new方法徹底一致。由於new方法其實就是構造函數的」高階組件「。按照這個步驟,咱們來嘗試一步一步建立一個高階組件。code

第一步,建立一個最簡單的基礎組件。component

class Basic extends Component {
    render() {
        return (
            <div>{ this.props.children }</div>
        )
    }
}

第二步,根據上慄new方法的步驟,來建立高階組件。

// src/Addsss.jsx
import React from 'react';

// 基礎組件做爲高階組件的參數傳入
function Addsss(Container) {

    // 建立一箇中間組件,該中間組件會在添加了邏輯以後返回
    return class Asss extends React.Component {
        componentDidMount() {}
        render() {
            return (
                // 高階組件往基礎組件中傳入了一個name屬性,這是高階組件賦予基礎組件的新能力,固然,根據實際需求還能夠添加更爲複雜的新能力
                <Container name="asper">{ this.props.children }</Container>
            )
        }
    }
}

export default Addsss;

高階組件在基礎組件中調用,並將高階組件的運行結果返回給模塊外部。所以基礎組件的代碼調整以下:

// src/basic.jsx
import React, { Component } from 'react';
import Addsss from './Addsss';

class Basic extends Component {
    componentDidMount() {
        // 在基礎組件中試圖訪問高階組件傳入的新參數
        console.log(this.props.name);
    }
    render() {
        return (
            <div className={this.props.name}>{ this.props.children }</div>
        )
    }
}

// 這裏至關於執行了一次new操做,返回了一個實例,其實運行結果然是高階組件中的中間組件
export default Addsss(Basic);

咱們看到其實在基礎組件中,對外拋出的接口是Addsss(Basic),這是高階組件裏定義的函數運行的結果。也就是說,其實基礎組件中返回的是高階組件中定義的Asss中間組件。這和new的思路幾乎徹底一致。

因此咱們能夠簡單理解爲:react組件的高階組件,就是在基礎react組件外面包一層,給該基礎組件賦予新的能力。

固然,想要熟練使用高階組件並非一件容易的事情,咱們還須要更多的思考他。在下面一篇文章中我將會以實際的案例來分析高階組件的使用場景與他到底給咱們帶來了哪些便利。

clipboard.png

相關文章
相關標籤/搜索