《深刻淺出React和Redux》(3) - Export & Import, 高階組件

Export & Import

export能夠導出函數、class等,有兩種導出方式:react

  • 默認導出(default export)
  • 命名導出(named export)

默認導出(default export)

default export不須要指定名稱,但每一個文件中只能有一個default exportgit

// 導出純值或表達式結果
export default 1;
const a = 1;
export default a;

// 導出函數
export default function(){
  ...
}

// 導出類
export default class {
  ...
};

命名導出(named export)

named export會指定導出名稱,同一個文件中能夠導出多個github

// 聲明變量的同時導出
export let a =1;

// 導出函數
export function func1(){
  ...
}

// 統一導出
const a = 1;
const obj1 = {name: 'obj1'};

export {a, obj1};

// 導出時修更名稱
const obj1 = {name: 'obj1'};

export {obj1 as newObjName};

導入

導入的方式隨導出方式的變化而變化chrome

導入default export並賦予名稱

export default function(){
  ...
}

import fun1 from './****.js';

導入named export

這時須要使用解構語法,還能夠在導入時重命名,或者同時導入default export與named export。redux

...
export {a, obj1};
// 解構導出
import {a, obj1} from './****.js';

// 導入時重命名
import {a, obj1 as newObject} from './****.js';

還可使用*所有導入,此時必須使用as指定名稱數組

export {a, obj1};

import * as name from './****.js';
// 使用
name.a
name.obj1

同時導入default export與named export:瀏覽器

export default function()...
export function func1()...
export function func2()...

import defaultFunc, * as named from './****.js';

前一部分首先導出的時default export,後面導出named export並用as指定名稱。app

開發輔助工具

React Devtools

能夠檢視react組件的樹形結構,以及每一個節點上的內容,安裝chrome插件便可使用。函數

Redux Devtools

能夠檢視redux數據流,記錄每一次action以及store的對應變化,能夠將store狀態跳躍到任意一個歷史狀態(時間旅行)。
安裝chrome插件後還須要一些代碼支持,由於redux devtools的工做原理是截獲全部應用中Redux Store的動做和狀態變化,因此必須經過Store Enhancer在Redux Store中加入鉤子。因此須要修改Store.js:工具

const storeEnhancers = 
  (window && window.devToolsExtension) ? window.devToolsExtension() : (f) => f;

export default createStore(reducer, {}, storeEnhancers);

這裏添加的storeEnhancers的做用就是讓redux devtools可以截獲當前應用的store狀態,安裝redux devtools後瀏覽器全局就會有window.devToolsExtension對象,但也須要作好兼容,在沒有這個工具的瀏覽器運行時,這個enhancer是一個什麼都不作的函數。

高階組件

高階組件(Higher Order Component,HOC)是使用react的一種模式,用於加強現有組件的功能。
簡單來講,一個高階組件就是一個函數,這個函數接受一個組件做爲輸入,而後返回一個新的組件做爲結果,並且,返回的新組件擁有了輸入組件所不具備的功能。
高階組件的做用:

  • 重用代碼,好比前面使用Redux時的容器組件部分是能夠複用的;
  • 修改現有React組件的行爲

高階組件按照實現方式又能夠分爲

  • 代理方式的高階組件
  • 繼承方式的高階組件

代理方式的高階組件

新組件扮演的角色是傳入參數組件的一個「代理」,在新組件的render函數中,把被包裹組件渲染出來,除了高階組件本身要作的工做,其他功能全都轉手給了被包裹的組件。
這種組織方式下,能夠用來修改被包裹組件的prop、style等。紹:
操縱prop
好比下面的addNewProps組件,能夠爲WrappedComponent添加屬性:

const addNewProps = (WrappedComponent, newProps) => {
  return class WrappingComponent extends React.Component {
    render() {
      return <WrappedComponent {...this.props} {...newProps} />
    }
  }
}

嚴格來講,這裏的addNewProps是一個組件工廠,但通常也直接將其看做組件。

抽取狀態
相似前面redux部分本身寫的容器組件,以及react-redux提供的connect函數生成的組件。
包裝組件
好比能夠用來組合多個react組件,或者爲被包裝的組件添加統一的樣式。

繼承方式的高階組件

前面的addNewProps用繼承方式實現以下:

function addNewProps(WrappedComponent) {
  return class NewComponent extends WrappedComponent {
    render() {
      const {newProps} = this.props;
      this.props = {...this.props, newProps};
      return super.render();
    }
  };
}

最大的區別在於return super.render();

在代理方式下WrappedComponent經歷了一個完整的生命週期,但在繼承方式下super.render只是一個生命週期中的一個函數而已;在代理方式下產生的新組件和參數組件是兩個不一樣的組件,一次渲染,兩個組件都要經歷各自的生命週期,在繼承方式下二者合二爲一,只有一個生命週期。因此繼承方式的高階組件能夠用來操縱組件的生命週期函數,好比下面的組件經過shouldComponentUpdate控制只有useCache=false纔會從新渲染:

const cacheHOC = (WrappedComponent) => {
  return class NewComponent extends WrappedComponent {
    shouldComponentUpdate(nextProps, nextState) {
      return !nextProps.useCache;
    }
  }
}

以函數爲子組件

高階組件與原組件之間是父子關係,他們的通訊要用到props,這也就意味着高階組件有個缺點,就是原組件的props中須要包含高階組件傳遞的字段,也就是說,能不能把一個高階組件做用於某個組件X,要先看一下這個組件X是否是可以接受高階組件傳過來的props。
高階組件這種要求參數組件必須和本身有契約的方式,會爲自身的使用帶來極大的侷限。
而「以函數爲子組件」的模式就是爲了克服這種侷限而生的。
前文的addNewProps用「以函數爲子組件」實現的話,就是:

const loginUser='fake user';
class AddNewProps extends Component{
  render(){
    const user = loginUser;
    return this.props.children(user);
  }
}

與高階組件那種組件工廠不一樣,這裏AddNewProps自己就是一個組件,經過this.props.children來調用原組件,並把值傳遞過去,原組件必須是一個函數。
AddNewProps的使用:

// 讓一個組件直接顯示user
<AddNewProps>
  {(user)=> <div>{user}</div>}
</AddNewProps>

// 將user傳遞給另外一個組件
<AddNewProps>
  {(user)=> <Foo user={user}>}
</AddNewProps>

// 將user傳遞給另外一個組件,支持不一樣的prop名稱
<AddNewProps>
  {(user)=> <Foo anotherProp={user}>
</AddNewProps>

從「以函數爲子組件」的第三個例子能夠看到,這種模式很是靈活,它徹底能夠應付prop不一樣名的狀況。做爲子組件的函數成爲了鏈接父組件與底層組件的橋樑,並且這個函數內部能夠包含各類邏輯。

參考書籍

《深刻淺出React和Redux》 程墨
徹底解析 JavaScript import、export

相關文章
相關標籤/搜索