在react-ts項目中實現一個自定義裝飾器

安裝依賴

npm i @babel/plugin-proposal-decorators -D

配置.babelrc

{
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ]
  ]
}

實現一個簡單的日誌打印方法

// TestDecorator.tsx
function Log(target: any, name: string, descriptor: any): any {
  // 這裏是緩存原始值,用於在攔截器最後運行原始方法
  const oldValue = descriptor.value;

  // 實現一個攔截器,在攔截器中實現日誌打印,並在最後返回舊值的結果
  descriptor.value = function log() {
    // 實現日誌打印
    console.log(
      `%c[${logid}]調用了方法:${name},參數爲:`,
      "color: orange;",
      Array.from(arguments)
    );
   	// 調用原始方法實現原始邏輯
    return oldValue.apply(this, arguments);
  };
	// 最後返回一個新的描述器
  return descriptor;
};

export default Log;

調用裝飾器

// TestComponent.tsx
import * as React from "react";
import Log from "./TestDecorator";

// 屬性接口
export interface IProps {
  name: string;
  age?: number;
}

class TestComponent extends React.Component<IProps, object> {
  // 狀態
  public state: any = {
    sum: 0
  };
	// 調用裝飾器
  @Log
  public add(a: number, b: number): void {
    this.setState({
      sum: a + b
    });
  }

  public render() {
    const { name, age = 0 } = this.props;
    const { sum } = this.state;
    return (
      <div
        onClick={() => {
          this.add(sum, age);
        }}
      >
        你好,我叫:{name},計數器:{sum},{age ? `今年${age}歲了` : ""}
        {this.props.children}
      </div>
    );
  }
}

export default TestComponent;

實現一個可傳遞參數的裝飾器

// TestDecorator.tsx
// 定義一個修飾器
function Logger(logid: string, handler: (name: string) => void) {
  // 採用高階函數的方式進行封裝,可接受額外的參數,如外層可接受logid和觸發的回調,返回的方法纔是真正對類或者方法處理的方法
  return function Log(target: any, name: string, descriptor: any): any {
    // 這裏是緩存舊的方法,也就是上面那個add()原始方法
    const oldValue = descriptor.value;

    // 這裏修改了方法,使其作用變成一個打印函數
    // 最後依舊返回舊的方法,真是巧妙
    descriptor.value = function log() {
      console.log(
        `%c[${logid}]調用了方法:${name},參數爲:`,
        "color: orange;",
        Array.from(arguments)
      );
      // 觸發回調原函數
      const args = Array.prototype.slice.call(arguments);
      args.unshift(name);
      handler.apply(this, args);
      return oldValue.apply(this, arguments);
    };

    return descriptor;
  };
}

export default Logger;


// TestComponent.tsx
import * as React from "react";
import Log from "./TestDecorator";

export interface IProps {
  name: string;
  age?: number;
}

class TestComponent extends React.Component<IProps, object> {
  public state: any = {
    sum: 0
  };
  
  @Log("日誌打印", (name, ...args) => {
    console.log(`觸發回調[${name}]:`, args);
  })
  public add(a: number, b: number): void {
    this.setState({
      sum: a + b
    });
  }

  public render() {
    const { name, age = 0 } = this.props;
    const { sum } = this.state;
    return (
      <div
        onClick={() => {
          this.add(sum, age);
        }}
      >
        你好,我叫:{name},計數器:{sum},{age ? `今年${age}歲了` : ""}
        {this.props.children}
      </div>
    );
  }
}

export default TestComponent;


這樣,我們就已經實現了一個簡單的日誌打印的裝飾器了。從上面的實例我們可以看出,其實裝飾器本質上就是React中的HOC,即高階組件,這個高階函數接收一個方法、類、變量等參數,在內部進行一定的出處理後返回一個新的方法、類、變量,以此來達到我們的要對目標方法、類、變量進行預處理的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章