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

RxJS v4 测试工具指南:深入理解 `Rx.ReactiveTest` 与虚拟时间断言体系

发布时间:2026/9/21 1:36:39 来源:云帆数科 栏目:资讯中心
RxJS v4 测试工具指南:深入理解 `Rx.ReactiveTest` 与虚拟时间断言体系
RxJS v4 测试工具指南深入理解Rx.ReactiveTest与虚拟时间断言体系【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJS导读Rx.ReactiveTest是 RxJS v4 测试体系rx.testing.js中的核心工具类为单元测试提供「在指定虚拟时间点构造通知记录」的工厂方法以及三个标准的虚拟时间常量。本文以 ReactiveTest 官方文档 为骨架结合 源码实现、TestScheduler 及其配套的 Recorded / Subscription / MockObserver 等测试组件系统讲解如何用onNext/onError/onCompleted构造时间戳化通知、如何用created/subscribed/disposed控制测试时间轴并给出可直接运行的完整测试用例。读完本文你将掌握 RxJS v4 虚拟时间测试的完整方法论能够独立编写可断言的响应式流测试。一、Rx.ReactiveTest是什么Rx.ReactiveTest是一个静态工具类本质是一个挂载在Rx.ReactiveTest上的对象字面量用于创建用于测试目的的通知记录notification records。它位于rx.testing.js中是Rx.TestScheduler虚拟时间测试体系的基础设施。从源码看Rx.ReactiveTest对象定义了三个字段与四个工厂方法reactivetest.js成员类型说明createdNumber 字段默认创建可观察序列的虚拟时间值恒为100subscribedNumber 字段默认订阅可观察序列的虚拟时间值恒为200disposedNumber 字段默认释放订阅的虚拟时间值恒为1000onNext(ticks, value)方法构造 OnNext 通知记录onError(ticks, exception)方法构造 OnError 通知记录onCompleted(ticks)方法构造 OnCompleted 通知记录subscribe(start, end)方法构造订阅记录Subscription1.1 与 Recorded / Subscription 的关系ReactiveTest工厂方法返回的对象并非普通通知而是携带虚拟时间戳的记录onNext/onError/onCompleted返回 Rx.Recorded 实例内部封装一个 Rx.Notificationsubscribe返回 Rx.Subscription 实例记录订阅与退订时间。这一点在 reactivetest.js 中体现得非常直接onNext: function (ticks, value) { return typeof value function ? new Recorded(ticks, new OnNextPredicate(value)) : new Recorded(ticks, Notification.createOnNext(value)); }, onError: function (ticks, error) { return typeof error function ? new Recorded(ticks, new OnErrorPredicate(error)) : new Recorded(ticks, Notification.createOnError(error)); }, onCompleted: function (ticks) { return new Recorded(ticks, Notification.createOnCompleted()); }, subscribe: function (start, end) { return new Subscription(start, end); }关键点onNext与onError的第二个参数既可以是具体的值/错误对象也可以是一个谓词函数predicate。传入函数时源码会分别包装成OnNextPredicate/OnErrorPredicatereactivetest.js其equals方法会在断言时用该函数对实际值求值从而实现「按条件比较」而非「严格相等比较」。二、类方法详解三个通知工厂2.1Rx.ReactiveTest.onCompleted(ticks)在给定时间创建 OnCompleted 通知记录的工厂方法。参数ticks(Number)—— OnCompleted 通知发生的记录虚拟时间。返回值(Recorded)—— OnCompleted 通知记录。位置rx.testing.js源码见 reactivetest.js示例构造一个在虚拟时间260完成的热可观察序列并断言结果var onCompleted Rx.ReactiveTest.onCompleted; var scheduler new Rx.TestScheduler(); var xs scheduler.createHotObservable( onCompleted(260) ); var res scheduler.startScheduler(function () { return xs.map(function (x) { return x; }); }); // 编写自定义断言 collectionAssert.assertEqual(res.messages, [ onCompleted(260) ]);2.2Rx.ReactiveTest.onError(ticks, exception)在给定时间、携带给定错误创建 OnError 通知记录的工厂方法。参数ticks(Number)—— OnError 通知发生的记录虚拟时间exception(Error | Function)—— 存入 OnError 通知的错误对象或一个谓词函数。返回值(Recorded)—— OnError 通知记录。位置rx.testing.js源码见 reactivetest.js示例用new Error(woops)构造错误断言时既可以直接比较错误对象也可以改用谓词按e.message匹配var ex new Error(woops); var onError Rx.ReactiveTest.onError; var scheduler new Rx.TestScheduler(); var xs scheduler.createHotObservable( onError(201, ex) ); var res scheduler.startScheduler(function () { return xs.map(function (x) { return x; }); }); // 编写自定义断言 collectionAssert.assertEqual(res.messages, [ // 使用谓词 onError(201, function (e) { return e.message woops; }) ]);2.3Rx.ReactiveTest.onNext(ticks, value)在给定时间、携带给定值创建 OnNext 通知记录的工厂方法。参数ticks(Number)—— OnNext 通知发生的记录虚拟时间value(Any | Function)—— 存入 OnNext 通知的值或一个谓词函数。返回值(Recorded)—— OnNext 通知记录。位置rx.testing.js源码见 reactivetest.js示例断言收到值42既可直接比较也可用谓词x 42var onNext Rx.ReactiveTest.onNext; var scheduler new Rx.TestScheduler(); var xs scheduler.createHotObservable( onNext(201, 42) ); var res scheduler.startScheduler(function () { return xs.map(function (x) { return x; }); }); // 编写自定义断言 collectionAssert.assertEqual(res.messages, [ // 使用谓词 onNext(201, function (x) { return x 42; }) ]);设计意图谓词形式的断言在比较引用类型、时间戳或异步错误时尤其有用——断言只关心「这条记录是否满足某个条件」而不必与预期对象严格同引用。这正是OnNextPredicate/OnErrorPredicate的equals实现reactivetest.js所保证的。三、类字段详解三个虚拟时间常量ReactiveTest还提供了三个默认虚拟时间常量它们是 TestScheduler.startScheduler 在未显式指定设置时的默认值字段值语义created100创建可观察序列的默认虚拟时间subscribed200订阅可观察序列的默认虚拟时间disposed1000释放订阅的默认虚拟时间源码中直接定义了这三个常量reactivetest.jscreated: 100, subscribed: 200, disposed: 1000,而startScheduler会在设置缺失时回退到这些值testscheduler.jssettings || (settings {}); settings.created null (settings.created ReactiveTest.created); settings.subscribed null (settings.subscribed ReactiveTest.subscribed); settings.disposed null (settings.disposed ReactiveTest.disposed);3.1created—— 创建时间默认 100默认用于在单元测试中创建可观察序列的虚拟时间。显式传入时通常以位置参数形式出现var scheduler new Rx.TestScheduler(); var xs scheduler.createHotObservable( Rx.ReactiveTest.onNext(201, 42), Rx.ReactiveTest.onNext(202, 56), Rx.ReactiveTest.onCompleted(203) ); var res scheduler.startScheduler( function () { return xs.map(function (x) { return x; })}, Rx.ReactiveTest.created, // 创建时间 Rx.ReactiveTest.subscribed, // 订阅时间 Rx.ReactiveTest.disposed // 释放时间 );3.2subscribed—— 订阅时间默认 200默认用于在单元测试中订阅可观察序列的虚拟时间。它是「热序列 vs 冷序列」行为差异的关键分界点下文 4.1 会详述。3.3disposed—— 释放时间默认 1000默认用于在单元测试中释放订阅的虚拟时间。释放后后续到达的通知不再被记录到res.messages。这三个字段除位置参数形式外还可以封装进一个settings对象传给startSchedulervar res scheduler.startScheduler( function () { return xs.map(function (x) { return x; })}, { created: Rx.ReactiveTest.created, subscribed: Rx.ReactiveTest.subscribed, disposed: Rx.ReactiveTest.disposed } );两种传参方式等价——startScheduler(create, settings)的第二个参数既可传扁平位置参数created, subscribed, disposed也可传{ created, subscribed, disposed }对象见 testscheduler.js。四、实战与 TestScheduler 组合的完整测试流程ReactiveTest的工厂方法本身不执行任何调度它们只负责「造记录」。真正驱动虚拟时间前进的是Rx.TestScheduler——一个继承自Rx.VirtualTimeScheduler的虚拟时间调度器专用于测试基于 Reactive Extensions 构建的应用与库testscheduler.js。4.1 经典完整用例热序列下面的例子是官方文档给出的标准工作流先创建热可观察序列在150发1、210发2、220发3、230完成再以默认时间轴created100、subscribed200、disposed1000启动对结果做map(x x * x)后断言function createMessage(expected, actual) { return Expected: [ expected.toString() ]\r\nActual: [ actual.toString() ]; } // 使用 QUnit 进行断言 var collectionAssert { assertEqual: function (actual, expected) { var comparer Rx.internals.isEqual, isOk true; if (expected.length ! actual.length) { ok(false, Not equal length. Expected: expected.length Actual: actual.length); return; } for(var i 0, len expected.length; i len; i) { isOk comparer(expected[i], actual[i]); if (!isOk) { break; } } ok(isOk, createMessage(expected, actual)); } }; var onNext Rx.ReactiveTest.onNext, onCompleted Rx.ReactiveTest.onCompleted, subscribe Rx.ReactiveTest.subscribe; var scheduler new Rx.TestScheduler(); // 创建热可观察序列订阅后立即开始发射 var xs scheduler.createHotObservable( onNext(150, 1), onNext(210, 2), onNext(220, 3), onCompleted(230) ); // 注意默认在 200 订阅因此会错过 150 时刻的通知 var res scheduler.startScheduler(function () { return xs.map(function (x) { return x * x }); }); // 实现集合断言 collectionAssert.assertEqual(res.messages, [ onNext(210, 4), onNext(220, 9), onCompleted(230) ]); // 检查订阅/退订时间 collectionAssert.assertEqual(xs.subscriptions, [ subscribe(200, 230) ]);这个用例同时展示了三件事通知断言res.messages记录了订阅窗口内收到的全部带时间戳通知订阅断言xs.subscriptions记录了热序列被订阅200与退订230因流在 230 完成的时间时间过滤150时刻的onNext(150, 1)因为发生在订阅时刻200之前而被正确忽略——这是热可观察序列hotobservable.js的固有行为通知按绝对虚拟时间发射订阅者只收到订阅之后的通知。4.2 冷序列偏移语义与热序列相反冷可观察序列createColdObservable的通知时刻是相对订阅时刻的偏移量。其实现coldobservable.js在订阅时才将相对时间换算为绝对时间因此下面例子里onNext(150, 1)实际出现在绝对时间200 150 350之后……不过要注意的是startScheduler默认在200订阅、1000释放冷序列onNext(150,1)、onNext(200,2)、onNext(250,3)、onCompleted(300)会分别落在绝对时间350、400、450、500var onNext Rx.ReactiveTest.onNext, onCompleted Rx.ReactiveTest.onCompleted, subscribe Rx.ReactiveTest.subscribe; var scheduler new Rx.TestScheduler(); // 创建冷可观察序列时刻相对订阅时间偏移 var xs scheduler.createColdObservable( onNext(150, 1), onNext(200, 2), onNext(250, 3), onCompleted(300) ); // 注意默认在 200 订阅 var res scheduler.startScheduler(function () { return xs.filter(function (x) { return x % 2 0; }); }); // 实现集合断言 collectionAssert.assertEqual(res.messages, [ onNext(400, 2), onCompleted(500) ]); // 检查订阅/退订时间 collectionAssert.assertEqual(xs.subscriptions, [ subscribe(200, 500) ]);由于filter只放行偶数1与3被过滤最终在绝对时间400 200 200收到2在500 200 300完成订阅记录为subscribe(200, 500)。冷序列因此特别适合测试「相对时序」与运算符的重排行为。4.3 用createObserver手工控制时间轴当需要更精细地控制「何时订阅、何时退订」时可以用scheduler.createObserver()创建记录型观察者再配合scheduleAbsolute手工编排。MockObservermockobserver.js内部维护一个messages数组每次收到通知都会用当前scheduler.clock打上时间戳var onNext Rx.ReactiveTest.onNext; var scheduler new Rx.TestScheduler(); var d new Rx.SerialDisposable(); var xs Rx.Observable.return(42, scheduler); var res scheduler.createObserver(); scheduler.scheduleAbsolute(null, 100, function () { return d.setDisposable(xs.subscribe( function (x) { d.dispose(); res.onNext(x); }, res.onError.bind(res), res.onCompleted.bind(res) )); }); scheduler.start(); collectionAssert.assertEqual(res.messages, [ onNext(101, 42) ]);Rx.Observable.return(42, scheduler)在虚拟时间100 1 101时刻发射42测试断言精确到单个 tick。4.4 测试 PromisecreateResolvedPromise/createRejectedPromiseTestScheduler 还提供了两个 Promise 工厂testscheduler.js内部用MockPromisemockpromise.js实现它把「resolve/reject」也翻译成带时间戳的虚拟通知createResolvedPromise(ticks, value)→ 在ticks时刻依次发射onNext(ticks, value)与onCompleted(ticks)createRejectedPromise(ticks, reason)→ 在ticks时刻发射onError(ticks, reason)。解析成功示例var onNext Rx.ReactiveTest.onNext, onError Rx.ReactiveTest.onError, onCompleted Rx.ReactiveTest.onCompleted; var scheduler new Rx.TestScheduler(); // 创建已解析的 Promise var xs scheduler.createResolvedPromise(201, 1); // 注意默认在 200 订阅 var res scheduler.startScheduler(function () { // 由于 issue #976需要显式传入测试调度器 return Rx.Observable.fromPromise(xs, scheduler); }); // 实现集合断言 collectionAssert.assertEqual(res.messages, [ onNext(201, 1), onCompleted(201) ]);拒绝示例var scheduler new Rx.TestScheduler(); // 创建被拒绝的 Promise var error new Error(); var xs scheduler.createRejectedPromise(201, error); var res scheduler.startScheduler(function () { // 由于 issue #976需要显式传入测试调度器 return Rx.Observable.fromPromise(xs, scheduler); }); // 实现集合断言 collectionAssert.assertEqual(res.messages, [ onError(201, error) ]);注意官方文档标注的坑调用Rx.Observable.fromPromise时必须把scheduler作为第二参数显式传入参见 issue #976否则 Promise 的调度时机无法被虚拟时间控制。4.5startScheduler的三个默认时刻startScheduler(create, settings)的完整签名与默认值testscheduler.js参数类型默认值语义createFunction—必填创建可观察序列的工厂函数settings.createdNumber100调用create创建序列的时刻settings.subscribedNumber200订阅所创建序列的时刻settings.disposedNumber1000释放订阅的时刻其内部流程如下源码级createObserver()创建记录型观察者在created时刻调度createFn()得到source在subscribed时刻调度source.subscribe(observer)在disposed时刻调度subscription.dispose()调用this.start()推进虚拟时钟直到队列清空返回携带时间戳通知的 observer。返回的 observer 的messages属性Recorded[]即最终断言对象。五、配套断言对象Recorded 与 Subscription 深入理解ReactiveTest工厂返回的对象类型才能写出准确的断言。5.1Rx.Recorded—— 值 虚拟时间Recorded记录「值在哪个虚拟时间产生」recorded.jsvar Recorded Rx.Recorded function (time, value, comparer) { this.time time; this.value value; this.comparer comparer || defaultComparer; };实例属性time产生值的虚拟时间、value产生的值、comparer比较器默认使用Rx.internals.isEqualequals(other)要求time严格相等且comparer(this.value, other.value)为真recorded.jstoString()返回valuetime形式如foo201recorded.js。var r1 new Rx.Recorded(201, foo); var r2 new Rx.Recorded(201, bar); var r3 new Rx.Recorded(201, foo); console.log(r1.equals(r2)); // false console.log(r1.equals(r3)); // true console.log(r1.toString()); // foo201当ReactiveTest.onNext收到谓词函数时value位置保存的其实是一个OnNextPredicate包装对象其equals通过调用谓词完成比较reactivetest.js。5.2Rx.Subscription—— 订阅/退订时间对Subscription记录对可观察序列的订阅与退订subscription.jsvar Subscription Rx.Subscription function (start, end) { this.subscribe start; this.unsubscribe end || Number.MAX_VALUE; };实例属性subscribe订阅虚拟时间、unsubscribe退订虚拟时间缺省为Number.MAX_VALUE即「无限/未退订」equals(other)订阅与退订时间均严格相等才返回truesubscription.jstoString()未退订输出(201, Infinite)已退订输出(201, 1000)subscription.js。var s1 new Rx.Subscription(201, 500); var s2 new Rx.Subscription(201); var s3 new Rx.Subscription(201, 500); console.log(s1.equals(s2)); // false console.log(s1.equals(s3)); // true console.log(s2.toString()); // (201, Infinite)热/冷可观察序列的subscriptions数组正是由Subscription实例构成订阅时记录subscribe时间退订时补全unsubscribe时间见 hotobservable.js。六、在仓库中的实际印证ReactiveTest并非「文档专属」的纸面 API仓库自身的测试套件就大量使用它。以 tests/observable/where.js 为例其头部直接从Rx.ReactiveTest解构出全部工厂方法与常量onNext Rx.ReactiveTest.onNext, onError Rx.ReactiveTest.onError, onCompleted Rx.ReactiveTest.onCompleted, subscribe Rx.ReactiveTest.subscribe, created Rx.ReactiveTest.created, subscribed Rx.ReactiveTest.subscribed, disposed Rx.ReactiveTest.disposed;随后几乎所有测试都遵循同一模式用Rx.TestSchedulercreateHotObservable/createColdObservable构造输入用startScheduler运行被测运算符最后用collectionAssert.assertEqual(res.messages, [...])断言通知序列、用xs.subscriptions断言订阅生命周期。你可以参考 tests/observable 目录下的 130 余个测试文件为每个运算符找到对应的ReactiveTest使用范式。加载与依赖说明ReactiveTest位于rx.testing.js构建产物见 modules/rx-lite-testing/rx.lite.testing.js 与 modules/rx-core-testing/rx.core.testing.js使用前提需先加载rx.js/rx.compat.js/rx.lite.js等任一完整核心库包分发npm 包rx、NuGet 包RxJS-All与RxJS-Testing对应 nuget/RxJS-Testing均包含该模块相关类型定义位于 ts/rx.testing.d.ts可配合 TypeScript 使用。七、小结Rx.ReactiveTest是 RxJS v4 虚拟时间测试的「语法核心」三个工厂方法onNext/onError/onCompleted把「值/错误/完成 虚拟时间」封装成Recorded记录且支持谓词形式的柔性断言三个时间常量created(100)/subscribed(200)/disposed(1000)定义了startScheduler的默认测试时间轴subscribe(start, end)生成Subscription记录用于断言订阅/退订生命周期与Rx.TestScheduler热/冷序列工厂、createObserver、Promise 工厂、startScheduler组合后即可对任意运算符的时序行为做确定性、可重复的单元测试。这套「记录-调度-断言」的测试方法论不仅支撑了本仓库 tests 目录下全部运算符测试也构成了后来 RxJS 各版本虚拟时间测试marble testing的直接前身。掌握ReactiveTest就等于掌握了 RxJS 时序测试的根基。【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Relay 14 指南:使用 useRefetchableFragment 以不同数据重取 Fragment
Relay 14 指南:使用 useRefetchableFragment 以不同数据重取 Fragment

