javascript-state-machine 版本演进与升级指南:从 1.0 到 3.1 的特性全景、破坏性变更与源码印证
开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载本文以 RELEASE_NOTES.md 为骨架完整梳理 javascript-state-machine 从 2011 年 1.0.0 发布到 2018 年 3.1.0 的版本演进历程3.x 的重大重写带来了哪些能力构造方式、任意数据与方法、可观察转换、条件转换、Promise 异步转换、生命周期事件、状态历史、可视化、Webpack 构建2.x 为库打下的异步与通配符基础以及 3.0.1 修复的三个关键缺陷。读完本文你将掌握每个版本引入的核心能力、破坏性变更点以及从 2.x 升级到 3.x 的完整迁移清单并能在仓库源码src/app.js、src/jsm.js、src/config.js、src/util/camelize.js等与测试用例中逐一验证这些结论。一、版本全景一条从 2011 到 2018 的演进时间线javascript-state-machine是一个用于构建有限状态机的 JavaScript 库其发布历史横跨七年共经历 1.x、2.x、3.x 三个大版本。以下为 RELEASE_NOTES.md 记录的完整发布序列版本发布日期定位与关键内容1.0.02011-06-01初始版本奠定状态机基础模型1.2.02011-06-21支持同一事件在不同当前状态下转换到不同目标状态2.0.02011-08-19引入异步状态转换、统一回调参数、onchangestate等2.1.02012-01-07模块化包装、事件取消、显式返回值、通配符事件、no-op 事件、自定义错误回调2.2.02013-01-26终态finalstate与isFinished()、fsm.is()支持数组、通用事件/状态回调、异步转换取消2.3.02014-03-15支持 bower、支持 Node.js、可在控制台通过 Node.js 运行测试2.3.22014-03-16修正 npmjs.org 包注册问题导致的版本号提升2.3.32014-10-17增加 Web Worker 兼容性2.3.42014-01-17增加辅助方法列出当前状态下允许触发的事件2.3.52014-01-20修复transitions()方法2.4.02016-11-20README 增加 npm 安装说明、修复 Jasmine/Node 运行报错、通配符事件纳入transitions()、修复双通配符卡死、新增fsm.states()、隐藏回调异常、改用 uglify-js 压缩3.0.0-rc.12017-01-10重大重写包含破坏性变更改进构造、任意数据与方法、可观察转换、条件转换、Promise 异步转换、改进生命周期事件、状态历史、可视化、Webpack 构建系统3.0.12017-06-10首个正式 3.x 版本修复 issue #109、#106、#107 三个关键缺陷3.1.02018-07-12许可证改回 MIT其中3.0.0-rc.1 的发布说明明确标注了IMPORTANT NOTE: this version includes breaking changes that will require code updates是整个演进过程的分水岭。下面分别从 3.x、2.x、1.x 三个时代展开。二、3.x 时代一次面向高级用法的彻底重写3.0.0-rc.1 的重写目标是为支持更高级的用例、同时改进既有使用体验详见 docs/upgrading-from-v2.md。发布说明高亮了十项核心特性每一项都能在仓库源码中找到对应实现。2.1 更符合惯例的构造方式new StateMachine()3.x 将构造方式从 2.x 的静态方法调用改为更符合 JavaScript 惯例的构造函数// 2.x var fsm StateMachine.create({ /* ... */ }); // 3.x var fsm new StateMachine({ /* ... */ });源码实现位于 src/app.jsfunction StateMachine(options) { return apply(this || {}, options); }。apply内部创建Config、build出公开方法与属性、再调用instance._fsm()完成初始化src/app.js。2.2 任意数据与方法data与methods2.x 只能通过回调挂载行为3.x 允许状态机携带任意数据和方法var fsm new StateMachine({ data: { color: red }, methods: { speak: function() { console.log(hello) } } }); fsm.color; // red fsm.speak(); // hello底层实现上Config.configureData会把对象或函数统一包装为数据工厂函数src/config.js而JSM.init在初始化时将数据 mixin 到状态机上下文src/jsm.jsbuild则把配置的methods以及每个转换名经 camelize 后生成的事件方法一并 mixin 到实例上src/app.js。2.3 可观察的转换observe()观察者机制3.x 的生命周期事件可以被外部观察者订阅var fsm new StateMachine({ /* ... */ }); // 观察单个生命周期事件 fsm.observe(onBeforeTransition, function() { /* ... */ }); fsm.observe(onLeaveState, function() { /* ... */ }); // 或以对象形式一次观察多个事件 fsm.observe({ onBeforeTransition: function() { /* ... */ }, onLeaveState: function() { /* ... */ } });JSM.observe会把观察者对象形式或单事件映射推入observers数组src/jsm.js转换发生时observersForEvent从所有观察者中筛选出注册了该事件的对象src/jsm.jsobserveEvents按顺序逐个调用src/jsm.js。这一机制在 test/observers.js 中有完整测试覆盖。2.4 条件转换运行时动态决定目标状态转换的to属性不再只能是字符串也可以是一个函数由其在运行时返回目标状态var fsm new StateMachine({ transitions: [ { name: save, from: idle, to: function() { return this.isValid() ? done : error; } } ] });实现上JSM.seek检测to为函数时以状态机上下文调用它src/jsm.jsConfig.mapTransition在to是函数时不会预先注册目标状态而是在转换真正发生时由transit中的addState(to)动态补注册src/config.js、src/jsm.js注释明确写明might need to add this state if its unknown (e.g. conditional transition or goto)。2.5 基于 Promise 的异步转换2.x 需要返回StateMachine.ASYNC再手动调用fsm.transition()完成异步流程3.x 全面改用标准 Promise从任意生命周期事件返回 Promise 即可暂停该转换的整个生命周期resolve继续、reject取消。典型用法jQuery 动画var fsm new StateMachine({ init: menu, transitions: [ { name: play, from: menu, to: game }, { name: quit, from: game, to: menu } ], methods: { onEnterMenu: function() { return new Promise(function(resolve) { $(#menu).fadeIn(fast, resolve); }); }, onLeaveMenu: function() { return new Promise(function(resolve) { $(#menu).fadeOut(fast, resolve); }); } // onEnterGame / onLeaveGame 同理 } });务必保证 Promise 最终会被 resolve或 reject否则状态机将永远卡在该 pending 转换中。此注意事项在 docs/async-transitions.md 中被明确强调。Promise 驱动的核心逻辑在JSM.observeEvents当某个观察者返回的对象带有then方法时先挂起生命周期resolve后继续执行剩余事件序列reject则调用failTransit抛出异常并复位pendingsrc/jsm.js。isPending()、can()等辅助方法都会感知 pending 状态src/jsm.js。2.6 改进的生命周期事件camelCase 与统一lifecycle参数3.x 将回调callbacks改名为生命周期事件lifecycle events方法名采用传统 JavaScript camelCase 命名并且把原来散落的(event, from, to)参数合并为一个lifecycle对象参数var fsm new StateMachine({ transitions: [ { name: do-something, from: initial-state, to: final-state } ], methods: { onBeforeDoSomething: function(lifecycle) { console.log(transition: lifecycle.transition); // do-something console.log(from: lifecycle.from); // initial-state console.log(to: lifecycle.to); // final-state } } });事件名与状态名的 camelCase 转换由 src/util/camelize.js 完成它按-/_切分标签、首词小写、后续词首字母大写并提供camelize.prepended在结果前拼接onBefore/onLeave/onEnter/onAfter等前缀Config在注册状态与转换时即为它们生成对应的生命周期事件名src/config.js。3.x 的完整生命周期执行顺序通用事件先于特定事件触发onBeforeTransition—— 任何转换之前onBeforeTRANSITION—— 特定转换之前onLeaveState—— 离开任意状态onLeaveSTATE—— 离开特定状态onTransition—— 转换期间onEnterState—— 进入任意状态onEnterSTATE—— 进入特定状态onSTATE——onEnterSTATE的便捷简写onAfterTransition—— 任何转换之后onAfterTRANSITION—— 特定转换之后onTRANSITION——onAfterTRANSITION的便捷简写上述顺序在 src/jsm.js 的transit方法中以事件数组的形式逐项编排changed状态from ! to决定是否触发 leave/enter 相关事件observeUnchangedState选项可以覆盖这一行为。完整说明见 docs/lifecycle-events.md。2.7 状态历史与可视化3.x 新增两项此前不存在的辅助能力状态历史State History可记录并以前进/后退语义遍历状态历史由 src/plugin/history.js 插件实现用法见 docs/state-history.md。可视化Visualization可将状态机导出为 GraphViz.dot语法由 src/plugin/visualize.js 插件实现。仓库 examples/ 目录中保留了atm.dot、matter.dot、wizard.dot等.dot源文件及其.svg渲染结果可作为可视化输出的直接参考配置说明见 docs/visualization.md。2.8 Webpack 构建系统与 Ava 测试体系3.x 引入基于 Webpack 的构建系统和基于 Ava 的单元测试体系。从 webpack.config.js 可以看到主入口src/app.js被打包为lib/state-machine.jsUMD 格式library: StateMachine而src/plugin/*.js下的每个插件history、visualize都会被独立打包成lib/name.js如 lib/history.js、lib/visualize.js。构建命令在 package.json 中定义npm run build依次执行webpack打包与bin/minify压缩测试则通过npm testnyc ava -v运行 test/ 目录下的全部用例。三、3.0.1 修复的三个关键缺陷与 3.1.0 的许可证变更3.0.1 是首个正式 3.x 发布版此前为 3.0.0-rc.1集中修复了三个与异步转换和生命周期事件相关的问题。3.1 issue #109异步生命周期方法被拒绝时转换的 Promise 也必须被拒绝修复前如果某个生命周期方法返回的 Promise 被 reject转换流程的 promise 没有被正确拒绝可能导致异常被吞掉。修复后JSM.observeEvents中 Promise 的reject路径会调用failTransitthis.pending false; throw result;见 src/jsm.js从而把拒绝传递出去。test/lifecycle.js 中用显式reject(rejected)的用例验证了转换 promise 最终以rejected结束且后续.then分支不会被触发测试中done(promise was rejected so this should never happen)不会被调用。3.2 issue #106异步转换向前传递 resolve 的值修复前异步生命周期方法 resolve 的值不会沿生命周期继续传递。修复后的observeEvents用result.then(this.observeEvents.bind(...))把 resolved value 作为previousResult一路传给后续事件并在生命周期结束时由endTransit(previousResult undefined ? true : previousResult)作为转换方法的返回值返回src/jsm.js。test/lifecycle.js 中通过setTimeout后resolve(resolved)的用例断言了转换返回值answer resolved。3.3 issue #107全大写事件名的生命周期事件修复前全大写名称如SAVE会导致 camelCase 转换出的生命周期事件名不符合预期进而无法触发对应观察方法。这与 src/util/camelize.js 的命名归一化逻辑直接相关它要求首词首字符已为小写时才原样返回否则会执行大小写归一化。修复后任意大小写形式的事件名都能正确映射到onBeforeName、onAfterName等观察方法。3.4 3.1.0许可证回到 MIT3.1.02018-07-12的变更说明只有一条将许可证改回 MIT。仓库根目录的 LICENSE 文件即为 MIT 许可证文本版权归 Jake Gordon and contributors2012–2018。四、2.x 时代从异步转换到生态完善在 3.x 重写之前2.x 系列用了五年时间逐步打磨出库的核心能力。4.1 2.0.0异步转换与统一回调约定2.0.0 是能力奠基版本引入异步状态转换伴随大量 qunit 测试见 test/async.js 的前身所有回调统一签名前三个参数永远是event、from、to其后才是调用者传给事件方法的实际参数新增通用onchangestate(event, from, to)回调用一个函数即可监听所有状态变更允许在创建时声明回调而非创建后再逐个挂载术语调整hooks更名为callbacks。4.2 2.1.0事件控制能力将库包裹在自执行函数中便于require.js/curl.js等加载器使用允许在onleavestate处理器中返回false来取消事件——注意这打破了异步转换的向后兼容异步流程此时必须返回StateMachine.ASYNC而不是false事件方法具有显式返回值支持通配符事件可从任意状态触发的转换这一能力在 3.x 中延续为默认wildcard: *见 src/app.js 与 src/config.js 的from数组展开逻辑支持no-op 事件转换到与当前状态相同的状态扩展自定义错误回调可覆盖非法状态转换时的异常也可捕获调用方回调抛出的任何异常修复 issue #4即使事件未导致状态变更before/after 事件钩子也必须被调用。4.3 2.2.0终态与辅助能力增加可选final状态和isFinished()辅助方法扩展fsm.is()使其接受状态数组而不只是单个状态增加通用事件回调onbeforeevent/onafterevent增加通用状态回调onleavestate/onenterstate修复undefined事件返回码问题允许取消异步事件转换。4.4 2.3.x运行环境扩展2.3.0增加bower支持与Node.js支持finally并允许在控制台通过 Node.js 运行测试npm install安装 node-qunit 后执行node test/runner.js2.3.3增加Web Worker兼容性2.3.4增加辅助方法列出当前状态下允许触发的事件2.3.5修复transitions()方法。4.5 2.4.0收尾完善README 增加 npm 安装说明修复在 Jasmine/Node 下运行时的 JavaScript 报错bower 安装时排除构建文件确保通配符事件出现在可用转换列表transitions()中——这一点在 3.x 中由Config.transitionsFor实现它合并当前状态与通配符状态下的全部转换键src/config.js修复双通配符导致状态机卡死在*状态的问题新增fsm.states()返回机器全部可用状态以支持自动化测试状态机隐藏回调异常用 uglify-js 替换 YUI compressor 作为压缩构建工具。五、1.x 时代库的诞生与最初形态1.0.02011-06-01初始版本确立了状态 转换 回调的有限状态机基础模型1.2.02011-06-21允许同一事件根据当前状态转换到不同目标状态——即多目标转换这是后来from数组与状态-转换映射表Config.map的雏形。六、升级路线图从 2.x 迁移到 3.x 的要点摘要如果你正在使用 2.x升级到 3.x 必须逐一核对以下变更完整指南见 docs/upgrading-from-v2.md其中文末提供了带锚点的变更摘要将StateMachine.create()替换为new StateMachine()重命名配置项initial→init、events→transitions、callbacks→methods、fsm.current→fsm.state更新回调方法改为传统 camelCase 命名并重构为接收单个lifecycle参数替代原来的event, from, to三个参数更新异步回调返回Promise而非StateMachine.ASYNC用resolve()替代手动调用fsm.transition()多实例构造将StateMachine.create({ target: FOO })替换为——若FOO是类则用StateMachine.factory(FOO, {})若FOO是对象则用StateMachine.apply(FOO, {})注意isFinished不再是内置方法可在methods中自定义如return this.state done有条件转换、goto、状态历史、可视化等 3.x 新增能力可分别参考 docs/states-and-transitions.md、docs/state-history.md、docs/visualization.md。结语从 2011 年 1.0.0 的朴素状态机到 2017 年 3.0 的全面重写再到 2018 年 3.1.0 回归 MIT 许可证javascript-state-machine的版本历史本身就是一部状态机库如何服务真实业务复杂度的演进样本2.x 打下的异步转换、通配符、事件控制基础在 3.x 中全部升级为基于 Promise、可观察、可条件化、可历史回溯、可可视化的现代形态。当前仓库的 src/ 源码、test/ 测试与 docs/ 文档三者相互印证任何声称某版本具备某能力的说法都可以在此逐一核实——这正是阅读一份发布说明时最值得依赖的证据链。赞分享开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载相关推荐Selectize.js 版本演进全解析从 v0.12 到 v0.15 的核心特性、破坏性变更与源码印证Selectize.js 版本演进全解析从 v0.12 到 v0.15 的核心特性、破坏性变更与源码印证 本篇文章以 Selectize.js 官方 CHANUI组件前端commitlint 版本演进全解析从 4.3.0 到 21.2.3 的特性、破坏性变更与升级指南commitlint 版本演进全解析从 4.3.0 到 21.2.3 的特性、破坏性变更与升级指南 本篇文章以仓库根目录的 CHANGELOG.md http开发工具Lint代码质量Luckysheet 版本演进全解析从 1.0 到 2.1.13 的功能迭代、破坏性变更与源码佐证Luckysheet 版本演进全解析从 1.0 到 2.1.13 的功能迭代、破坏性变更与源码佐证 本篇文章以 Luckysheet 官方 CHANGELOG前端UI组件上一篇终极屏幕文字提取革命PowerToys Text Extractor让图片文字秒变可编辑文本下一篇Kubernetes Descheduler实战宝典彻底解决集群资源碎片化的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考