news 2026/9/19 5:10:20

window.postMessage 跨域通信实战指南:从参数详解到安全避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
window.postMessage 跨域通信实战指南:从参数详解到安全避坑

接手过跨域页面通信需求的人,大概率都经历过这样的场景:页面上嵌了一个第三方 iframe,需要告诉它"用户已登录,uid 是 123",或者反过来子页面要通知父页面"订单状态变了"。传统的 Cookie、URL 参数在这些场景下要么不安全,要么绕太远,而 window.postMessage 就是浏览器原生提供的、专门解决跨源通信问题的接口。这篇文章我会把 postMessage 的完整用法、参数细节、transferable 接口原理,以及我在实际项目中踩过的坑,一次性讲清楚。

这篇文章适合谁?前端开发者、正在做微前端方案选型的人、用 Web Worker 做性能优化的同学,还有所有被"跨域通信"折磨过的人。不需要多深的基础,但我默认你至少写过一点 JavaScript,能看懂事件监听和回调就行。我会尽量把一个偏底层的 API 讲成人话,保证你看完能直接上手,也能避开那些文档里不会写明的雷区。

1. 先搞懂 postMessage 到底解决什么问题

1.1 同源策略的边界在哪

浏览器的同源策略是个老生常谈的话题:协议、域名、端口三者一致才算同源。同源策略限制了三个核心能力——DOM 访问、Cookie 与 Storage 读取、以及网络请求的跨源限制。也就是说,一个运行在https://a.example.com的页面,默认情况下是碰不到https://b.example.com里的任何 DOM 的,也读不到对方的 localStorage,更没法通过XMLHttpRequest直接去请求对方的数据。

这个策略在保护用户数据安全方面立了大功,但也带来了一个现实问题:业务上确实存在合法的跨源通信需求。典型的例子就是支付页面——主站页面嵌入了第三方支付平台提供的 iframe,用户输入银行卡信息,支付完成后 iframe 需要告诉主站"支付成功,订单号是这个"。这种场景如果全靠 URL 参数传递,信息会暴露在地址栏和浏览器历史记录里;如果用 Cookie,又面临 SameSite 和跨域读写权限的复杂限制。于是浏览器提供了window.postMessage这个专用通道,让两个不同源的窗口可以安全地交换消息。

1.2 postMessage 的核心价值与适用场景

postMessage最核心的价值是:它让跨源通信变成了一条"有门禁的通道"。消息虽然是明文发送的,但接收方可以通过event.origin校验发送方的身份,发送方也可以通过targetOrigin限定消息只发给指定源。相比直接操作 DOM 或者 URL 传参,这条通道要安全得多,而且是浏览器原生支持的,不需要任何第三方库。

就我接触过的项目,postMessage 最常见的三个场景是:第一,父页面与 iframe 互相通信,比如主站给嵌入式报表 iframe 传筛选条件、iframe 把操作结果回传给主站;第二,主线程与 Web Worker 之间传递大体积数据,比如图像处理、音视频解码这类耗时任务;第三,页面与window.open弹出的子窗口之间同步状态,比如 OAuth 登录弹窗登录完成后通知父页面刷新用户信息。后面的章节我会逐一给出可用的示例代码。

2. 参数逐个拆解:message、targetOrigin、transfer

2.1 message 参数:能传什么,不能传什么

postMessage的完整签名是targetWindow.postMessage(message, targetOrigin, transfer)。第一个参数message是真正要传递的数据,它可以是任意结构化可克隆的值。这句话看上去轻描淡写,实际上"结构化可克隆"这五个字划定了严格的数据边界。

可以用message传递的数据包括:原始类型(string、number、boolean、null、undefined、bigint)、数组和普通对象、Date、RegExp、Blob、File、ArrayBuffer 以及各类 TypedArray 视图、ImageBitmap、ImageData、Map、Set、Error 对象(现代浏览器支持)等。但是函数、DOM 元素、Symbol、自定义类的实例(原型链会丢失,传过去变成普通对象)、WeakMap/WeakSet、Promise 这些都无法通过结构化克隆算法处理。如果你传了不允许的数据,浏览器会直接抛DataCloneError

