什么是状态机

举个例子,最简单的状态机是红绿灯,它有三种状态:红灯、绿灯、黄灯,每种状态都代表你能做什么事。在路上行走我们关注它的状态,从设计模式上来讲就是观察者模式,观察红绿灯的状态变化决定我们的行为。

从这个例子中如果使用程序实现可以得出:

  1. 需要一个 state 用来描述当前状态。
  2. 需要一个方法 changeState 去改变状态。
  3. 需要注册一个钩子函数去监听状态的变化,从而控制行人的行为。

抽象来说:

  1. 进入动作(entry action):在进入状态时进行(可以来自于状态变化的watch)
  2. 退出动作 (exit action):在退出状态时进行(同上)
  3. 输入动作: 依赖于当前状态和输入条件
  4. 转移动作:在进行特定转移时进行

以上来自维基百科

无状态服务与有状态服务

无状态程序

无状态程序是程序本身不保存状态,常见的无状态服务就是我们熟悉的 HTTP 服务,当请求命中路由时,会解析 HTTP 请求,把请求参数转换成编程语言的对象,使其成为一个临时变量,根据结构化的对象进行一系列操作来处理我们的业务逻辑,可能是数据库增删改查、文件读取\写入等最终返回一个结果,就像执行一个函数一样,进入函数时为其创建一个新的上下文,函数执行结束后销毁这个上下文,所有状态都是临时的,程序本身并不会持久化这些状态,而是交给其他程序为其保存状态。

有状态程序

常见的有状态程序有:手机的APP、浏览器执行的网页程序、PC上的应用等,这些程序可以持续、连贯的响应用户的交互,并为用户保存各种状态,从这个层面这些都可以称为有状态程序。

UI状态管理

起初开发一个UI程序需要开发者自己开发UI绘制的程序,随着有了结构化表达语言 XML 的诞生,之后大部分 UI 描述语言使用了 XML 的特性,比如网页上的 html 就是基于 XML 的,通过标签表示 UI 节点,通过属性表示节点的状态:

<div id="foo" width="100px">hello world</div>

以上通过 html 创建了一个宽 100px 显示 ‘hello world’ 的div,通过 DOM API document.querySelector('#foo') 可以获取这个节点,然后对其进行操作,比如改变文本内容、元素宽高等,本质上还是在改变元素的状态,这本身就是浏览器的封装,屏蔽了图形绘制的细节,通过语义化的表达即可绘制图形,当然这些都构建在 W3C 的规范之上,使其逻辑自洽。

Vue 中的状态表达

随着 web 应用的复杂度不断提升,通过命令式编程变得程序难以维护,不得不为其开发 UI 框架,本身 web 应用本质上属于有状态程序,使得 React、Vue 这类通过维护状态来管理 UI 的框架变得合理。

在单文件组件中可以这这么写:

<template>
    <div>
        <div>{{ msg }}</div>
        <div>{{ flag }}</div>
        <button @click="changeState">改变状态</button>        
    </div>
</template>

<script>
const FLAG_MAP = ['开始','进行中','结束']
export default {
    name:'State',
    data(){
        return {
            flag:0
        }
    },
    watch:{
        flag(oldVal,newVal){
            doSameting()
        }
    },
    computed:{
        msg(){
            return FLAG_MAP[this.flag]
        }
    },
    methods:{
        changeState(){
            if (this.flag >= FLAG_MAP.length) {
                return console.error('已经结束')
            }
            this.flag ++
        }
    }
}
</script>

上面代码描述了界面上有一个展示当前状态描述的 div、一个状态显示的 div、当我们点击“改变状态”按钮时,flag 改变,对应的状态描述也跟着发生改变,在状态发生改变时,监听函数也会执行相应的逻辑,可以看到通过状态映射UI状态,显著降低了编写代码的心智负担。