• (前端)「状态」设计模式在项目开发中的应用


    1. 事件起因

      最近在做一个关于星座的移动端项目,想实现这样一个需求,每次切换导航栏NavBar item时,都会使下面的页面级组件TodayView更改背景色样式(如图1到图2,导航栏从双鱼座切换到处女座,下面页面级组件的背景颜色由黄色切换至粉色)。

     

     

               图1                     图2

     

      如果利用传统的办法,在点击事件的事件处理函数中进行多层条件语句判断,代码如下:

    function handleClick(e) {
    if(e.target.innerText === '双鱼座‘) {
    store.state.vnode.style.backgroundColor = 'yellow';
    }else if(e.target.innerText === '处女座‘){
    store.state.vnode.style.backgroundColor = 'pink';
    }else if(...){
    ...
    }
    }

      我们大概有12个星座,那就要写12层条件判断语句,并且每一层的判断以及做的事情其实是一样的,如此代码会十分冗余。因此考虑「状态」设计模式。

     

    2. 解决方案

      利用「状态」设计模式。

      大致思路: 每当切换NavBar item时,都给关于NavBar的一个状态类添加状态,例如切换到双鱼座时,就给这个状态类添加一个状态为“双鱼座”,然后执行该状态对应的动作方法,此方法内就是对页面级组件DOM的背景色修改为特定的颜色。

      先封装状态类,代码如下: 

      src/NavBarState/index.ts:

    // CONSTELLATIONS是我定义的枚举类型, 里面的每个枚举都对应了一个星座, 并且我把该枚举就作为我要添加的状态名,以及该状态的对应的动作方法的名字
    import { CONSTELLATIONS } from '../typings/index';
    const NavBarState = function(vnode: any) {
    // currentStates里面存储所有的状态(key), 对应的值为true(value)则表示可以执行该状态对应的动作方法
    let currentStates = {}; // key为动作方法的函数名, 也是状态
    // statesAction中是 状态-动作方法 的映射关系, 状态名即为动作方法名, 当然也可以写成 '状态名': function 动作方法名(){...}
    const statesAction = {
    // 如果该状态为'双鱼座', 就将虚拟DOM的背景色改为黄色
    [CONSTELLATIONS.m1]() {
    vnode.style.backgroundColor = 'yellow';
    },
    [CONSTELLATIONS.m2]() {
    vnode.style.backgroundColor = 'pink';
    },
    [CONSTELLATIONS.m3]() {
    vnode.style.backgroundColor = 'purple';
    },
    [CONSTELLATIONS.m4]() {
    vnode.style.backgroundColor = 'green';
    },
    [CONSTELLATIONS.m5]() {
    vnode.style.backgroundColor = 'red';
    },
    [CONSTELLATIONS.m6]() {
    vnode.style.backgroundColor = 'orange';
    },
    [CONSTELLATIONS.m7]() {
    vnode.style.backgroundColor = 'skyblue';
    },
    [CONSTELLATIONS.m8]() {
    vnode.style.backgroundColor = 'blue';
    },
    [CONSTELLATIONS.m9]() {
    vnode.style.backgroundColor = '#FFBB00';
    },
    [CONSTELLATIONS.m10]() {
    vnode.style.backgroundColor = '#880000';
    },
    [CONSTELLATIONS.m11]() {
    vnode.style.backgroundColor = '#D28EFF';
    },
    [CONSTELLATIONS.m12]() {
    vnode.style.backgroundColor = '#FFC8B4';
    }
    }
    // Action中封装了2个方法, addState用于给状态类NavBarState添加进状态, goes用于执行状态类现有状态的动作方法
    const Action = {
    addState(...args: any[]) {
    // eslint-disable-next-line prefer-rest-params
    currentStates = {};
    for(const key in args) {
    currentStates[args[key]] = true;
    }
    // 把调用者return出去是为了方便后续的链式调用, 例如NavBarState(vnode).addState('双鱼座').goes()
    return this;
    },
    goes() {
    for(const key in currentStates) {
    if(currentStates[key] === true) {
    statesAction[key] && statesAction[key]();
    }
    }
    return this;
    }
    }
    return {
    addState: Action.addState,
    goes: Action.goes
    }
    }
    export default NavBarState;

      

      我们在组件中试一下:

      src/components/NavBar/index.vue:

    /**
    * 当切换NavBar item时子组件(NavBar/Item.vue)会给父组件发布事件, 并带上自己的index过去
    * 父组件监听这个事件触发的回调就是changeCurNavId, 更新记录标记curIdx
    */
    const changeCurNavId = (idx: number): void => {
    curIdx.value = idx;
    }
    /*
    * 当监听到curIdx变化时, 说明切换了Item, 立刻给状态类NavBarState添加进状态('双鱼座'), 然后执行该状态对应的动作方法, 进而修改虚拟DOM的样式
    * 这里有个问题就是, 如果我同步给NavBarState添加状态并执行的话, 效果是不会出来的, 必须异步添加状态才可以。
    * 我猜想可能是因为在外壳组件App.vue中, NavBar组件是先于页面级组件TodayView渲染的, 而我将TodayView的虚拟DOM设置进store中
    是在TodayView组件中完成的, 也就是说当NavBar组件渲染时store.state.todayDom还没有值, 为null, 因此赋予其状态并修改其样式
    自然是无效的。
    */
    watch(curIdx, () => {
    store.commit(actionTypes.SET_CONSNAME, navCons[curIdx.value].cons);
    // 在today中已经改变了todayDomRef, 接下来给这个todayDomRef在切换curIdx时赋予不同样式; 并延迟更改NavBar的状态
    setTimeout(() => {
    NavBarState(store.state.todayDom).addState(navCons[curIdx.value].cons).goes();
    })
    }, { immediate: true });

     

      以上,就是我关于「状态」设计模式在项目开发中的一些应用场景,感谢阅读!

     

      参考书籍: 《JavaScript设计模式》 张容铭 著

     

  • 相关阅读:
    npm与包
    云原生之旅 - 2)Docker 容器化你的应用
    JSON数据格式
    阿里邮箱/网易邮箱个人版设置POP3使用
    python从入门到出家(四)条件语句
    Flume分布式日志采集
    微信小程序入门
    强化学习实践(一)Gym介绍
    TDSQL-C 真·秒级启停:连接断了,又没断
    硬技能之上的软技巧(三)
  • 原文地址:https://www.cnblogs.com/lingda-blogs/p/16552670.html