这里有一个很容易踩的点:很多人以为传一个普通对象就万事大吉,但对象内部如果嵌套了函数或者 DOM 引用,整个序列化就会失败。我见过一个同学把包含了onClick回调的对象直接丢进postMessage,结果页面报错半天没定位到原因。所以发送前最好心里过一遍:这个数据里有没有函数、有没有 DOM 节点引用、有没有类实例。如果有,要么改造数据结构,要么走别的通信方案。

2.2 targetOrigin 参数:安全的第一道闸门

第二个参数targetOrigin指定了消息允许发送到哪个源,它有以下几种写法:明确指定源的字符串,比如'https://example.com';写/表示与当前窗口同源;以及通配符'*'表示不限制目标源。

我强烈建议,在能明确目标源的情况下永远不要用'*'。原因很简单:'*'意味着任何其他窗口都能收到这份消息,如果消息里包含了用户标识、业务数据,就等于把这些信息广播到了所有同浏览器上下文里能访问到该窗口的对象中。安全上最稳妥的做法是显式写出目标源的协议、域名和端口,这样即使消息被恶意页面截获,目标窗口在逻辑上也会拒绝处理来源不明的消息。

另外注意targetOrigin匹配的是目标窗口的源,不是发送方的源。比如父页面在https://a.com,给https://b.com的 iframe 发消息,targetOrigin应写'https://b.com'。有些同学会习惯性写自己的域名,消息发出去对面收不到,排查半天才发现是这里搞反了。

2.3 transfer 参数:不仅传数据,还传所有权

第三个参数transfer是最容易被忽略、但性能收益最明显的一个。它是一个 Transferable 对象的数组,用来把某些特殊类型的对象的所有权从发送方转移给接收方,而不是复制一份。所谓"所有权转移",你可以类比成你把一把钥匙亲手递给别人:交出去之后,你自己手里就没有这把钥匙了,不能再使用它。

具体到代码层面,最典型的例子是ArrayBuffer。当你用worker.postMessage(buffer, [buffer])发送一个 ArrayBuffer 时,发送方的buffer.byteLength会变成 0,实际数据被移交给了接收方,整个过程零拷贝。相比之下,如果不传transfer数组,浏览器会通过结构化克隆把整个 ArrayBuffer 的数据复制一份,数据量越大,拷贝开销越明显。

不过 transfer 并不是所有场景都适用,它要求对象必须实现 Transferable 接口。目前在浏览器里支持转移的对象主要有:ArrayBuffer、MessagePort、ImageBitmap、OffscreenCanvas,以及部分浏览器支持的 ReadableStream、WritableStream、TransformStream。普通对象、字符串、Blob 都不在 transfer 的范围内,你就算把普通对象写进transfer数组,浏览器也会忽略它。

3. Transferable 接口与结构化克隆算法深挖

3.1 结构化克隆到底做了什么

要理解 transferable,必须先搞清楚结构化克隆算法(Structured Clone Algorithm)怎么工作。它是 HTML 标准定义的一套序列化机制,比 JSON.stringify 强大得多:JSON 只能处理基本数据类型、数组和普通对象,遇到 Date 会变成字符串、遇到 Map 和 Set 会变成空对象、遇到 ArrayBuffer 干脆变成{}。而结构化克隆能够递归地保留这些类型的内在结构,Date 传过去还是 Date,Map 传过去还是 Map,TypedArray 的数据和视图信息都能完整保留。

结构化克隆在实现上走的是"拷贝"路线,也就是发送方和接收方各持有一份独立的数据。对于小数据量,这种方式完全够用,代码也更简单;但是当你需要传递一个 50MB 的 ArrayBuffer 时,一次完整的拷贝就意味着要额外分配 50MB 内存,加上序列化和反序列化的时间,主线程会出现肉眼可见的卡顿。Transferable 就是为了解决这个问题而存在的,它直接跳过拷贝过程,把底层内存块的所有权交给接收方。

需要特别注意的是,结构化克隆对"类实例"并不友好。一个带有原型的自定义类对象经过克隆后,接收方拿到的是一个普通对象,原型链上的方法全部丢失。这一点在设计通信协议时要提前想到,最好只在消息里传纯数据,不要传"会干活的对象",逻辑留在接收端自己处理。

3.2 常见 Transferable 对象盘点

