news 2026/10/14 5:59:09

ESM与CommonJS互操作实战:从入门到工程避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESM与CommonJS互操作实战:从入门到工程避坑

这两年聊 JavaScript 模块化,话题已经从“该不该用 ESM”彻底变成了“怎么用才能不炸、怎么和 CJS 兼容着用”。我自己的一个真实感受是:ES Module 的入门门槛其实很低,几个语法一学就会;但一旦放到真实工程里——要改配置、要同时供 Node 和浏览器用、要和一堆老包共存——坑是一个接一个。这篇就把 ESM 的入门、工程落地,以及和 CommonJS 互操作时最容易翻车的那些点,一次讲透。内容定位在 2026 年的现状:Node 已经把 ESM 当成一等公民,新项目几乎默认type: module;但存量 CJS 包量依然巨大,互操作问题还是每天都要面对。

1. 先分清两位主角:ESM 与 CJS 的底层思维差异

1.1 一句话概括两者的本质

CommonJS 是 Node.js 在 2009 年前后设计出来的模块系统,核心是require()和module.exports。它的本质是“运行时加载”:代码执行到require()这一行,Node 才去读取目标文件、执行目标模块、拿到导出对象。整个过程是同步的,所以早期 Node 服务端代码可以用const fs = require('fs')这种方式,非常直观。

ES Module 则是 ECMAScript 语言标准的一部分,也就是 JavaScript 这门语言自己定义的模块系统。核心是import和export。它的本质是“静态加载”:import语句会被 JavaScript 引擎在代码执行之前就先解析,建立模块之间的依赖关系图,然后再按依赖关系执行模块代码。正是因为这种静态特性,打包工具才得以对代码做 tree-shaking(摇树优化),把没用到的东西在编译阶段直接扔掉。

1.2 设计理念不同,导致行为完全不同

CJS 的“动态”和 ESM 的“静态”不只是理论上的区别,它们直接决定了很多让新手困惑的行为差异。

举几个例子。CJS 里require()可以出现在条件分支里、函数里、甚至字符串拼接之后:

// 这是合法的 CJS if (process.env.NODE_ENV === 'production') { const prodConfig = require('./config.prod.js'); } else { const devConfig = require('./config.dev.js'); }

但在 ESM 里,import语句必须写在模块的顶层,不能包在if里,不能包在函数里。这不是风格洁癖,是语言规范强制要求的——因为引擎需要在执行任何代码之前就确定这个模块依赖谁。想按条件加载?唯一的出路是import()动态导入,它返回的是一个 Promise。

还有一个很实际的区别:CJS 里module.exports可以是任意值,比如一个字符串、一个函数、一个数字;而 ESM 的export只能导出“命名绑定”,形式更规范,也更容易被工具静态分析。

1.3 一张表看清核心差异

对比维度CommonJSES Module
标准归属Node.js 社区定义ECMAScript 官方标准
加载时机运行时同步加载编译时静态解析,执行前建立依赖图
语法require()/module.exportsimport/export
是否支持动态条件导入支持,随意写在任意位置静态 import 不行,需要import()动态导入
是否支持 tree-shaking基本不行,因为运行时才知道 exports支持,打包工具可静态分析
顶层 this指向module.exports指向undefined
循环依赖处理有一定容忍度,但值可能不可靠有暂时性死区风险,需要规范写法
文件扩展名偏好.js/.cjs.js/.mjs
浏览器原生支持不支持(Node 专用)支持,<script type="module">可直接用

这张表算是入门第一课。看完你可能觉得 ESM 各方面都“更先进”,那为什么 CJS 还没消失?原因很简单:历史包袱。npm 上存量包大部分还是 CJS 写的,而且很多包是双格式发布,Node 生态从 CJS 走向 ESM 注定是一个漫长的过渡。2026 年的今天,这个过渡期远远没有完全结束。

2. ESM 入门必会的语法与运行时规则

2.1 导出和导入的基础形态

ESM 的导出分为两种:命名导出和默认导出。

// 命名导出:分别导出 export const name = 'demo'; export function sayHi() { return `Hello, ${name}`; } // 命名导出:统一导出 const version = '1.0.0'; export { version }; // 默认导出:每个模块只能有一个 export default function main() { console.log('this is default export'); } // 也可以直接默认导出一个匿名函数或对象 // export default { name, version };

对应的导入方式:

