React組件略講

React是前端組件化開發的開山鼻祖,這種開發方式完全解決了的前端組件複用的痛點。今天,就來研究一下React組件開發。html

前端同窗通常都會從Vue入門,由於Vue使用的<template>的組件開發方式讓前端人員更容易的平滑過渡。Vue的組件很簡單,通常來講,一個.vue文件就是一個組件。就像webpack的模塊化開發,一個文件就是一個組件。咱們在使用組件時也很容易,經過 ES6 的import導入、註冊(components),就能夠直接使用。前端

上面簡單說了Vue的組件模式,其實,React的使用方式也差很少,畢竟是借鑑它的組件思想。最明顯的差異就是聲明方式,React用的是JSX語法,這讓React變的很靈活。固然,使用方式的靈活也意味着使用者的腦子也要足夠的靈活,從而增長了入門的難度。玩的6的人是真喜歡,未入門的是真不能理解爲啥把html標籤寫到js裏面呢。vue

好了,上面扯了那麼多,下面就來一探React組件的究竟...react

React組件基礎

最開始,讓咱們先來了解一下React組件是如何定義的和分類的。webpack

組件定義

React組件的聲明方式有兩種(React.createClass已棄用):函數組件和class組件。它們的區別是什麼呢?web

若是一個組件只根據props渲染頁面,沒有內部狀態state,咱們徹底能夠用函數組件的形式來實現。也就是說,函數組件只是簡單的負責展現數據,它裏面沒有生命週期函數和state狀態,不能作業務邏輯處理。redux

函數組件(無狀態組件):設計模式

import React from "react";

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

class組件:數組

import React, { Component } from "react";

class Welcome extends React.Component {
  constructor(props) {
    super(props);
    // 設置 initial state
    this.state = {
      text: props.initialValue || 'placeholder'
    };
    // ES6 類中函數必須手動綁定
    this.handleChange = this.handleChange.bind(this);
  }
  handleClick(event) {
    this.setState({
      text: event.target.value
    });
  }
  componentDidMount() {
  // do something
  }
  componentWillUnmount() {
    clearInterval(this.timer);
  }
  render() {
    return (
      <div>
        <h1>Hello, {this.props.name}</h1>;
        <button onClick={this.handleClick}></button>
      </div>
    )
  }
}

注意:性能優化

  1. React自義定的組件均爲大寫字母開頭
  2. Props是隻讀的,若是要改變狀態,須要使用狀態提高

組件分類

React的組件分紅兩種類型:展現組件和容器組件。 將組件分紅這兩類以後,你會發現它們容易更被重用和理解。一般,展現組件負責展現內容,容器組件負責處理業務邏輯。是否是很像上面的函數組件和class組件的區別。

容器組件和展現組件名詞都來自於redux中文文檔。

  • 展現組件

    1. 關注頁面的展現效果(外觀)

    2. 內部能夠包含展現組件和容器組件,一般會包含一些本身的DOM標記和樣式(style)

    3. 一般容許經過this.props.children方式來包含其餘組件

    4. 對應用程序的其餘部分沒有依賴關係,例如Flux操做或store

    5. 不用關心數據是怎麼加載和變更的

    6. 只能經過props的方式接收數據和進行回調(callback)操做

    7. 不多擁有本身的狀態,即便有也是用於展現UI狀態的

    8. 會被寫成函數式組件除非該組件須要本身的狀態,生命週期或者作一些性能優化

Example:Page、Header、Sidebar、List、UserInfo...

  • 容器組件

    1. 關注應用的是如何工做的

    2. 內部能夠包含容器組件和展現組件,但一般沒有任何本身的DOM標記 (除了一些包裝用的 div) ,而且從不具備任何樣式

    3. 提供數據和行爲給其餘的展現組件或容器組件

    4. 調用Flux操做並將它們做爲回調函數提供給展現組件

    5. 每每是有狀態的,由於它們傾向於做爲數據源

    6. 一般使用高階組件生成,例如React Redux的connect(),Relay的createContainer()或Flux Utils的Container.create(),而不是手工編寫

    Example:UserPage、StoryContainer、 FollowedUserList...

  • 優勢
    1. 更好分離關注點。用這種方式寫組件,能夠更好理解你的應用程序和 UI
    2. 重用性高,展現組件能夠用於多個不一樣數據源。
    3. 展現組件就是你的調色板,能夠把他們放到單獨的頁面,在不影響應用程序的狀況下,讓設計師調整UI。
    4. 這迫使您提取諸如側邊欄,頁面,上下文菜單等「佈局組件」並使用this.props.children,而不是在多個容器組件中複製相同的標記和佈局。

如今,咱們已經知道了展現組件和容器組件的不一樣點。容器組件傾向於有狀態,展現組件傾向於無狀態,這不是硬性規定,由於容器組件和展現組件均可以是有狀態的。 一般,展現組件被容器組件包裹的,由於容器組件內能夠處理業務邏輯,將處理好的數據傳遞給展現組件。

