React是前端組件化開發的開山鼻祖,這種開發方式完全解決了的前端組件複用的痛點。今天,就來研究一下React組件開發。html
前端同窗通常都會從Vue入門,由於Vue使用的<template>
的組件開發方式讓前端人員更容易的平滑過渡。Vue的組件很簡單,通常來講,一個.vue
文件就是一個組件。就像webpack
的模塊化開發,一個文件就是一個組件。咱們在使用組件時也很容易,經過 ES6 的import
導入、註冊(components),就能夠直接使用。前端
上面簡單說了Vue的組件模式,其實,React的使用方式也差很少,畢竟是借鑑它的組件思想。最明顯的差異就是聲明方式,React用的是JSX語法,這讓React變的很靈活。固然,使用方式的靈活也意味着使用者的腦子也要足夠的靈活,從而增長了入門的難度。玩的6的人是真喜歡,未入門的是真不能理解爲啥把html標籤寫到js裏面呢。vue
好了,上面扯了那麼多,下面就來一探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> ) } }
注意:性能優化
React的組件分紅兩種類型:展現組件和容器組件。 將組件分紅這兩類以後,你會發現它們容易更被重用和理解。一般,展現組件負責展現內容,容器組件負責處理業務邏輯。是否是很像上面的函數組件和class組件的區別。
容器組件和展現組件名詞都來自於redux中文文檔。
展現組件
關注頁面的展現效果(外觀)
內部能夠包含展現組件和容器組件,一般會包含一些本身的DOM標記和樣式(style)
一般容許經過this.props.children方式來包含其餘組件
對應用程序的其餘部分沒有依賴關係,例如Flux操做或store
不用關心數據是怎麼加載和變更的
只能經過props的方式接收數據和進行回調(callback)操做
不多擁有本身的狀態,即便有也是用於展現UI狀態的
會被寫成函數式組件除非該組件須要本身的狀態,生命週期或者作一些性能優化
Example:Page、Header、Sidebar、List、UserInfo...
容器組件
關注應用的是如何工做的
內部能夠包含容器組件和展現組件,但一般沒有任何本身的DOM標記 (除了一些包裝用的 div) ,而且從不具備任何樣式
提供數據和行爲給其餘的展現組件或容器組件
調用Flux操做並將它們做爲回調函數提供給展現組件
每每是有狀態的,由於它們傾向於做爲數據源
一般使用高階組件生成,例如React Redux的connect(),Relay的createContainer()或Flux Utils的Container.create(),而不是手工編寫
Example:UserPage、StoryContainer、 FollowedUserList...
如今,咱們已經知道了展現組件和容器組件的不一樣點。容器組件傾向於有狀態,展現組件傾向於無狀態,這不是硬性規定,由於容器組件和展現組件均可以是有狀態的。 一般,展現組件被容器組件包裹的,由於容器組件內能夠處理業務邏輯,將處理好的數據傳遞給展現組件。
當不重要或說很難分清時,不要把分離容器組件和展現組件當作教條, 若是你不肯定該組件是容器組件仍是展現組件是,就暫時不要分離,寫成展現組件吧。
高階組件(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的反作用。