// 导入命名导出 import { name, sayHi } from './module.js'; // 命名导出重命名 import { name as appName } from './module.js'; // 导入默认导出 import main from './module.js'; // 默认 + 命名混着导入 import main, { version } from './module.js'; // 整体导入,把所有导出挂到一个命名空间对象上 import * as all from './module.js';

这里我重点提醒一个字面容易被误解的点:import * as all得到的all是一个模块命名空间对象,它的属性是固定的,而且不可以被重新赋值。你不能再all.version = '2.0',引擎直接报错。另外,它和“把整个模块执行一遍再取返回值”不太一样,CJS 里require('./module')返回的是一个普通对象,可以随意增删属性,这是两者比较大的行为差异。

2.2 package.json 的 type 字段和文件扩展名规则

很多初学者栽的第一个跟头,就是写了 ESM 语法的.js文件,然后 Node 直接报错:Cannot use import statement outside a module。原因就是 Node 默认把.js文件当作 CJS 来解析。

从 2026 年的现状看,Node 判定一个文件属于 ESM 还是 CJS 主要看三条规则,按优先级从高到低:

  1. 文件扩展名是.mjs,强制视为 ESM;.cjs,强制视为 CJS。
  2. 文件所在的最近的package.json里声明了"type": "module",则该package.json目录下的.js文件都按 ESM 解析。
  3. 以上都不满足,.js文件默认按 CJS 解析。

所以最直接的做法,就是在项目根目录的package.json里加一行:

{ "name": "my-app", "type": "module" }

加了之后,项目里所有.js文件都默认是 ESM。如果你还有个别文件必须走 CJS,把扩展名改成.cjs就行。反过来,如果一个项目没有type字段、默认 CJS,你可以在某个文件上单独用.mjs扩展名写 ESM,完全不需要动package.json。

我个人的建议是:新项目直接"type": "module",这是最省事、也最符合未来方向的配置;老项目要迁移,也别一次性全改,先用.mjs或局部type控制范围,等到整个目录都稳定了再统一转换。

2.3 解析规则:扩展名、目录导入、JSON

ESM 的模块解析规则比 CJS 严格得多,这个“严格”是很多人从 CJS 切过来抓狂的根源。

CJS 里你可以写require('./utils'),Node 会依次尝试补全.js、.json、.node等扩展名,甚至./utils/index.js这种目录导入也能被自动解析到。但 ESM 里这招行不通。在 Node 的 ESM 环境中,相对导入必须写明完整文件名和扩展名:

// 错误写法 import utils from './utils'; import utils from './utils/index.js'; // 正确写法 import utils from './utils/index.js'; // 或者你直接把文件命名为 utils.js import utils from './utils.js';

听起来很死板,但这是有意为之。浏览器原生 ES 模块就是要求完整 URL,不能靠服务器去猜路径;Node 在实现 ESM 时遵循了同样的思路,让代码在浏览器和 Node 之间更容易通用。所以别抱怨,这就是规范。

还有目录下的index.js,在 ESM 里也要写完整路径,不能光写目录名。前端项目里很多人是靠打包工具(比如 Vite、webpack)把这些细节抹掉了,所以平时写import xxx from '@/utils'毫无压力。但只要你用 Node 直接跑 ESM 代码,这些规则就绕不开。

JSON 导入也需要特别注意。Node 的 ESM 原生支持 JSON 导入,但要求必须带导入属性(with语法,早期叫断言):

import data from './config.json' with { type: 'json' };

不带with { type: 'json' }会直接报错。这个语法在 2026 年已经很稳定了,但很多老教程还是旧写法,照搬会翻车。

2.4 ESM 的运行时特性:静态分析、缓存、顶层 await

既然说 ESM 是静态的,那它到底给开发者带来了什么实际好处?最直观的是 tree-shaking。现代打包工具在构建时能通过静态分析知道某个导出到底有没有被用到,没用的导出就不会打进最终的 bundle,从而减少产物体积。CJS 做不到这一点,因为它要等代码执行完才知道module.exports里最终有什么。

缓存机制上,ESM 和 CJS 都是“模块只执行一次,后续引用拿到同一个实例”。区别在于:CJS 的缓存是显式的require.cache可以干涉;ESM 的缓存是引擎内部的,模块命名空间对象在不同模块之间共享,你基本没有办法也不需要手动去清缓存。这个特性在开发阶段偶尔会让人困惑:你改了一个模块的代码,单独跑另一个入口,发现输出还是旧的——多半就是 Node 进程还没退出,缓存在内存里没刷新。刷新方式简单粗暴:重启进程。