目前实际工程里最常用的 Transferable 对象是ArrayBufferMessagePortImageBitmapOffscreenCanvas

ArrayBuffer是最典型的一个,主要用在 Web Worker 场景。比如主线程读取了一个大文件的 ArrayBuffer,要交给 Worker 做哈希计算,用transfer转移可以避免一次大块内存拷贝。MessagePortMessageChannel的产物,当你用new MessageChannel()创建一对端口后,可以通过 transfer 把一个端口交给另一个执行上下文,从而建立两条上下文之间的直接通信管道。ImageBitmapOffscreenCanvas则更多用在图像处理场景,你可以把解码后的图像位图直接转给 Worker 做滤镜处理,或者是把 WebGL 的绘制结果转移出去。

下表是我整理的常见类型在 postMessage 中是否支持结构化克隆、是否支持 transfer:

数据类型结构化克隆transfer 转移说明
原始类型(string/number/boolean)支持不支持按值传递
普通对象 / 数组支持不支持深拷贝语义
Date / RegExp / Map / Set支持不支持保留类型信息
Blob / File / FileList支持不支持内部数据引用复制
ArrayBuffer支持支持最常用的可转移对象
TypedArray(Int8Array 等)支持支持(转移其底层 buffer)需取.buffer放入 transfer
ImageBitmap支持支持图像数据处理利器
MessagePort支持支持建立独立通信管道的核心
OffscreenCanvas支持支持图形绘制转移
函数 / Symbol不支持不支持直接抛 DataCloneError
DOM 元素不支持不支持无法跨上下文传递

3.3 什么时候该用 transfer,什么时候不该用

transfer 并不是万能的,它有一个必须权衡的代价:数据一旦转移,发送方就彻底失去了对这块内存的访问权。如果你发送完 ArrayBuffer 之后还需要继续读取原始数据做其他计算,那就不能使用 transfer,否则会拿到一个 byteLength 为 0 的空壳。

我的建议是:数据量小于 1MB 时,没有必要用 transfer,直接走结构化克隆即可,代码更简洁也不会有明显的性能问题;数据量达到几十 MB 甚至上百 MB,或者你明确知道接收方处理完数据后发送方不再需要原始数据时,才值得付出"所有权移交"的代价换取零拷贝的性能收益。另外一个实用技巧是:如果你使用的是 TypedArray 视图,transfer 时需要传入typedArray.buffer而不是视图本身,接收方再根据自己的需求重新构造视图即可。

4. 三大典型场景的完整示例

4.1 父页面与 iframe 的通信

这是 postMessage 用得最多的场景。假设主站页面运行在https://app.example.com,内嵌一个报表 iframe,地址是https://report.example.com。主站需要把当前用户的筛选条件发给 iframe,iframe 加载完数据后通知主站"渲染完成"。

父页面的发送代码如下,核心点在于等 iframe 的load事件触发后再发送,避免消息发出去时子页面还没准备好监听器:

const iframe = document.getElementById('reportFrame'); iframe.addEventListener('load', function () { iframe.contentWindow.postMessage({ type: 'FILTER_CHANGE', payload: { dateRange: ['2025-01-01', '2025-03-31'], region: 'east', userId: 10086 } }, 'https://report.example.com'); });

iframe 内部的接收代码,必须做两件事:校验event.origin是不是可信来源,校验event.data.type是不是自己关心的消息类型:

