开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 14 天,点击查看活动详情
1 前言
随着业务发展,视图越来越负责。随着前端的发展,UI越来越复杂,前端代码复杂也在增加。当前数据驱动视图模式是热门的前端模式,Vue、React都是这种范式的体现。可以使用状态机来管理前端状态。状态机作为一种有效处理代码复杂程度的正收到关注。很多对象可以写成有限状态机。好的状态管理,有助于代码维护。本文将介绍一个状态机库-Xstate,希望为业务复杂度较高的前端开发提供借鉴。
2 状态机与状态图
2.1 状态机
有限状态机(Finite-state Machine,FSM),又称优先状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。FSM由一组状态、一个初始状态、输入和根据输入及现有状态转移为下一个状态的转换函数组成(State、Event、Action、Transition)。其主要作用是描述对象在它的生命周期内所经历的状态序列以及如何响应来自外界的各种事件。
2.2 状态图
可以利用状态图描述状态机。状态图是状态机的可视化表达。从状态机的角度看,JS中有很多功能符合状态机的特质,如js对象、异步回调、状态管理、MVVM模型。
// MVVM
UI = h(data)
3 Xstate
3.1 介绍
从Xstate(xstate.js.org/)的官网可知,Xsta… Web开发的JavaScript和TypeScript的有限状态机和状态图。Xstate还提供了可视化工具XState Visualizer(stately.ai/viz)。
3.2 安装
可以用npm或yarn安装
npm install xstate --save
# OR
yarn add xstate --save
也可以通过CDN
<script src="https://unpkg.com/xstate@4/dist/xstate.js"></script>
3.3 API
如果采用CDN引入方式,变量Xstate可以在全局范围使用。
<script src="https://unpkg.com/xstate@4/dist/xstate.js"></script>
...
const { createMachine, actions, interpret } = XState; // 全局变量: window.XState
const lightMachine = createMachine({
// ...
});
const lightService = interpret(lightMachine);
4 Xstate实践
4.1 计数器
下面是一个计数器例子。一个计数器有一个active状态和两个可能的事件。
'INC':表示+1,
'DEC':表示-1
count存储在context中。
import { createMachine, interpret, assign } from 'xstate';
const increment = (context) => context.count + 1;
const decrement = (context) => context.count - 1;
const counterMachine = createMachine({
initial: 'active',
context: {
count: 0
},
states: {
active: {
on: {
INC: { actions: assign({ count: increment }) },
DEC: { actions: assign({ count: decrement }) }
}
}
}
});
const counterService = interpret(counterMachine)
.onTransition((state) => console.log(state.context.count))
.start();
// => 0
counterService.send('INC');
// => 1
counterService.send('INC');
// => 2
counterService.send('DEC');
// => 1
通过'INC'和'DEC'可以模拟最大与最小
const isNotMax = (context) => context.count < 10;
const isNotMin = (context) => context.count >= 0;
const counterMachine = createMachine({
initial: 'active',
context: {
count: 0
},
states: {
active: {
on: {
INC: {
actions: assign({ count: increment }),
cond: isNotMax
},
DEC: {
actions: assign({ count: decrement }),
cond: isNotMin
}
}
}
}
});
// ...
// assume context is { count: 9 }
counterService.send('INC');
// => 10
counterService.send('INC'); // no transition taken!
// => 10
4.2 红绿灯
Xstate提供了应用于React、Vue、Svelte的方案。下面介绍一个利用Xstate开发状态机的例子。颜色只能从红->黄->绿->红。
import {Machine, interpret} from 'xstate';
const lightMachine = Machine({
id: 'light',
initial: 'red',
states: {
red: {
on: {
TRANS: 'green',
},
},
green: {
on: {
TRANS: 'yellow',
},
},
yellow: {
on: {
TRANS: 'red',
},
},
},
});
const lightService = interpret(lightMachine).onTransition((state) => {
console.log(state.value);、
});
// 启动状态机 初始化
lightService.start();
// 发送事件
lightService.send('TRANS'); // 'green'
lightService.send('TRANS'); // 'yellow'
lightService.send('TRANS'); // 'red'
// 批量发送事件
lightService.send(['TRANS', 'TRANS']);
// 终止状态机
lightService.stop();
React Demo
import React from 'react';
import {useMachine} from '@xstate/react';
import lightMachine from './lightMachine';
export default function App() {
const [state, send] = useMachine(lightMachine);
const onClick = () => {
send('TRANS');
};
return (
<>
<div
style={{
width: '50px',
height: '50px',
background: state.value,
borderRadius: '100%',
}}
/>
<button onClick={onClick}>click</button>
</>
);
}
4 总结
本文主要介绍了状态机、状态图以及一个状态机库——Xstate。状态机可以为复杂前端业务处理、降低代码维护成本、状态逻辑复用提供新思路供借鉴。