首页/新闻资讯/正文详情

javascript-state-machine 升级指南:从 2.x 迁移到 3.x 的完整改造手册

发布时间:2026/9/25 5:21:45 来源:云帆数科 栏目:资讯中心
javascript-state-machine 升级指南:从 2.x 迁移到 3.x 的完整改造手册
开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载本文是 javascript-state-machine 官方 升级指南 的深度展开版核心面向正在从 2.x 版本升级到 3.x 的现有用户。3.0 是一次大爆炸式重写构造方式、术语体系、回调机制、异步过渡模型全部发生了不兼容变更。读完本文你将掌握new StateMachine()与StateMachine.factory()的新式构造、init/transitions/methods新术语、camelCase 生命周期事件、基于 Promise 的异步过渡、条件过渡与goto、状态历史与可视化等全部升级动作并能对照 src/、test/ 下的源码理解每个变更的底层原理。文末附有官方 UPGRADE SUMMARY可一键对照完成迁移。为什么 3.0 是一次大爆炸式重写官方文档明确说明Version 3.0 is a significant rewrite from earlier versions升级指南。这次重写的目标有两个支持更高级的使用场景条件过渡、goto、状态历史、可视化、工厂批量实例化改善已有的使用场景更符合 JavaScript 惯例的构造、可观察的生命周期、标准 Promise。代价是大量 API 与 2.x 不兼容升级时必须同步修改代码。官方刻意把破坏性变更一次性集中到 3.0以避免未来再次出现大爆炸式变更。README 中也特别提示VERSION 3.0 Is a significant rewrite from earlier versions. Existing 2.x users should be sure to read the Upgrade GuideREADME.md。Construction用new StateMachine()取代StateMachine.create()2.x 时代构造单实例使用StateMachine.create({ ... })// Version 2.x var fsm StateMachine.create({ /* ... */ })3.x 改用更符合 JavaScript 惯例idiomatic的构造函数模式// Version 3.x var fsm new StateMachine({ /* ... */ }) // -- more idomatic这一变更在源码中得到了完整印证。查看 src/app.jsStateMachine本身就是一个普通函数调用时支持new与直接调用两种方式function StateMachine(options) { return apply(this || {}, options); }new StateMachine(options)时this指向新对象apply会把整套状态机行为公共方法、数据、方法、过渡方法、_fsm内核构建到该对象上并完成初始化src/app.jsfunction apply(instance, options) { var config new Config(options, StateMachine); build(instance, config); instance._fsm(); return instance; }build会依次校验目标必须是对象、构建插件、挂载state属性与公共方法、混入config.methods、为每个过渡生成 camelCase 的调用方法src/app.js。也就是说new StateMachine({...})返回的对象上所有能力都是在build中动态装配的。State Machine FactoryStateMachine.factory()简化多实例创建2.x 中要基于一个类创建多实例需要手动把配置挂到原型上// Version 2.x function FSM() { } StateMachine.create({ target: FSM.prototype, // ... }) var a new FSM(), b new FSM();3.x 直接生成一个工厂即一个构造函数再通过new创建实例// Version 3.x var FSM StateMachine.factory({ /* ... */ }), // -- generate a factory (a constructor function) a new FSM(), // -- then create instances b new FSM();从源码看factory有两种形态src/app.js传入配置对象自动生成一个内部构造函数function() { this._fsm.apply(this, arguments) }并把配置构建到其原型上传入已有构造函数StateMachine.factory(FOO, { ... })把状态机行为构建到FOO.prototype上。两种形态都会在原型上挂_fsm.config便于在实例之外访问共享配置。注意升级摘要中特别强调2.x 的StateMachine.create({ target: FOO })在 3.x 中应替换为若FOO是一个类 →StateMachine.factory(FOO, { ... })若FOO是一个对象 →StateMachine.apply(FOO, { ... })。apply模式适合给既有对象如 React 组件现场注入状态机行为详见 State Machine Factory 文档。Data and Methods状态机现在可以携带任意数据与方法这是 2.x 完全不支持的新能力Version 2.x:not supported。3.x 中可以在构造时声明data与methods// Version 3.x var fsm new StateMachine({ data: { color: red }, methods: { speak: function() { console.log(hello) } } }); fsm.color; // red fsm.speak(); // hello底层机制Config构造时调用configureDatasrc/config.js——data可以是对象每次返回同一对象或函数每次调用返回值configureMethodssrc/config.js直接保存方法表。初始化时JSM.init会把config.data.apply(this.context, args)的结果混入状态机实例src/jsm.js构造实例时传入的参数会原样传给data方法。⚠️工厂场景下的数据陷阱如果用工厂创建多个实例且data是普通对象则该对象会被所有实例共享这通常不是你想要的。正确的做法是把data写成方法让每个实例调用时返回独立数据详见 Data and Methods 文档var FSM StateMachine.factory({ data: function(color) { // -- use a method that can be called for each instance return { color: color } } }); var a new FSM(red), b new FSM(blue); a.color; // red b.color; // blueRenamed Terminology一套更一致的术语体系3.0 对核心术语做了统一命名。官方文档给出概念对照状态机由一组States组成见 States and Transitions通过Transitions改变状态在过渡期间通过观察Lifecycle Events执行动作还可携带任意Data and Methods见 Data and Methods。API 层面2.x 与 3.x 的配置项对比如下// Version 2.x var fsm StateMachine.create({ initial: ready, events: [ /* ... */ ], callbacks: { /* ... */ } }); fsm.current; // ready// Version 3.x var fsm new StateMachine({ init: ready, // -- renamed s/initial/init/ transitions: [ /* ... */ ], // -- renamed s/events/transitions/ data: { /* ... */ }, // -- new methods: { /* ... */ } // -- renamed s/callbacks/methods/ // ... which can contain arbitrary methods AND lifecycle event callbacks }); fsm.state; // ready // -- renamed s/current/state/对应的四组重命名速记initial→initevents→transitionscallbacks→methodsfsm.current→fsm.statestate属性在源码中是只读的src/app.js它通过 getter 暴露this._fsm.state而 setter 会直接抛错use transitions to change state——状态只能通过过渡改变不能手动赋值。init的解析逻辑在 src/config.js字符串形式的init会被映射为一个默认名为init、from: none的特殊过渡StateMachine.defaults.init见 src/app.js构造实例时会自动过渡到初始状态。Lifecycle Events从 callbacks 到可观察的 camelCase 事件Callbacks 被重命名为 Lifecycle Events并统一在methods中声明方法名采用传统的 JavaScript camelCase 约定。先看命名迁移状态名、过渡名带连字符的情况事件名也会被 camelize// Version 2.x var fsm StateMachine.create({ initial: initial-state, events: [ { name: do-something, from: initial-state, to: final-state } ], callbacks: { onbeforedosomething: function() { /* ... */ }, onleaveinitialstate: function() { /* ... */ }, onenterfinalstate: function() { /* ... */ }, onafterdosomething: function() { /* ... */ } } })// Version 3.x var fsm new StateMachine({ init: initial-state, transitions: [ { name: do-something, from: initial-state, to: final-state } ], methods: { // -- renamed s/callbacks/methods/ onBeforeDoSomething: function() { /* ... */ }, // -- camelCase naming convention onLeaveInitialState: function() { /* ... */ }, // -- onEnterFinalState: function() { /* ... */ }, // -- onAfterDoSomething: function() { /* ... */ } // -- } })camelCase 的底层实现位于 src/util/camelize.jscamelize按-/_分词后拼接首词小写、后续词首字母大写camelize.prepended再补上前缀onBefore、onEnter等并大写首字母。在 src/config.js 中每注册一个状态/过渡就会用这套规则动态生成其专属生命周期事件名如onEnterSTATE、onBeforeTRANSITION。单一lifecycle参数取代(event, from, to)2.x 的事件回调接收三个独立参数// Version 2.x callbacks: { onbeforestep: function(event, from, to) { console.log(event: event); // step console.log(from: from); // none console.log(to: to); // complete }, }3.x 合并为一个lifecycle对象参数// Version 3.x methods: { onBeforeStep: function(lifecycle) { // -- combined into a single argument console.log(transition: lifecycle.transition); // step console.log(from: lifecycle.from); // none console.log(to: lifecycle.to); // complete } }官方对这一设计变更的说明是这让我们未来可以加入更多信息而不必让生命周期观察方法的参数数量变得离谱。源码中的lifecycle对象实际包含四个字段transition、from、to、fsmsrc/jsm.js并且在事件分发过程中还会动态补上event字段src/jsm.js。除此之外调用过渡方法时传入的任意参数会紧跟在lifecycle之后传给观察者详见 Lifecycle Events 文档。事件可被外部观察fsm.observe(...)2.x 不支持外部观察not supported。3.x 中除了状态机自身通过methods观察事件外外部代码也可以注册观察者var fsm new StateMachine({ /* ... */ }); // observe individual lifecycle events with observer methods fsm.observe(onBeforeTransition, function() { /* ... */ }); fsm.observe(onLeaveState, function() { /* ... */ }); // or observe multiple lifecycle events with an observer object fsm.observe({ onBeforeTransition: function() { /* ... */ }, onLeaveState: function() { /* ... */ } });实现上JSM构造时把状态机自身context作为第一个观察者放进this.observerssrc/jsm.jsobserve方法支持单事件回调或观察者对象两种注册形式src/jsm.js事件分发时按注册顺序逐个调用src/jsm.js。生命周期事件顺序transition取代event通用事件改用transition一词并且发生在各自专有事件之前。2.x 的顺序是onbeforeEVENTonbeforeeventonleaveSTATEonleavestateonenterSTATEonenterstateonSTATEonafterEVENTonaftereventonEVENT3.x 的顺序是onBeforeTransition- 任何过渡开始之前触发onBeforeTRANSITION- 特定 TRANSITION 开始之前触发onLeaveState- 离开任意状态时触发onLeaveSTATE- 离开特定 STATE 时触发onTransition- 过渡过程中触发onEnterState- 进入任意状态时触发onEnterSTATE- 进入特定 STATE 时触发onSTATE-onEnterSTATE的便捷简写onAfterTransition- 任何过渡完成后触发onAfterTRANSITION- 特定 TRANSITION 完成后触发onTRANSITION-onAfterTRANSITION的便捷简写这个顺序与 src/jsm.js 中transit方法的事件队列完全一致onBeforeTransition→onBefore[transition]→onLeaveState→onLeave[from]→onTransition→ 状态切换动作doTransit→onEnterState→onEnter[to]→on[to]→onAfterTransition→onAfter[transition]→on[transition]。config.lifecycle中通用事件名onBeforeTransition等也在 src/config.js 中预先定义。更多细节见 Lifecycle Events。Promise-Based Asynchronous Transitions用标准 Promise 取代StateMachine.ASYNC异步过渡在 3.x 中改用标准 JavaScript Promise实现。规则是从任意生命周期事件返回一个 Promise则该过渡的整个生命周期都会挂起直到该 Promise 被 resolve若 Promise 被 reject则过渡被取消。2.x 的写法配合 jQuery 动画依赖StateMachine.ASYNC常量和手动调用fsm.transition()// Version 2.x callbacks: { onbeforestep: function() { $(#ui).fadeOut(fast, function() { fsm.transition(); }); return StateMachine.ASYNC; } }3.x 直接返回 Promise动画完成即 resolve// Version 3.x methods: { onBeforeStep: function() { return new Promise(function(resolve, reject) { // -- return a Promise instead of StateMachine.ASYNC $(#ui).fadeOut(fast, resolve); // -- resolve the promise instead of calling .transition() }); } }源码层面Promise 的处理在 src/jsm.js 的observeEvents中观察者返回值若带then方法即 thenable/Promise就用result.then(...)继续串行执行剩余事件链.catch则转入failTransit取消过渡并把异常抛出若返回false则直接取消过渡endTransit(false)否则同步继续。更完整的异步场景菜单淡入淡出切换游戏界面见 Asynchronous Transitions。官方特别警告务必保证 Promise 最终被 resolve 或 reject否则状态机会永远卡在 pending 过渡中。isPending()与onPendingTransitionsrc/jsm.js的存在就是为了兜底检测这类情况。Conditional Transitions运行时动态选择目标状态2.x 不支持not supported。3.x 中过渡的to属性可以是一个函数在运行时根据当前状态与参数计算目标状态var fsm new StateMachine({ init: A, transitions: [ { name: step, from: *, to: function(n) { return increaseCharacter(this.state, n || 1) } } ] }); fsm.state; // A fsm.step(); fsm.state; // B fsm.step(5); fsm.state; // G // helper method to perform (c c n) on the 1st character in str function increaseCharacter(str, n) { return String.fromCharCode(str.charCodeAt(0) n); }从源码看条件过渡由seek处理src/jsm.js当to是函数时以状态机上下文为this调用之此外to还支持通配符*返回当前状态即自环。一个值得注意的细节条件目标状态只有在运行时真正到达后才会被计入allStates()因为transit中会动态调用config.addState(to)补注册src/jsm.js。条件过渡的完整说明见 States and Transitions。Goto无需预定义过渡即可改状态2.x 不支持not_supported。3.x 中可以用条件过渡 通配符from组合实现任意goto行为var fsm new StateMachine({ init: A transitions: [ { name: step, from: A, to: B }, { name: step, from: B, to: C }, { name: step, from: C, to: D }, { name: goto, from: *, to: function(s) { return s } } ] }) fsm.state; // A fsm.goto(D); fsm.state; // Dto: function(s) { return s }直接把传入参数作为目标状态。使用goto时完整的一套 Lifecycle Events 依然照常触发States and Transitions。State History记录并回溯状态历史2.x 不支持not supported。3.x 通过state-machine-history插件让状态机记录历史并支持 back/forward 遍历var StateMachineHistory require(javascript-state-machine/lib/history) var fsm new StateMachine({ init: A, transitions: [ { name: step, from: A, to: B }, { name: step, from: B, to: C }, { name: step, from: C, to: D } ], plugins: [ new StateMachineHistory() // -- plugin enabled here ] }) fsm.history; // [ A ] fsm.step(); fsm.history; // [ A, B ] fsm.step(); fsm.history; // [ A, B, C ] fsm.clearHistory(); fsm.history; // [ ]历史遍历方法historyBack/historyForward会真实改变当前状态并继续触发完整生命周期事件可用canHistoryBack/canHistoryForward判断是否可继续回溯插件还支持{ max: 100 }限制只保留最近 N 个状态以及{ name: memory }自定义属性名以避免命名冲突。完整用法见 State History插件实现在 src/plugin/history.js。Visualization用 GraphViz 把状态机画成有向图2.x 不支持not_supported。3.x 提供visualize方法把状态机配置转换成 GraphViz 的.dot语言var visualize require(javascript-state-machine/lib/visualize); var fsm new StateMachine({ init: open, transitions: [ { name: close, from: open, to: closed }, { name: open, from: closed, to: open } ] }); visualize(fsm)生成如下.dot语法digraph fsm { closed; open; closed - open [ label open ]; open - closed [ label close ]; }可视化还支持增强定制在过渡上附加dot属性如color、headport、tailport并通过visualize(fsm, { name: door, orientation: horizontal })指定图名与方向rankdirLR工厂StateMachine.factory同样可以直接可视化。实现见 src/plugin/visualize.js详细语法与多种示例门、物质三态、向导、ATM见 Visualization。Build SystemWebpack Ava 的新构建与测试体系2.x 不支持not_supported。3.x 提供了基于Webpack的构建系统和基于Ava的单元测试套件。从 package.json 可以看到完整脚本scripts: { start: npm run watch, build: npm run bundle npm run minify, bundle: webpack, minify: bin/minify, watch: ava --watch, test: nyc ava -v nyc report --reporterhtml }npm run build先 webpack 打包生成 dist/ 下的浏览器可用版本再 minify 压缩npm test用nyc覆盖率ava运行 test/ 目录下的全部测试。仓库根目录的 webpack.config.js 定义了打包入口src/jsm.js、src/app.js、src/plugin/*等src/ 下的源码经过打包生成 lib/ 下的可直接 require 的文件如lib/state-machine.js、lib/history.js、lib/visualize.js。参与贡献的方式见 Contributing。Other Breaking ChangesisFinished不再是内置方法3.0 中isFinished不再内置。官方给出的替代方案是用自定义methods轻松补回var fsm new StateMachine({ methods: { isFinished: function() { return this.state done } } })这也是methods新机制的直接收益——任何自定义方法都可以和状态机能力平级挂载。UPGRADE SUMMARY 升级速查表以下清单汇总了升级到 3.0 所需的全部改动官方原版摘要用new StateMachine()替换StateMachine.create()重命名initial→initevents→transitionscallbacks→methodsfsm.current→fsm.state更新你的回调方法方法名改为传统 JavaScriptcamelCase改用单个lifecycle参数取代原来的event, from, to独立参数更新异步回调方法返回Promise取代StateMachine.ASYNC就绪后resolve()该 Promise取代手动调用fsm.transition()用StateMachine.create({ target: FOO })的替换方案若FOO是类 →StateMachine.factory(FOO, { ... })若FOO是对象 →StateMachine.apply(FOO, { ... })迁移建议与验证路径升级时可以对照仓库内的测试用例验证迁移结果test/construction.js、test/defaults.js、test/observers.js、test/lifecycle.js、test/plugins.js 等覆盖了 3.x 全部新 API 的行为。官方在 README.md 中还给出了浏览器script srcstate-machine.js、npm install --save-dev javascript-state-machine、require(javascript-state-machine)三种使用入口升级后按此接入即可。最后重申官方文档的建议如果你正从 2.x 升级到 3.x请仔细通读本文原文档Please read this article carefully if you are upgrading from version 2.x to 3.x。这套清单配合源码链路足以让你在最短时间内完成一次无痛迁移。赞分享开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载相关推荐javascript-state-machine版本迁移指南从2.x到3.xjavascript state machine版本迁移指南从2.x到3.x 你是否正在使用javascript state machine 2.x版本想要开发工具终极迁移指南如何从javascript-state-machine 2.x平滑升级到3.x终极迁移指南如何从javascript state machine 2.x平滑升级到3.x javascript state machine 3.0版本是一个开发工具终极Bacon.js迁移指南从2.x到3.x版本升级完全手册终极Bacon.js迁移指南从2.x到3.x版本升级完全手册 Bacon.js是一款功能强大的函数式响应式编程库专为TypeScript和JavaScrip前端上一篇Sunshine 安装完全指南免费把一台 PC 变游戏串流服务器下一篇SGLangAscend性能优化指南如何配置参数实现28000令牌的高效预填充创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