const TRUSTED_ORIGIN = 'https://app.example.com'; window.addEventListener('message', function (event) { if (event.origin !== TRUSTED_ORIGIN) { return; // 来源不可信,直接丢弃 } const data = event.data; if (!data || typeof data !== 'object' || data.type !== 'FILTER_CHANGE') { return; // 结构不符合预期,丢弃 } renderReport(data.payload); });

子页面处理完数据后,可以用event.source给父页面回消息。event.source是发起消息的窗口对象的引用,回传时配合event.origin使用,可以保证消息准确回到来源窗口:

function notifyParent() { if (window.parent === window) { return; // 当前不是 iframe,没有父窗口 } window.parent.postMessage({ type: 'REPORT_READY', payload: { cost: 42.5 } }, 'https://app.example.com'); }

4.2 主线程与 Web Worker 的数据交互

Web Worker 场景下,postMessage 是唯一的通信手段,而且transfer参数在这里能发挥最大价值。假设我们要在一个 Worker 里对大文件做 SHA-256 哈希计算,主线程读取到了文件的 ArrayBuffer。

主线程代码:

const worker = new Worker('/hash-worker.js'); async function hashFile(file) { const buffer = await file.arrayBuffer(); // 注意:transfer 数组传入的是 buffer worker.postMessage({ type: 'HASH', buffer: buffer }, [buffer]); // 到这里,原 buffer 已经被转移,buffer.byteLength 为 0 } worker.addEventListener('message', function (event) { if (event.data.type === 'HASH_DONE') { console.log('SHA-256:', event.data.hash); } });

Worker 内部代码:

self.addEventListener('message', function (event) { const data = event.data; if (data.type !== 'HASH') return; const buffer = data.buffer; const hash = computeSha256(new Uint8Array(buffer)); self.postMessage({ type: 'HASH_DONE', hash: hash }); });

这里最关键的细节是:传入transfer数组的是buffer本身,而 Worker 收到后拿到的event.data.buffer就是那片被转移过来的内存。如果你不想转移,只希望拷贝一份,那就不传第三个参数,直接worker.postMessage({ buffer: buffer })即可。前者快但斩断了发送方的访问权,后者安全但多一次完整拷贝,根据业务取舍。

4.3 页面与 window.open 弹出的窗口通信

这种模式常见于第三方登录。主站打开一个登录弹窗,登录成功后弹窗要把结果通知主站。主站需要保存通过window.open返回的窗口引用,然后用这个引用发送消息。

主站代码:

const authWindow = window.open( 'https://auth.example.com/login', 'authWindow', 'width=480,height=640' ); window.addEventListener('message', function (event) { if (event.origin !== 'https://auth.example.com') return; if (event.data.type === 'LOGIN_SUCCESS') { localStorage.setItem('token', event.data.token); authWindow.close(); } });

弹窗页面里的通知代码:

// 弹窗登录成功后 if (window.opener) { window.opener.postMessage({ type: 'LOGIN_SUCCESS', token: 'eyJhbGciOi...' }, 'https://app.example.com'); }

需要注意,window.open返回的引用在跨域情况下能力非常有限,你唯一能安全使用的方法就是postMessage。别想着通过这个引用去访问子窗口的 DOM 或者变量,跨域情况下这些访问会被浏览器直接拒绝。反过来,弹窗里也别用window.opener去碰父页面的 DOM,只把它当作一个消息投递目标即可。

5. 使用注意点与安全实战

5.1 origin 校验不是可选项

我不知道强调多少遍才够:接收端的event.origin校验永远是第一优先级的检查项。很多人写 demo 的时候图省事,直接if (event.data.type === 'xxx')就往下走,这在生产环境里是巨大的安全漏洞。任何页面只要拿到了你 window 对象的引用,都可以向它 postMessage,不校验来源等于把一扇门敞开给所有访客。

校验时要注意一个细节:event.origin是字符串,比较时最好精确到完整的源,即协议 + 域名 + 端口。默认端口情况下 URL 里的 443 和 80 会被省略,event.origin里也不会带端口,直接写'https://example.com'即可。如果你在内网调试环境里用的端口不稳定,可以把可信来源维护成一个数组,统一遍历校验,而不是写死在 if 判断里。

5.2 event.source 的妙用与校验

event.source是发送消息的窗口引用。在 iframe 场景中它通常是iframe.contentWindow,在弹窗场景中它是window.opener。回消息时用event.source.postMessage(...)比绕过引用链去手动找窗口更可靠,因为它是事件自带的、一定指向发送方。

不过event.source同样需要谨慎使用。如果你没有校验event.origin,直接相信event.source发来的任意消息,那么攻击者可以伪造一个可信来源无关的窗口来向你注入恶意指令。正确的做法永远是"先校验 origin,再处理数据,最后才考虑用 event.source 回消息"。另外,某些浏览器环境下event.source可能是 null,比如消息来自同一个窗口的内部上下文,使用前做个空值判断更稳妥。

5.3 事件监听的注册与解绑

接收消息用的是标准事件监听器,所以同样存在内存泄漏的风险。特别是在单页应用里,如果每次进入页面都注册一个新的message监听器而不解绑,消息会被重复处理多次,表现为"回调执行了 N 次",数据被重复提交。

我的习惯是给处理函数命名,并在组件卸载或页面隐藏时移除监听。React 里典型写法如下:

useEffect(function () { function handleMessage(event) { if (event.origin !== TRUSTED_ORIGIN) return; // 处理业务逻辑 } window.addEventListener('message', handleMessage); return function () { window.removeEventListener('message', handleMessage); }; }, []);

Vue 里则是在onMounted注册、onUnmounted移除。如果你用的是 jQuery 时代的页面脚本,同样记得在页面关闭或模块销毁时主动off。监听器重复注册这个问题在开发环境很难发现,一到生产环境用户频繁切换路由后问题就爆发了,排查起来还挺费劲。

5.4 别用 postMessage 传敏感数据

最后这条安全建议很多人会忽略:postMessage 的消息内容本身是明文可见的,任何能访问该窗口上下文的人都能通过 devtools 断点或者覆盖监听的方式看到消息内容。虽然跨源页面不能直接读取你的窗口内部数据结构,但消息在传递过程中是经过序列化的,不要在里面放密码、短信验证码、明文 token 这类敏感信息。

如果确实需要传递认证类数据,正确的做法是传递一个短期有效的授权码或一次性凭证,接收方拿到后通过后端接口换取真正的访问凭证。换句话说,postMessage 只负责"说句话",不要负责"递钥匙"。另外,接收端拿到消息数据后,如果数据里带有 URL,千万不要直接拼进 innerHTML 或当作跳转地址,先做 schema 和域名白名单校验,防止把 postMessage 变成 XSS 的跳板。

6. 常见问题与排查技巧实录

6.1 消息发出去了,对面收不到

这种情况十个里有八个是targetOrigin写错了。如果目标窗口实际运行在https://b.example.com,你却在targetOrigin里写了'https://a.example.com'(自己的域名),浏览器会直接拒绝发送消息,不会抛任何错误。另一个常见原因是 iframe 还没加载完成就发送消息,子页面监听器还没注册,消息凭空丢了。

排查思路很固定:先在发送端把targetOrigin改成'*'试试(只用于本地调试,别上生产),看能否收到;能收到说明是 origin 不匹配,对照目标窗口地址修改即可。然后确认发送时机,iframe 场景务必在load事件之后再发,弹窗场景在open之后也不要立即发送,等弹窗加载到注册监听器的脚本后再投递,必要时可以做一次握手确认。

6.2 传过来的数据变成 undefined 或者抛 DataCloneError

如果你在 postMessage 时抛了DataCloneError,几乎可以确定消息里包含了结构化克隆无法处理的值,比如函数、Symbol 或者 DOM 节点。如果你收到的数据是 undefined,一种可能是你传的对象里嵌套了不可克隆的属性被静默丢弃了,另一种可能是接收端解构时属性名对不上——两个页面各自维护了一份通信协议定义,字段名大小写不一致、多了个下划线之类的问题,在纯前端联调里特别常见。

我的建议是:把通信消息的 type 和 payload 字段名形成一份固定的协议文档,收发两端严格按文档来;在发送前对 message 做一次JSON.parse(JSON.stringify())预演也能帮你提前发现问题,虽然它不能完全等价于结构化克隆,但至少能暴露大部分序列化异常。接收端一律做防御式判断:if (!event.data || typeof event.data.type !== 'string') return;

6.3 transfer 之后原对象 detached 了

这是很多第一次用 transfer 参数的人会踩的坑。发送方把一个 ArrayBuffer 塞进 transfer 数组发出去之后,回头想读arrayBuffer.byteLength,发现变成 0 了,或者访问 Uint8Array 视图时抛错,提示 buffer 已被 detached。这不是 bug,而是 transfer 的设计预期:所有权交出后,原上下文对该对象的访问就是非法的。

解决办法很简单:在调用 postMessage 之前,把你还需要用的数据先复制一份保留下来,或者调整业务逻辑,保证发送之后不再碰原始数据。如果你发现自己在发送后还要频繁读取原对象,那说明这个场景不适合用 transfer,退回结构化克隆就好,多一次拷贝换来得心应手,这笔账是划算的。

6.4 postMessage 的性能与替代方案选型

postMessage 更适合低频、控制类的消息,比如"刷新数据""切换主题""登出"这类指令。如果需要在两个同源窗口之间高频同步大量状态,比如多标签页实时同步购物车数据,postMessage 也能做,但需要考虑替代方案:同源下的BroadcastChannel专门用于同源上下文之间的广播通信,API 更简单,语义也更清晰;同一个页面内部多个模块之间通信,MessageChannel可以建立私有管道,避免全局监听器的噪声;而同源多标签页的场景,localStoragestorage事件也是个轻量选择。

我个人的选型经验是:跨源场景没得选,只能用 postMessage;同源场景优先看 BroadcastChannel;需要一对一精准通信时用 MessageChannel;只是简单的跨标签页通知,用 localStorage 的 storage 事件反而最省事。下表把这个对比整理了出来,方便你按需取用:

通信方案跨源支持通信方向典型场景
window.postMessage支持窗口间单向或双向iframe、弹窗、Worker
BroadcastChannel不支持同源多上下文广播多标签页状态同步
MessageChannel支持一对一私有管道模块间定向通信
localStorage + storage 事件不支持同源多标签页轻量通知、缓存同步
CustomEvent视页面而定同页面内组件间解耦通信

单独说一下 BroadcastChannel 和 postMessage 的一个关键差异:BroadcastChannel 只能在同源上下文之间广播,不需要校验 origin,因为同源策略已经帮你做了隔离;而 postMessage 天生就是为跨源设计的。如果你在两个同源页面之间通信,用 BroadcastChannel 可以减少很多安全校验代码,但如果你有哪怕一个子页面是跨域的,那还是老老实实用 postMessage。

最后再分享一个我在项目里反复验证过的经验:通信协议设计得越窄越不容易出错。不要图方便把整个业务对象一股脑丢进 postMessage,尽量只传typepayload这种扁平结构,字段取名用稳定的英文常量,接收端用白名单校验 type。这样做了之后,消息链路的可维护性会好非常多,后来接手的人看代码也不会一头雾水。前一阵子我们排查一个线上偶发问题,就是靠协议白名单五分钟锁定了异常来源,而旁边的同事还在层层打断点。这种小习惯,长期坚持下来比任何技巧都管用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 5:14:22

Flask人脸识别签到系统实战:离线部署与性能优化

简介:这是一份面向Python开发者与人工智能初学者的实战型人脸识别签到系统教学资源,聚焦Web端人脸考勤场景,解决会议、课堂、活动等轻量级签到需求。资源以PDF文档形式交付,共1个文件(897KB),完…

作者头像 李华
网站建设 2026/9/19 1:45:35

目标检测评价指标从Acc到mAP实战解析

1. 为什么目标检测模型的评价指标不能只看准确率?——从Acc到mAP的实战认知升级刚入行做目标检测时,我犯过一个典型错误:把分类任务那套思维直接搬过来,盯着Accuracy猛看。模型在验证集上Acc达到92%,我兴冲冲交差&…

作者头像 李华
网站建设 2026/9/19 5:21:01

ERP、PLM、MES、WMS系统集成:制造企业数据主权与架构落地方案

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

作者头像 李华
网站建设 2026/9/19 5:17:35

PTA天梯赛L2“冰岛人”题解:五代祖先判定与输出逻辑详解

先说我第一次看到“冰岛人”这三个字的反应:这怕不是一道历史文化题?等我把题面读完才发现,这是 PTA 天梯赛里一道非常典型的 25 分 L2 题,考点不是冰岛历史,而是“你能不能把一段模糊的自然语言规则,翻译成…

作者头像 李华
网站建设 2026/9/19 5:10:42

数据库课程设计实战:银行管理系统表结构与CRecordSet访问解析

简介:《数据库课程设计报告银行管理系统》是一份面向高校学生的数据库课程设计报告,适合作为计算机、软件工程专业完成银行管理类题目的参考资料。文档基于 Visual C 6.0 与 SQL Server,围绕储户、活期存取款、定期存取款等核心数据表展开&am…

作者头像 李华
网站建设 2026/9/19 5:13:14

在 Baseten 上跑 Grounded Inference,TaoToken 端点与 Key 分开管

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

作者头像 李华