跳转到内容

通过状态思维应对多变页面

刀刀

4/7/2025

0 字

0 分钟

状态思维

有的时候会碰到页面会基于各种因素,来产生很多变化的情况,与其一个个做判断,不妨把页面归纳为几种状态,把多个因素统一归为几种状态。然后用数据定义好每种状态下的展示。这样就只用关心修改状态,定义不同状态下的数据就好

案例分析

下面有一个页面,根据不同的状态,展示不同的内容:

  1. 用户一进入页面,此时没有 key 值,要有一个输入框和确认按钮,让用户输入 key 值。

    无key无确认

  2. 用户输入 key 值后,还未确认,输入框隐藏,有一个文本显示当前的 key 值,按钮内容变为 “我已确认无误,使用 key”。

    无key有确认

  3. 点击确认无误按钮后页面存在 key 值了,页面由显示 key 值的文本和按钮组成,按钮内容变为 “修改”。

    有key待修改

  4. 点击修改按钮后进入修改状态,页面多出一个输入框,按钮内容变为 “确认修改”。

    有key正在改

  5. 最后点击确认修改的按钮后,询问用户是否要使用新 key 值。

    有key待确认

思考实现

现在来想想要如何实现这个需求案例,根据上面的分析,可以把页面分为 5 种状态,每种状态用一个英文单词来表示,如:第一个无 key 无确认,可以定义为 unbind ;第二个是无 key 待确认,可以定义为 awaitBind ;第三个是有 key 待修改,可以定义为 binded ;第四个是有 key 正在修改,可以定义为 changing ;第五个是有 key 待确认,可以定义为 awaitChang

然后定义一个变量,把上方的 5 种状态都作为该对象的键名。键值为一个对象,对象里定义该状态下需要展示什么内容,如【是输入框,是按钮,还是文本内容等】;以及文本内容,如【按钮的文本内容和输入框前面的文本内容】。

定义好了变量后,通过 Vue 的计算属性 computed 提取对应状态对象,通过该对象来动态渲染 DOM 元素即可。页面 DOM 元素根据上方五个示例图,可以划分为上中下三块。上方可能是文本,可能是输入框;中间是输入框或文本或空;下方是按钮,只不过内容不一样。

js
const pageState = ref('unbind') // 当前页面的状态
const now = ref('') // 第一区域的输入框或文本要显示的内容
const pre = ref('') // 第二区域的输入框或文本要显示的内容

const stateObj = {
  unbind: {
    nowLabel: '请输入key值',
    nowType: 'input',
    buttonText: '确认',
    buttonClick: () => {}
  },
  awaitBind: {
    nowLabel: '待确认key值',
    nowType: 'span',
    buttonText: '我已确认无误,使用key',
    buttonClick: () => {}
  },
  binded: {
    nowLabel: '你的key值',
    nowType: 'span',
    buttonText: '修改',
    buttonClick: () => {}
  },
  changing:: {
    nowLabel: '当前key值',
    nowType: 'span',
    preLabel: '要修改为',
    preType: 'input',
    buttonText: '确认修改',
    buttonClick: () => {}
  },
  awaitChang:: {
    nowLabel: '当前key值',
    nowType: 'span',
    preLabel: '要修改为',
    preType: 'span',
    buttonText: '确认使用新key',
    buttonClick: () => {}
  },
}

const state = computed(() => {
  return stateObj[pageState.value]
})
vue
<template>
  <div v-if="state.nowLabel && state.nowType">
    <span>{{ state.nowLabel }}</span>
    <input v-if="state.nowType === 'input'" v-model="now" />
    <span v-if="state.nowType === 'span'">{{ now }}</span>
  </div>
  <div v-if="state.preLabel && state.preType">
    <span>{{ state.preLabel }}</span>
    <input v-if="state.preType === 'input'" v-model="pre" />
    <span v-if="state.preType === 'span'">{{ pre }}</span>
  </div>
  <div>
    <button @click="state.buttonClick">{{ state.buttonText }}</button>
  </div>
</template>

进入页面的时候需要先判断用户上一次执行到哪一个状态步骤了,方便用户接下去。因此需要先调用接口获取 nowpre 的值,分为以下几种状态:

  • now 值有 pre 值,说明执行到 awaitChang 状态
  • now 值无 pre 值,说明执行到 binded 状态
  • now 值有 pre 值,说明执行到 awaitBind 状态
  • now 值无 pre 值,说明执行到 unbind 状态
js
function getNowKeyState() {
  axios.get("/xxx").then((res) => {
    const data = res.data.data;
    now.value = data.now;
    pre.value = data.pre;
    if (data.now) {
      if (data.pre) pageState.value = "awaitChang";
      else pageState.value = "binded";
    } else {
      if (data.pre) pageState.value = "awaitBind";
      else pageState.value = "unbind";
    }
  });
}

getNowKeyState();

现在页面状态结构已经实现了,接下来就是实现每个状态下的按钮点击事件了。每一个点击事件都调用各自的接口,成功后调用上方的方法 getNowKeyState 判断修改当前最新的页面状态。