The Concise TypeScript Book 精读:TypeScript 索引签名(Index Signatures)的三种键类型与 JavaScript 键转换机制
The Concise TypeScript Book 精读:TypeScript 索引签名(Index Signatures)的三种键类型与 JavaScript 键转换机制

文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 导读 索引签名&am… · 2026/9/25 5:21:39

Swift Package Manager 的 `swift package` 命令完全指南:创建、编辑、检查与维护 Swift 包
Swift Package Manager 的 `swift package` 命令完全指南:创建、编辑、检查与维护 Swift 包

开发工具构建工具 【免费下载链接】swift-package-manager The Package Manager for the Swift Programming Language 项目地址: https://gitcode.com/gh_mirrors/sw/swift-package-manager 点击查看 免费下载 swift package 是 Swift Package Manager(… · 2026/9/25 5:21:39

Amazon SNS 实战指南(AWS SDK for Java 2.x):从 Hello SNS 到 FIFO 主题与 SNS 到 SQS 扇出
Amazon SNS 实战指南(AWS SDK for Java 2.x):从 Hello SNS 到 FIFO 主题与 SNS 到 SQS 扇出

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地… · 2026/9/25 5:21:39

高云FPGA ILA调试实战:从配置失效到波形捕获的全流程解析
高云FPGA ILA调试实战:从配置失效到波形捕获的全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:24:06