另外一个值得说的点是顶层await。ESM 允许在模块顶层直接使用await关键字,不需要包一个async function:

// module.mjs const data = await fetch('https://api.example.com/data').then(res => res.json()); export default data;

这在 CJS 里做不到。顶层await让模块可以异步初始化,但也带来一个副作用:它会让依赖该模块的其他模块等待它完成,运行顺序会受影响。设计 API 时要注意:不是所有模块都适合在加载阶段就去做网络请求,这会显著拖慢启动时间。

3. 工程化落地:从零搭一个 2026 年的 ESM 项目

3.1 初始化与双模块体系配置

现在我们来从头搭一个同时支持 ESM 和 CJS 的 Node 项目,这也是 2026 年最常见的现实场景:你写的新模块希望 ESM 用户能用,CJS 老用户也能用。

第一步,初始化项目:

mkdir esm-cjs-demo cd esm-cjs-demo npm init -y

第二步,在package.json中设置"type": "module"并配置exports字段:

{ "name": "esm-cjs-demo", "version": "1.0.0", "type": "module", "main": "./dist/index.cjs", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs", "default": "./dist/index.cjs" } } }

exports字段是 Node 于 12.x 引入、在 2026 年已经完全成熟的“模块入口映射”机制。它的优先级高于main字段。当我们配置了"import"和"require"两个条件入口时,ESM 用户的import会被指向.mjs文件,CJS 用户的require会被指向.cjs文件。default是兜底,给那些不识别条件的工具用。

第三步,源码结构大致是这样:

project/ ├── src/ │ ├── index.js # ESM 入口源码 │ └── legacy.js # 一些需要 CJS 兼容的逻辑 ├── dist/ # 构建后的产物目录 │ ├── index.mjs │ └── index.cjs ├── package.json └── ...

构建这一步,很多项目会直接用打包工具双份输出。但我得说实话:如果你的模块本身不复杂,可以不用打包工具,直接用 Node 生态自带的tsc或者“源码直接双写”的方式。当然,工程化程度高的项目,主流做法还是用打包工具输出两份格式,一份 ESM 一份 CJS,然后把exports配好就行。

第四步,写一个最简单的入口文件:

// src/index.js export function add(a, b) { return a + b; } export default { add };

构建后分别输出到dist/index.mjs和dist/index.cjs,这样你的包就具备了双格式供给能力。

3.2 浏览器端与 Node 端的差异处理

ESM 一个很大的优势就是浏览器原生支持。直接写:

<script type="module" src="/src/main.js"></script>

浏览器就会以 ESM 的方式加载并执行。这意味着你可以在不经过打包工具的情况下,直接在浏览器里跑原生 ESM。开发时用 Vite 这类工具也只是帮你做模块合并、压缩、兼容处理,底层运行的还是 ESM 的机制。

不过浏览器和 Node 的 ESM 还是有些差异需要留意:

浏览器里import的路径必须是完整 URL 或相对路径,不能用import fs from 'fs'这样的 Node 内置模块;同时浏览器环境本身没有process、__dirname、Buffer这些 Node 全局对象。反过来,在 Node 里写 ESM 也可能引用了window、document等浏览器 API,直接运行自然也会报错。

要做到一份代码两边跑,通常得靠打包工具做环境变量替换和 polyfill,比如 Vite 的define、Webpack 的DefinePlugin。这些属于工程层面的处理,不是 ESM 语法本身的问题,但新手经常会混淆:以为是模块系统报错,其实是环境变量缺失。

3.3 双包发布:一个包如何同时服务 ESM 和 CJS 用户

做开源库或者公司内部公共包的时候,“双包发布”是个高频需求。除了刚才配置的exports条件入口,还有一个容易忽略的问题:如果这个包既有 ESM 又有 CJS 两份实现,而两份实现对应两套不同的实例状态,就会出现“双包危险”(dual package hazard)。

举个例子。假如你的包里有一个单例缓存对象:

// 同一个模块逻辑,分别编译成 mjs 和 cjs 两份 // dist/index.mjs export const cache = new Map(); // dist/index.cjs // 构建工具转译后对应一份独立的 cache 实例

如果项目里一个子模块走 ESM 入口加载了cache,另一个还在走 CJS 入口加载了cache,内存里就有两份Map,两边互相看不见对方写入的数据。单测环境还能将就,生产环境一旦出现这种情况,排查难度直接翻倍。

缓解方案有几个:

  • 保证发布包的 ESM 和 CJS 产物由同一份源码构出,并做同步更新。
  • 尽量让导入路径在同一个项目内保持统一,不要一部分模块用import,另一部分用require。
  • 如果可能,把有状态的核心逻辑单独抽成一个内部 CJS 模块,两边都去 require 它,避免状态分裂。

双包发布不是“构建一次输出两份”就完事,还要考虑状态一致性问题。这一点在实际工程里踩坑概率很高,值得提前设计。

4. CJS 与 ESM 互操作:能互相 import,但处处是细节

4.1 ESM 引 CJS:默认导出、具名导出、lexer 的极限

ESM 导入 CJS 模块在语法上很简单:

import fs from 'fs'; import path from 'path'; import express from 'express';

因为在 Node 的 ESM 环境中,当一个 ESM 文件 import 一个 CJS 模块时,Node 会把 CJS 的module.exports整体作为默认导出暴露给 ESM。所以上面三个例子拿到的是fs、path、express的完整导出对象,这符合大部分人的直觉。

比较微妙的是具名导入。你有时候会看到这样的写法:

import { readFile } from 'fs'; import { join } from 'path';

这也能跑。原因是 Node 内部用了cjs-module-lexer这个库,去静态分析 CJS 模块的module.exports/exports.xxx赋值语句,尽量识别出“具名导出”的名称,从而让 ESM 可以按名字导入。但这是尽力而为,不是规范保证。

如果你的 CJS 模块是这样写的:

module.exports = buildExports(); // 或者 const result = {}; Object.assign(result, { a: 1, b: 2 }); module.exports = result;

lexer 基本就无法识别出a和b。这时候你写import { a } from './cjs-module.js'大概率拿到的是undefined,甚至直接报错。解决办法有两个:

// 方案一:按默认导出拿,再解构 import cjsModule from './cjs-module.js'; const { a, b } = cjsModule; // 方案二:整体导入命名空间,再取值(和默认导入类似) import * as mod from './cjs-module.js'; console.log(mod.a);

不过我建议优先用方案一。import * as mod拿到的命名空间对象在某些边界情况下行为不太符合直觉,而默认导入更接近 CJS 原始模型,心智负担小很多。

4.2 CJS 引 ESM:require 的限制、动态 import 的正确姿势

CJS 反过来想require一个 ESM 模块,是互操作里最坑的部分。

Node 的传统版本中,require()是不能直接加载 ESM 模块的,因为 ESM 是异步执行的,而require是同步的,底层的执行模型对不上。所以以前只能在 CJS 模块里用动态import()去加载 ESM:

// legacy.cjs async function loadEsmModule() { const mod = await import('./esm-module.mjs'); return mod; }

注意:这里的import()是动态导入函数,虽然写在.cjs文件里,但它返回的是 Promise,也就是说你只能在异步上下文里调用它。如果非要在同步流程里立刻拿到 ESM 模块的导出,那做不到,必须把业务逻辑改成异步。

2025 到 2026 年这个节点上,Node 已经逐步放开了require(esm)的能力,在较新的 LTS 版本中,同步require()一个纯 ESM 模块在某些条件下是可行的。但 Node 官方文档明确说:如果 ESM 模块中使用了顶层 await,require()就会失败,因为同步加载无法等待异步操作完成。同时,require(esm)的行为仍然在演进中,很多老版本 Node 完全不支持。

所以我的建议很直接:如果你的项目还在用 CJS,而且必须引用一个 ESM-only 的包,别纠结于同步require,直接写await import(...)。不需要为了好看去升级依赖或者改运行版本,能跑、可维护才是第一位的。

4.3 循环依赖与执行顺序的坑

循环依赖是模块系统里永远绕不开的话题。CJS 时代大家就吃过亏,ESM 时代不同的是:ESM 是静态分析的,循环依赖在模块注册阶段就会被发现,然后按照“先深度优先、后执行”的顺序加载,所以出错的时机可能更隐蔽。

先说 CJS 的循环依赖:

// a.cjs const b = require('./b.cjs'); console.log('a 加载中,b 是:', b); module.exports = { name: 'A' }; // b.cjs const a = require('./a.cjs'); console.log('b 加载中,a 是:', a); module.exports = { name: 'B' };

如果先加载a.cjs,它require('./b.cjs'),而b.cjs又反过来require('./a.cjs'),此时a.cjs还没执行完、module.exports还是默认的空对象,所以b.js拿到的是{}。等你回到a继续执行完,a的导出才完整。这就是 CJS 循环依赖的经典表现:拿到的可能是半成品。

ESM 的循环依赖行为更严格。因为import的绑定是实时的,模块会在执行前先收集导出项并建立绑定关系,然后才开始执行。循环中出现“还没初始化好的绑定”时,访问它就会触发暂时性死区错误:

// a.mjs import { foo } from './b.mjs'; export const a = 'A'; console.log('a.mjs 执行,foo 的值是:', foo); // b.mjs import { a } from './a.mjs'; export const foo = 'FOO'; console.log('b.mjs 执行,a 的值是:', a);

这段代码里b.mjs执行时访问a,但a.mjs还没有执行到export const a = 'A',按规则就会报错。解决方案永远是类似的:尽量把共享的、可能产生循环依赖的代码拆到独立的底层模块中,让依赖方向变成单向的,而不是在两个模块之间形成环。如果实在拆不开,就在模块内部延迟访问,不要在最顶层直接读取对方未初始化的导出。

5. 2026 避坑清单:我这些年踩过的真实问题

5.1 高频报错到解决方案速查表

下面这张表整理的是我在实际项目里遇到频率最高的 ESM 相关报错,每一条都对应一个真实的调试经历。建议收藏。

报错信息或现象根本原因解决方案
Cannot use import statement outside a module文件被 Node 当成 CJS 执行加"type": "module",或改.mjs扩展名
ERR_MODULE_NOT_FOUNDESM 相对导入没写完整扩展名把import './utils'改成import './utils.js'
ERR_REQUIRE_ESM在 CJS 里用require加载纯 ESM 模块改成await import(...)
__dirname is not defined in ES module scope在 ESM 中直接用了 CJS 全局变量用import.meta.url配合fileURLToPath获取
Unexpected token 'export'浏览器直接加载了未处理的 ESM 文件浏览器原生支持 ESM,但注意路径和type="module"设置
Invalid or unexpected token可能混用了import和require语法确认文件格式统一,不要在一个文件里混合模块体系
具名导入得到undefinedCJS 模块的具名导出无法被静态分析识别改用默认导入再解构
循环依赖模块被访问到未初始化变量ESM 暂时性死区重构依赖方向,或延迟访问
JSON 导入报错ESM 里 JSON 导入必须带with属性写成with { type: 'json' }
Top-level await在 CJS 文件里报错顶层await只在 ESM 模块中支持包一层async function,或改文件为.mjs

5.2 __dirname 没了,怎么拿当前目录

ESM 里没有__dirname和__filename,这是 CJS 老代码迁移到 ESM 时最痛的一点。因为__dirname不是 JavaScript 语言标准的一部分,而是 Node 在 CJS 里注入的“魔法变量”,ESM 里需要自己组合。

替代方法非常统一:

import { fileURLToPath } from 'node:url'; import path from 'node:path'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename);

