美文网首页
2021-09-07-☀️☀️设计模式在react项目中的落地

2021-09-07-☀️☀️设计模式在react项目中的落地

作者: 沐深 | 来源:发表于2021-09-07 14:44 被阅读0次

1.订阅发布模式

一句话总结:创造事件,等待一个事件的发生。

比如小明(订阅者)去到报亭的大爷(发布者)那订了一份报纸,第二天报纸来了,大爷通知小明来取报纸。

众所周知,react中没有bus这个概念,如果遇到react中使用bus的情况,怎么办呢
首先 , 什么是bus, event Bus is only a Global Function Register;


let Bus = function () {
  this.cache = [];
};
// register
Bus.prototype.$on = function (handleEvent, fn) {
  for (let i = 0; i < this.cache.length; i++) {
    let [first] = Object.keys(this.cache[i]);
    if (first === handleEvent) {
      return;
    }
  }
  this.cache.push({
    [handleEvent]: fn
  });
};
// trigger
Bus.prototype.$emit = function (handleEvent) {
  const [first, ...rest] = Array.from(arguments);
  for (let i = 0; i < this.cache.length; i++) {
    if (this.cache[i][handleEvent]) {
      this.cache[i][handleEvent](...rest);
    }
  }
};
// cancel register
Bus.prototype.$off = function (handleEvent) {
  for (let i = 0; i < this.cache.length; i++) {
    let [first] = Object.keys(this.cache[i]);
    if (first === handleEvent) {
      this.cache.splice(i, 1);
      i = i - 1;
    }
  }
};

Example:


let bus = new Bus();
bus.$on("send",()=>{
  console.log("onSend")
})
bus.$emit("send") // onSend

相对而言,Vue就无脑多了,内部已经集成了。

import Vue from "vue";
Vue.prototype.bus = new Vue();

this.bus.$on('event',(record) => {
  // 返回参数
})

this.bus.$emit('event', param)

2.装饰器模式

js 本身没有装饰器 @语法糖,我们可以借助babel插件


npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -S

然后配置 babal-loader

 {
    test: /\.(tsx?|jsx)$/,
    exclude: /node_modules/,
    use: {
      loader: "babel-loader",
      options: {
        sourceMaps: true,
        presets: [
          ["@babel/preset-typescript", { isTSX: true, allExtensions: true}]
        ],
        plugins: [
          [
            "@babel/plugin-proposal-decorators",
            {
              "legacy": true
            }
          ],
          [
            "@babel/plugin-proposal-class-properties",
            {
              "loose": true
            }
          ]
        ]
      }
    }
  };

cool,我们接下来可以愉快的使用装饰者了,

Example:
在软件开发中,我们经常碰到,连续点击导致问题的情况,此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果.
已 debounce为例:
App.jsx


import debounce from "./debounce"
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  @debounce(500, false)
  handleOk() {
     this.post('xxx')
  }

  render() {
    return (
      <div className="App">
        <button onClick={handleOk.bind(this)}
        </button>
      </div>
    );
  }
}

debounce.js


function _debounce(func, wait) {
  let timeout;
  return function () {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    } else {
      timeout = setTimeout(() => {
        func.apply(this);
      }, wait);
    }
  };
}

const debounce = wait => {
  return function handleDescriptor(target, key, descriptor) {
    const callback = descriptor.value;
    if (typeof callback !== "function") {
      throw new SyntaxError("Only functions can be debounced");
    }
    const fn = _debounce(callback, wait);
    return {
      ...descriptor,
      value() {
        fn.apply(this);
      }
    };
  };
};
export default debounce;

3.代理模式

代理模式符合设计模式中单一原则,react HOC本身就是代理模式的变种。
作用:通过一个中间模块去调用别的模块,实现功能分离和组合。

Easy Example:


        /**************** 计算乘积 *****************/
        var mult = function(){
            var a = 1;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
              a = a * arguments[i];
            }
            return a;
        };

        /**************** 计算加和 *****************/
        var plus = function(){
            var a = 0;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
              a = a + arguments[i];
            }
            return a;
        };

        /**************** 创建缓存代理的工厂 *****************/
        var createProxyFactory = function( fn ){
            var cache = {};
            return function(){
              var args = Array.prototype.join.call( arguments, ', ' );
              if ( args in cache ){
                  return cache[ args ];
              }
              return  cache[ args ] = fn.apply( this, arguments );
            }
        };

        var proxyMult = createProxyFactory( mult ),
        proxyPlus = createProxyFactory( plus );

        alert ( proxyMult( 1, 2, 3, 4 ) );    // 输出:24
        alert ( proxyMult( 1, 2, 3, 4 ) );    // 输出:24
        alert ( proxyPlus( 1, 2, 3, 4 ) );    // 输出:10
        alert ( proxyPlus( 1, 2, 3, 4 ) );    // 输出:10