前端开发工具 【免费下载链接】relay Relay is a JavaScript framework for building data-driven React applications. 项目地址: https://gitcode.com/gh_mirrors/relay29/relay 点击查看 免费下载 本篇技术指南围绕 Relay 中“以不同数据重取 Fragment&#xff… · 2026/9/21 1:36:39

基于 WKWebView 的官方 iOS WebView 插件:webview_flutter_wkwebview 深度指南
基于 WKWebView 的官方 iOS WebView 插件:webview_flutter_wkwebview 深度指南

基于 WKWebView 的官方 iOS WebView 插件:webview_flutter_wkwebview 深度指南 【免费下载链接】plugins Plugins for Flutter maintained by the Flutter team 项目地址: https://gitcode.com/gh_mirrors/pl/plugins webview_flutter_wkwebview 是 Flutter … · 2026/9/21 1:36:39

在赛灵思FPGA上部署YOLOv2目标检测:从Darknet权重到硬件加速全流程解析
在赛灵思FPGA上部署YOLOv2目标检测:从Darknet权重到硬件加速全流程解析

简介:FPGA凭借可编程与高能效特性成为深度学习边缘部署的理想选择。面向AI嵌入式开发者与FPGA工程师,这份资源聚焦在赛灵思平台上完成YOLOv2实时目标检测算法的移植,覆盖模型优化、硬件逻辑设计、Vivado HLS生成IP核、系统集成与验证等关键环… · 2026/9/21 1:36:39