如果你想要一个直接用__dirname的“模拟效果”,就把上面的代码抽到一个公共工具文件里,然后每个模块顶部引入使用:

import { getDirname } from './utils/path-utils.mjs'; const __dirname = getDirname(import.meta.url);

这里要注意fileURLToPath对 Windows 和 Linux 路径的差异处理得并不完全一致,但常规用法问题不大。重点还是别把__dirname当成语言自带变量来用,所有目录计算都依赖import.meta.url才能保证通用性。

还有一个细节:import.meta.url在浏览器里会是完整的网页 URL,比如https://example.com/assets/main.js,这时候后端工具函数不一定适用,所以如果你写的是同构代码,最好加一层环境判断。

5.3 从 CJS 迁 ESM 的稳妥路线

最后聊聊迁移。我经历过好几个项目的模块体系切换,最大的体会就一句话:不要搞大爆炸式迁移。

一个实际的稳定路线是:

  1. 先升级 Node 版本到当前的活跃 LTS,确保 ESM 特性完整。
  2. 把入口文件改成.mjs,验证最小链路能跑通。
  3. 逐步把require改成import,但每一步都跑测试。
  4. 处理掉__dirname、require动态加载、JSON 导入等特殊语法。
  5. 最后再把package.json的type切到module,让所有.js文件默认按 ESM 解析。

