惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
S
SegmentFault 最新的问题
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
Stack Overflow Blog
Stack Overflow Blog
博客园 - 【当耐特】
Recent Announcements
Recent Announcements
I
InfoQ
U
Unit 42
博客园_首页
GbyAI
GbyAI
Hugging Face - Blog
Hugging Face - Blog
罗磊的独立博客
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
DataBreaches.Net
aimingoo的专栏
aimingoo的专栏
月光博客
月光博客
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 聂微东
T
Tailwind CSS Blog
量子位

kshao-blog-前端知识记录

win or mac clash 无 TUN 让 Antigravity、Chrome 强制 proxy(解决 Antigravity 无法加载选择 model、自动更新无法登录、跳转) 【大杂烩】在 pnpm 中直接修改 node_modules(.pnpm) 中的依赖项,项目中持久化 - pnpm 中的依赖处理、幽灵依赖、寻址规则等 在 html 中直接使用 Esm、Jsx 脚本快速调试和使用 React@19 和 Vue@3 源码,解决 React19 UMD 构建等问题 一键在本地批量检测并升级更新 package.json 中的模块依赖,ncu(npm-check-updates)在 npm、pnpm 或 workspace 项目中的使用教程 解决 Mac Docker Desktop 中启动出现的问题合集 通过阿里云、腾讯云无服务器搭建自定义的企业域名邮箱,实现在 QQ邮箱 收发等功能(附腾讯 SMTP 和 IMAP) 解决使用代理(clash 等)进行 SSH 连接(如 Github ssh key clone/push)出现 kex_exchange_identification 错误 静态文件资源 cdnjs, jsdelivr 抖音字节国内快速 CDN 镜像推荐【2025】- 仍在使用 bootcdn 和 staticfile CDN 请注意验证资源的完整性(SRI) pnpm monorepo 中管理依赖的最佳实践,与 Catalogs(目录)协议的使用(monorepo 中统一版本管理) Web 安全中的 Secure Contexts(安全上下文)- 解决在本地中使用 clipboard 或 Crypto 等 API 限制或关闭上下文限制 使用 serve 配合 openssl 或 mkcert 创建本地自签名可信任的证书 - 创建本地 TLS\SSL https 协议服务 利用 Github Actions 和 Acme 自动申请、更新和部署至阿里云、腾讯云 CDN Lets Encrypt SSl\TLS ECC RSA 双证书 【CSS】解决在 flex 容器中使用 align-content 或 justify-content 属性 center 居中时的溢出滚动和截断问题 - 理解 safe 关键字 在线工具 - 一键获取下载抖音无水印视频、抖音去水印解析工具、下载抖音无水印高清图集【2025 最新】 【React Router】v6 data router 在非组件(或工具方法)中如何优雅的跳转路由 【React】为什么路由跳转时页面滚动高度不会被重置(保留上个页面高度)?理解 history scrollRestoration 的场景与使用,以及如何使用 React Router 重置和跳转前保留滚动高度 【React】在本地 Html 中快速 debug(调试)React 源码 【React】结合源码和 EventLoop 分析 - 为什么 useLayoutEffect 会阻止 DOM 重绘(而 useEffect 闪烁)?为什么其内部 useState 会“同步”执行? React Compiler - 解放在函数中编程时的性能焦虑(React Conf 2024)附 Next 在线演示 在 github actions 中获取时间,并转换为中国标准时间(中国时区) 【npm】npm ci - npm clean install,在 CI、CD 中保持构建的一致性和可重复性 eslint 9.x 升级或使用指南,eslint.config.js 配置,包含 react、typescript、prettier 等常用配置升级迁移 使用 Spicetify 自定义 Spotify - 歌词翻译、全屏展示、主题替换 在 node 中快速代理请求(Proxy),解决跨域或请求转发问题 - http-proxy-middleware 修改请求体和返回 在 Hexo 中使用 AI(Gemini、deepseek、Azure)生成文章摘要,支持自定义模板。hexo-ai-summaries 插件文档(默认适配主题 Butterfly) 解决在 webstorm 或 idea 等 jetbrains 工具中遇到 Git 无法 force push,或 force push 灰色禁用无法点击(protected branches) hexo-seo-submit,Hexo 博客 SEO 优化插件 - 每日定时自动或手动提交链接至百度、Bing、Google,支持 Github Actions 和 Coding Jenkins 等CI(Hexo 插件编写) 一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、yarn 的用法对比(pnpm 9.x 内部安装依赖问题 link-workspace-packages) 【Node】Corepack - 解决 pnpm 或 yarn 的多版本管理、解决本地版本与 packageManager 中的版本一致性问题 解决 npm、cnpm 或 pnpm install 遇到 certificate has expired (证书已过期)
【message-channel】了解频繁出现在框架的 Message Channel,...
kshao · 2022-10-19 · via kshao-blog-前端知识记录

