關於 Decorator 究竟是 ES 6 引入的仍是 ES 7 引入的我也不是很明白了,兩種說法都有,這種問題懶得糾結了……在用的時候發現這個東西很好用,日常用處可能不大,可是結合 React 就很好使了。接下來就講一講。react
我搭建了一個 React 開發環境,結合 babel 的插件——babel-plugin-transform-decorators-legacy
一塊兒使用,這個插件可讓你寫 Decorator。git
GitHub 地址:https://github.com/zhongdeming428/HOC
github
能夠經過以下命令克隆:redux
$ git clone https://github.com/zhongdeming428/HOC.git
複製代碼
克隆下來之後就能夠嘗試啦!bash
裝飾器自己就是一個函數,使用起來挺簡單,無非就是修飾類或者類的函數。使用 @
調用,扔在要修飾的類或者類方法前面就能夠了。可是在修飾類和類函數的時候又有細微的差別。babel
class A {
@sayB
sayA() {
console.log('a');
}
}
function sayB(target, name, descriptor) {
// ...
}
複製代碼
在使用裝飾器裝飾類函數的時候,能夠接受三個參數。第一個是要修飾的對象,第二個是修飾的屬性名,第三個是屬性描述符。能夠在我搭建的項目中進行嘗試。函數
在用裝飾器裝飾類的時候,只可以接受一個參數——target。這區別於上面的狀況:優化
@APlus
class A {
}
function APlus(target, name, descriptor) {
// ... 打印一下能夠發現 name、descriptor 是 undefined。
}
複製代碼
另外,裝飾器還能夠接受參數,返回一個符合裝飾器規範的新函數便可,這樣又能夠對裝飾器的裝飾行爲進行定製了。好比:ui
@attach2Prop({ name: 'A' })
class A {
}
@attach2Prop({ name: 'B' })
class B {
}
function attach2Prop(obj) {
return function(target) {
target.prototype.$data = obj;
}
}
console.log((new A()).$data.name);
console.log((new B()).$data.name);
複製代碼
結果會輸出 A
和 B
。spa
這就就能夠用同一個裝飾器實現不一樣行爲的裝飾了。
那麼結合 React 有什麼妙用呢?
以往在使用 react-redux 時,在定義好 UI 組件後,還要定義容器組件:
class UIComponent extends React.Component {
}
const ContainerComponent = connect(mapState2Props, mapDispatch2Props)(UIComponent);
export default ContainerComponent;
複製代碼
有了裝飾器以後:
@connect(mapState2Props, mapDispatch2Props)
class UIComponent extends React.Component {
}
export default UIComponent;
複製代碼
這樣用簡化的代碼達到了一樣的效果,還省去了給容器組件命名的麻煩……代碼也更加的整潔。
上一小節中的容器組件實際上就是一個高階組件,可是咱們本身有時候也要定義一些高階組件,實現代碼的更高層次的複用。
例如:咱們作了一個組件庫,裏面有一部分的組件是有一個功能特徵的,那就是能夠拖拽;又好比咱們作的移動端組件,須要實現一個左滑刪除功能。咱們須要給每種具備這個特徵的組件寫一遍拖拽或者左滑刪除邏輯嗎?
顯然是否認的,咱們能夠實現一個純邏輯組件,而非 UI 組件,它的功能就是使得你的 UI 組件具備某種特定功能。好比上面提到的左滑刪除或者拖拽。
這個純邏輯組件就能夠是一個裝飾器,是一個高階組件。
在我搭建的開發環境中,就實現了這樣一個簡單的高階組件,讓你的 UI 組件在鼠標滑入時顯示爲一隻手。
裝飾器代碼以下:
// src/decorators/CursorPointer.js
import React from 'react';
export default Component => class extends React.Component {
render() {
return <div style={{cursor: 'pointer', display: 'inline-block'}}> <Component/> </div>
}
}
複製代碼
這個裝飾器(高階組件)接受一個 React 組件做爲參數,而後返回一個新的 React 組件。實現很簡單,就是包裹了一層 div,添加了一個 style,就這麼簡單。之後全部被它裝飾的組件都會具備這個特徵。
使用這個裝飾器:
import React from 'react';
import Clickable from '../decorators/CursorPointer';
@Clickable
class ClickablePanel extends React.Component {
render() {
return <div className="panel"> </div>
}
}
export default ClickablePanel;
複製代碼
將裝飾器與高階組件相結合,能夠大大優化你的 React 代碼!