前端设计模式
目录
1. 观察者模式 1
2. 策略模式 3
3. 单例模式 4
4. 工厂模式 5
5. 中间人模式 5
6. 代理模式 5
7. 装饰器模式 5
8. 外观模式 7
9. 工厂模式 7
10. 建造者模式 7
11. 享元模式 8
12. 职责链 9
13. 适配器模式 10
14. 模板方法 10
15. 备忘录模式 10
1. 观察者模式
解决层层调用的耦合问题
class A {
logMessage(msg){
console.log(123, msg);
}
}
class B {
a = new A();
// may influence A
findA(msg){
this.a.logMessage(msg);
}
}
class C{
say2A(msg){
(new B()).findA(msg);
}
}
(new C()).say2A('hi'); // 有层层调用的耦合问题
const {EventEmitter} = require('./01');
const evet= new EventEmitter();
evet.$on('message', (args)=>{
(new A()).logMessage(args);
});
evet.$emit('message', 'say2A'); //这样解耦
一个EventEmit例子
React源码:
https://github.com/facebook/react/blob/master/packages/react-devtools-shared/src/events.js
Vue源码:https://github.com/vuejs/vue/blob/dev/src/core/instance/events.js
export class EventEmitter{
callbacks:Map<string, Array<Function>> = new Map();
$on(name:string, fn:Function){
if(!this.callbacks.has(name)){
this.callbacks.set(name, [fn]);
} else {
const fns = this.callbacks.get(name)
fns.push(fn);
}
}
$emit(name:string, ...args) {
if(this.callbacks.has(name)){
const fns = this.callbacks.get(name);
fns.forEach((fn)=>{
try{
fn.apply(null, args);
} catch(error){
}
});
}
}
$off(name:string) {
this.callbacks.delete(name);
}
}
const event1 = new EventEmitter();
event1.$on('event1', (msg)=>{
console.log(`from event1 ${msg}`)
});
event1.$on('event1', (msg)=>{
console.log(`from event11 ${msg}`)
});
event1.$emit('event1', 'hello');
setTimeout(()=>{
event1.$emit('event1', 'hello');
}, 1000)
event1.$on('event1', (msg)=>{
console.log(`from event111 ${msg}`)
});
2. 策略模式
解决条件式调用的问题
例子是使用策略来重构组件
import React from 'react';
import { View, Button, Input, RadioGroup, Radio } from '@tarojs/components'
const Form = (props) => {
let {option} = props;
const strategies = {
'input': (options, index)=>{
return <Input value={options.value} key={`form_${index}`} ref={options.ref}/>;
},
'select': (options, index)=>{
const {list, value, ref} = options;
return (<RadioGroup onChange={handleChange} ref={ref} value={value} key={`form_${index}`}>
{options.list.map(value=>{
return <Radio value={value}>{value}</Radio>
})}
</RadioGroup>);
}
};
option = option.map(item=>{
item['ref'] = React.createRef();
return item;
});
const handleChange = (e)=>{
console.log(e);
}
const handleClick = (e)=>{
const result = props.option.map((item, index)=>{
const key = item.key||index;
return {key, value:item.ref?.current?.value};
});
props.onsubmit(result);
}
return (<View>
{option.map((item, index)=>{
return strategies[item.type](item, index);
})}
<Button onClick={handleClick}>提交</Button>
</View> );
}
export default Form;
3. 单例模式
Element例子: https://github.com/ElemeFE/element/blob/dev/packages/message-box/src/main.js
button id="alert-btn">Click</button>
<script>
function createEle(){
const div = document.createElement('div');
div.innerHTML = 'hello';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
let instance;
function createSingleElemnet(fn){
return ()=>{
if(!instance) instance = fn.apply(null, arguments);
return instance;
}
}
document.getElementById('alert-btn').addEventListener('click', ()=>{
const div = createSingleElemnet(createEle)();
div.style.display = 'block';
});
4. 工厂模式
5. 中间人模式
减少耦合
Vux, store
6. 代理模式
为一个对象提供一个代用品, 以便控制对他的访问
之前的节流和防抖
//类似模块
const imgFun = (function(){
const imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src){
imgNode.src= src;
}
}
})();
const proxyImage = (function(){
const img = new Image();
img.onload = function(){
imgFun.setSrc(this.src);
}
return {
setSrc: function(src){
imgFun.setSrc('./loading.png');
img.src= src;
}
};
})();
proxyImage.setSrc('/pic.png');
7. 装饰器模式
React @Connect HOC
运行中执行其他的操作
V-checkbox也算是装饰, 装饰了 原生的checkbox
import React from 'react';
const withLog = (Component) => {
class NewComponent extends React.Component {
componentWillMount(){
console.time('render');
console.log('ready');
}
render(){
return <Component {...this.props}></Component>
}
componentDidMount(){
console.time('render');
console.log('ready ok');
}
}
return NewComponent;
}
export default withLog;
@withLog
class Index extends Component {
Function.prototype.before = function(beforeFn){
const _self = this;
return function(){
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function(afterFn){
const _self = this
return function(){
const _ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return _ret;
}
}
//装饰器模式不一i的那个是@, 只要不改变函数, 就算装饰器
let hello = function(){
console.log('hello');
}
hello = hello.before(()=>console.log('before'));//需要赋值
hello = hello.after(()=>console.log('after')); //需要赋值
hello();
8. 外观模式
表面上一个函数, 其实内部支持多格式, 透露统一的api。
myEvent = {
stop: function(e){
if(typeof e.preventDefault() === 'function'){
e.preventDefault();
}
if(typeof e.stopPropagation() == 'function'){
e.stopPropagation();
}
// for IE
if(typeof e.returnValue === 'boolean'){
e.returnValue = false;
}
if(typeof e.cancelBubble === 'boolean'){
e.cancelBubble = true
}
},
addEvent(dom, type, fn) {
if(dom.addEventListener){
dom.addEventListener(type, fn, false);
} else if(dom.attachEvent){
dom.attachEvent(`on${type}`, fn);
} else {
dom['on'+type] = fn;
}
}
}
9. 工厂模式
批量生产用例, 目的也是为了减少用例间的互相影响
https://github.com/ElemeFE/element/blob/dev/packages/notification/src/main.js
- 建造者模式
相比工厂模式, 建造者模式i参与了创建过程
Function Person(){
Const _person = new Human();
_person.dance = new Dance();
Return _person;
}
Const person = new Person();
11. 享元模式
Flyweight
// waster memory
function Model(gender, cloth){
this.gender = gender;
this.cloth = cloth;
}
Model.prototype.print = function(){
console.log(`${this.gender}_${this.cloth}`);
}
for(let i=0;i<50;i++){
const model = new Model('male', `cloth_${i}`);
model.print();
}
// 享元
function Model(gender, cloth){
this.gender = gender;
this.cloth = cloth;
}
const model = new Model();
for(let i=0;i<50;i++){
model.cloth = `cloth_${i}`
model.print();
}
Vuem message有四种,可以 将四种的属性保存为内部变量
New Vue({
Data: {
Fontstyle: 123
},
Method:
{
Message1: {
This.fontstyle = 12;
Docuemn.append();
}
}
})
12. 职责链
把请求放在一个链上, 每一步拿上一步结果, 不合适继续走
Koa
App.Use(async (ctx, next)=>{
//Do staff
Await next();
Ctx.set(‘x-response’, 123)
})
App.use...
或者
const { nextTick } = require("@tarojs/taro");
const order500 = ()=>{
if(nodetpye=1){
console.log('do 500');
return;
} else {
return 'nextTick';
}
}
const order200 = ()=>{
if(nodetpye=2){
console.log('do 200');
return;
} else {
return 'nextTick';
}
}
function Chain(fn){
this.fn = fn;
this.next = null;
}
Chain.prototype.setNext =function(next){
return this.next = next;
}
Chain.prototype.passRequest=function(){
let ret = this.fn.apply(this, arguments);
if(ret ==='nextTick'){
return this.next && this.next.passRequest.apply(this.next, arguemtns)
}
return ret;
}
const chainorder500 = new Chain(order500);
const chainorder200 = new Chain(order200);
chainorder500.setNext(chainorder200);
chainorder500.passRequest();
13. 适配器模式
解决接口不一致的
Const Baidumap = {
Show: ()=>{}
}
Tecentmap => {
Display: ()=>{}
}
adaterTencent => {
Show: ()=>{
Return Tecentmap .display
}
}
14. 模板方法
15. 备忘录模式