游戏窗口拉不动?用SRWE的Force EXITSIZEMOVE开关解决Hotsampling失效问题
游戏窗口拉不动?用SRWE的Force EXITSIZEMOVE开关解决Hotsampling失效问题

游戏窗口拉不动?用SRWE的Force EXITSIZEMOVE开关解决Hotsampling失效问题 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 玩窗口化游戏想拍高清截图,却遇到"改了分辨率游戏画面不跟… · 2026/9/25 6:24:06

CTF夺旗赛入门指南:从Web渗透到逆向分析的完整学习路径
CTF夺旗赛入门指南:从Web渗透到逆向分析的完整学习路径

1. CTF到底是个什么竞赛先说一句可能会得罪人的话:很多刚接触网络安全的人,是被"黑客""攻防""破解"这些词吸引进来的,但真正入行以后你会发现,CTF才是离"白帽思维"最近的训练场。CTF&… · 2026/9/25 6:24:06

ATGM332D RMC报文解析与北京时间转换实战:从原始数据到可用定位
ATGM332D RMC报文解析与北京时间转换实战:从原始数据到可用定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:24:00

ESP32换板为何不能直接运行?小智源码适配本质解析
ESP32换板为何不能直接运行?小智源码适配本质解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:24:00

CANoe中LIN诊断调度表4种切换模式深度解析
CANoe中LIN诊断调度表4种切换模式深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:23:59

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码