前端设计模式

前端设计模式

目录

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

  1. 建造者模式

相比工厂模式, 建造者模式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. 备忘录模式

 

原文地址:https://www.cnblogs.com/connie313/p/13871593.html