當不重要或說很難分清時,不要把分離容器組件和展現組件當作教條, 若是你不肯定該組件是容器組件仍是展現組件是,就暫時不要分離,寫成展現組件吧。

高階組件

高階組件(HOC)是 React 中用於複用組件邏輯的一種高級技巧。HOC 自身不是 React API 的一部分,它是一種基於 React 的組合特性而造成的設計模式。

具體而言,高階組件是參數爲組件,返回值爲新組件的函數。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

組件是將 props 轉換爲 UI,而高階組件是將組件轉換爲另外一個組件。還記得上面說的容器組件嗎?高階函數和容器組件有類似之處,都能將相同的邏輯抽離出來複用,都須要包裹展現組件。

高階組件來源於JavaScript的高階函數—— 高階函數就是接受函數做爲輸入或者輸出的函數。高階組件僅僅只是是一個接受組件組做輸入並返回組件的函數。看上去並無什麼,那麼高階組件能爲咱們帶來什麼呢?首先看一下高階組件是如何實現的,一般狀況下,實現高階組件的方式有如下三種:

屬性代理

操做props

const HOC = (WrappedComponent) =>
    class WrapperComponent extends Component {
      render() {
        const newProps = {
          name: 'HOC'
        }
        return <WrappedComponent
          {...this.props}
          {...newProps}
        />;
      }
    }

咱們看到以前要傳遞給被包裹組件WrappedComponent的屬性首先傳遞給了高階組件返回的組件(WrapperComponent),這樣咱們就得到了props的控制權(這也就是爲何這種方法叫作屬性代理)。咱們能夠按照須要對傳入的props進行增長、刪除、修改(固然修改帶來的風險須要你本身來控制)。

抽象state

​ 屬性代理的狀況下,咱們能夠將被包裹組件(WrappedComponent)中的狀態提到包裹組件中,一個常見的例子就是實現不受控組件受控的組件的轉變。

class WrappedComponent extends Component {
    render() {
        return <input name="name" {...this.props.name} />;
    }
}

const HOC = (WrappedComponent) =>
    class extends Component {
        constructor(props) {
            super(props);
            this.state = {
                name: '',
            };

            this.onNameChange = this.onNameChange.bind(this);
        }

        onNameChange(event) {
            this.setState({
                name: event.target.value,
            })
        }

        render() {
            const newProps = {
                name: {
                    value: this.state.name,
                    onChange: this.onNameChange,
                },
            }
            return <WrappedComponent {...this.props} {...newProps} />;
        }
    }

上面的例子中經過高階組件,咱們將不受控組件(WrappedComponent)成功的轉變爲受控組件。

組合方式

const HoC = (WrappedComponent, LoginView) => {
    const WrappingComponent = () => {
        const {user} = this.props;  
        if (user) {
            return <WrappedComponent {...this.props} />
        } else {
            return <LoginView {...this.props} />
        }
    };
    return WrappingComponent;
};

上述代碼中有兩個組件,WrappedComponent 和 LoginView,若是傳入的props中存在user,則正常顯示的 WrappedComponent 組件,不然顯示 LoginView 組件,讓用戶去登陸。HoC 傳遞的參數能夠爲多個,傳遞多個組件定製新組件的行爲,例如用戶登陸狀態下顯示主頁面,未登陸顯示登陸界面;在渲染列表時,傳入 List 和 Loading 組件,爲新組件添加加載中的行爲。

反向繼承

反向繼承是指返回的組件去繼承以前的組件

操做props和state

在某些狀況下,咱們可能須要爲高階屬性傳入一些參數,那咱們就能夠經過柯里化的形式傳入參數,例如:

import React, { Component } from 'React';

const HOCFactoryFactory = (...params) => {
    // 能夠作一些改變 params 的事
    return (WrappedComponent) => {
        return class HOC extends Component {
            render() {
                return <WrappedComponent {...this.props} />;
            }
        }
    }
}

能夠經過下面方式使用:

HOCFactoryFactory(params)(WrappedComponent)

這種方式是否是很是相似於React-Redux庫中的connect函數,由於connect也是相似的一種高階函數。反向繼承不一樣於屬性代理的調用順序,組件的渲染順序是: 先WrappedComponent再WrapperComponent(執行ComponentDidMount的時間)。而卸載的順序也是先WrappedComponent再WrapperComponent(執行ComponentWillUnmount的時間)。

總結

寫到這裏算是結束了,很簡略地介紹了React的組件。文章雖短,但願能讓你大概瞭解React組件的一些特徵。最後總結一下:

  • React組件的聲明方式用兩種:函數組件和class組件。函數組件只有render一個返回函數,class組件能夠有生命週期和其餘業務邏輯處理函數

  • React組件從使用形式上分兩種:展現組件和容器組件。基本原則:容器組件負責數據獲取,展現組件負責根據props顯示信息

  • React高階組件就是參數爲組件,返回值爲新組件的函數。這樣作的好處就是能夠抽離一些相同的業務邏輯便於複用。做用相似Mixin,但避免了Mixin的反作用。

相關文章
相關標籤/搜索