最近在 GitHub 上”学习”的时候发现了别人在用 MessageChannel,这个 Api 很少见(没见过)基本不了解如何使用,搜了下 发现 React 和 Vue 都使用过~ 这不学习学习跟同事小装一手?

Message Channel 允许两个不同的脚本运行在同一个文档的不同浏览器上下文(例如两个 Iframe,文档主体和 Iframe,worker 之间或 window.open 的窗口)来直接通讯,在每一端使用一个端口(port)通过双向频道(Channel)来传递信息。

Message Channel 是以 Dom Event 的形式发送信息,属于异步的宏任务

使用

  • 使用 MessageChannel() 构造函数来创建通讯信息,获取两个端口的 Message Port 对象:Port1、Port2
  • 端口之间可以互相通信
  • 端口接受到无法被序列化的消息时,使用 onmessageerror 处理
  • 使用 close 关闭端口

示例

const { port1, port2 } = new MessageChannel();
port1.onmessage = function (event) {
  console.log('收到来自port2的消息:', event.data); // 收到来自port2的消息: pong
};
port2.onmessage = function (event) {
  console.log('收到来自port1的消息:', event.data); // 收到来自port1的消息: ping
  port2.postMessage('pong');
};
port1.postMessage('ping');

use addEventListener

const { port1, port2 } = new MessageChannel();
port1.addEventListener('message', function (event) {
  console.log('收到来自port2的消息:', event.data); // 收到来自port2的消息: pong
});
port1.start();
port2.addEventListener('message', function (event) {
  console.log('收到来自port1的消息:', event.data); // 收到来自port1的消息: ping
  port2.postMessage('pong');
});
port2.start();
port1.postMessage('ping');

使用 EventListener 需要手动调用 start() 后才可通信,因为初始化是暂停的。在使用 onmessage 时隐式调用了 start() 方法。

与 Iframe 通信

Main

const channel = new MessageChannel();
const { port1, port2 } = channel;

port1.onmessage = (event) => {
  console.log(event.data, '----- port1 get message');
};

port2.onmessage = (event) => {
  console.log(event.data, '------ port2 get message');
};

const iframe = document.querySelector('iframe');

iframe.addEventListener('load', () => {
  iframe.contentWindow.postMessage('from main -- postMessage', '*', [port1]);
});

Iframe

window.addEventListener('message', (event) => {
  const { data, ports } = event;
  const [port1] = ports || [];
  console.log(data, 'iframe get message', event);
});

互相通信

Iframe

+ let messagePort;
window.addEventListener('message', (event) => {
    const { data, ports } = event;
    const [port1] = ports || [];
    console.log(data, 'iframe get message', event);
+   messagePort = port1;
});

+button.onclick = () => {
    // message channel
+   messagePort?.postMessage('message from iframe')
+}

此处的 port1 是来自:iframe.contentWindow.postMessage('xxxx', '*', [port1]);( postMessage 方法能够接受一个由 Transferable Objects 组成的数组作为参数,而 MessageChannel 导出的 MessagePort 刚好是 Transferable Objects )

Window.open

Main

let strWindowFeatures = 'top=10,left=10,width=400,height=200';
const newWindow = window.open(url, 'message', strWindowFeatures);

newWindow.addEventListener('load', () => {
  newWindow.postMessage('message from new window', '*', [port1]);
});

new window
与 Iframe 逻辑大体一致

let messagePort;
window.addEventListener('message', (event) => {
  const { data, ports } = event;
  const [port1] = ports || [];
  console.log(data, 'iframe get message', event);
  messagePort = port1;
});

button.onclick = () => {
  // message channel
  messagePort?.postMessage('message from iframe');
};

Event Loop 中执行顺序

const interval = setInterval(() => {
  console.log('setInterval');
  window.clearInterval(interval);
});

setTimeout(() => {
  console.log('timeout');
});

const { port1, port2 } = new MessageChannel();
port1.postMessage('send message');
port2.onmessage = () => {
  console.log('get message');
};

requestAnimationFrame(() => console.log('requestAnimationFrame'));

new Promise((resolve) => {
  console.log('promise');
  resolve();
}).then(() => {
  console.log('then');
});

async function A() {
  console.log('A run');
  await new Promise((resolve) => resolve());
  console.log('A end');
}

A();

输出为

promise, A run
then, A end
requestAnimationFrame
timeout(先调先运行), get message, setInterval

https://segmentfault.com/a/1190000042501046
https://zhuanlan.zhihu.com/p/432726048
https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel/MessageChannel