React HOC Example:

在一个软件中,有许多表格类,网页,每个网页都有搜索栏,表格页码,联动,如果每个页面我们都要组合这些组件,写搜索逻辑,it's waste,应该把这部分重复的内容放在高阶组建里;

hotc.js

import { getLocation } from "@haier/router";
const tableHotc = Hotc => {
  return class WrapComponet extends Hotc {
    constructor(props) {
      super(props);
      this.state = {
        data: [],
        // 搜索参数
        fetchListParams: {},
        // 页码,数量,可选页码数
        pagination: {
          pageSizeOptions: ["20", "50", "100"],
          pageNum: 1,
          pageSize: 20,
          total: 0
        }
      };
    }
    componentDidMount() {
      this.fetchList();
    }
    // 搜索之前参数的操作
    recombineUrlParams() {
      const res = {};
      for (let i of mergeURls) {
        if (i in location.params) {
          res[i] = !isNaN(location.params[i]) && location.params[i] ? Number(location.params[i]) : location.params[i];
        }
      }
      return res;
    }
    // 列表接口
    fetchList(
      params,
      pagination = { pageNum: this.state.pagination.pageNum, pageSize: this.state.pagination.pageSize }
    ) {
      params = params || this.fetchListParams || {};
      params = { ...params, ...this.recombineUrlParams() };
      params = this.recombination && this.recombination(params);
      params["pageNum"] = pagination.pageNum;
      params["pageSize"] = pagination.pageSize;
      this.fetchListParams = params;
      this.fetchListApi(params).then(res => {
        // 请求成功之后,页码改变,数据分配
        if (res.success && res.data) {
          this.setState({
            data: res.data.list || res.data
          });
        }
        const total = res.data.total;
        const pageSize = pagination.pageSize;
        const obPagination = Object.assign({}, this.state.pagination, { total, pageSize });
        this.setState({
          pagination: obPagination
        });
      });
      this.fetchListCallback && this.fetchListCallback(params);
    }
    // 搜索动作
    handleChange(pagination) {
      const { current, pageSize } = pagination;
      const pageNum = current;
      const obPagination = Object.assign({}, this.state.pagination, { pageNum });
      this.setState({ pagination: obPagination });
      this.fetchList(this.fetchListParams, {
        pageNum: current,
        pageSize
      });
    }
    render() {
      return (
        <div>
          <Hotc
            {...this.props}
            state={this.state}
            fetchList={this.fetchList.bind(this)}
            handleChange={this.handleChange.bind(this)}
          />
        </div>
      );
    }
  };
};

export default tableHotc;

table.js

import tableHotc from "~/hotc/tableHotc";
class Table extends React.Component {
  state = {
    loadingTable: false,
    selectedRowKeys: [],
    data: []
  };
  childRef = React.createRef();
  tableParam = {
    scroll: { x: 1500, y: 400 },
    bordered: true,
    columns: [
      {
        title: "商品SKU",
        dataIndex: "sku",
        key: "sku",
        width: 200,
        fixed: "left",
        ellipsis: {
          showTitle: true
        }
      },
      {
        title: "商品名称",
        dataIndex: "name",
        key: "name",
        width: 200,
        ellipsis: {
          showTitle: true
        }
      },
      {
        title: "规格",
        dataIndex: "spec",
        key: "spec",
        width: 100
      },
      {
        title: "重量",
        dataIndex: "weight",
        key: "weight",
        width: 100
      }
      }
    ]
  };

  fetchListApi = productList;
  recombination(params: Object) {
    params.state = "02,10";
    return params;
  }
  render() {
    const { data, pagination } = this.props.state;
    return (
      <div>
        <Card>
          <Table
            {...(this.tableParam as any)}
            dataSource={data}
            pagination={pagination}
            onChange={this.props.handleChange.bind(this)}
          />
        </Card>
      </div>
    );
  }
}

// 通过访问table, 调用tableHotc,实现功能代理。
export default tableHotc(Table);

table.js 通过this.props.xxx调用 hotc.js中的属性方法。
hotc.js 通过this.xxx调用 table.js中的属性方法。

相关文章

网友评论

      本文标题:2021-09-07-☀️☀️设计模式在react项目中的落地

      本文链接:https://www.haomeiwen.com/subject/ibzuwltx.html