1.订阅发布模式 一句话总结:创造事件,等待一个事件的发生。
比如小明(订阅者)去到报亭的大爷(发布者)那订了一份报纸,第二天报纸来了,大爷通知小明来取报纸。
众所周知,react中没有bus这个概念,如果遇到react中使用bus的情况,怎么办呢 首先 , 什么是bus, event Bus is only a Global Function Register;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 let Bus = function ( ) { this .cache = []; }; 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 }); }; 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); } } }; 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:
1 2 3 4 5 6 7 let bus = new Bus ();bus.$on("send" ,()=> { console .log ("onSend" ) }) bus.$emit("send" )
相对而言,Vue就无脑多了,内部已经集成了。
1 2 import Vue from "vue" ;Vue .prototype .bus = new Vue ();
1 2 3 4 5 6 this .bus .$on('event' ,(record ) => { }) this .bus .$emit('event' , param)
2.装饰器模式 js 本身没有装饰器 @语法糖,我们可以借助babel插件
1 2 3 npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class -properties -S
然后配置 babal-loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 ) ); alert ( proxyMult ( 1 , 2 , 3 , 4 ) ); alert ( proxyPlus ( 1 , 2 , 3 , 4 ) ); alert ( proxyPlus ( 1 , 2 , 3 , 4 ) );
React HOC Example:
在一个软件中,有许多表格类,网页,每个网页都有搜索栏,表格页码,联动,如果每个页面我们都要组合这些组件,写搜索逻辑,it’s waste,应该把这部分重复的内容放在高阶组建里;
hotc.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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 > ); } } export default tableHotc (Table );
table.js 通过this.props.xxx 调用 hotc.js中的属性方法。 hotc.js 通过this.xxx 调用 table.js中的属性方法。