经纬志
首页 / 实战手册 / 工程化

控制台警告有三份 three.js,拾取全失效

先看结论

模型能显示,但点击拾取永远不命中;控制台第一条就是“Multiple instances of Three.js being imported”。

最后验证 2026-11-03 · 多技术栈 · Webpack 5 · 更新 2026-10-29 · 引用本文

现象

控制台重复出现警告,之后所有依赖 instanceof 的判断都失败:

WARNING: Multiple instances of Three.js being imported.

Uncaught TypeError: Cannot read properties of undefined (reading 'isVector3')
    at Raycaster.intersectObject (Raycaster.js:312)

拖拽控制器也失灵,因为控制器来自另一份 three,与场景中的对象类型不匹配。

原因

依赖树里出现了多份 three:项目直接依赖 r169, 某个插件依赖 r150,另一个 GIS 组件又带了 r160。 每份都有自己的 Vector3、Mesh 类, obj.isMesh 这类鸭子类型判断在跨实例时不受影响, 但 instanceof 与基于构造函数的检查会失败。

排查过程

列出依赖树中的所有 three 副本:

npm ls three
# ├── three@0.169.0
# ├─┬ some-plugin@2.3.1
# │ └── three@0.150.1
# └─┬ gis-widget@1.4.0
#   └── three@0.160.0

也可以在浏览器里比较两份模块的版本常量:

console.log(THREE.REVISION);        // 169
console.log(otherTHREE.REVISION);   // 150,说明确实有两份

解决方案

先尝试去重。多数情况下依赖范围是兼容的,提升到同一份即可:

npm dedupe
# 或在 package.json 中加 overrides
{
  "overrides": {
    "three": "0.169.0"
  }
}

插件不兼容新版本时,用构建别名强制所有引用指向同一份:

// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      three: path.resolve(__dirname, 'node_modules/three')
    }
  }
};
// vite.config.js
export default {
  resolve: {
    dedupe: ['three']
  }
};

编写内部组件库时,把 three 放到 peerDependencies, 不要放进 dependencies,从源头上避免副本:

{
  "peerDependencies": {
    "three": "^0.169.0"
  },
  "devDependencies": {
    "three": "^0.169.0"
  }
}

验证

控制台不再出现 Multiple instances of Three.js。 npm ls three 只剩一条记录。 射线拾取能命中模型,拖拽控制器与场景对象配合正常。

警告不是噪音:这条警告出现时,很多“莫名其妙”的类型错误都由它引起。 看到就先处理副本,再排查业务代码,能省大量时间。

引用本文

经纬志(2026)。《控制台警告有三份 three.js,拾取全失效》。https://www.jingweizhi.com/cookbook/duplicate-library-instance.html(访问日期 2026-11-03)。