线扫相机触发方案全解析:编码器选型、分辨率匹配与调试避坑
线扫相机触发方案全解析:编码器选型、分辨率匹配与调试避坑

1. 线扫相机触发到底在解决什么问题线扫相机和面阵相机最大的区别,在于它每次只拍一条线。面阵相机是“咔嚓”一下拿一整幅图,线扫相机则是像扫描仪一样,一行一行地把图像拼出来。这就带来一个绕不开的问题:相机什么时候该拍下一行… · 2026/9/21 2:24:48

STM32外设DeInit()函数详解:为何必须与Init成对使用
STM32外设DeInit()函数详解:为何必须与Init成对使用

简介:一份讲解STM32中DeInit()函数作用与必要性的PDF资料,面向嵌入式开发者和高校单片机学习者。文档围绕“为什么每个STM32模块都提供DeInit()”这一常见疑惑,先厘清Init()负责配置工作模式、波特率、中断等并启动模块,而DeInit(… · 2026/9/21 2:24:48

宏基因组功能注释实战:CAZyme与VFDB数据库搭建及DIAMOND比对全流程
宏基因组功能注释实战:CAZyme与VFDB数据库搭建及DIAMOND比对全流程

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

BrowserSkill VOM视觉观察模型解析:从页面构建语义图的完整流程
BrowserSkill VOM视觉观察模型解析:从页面构建语义图的完整流程

BrowserSkill VOM视觉观察模型解析:从页面构建语义图的完整流程 【免费下载链接】BrowserSkill Let AI agents use your real, logged-in browser without interrupting your work. CLI extension for browser automation across any shell-capable AI agent. 项… · 2026/9/21 2:24:48

DBX CLI 数据库安全查询与 Schema 探索指南:面向 AI Agent 的命令行操作手册
DBX CLI 数据库安全查询与 Schema 探索指南:面向 AI Agent 的命令行操作手册

DBX CLI 数据库安全查询与 Schema 探索指南:面向 AI Agent 的命令行操作手册 【免费下载链接】dbx 25 MB lightweight cross-platform database client for 90 databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. B… · 2026/9/21 2:24:48

硅基集成光电子:核心材料体系与集成路线全解析
硅基集成光电子:核心材料体系与集成路线全解析

简介:《新型硅基集成微电子及光电子的材料》是一份面向微电子、光电子及相关专业学生与技术人员的PPT文档,系统讲解硅基集成微电子与光电子材料领域的关键技术。内容以摩尔定律为线索,梳理IC集成度每两年翻一番、特征尺寸持续缩小的产业规律&… · 2026/9/21 2:23:47

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化
Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡… · 2026/9/21 0:02:39

Word表格编号全攻略:从列表编号到题注交叉引用
Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技… · 2026/9/21 0:02:39

从第一个站到第二个站:独立开发者的静态网站选型与落地实践
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&… · 2026/9/20 0:00:41

Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行
Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行

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

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程
agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and … · 2026/9/21 0:00:18

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,… · 2026/9/21 0:00:18

了解更多?预约专属演示

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

企业微信二维码