这里有一个很容易被忽略的问题:如果你在改造过程中发现某个文件里既用了require又用了import,实际上 Node 是“一艘船不能同时挂两面旗”的——一个文件要么是 CJS,要么是 ESM,不能混着用。用.cjs或.mjs来控制局部文件格式是最灵活的手段。

还有一点和测试相关:迁移期可能出现“同样的模块,不同入口加载行为不一致”的现象。别只看功能正常就以为没问题,最好用node --check检查一遍语法,再把依赖图的解析路径打出来看一看。Node 提供了node --trace-modules这种调试参数,遇到解析怪问题时真能救命。

在我实操过程中最受用的一个经验是:迁移后的第一周,把日志里所有模块加载告警当成一等 bug 对待,而不是“能跑就行”。因为这些告警往往预示着你正在依赖 Node 的兼容层在补洞,一旦换运行环境或升级版本,问题会集中爆发。

最后再分享一个小组节

说了这么多,我最后再给一个实际建议:如果条件允许,尽量把你写的库源码直接用 ESM 维护,构建时再输出 CJS 副本。这个方向比反过来舒服得多,因为 ESM 向上的生态越来越好,而 CJS 只是兼容对象。2026 年再去学 ESM,你学的是未来;再去死抠 CJS 动态加载的技巧,维护的是历史。两条腿走路当然可以,但重心往哪边放,心里要有数。互操作这些坑记熟,工程里遇到一个拆一个,基本就稳了。

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

