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


    🚀 优质资源分享 🚀

    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统
    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(...){ |
    |  |  ...  |
    |  |  } |
    |  | } |
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    1. 解决方案

    利用「状态」设计模式。

    大致思路: 每当切换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; |
    
    
    
    • 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

    我们在组件中试一下:

    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 }); |
    
    
    
    • 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

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

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

  • 相关阅读:
    Idea_最右侧常用栏中没有Maven选项
    【HTML专栏2】VSCode的使用(新建HTML文件)
    支付通道的安全性探讨
    如何在ubnutu上安装docker
    专业级游戏测试书上架:精通游戏测试(第3版)
    LeetCode --- 1534. Count Good Triplets 解题报告
    简历的项目经历,测试人员书写要注意的几个问题
    bug:Chrome插件SwitchyOmega安装时程序包无效:“CRX_HEADER_INVALID“问题
    第一章:最新版零基础学习 PYTHON 教程(第十三节 - Python 条件和循环语句–Python 尝试异常)
    Python函数式编程(二)高阶函数functools
  • 原文地址:https://blog.csdn.net/u012804784/article/details/126169214