私域引流合规化:系统卡片智能导流方案解析

1. 私域引流合规化背景解析在内容平台与私域流量运营的交叉领域&#xff0c;如何安全合规地实现用户导流始终是个技术活。去年某社交平台封禁了超过12万个违规导流账号&#xff0c;其中近40%是因为直接发布联系方式触发风控。这促使我们开发了一套基于系统卡片的智能导流方案&a…

作者头像 李华
网站建设 2026/10/14 5:58:08

制药AI治理:GxP合规的三大硬性锚点与工程落地路径

1. 为什么制药企业谈AI治理&#xff0c;第一句话必须是“GxP不是可选项&#xff0c;而是入场券”我第一次在某跨国药企的AI项目评审会上听到“我们先跑通模型&#xff0c;合规后面补”这句话时&#xff0c;会议室里有三个人同时低头看表——不是因为无聊&#xff0c;而是因为知…

作者头像 李华
网站建设 2026/10/14 5:57:53

图像跟踪算法Demo实战:基于OpenCV的CSRT/KCF目标跟踪解析

图像跟踪算法demo终于跑通了&#xff0c;源码在结尾部分可以直接拿去用。整个过程不算复杂&#xff0c;但坑比我想象的多。我原以为“把一个视频里的目标框起来&#xff0c;然后让框跟着目标走”这种需求&#xff0c;最多就是调用现成接口的事&#xff0c;真上手才发现&#xf…

作者头像 李华
网站建设 2026/10/14 5:57:45

ORB-SLAM3融合YOLOv8:动态场景特征剔除实战

简介&#xff1a;这份项目代码面向计算机视觉、SLAM与目标检测方向的学习者与开发者&#xff0c;聚焦ORBSLAM3与YOLOV8的融合应用&#xff0c;重点解决动态环境下动态物体干扰定位与建图精度的问题。通过YOLOV8对动态目标进行识别与剔除&#xff0c;ORBSLAM3得以在更纯净的场景…

作者头像 李华
网站建设 2026/10/14 5:57:45

AI视频生成新路线:Opus 5.5用Canvas代码逐帧绘制动画

1. 视频生成圈最容易被忽视的两条路线&#xff0c;Opus 5.5 选了后者如果你和我一样&#xff0c;过去一年多被各种“一句话生成视频”的演示刷屏&#xff0c;大概率会默认一件事&#xff1a;视频生成就是你给模型一段文字描述&#xff0c;模型内部把无数候选像素拼在一起&#…

作者头像 李华
网站建设 2026/10/14 5:57:16

零侵入Profiling:精准定位GPU利用率低的真实瓶颈

1. GPU 利用率低&#xff1a;一场“看不见对手”的排查战争干深度学习这行的人&#xff0c;谁没被 GPU 利用率折磨过&#xff1f;我见过太多类似的场景&#xff1a;训练脚本跑起来了&#xff0c;nvidia-smi一刷&#xff0c;GPU-Util 在 40% 到 70% 之间反复横跳&#xff0c;显存…

作者头像 李华