You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vite+Svelte-TS项目中处理三个冲突的THREE.js依赖版本

解决多版本THREE.js依赖冲突的WebXR应用方案

针对基于Vite + Svelte-TS开发WebXR应用时遇到的多版本THREE.js依赖冲突问题,以下是三个可落地的解决方案,均能满足共享代码复用与单应用部署的需求:

方案一:Vite别名隔离 + 组件封装

通过npm别名区分不同版本THREE,配合Vite路径别名与组件隔离,让每个XR库加载各自依赖的THREE版本,同时复用共享代码。

步骤:

  1. 安装依赖:用npm别名区分不同版本THREE,避免安装冲突
npm install three@latest
npm install three-legacy@npm:three@<ModelViewer所需版本号>
npm install super-three aframe @google/model-viewer
  1. Vite配置别名:在vite.config.ts中为每个库指定对应的THREE依赖路径
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [svelte()],
  resolve: {
    alias: {
      // ModelViewer指向旧版THREE
      'three$': 'three-legacy',
      // A-Frame指向super-three分支
      'three': 'super-three',
      // 业务代码与共享库使用最新版THREE
      '@three-latest': 'three'
    },
    // 强制不同库使用各自的THREE副本,禁止依赖提升
    dedupe: ['three', 'super-three', 'three-legacy']
  }
});
  1. 组件隔离封装:为每个XR库创建独立Svelte组件,共享代码放在src/lib目录供所有组件导入
  • src/components/ThreeXR.svelte(使用最新版THREE):
<script lang="ts">
  import * as THREE from '@three-latest';
  import { sharedUtils } from '$lib/shared-code';

  let scene: THREE.Scene;
  onMount(() => {
    scene = new THREE.Scene();
    sharedUtils.setupCommonXR(scene);
    // 剩余THREE WebXR初始化逻辑
  });
</script>

<div class="xr-container" bind:this={container}></div>
  • src/components/AframeXR.svelte(使用A-Frame):
<script lang="ts">
  import 'aframe'; // A-Frame自动将super-three挂载到window.THREE
  import { sharedUtils } from '$lib/shared-code';

  onMount(() => {
    const scene = document.querySelector('a-scene');
    sharedUtils.setupCommonAframe(scene);
  });
</script>

<a-scene xr-mode-ui="enabled: false">
  <!-- A-Frame XR内容 -->
</a-scene>
  • src/components/ModelViewerXR.svelte(使用ModelViewer):
<script lang="ts">
  import '@google/model-viewer';
  import { sharedUtils } from '$lib/shared-code';

  onMount(() => {
    const viewer = document.querySelector('model-viewer');
    sharedUtils.setupCommonModelViewer(viewer);
  });
</script>

<model-viewer src="model.glb" ar></model-viewer>
  1. 主应用切换组件:通过状态或路由切换不同XR组件,实现无冲突切换
<script lang="ts">
  let activeXR = 'three'; // three/aframe/modelviewer
</script>

<div class="app">
  <button on:click={() => activeXR = 'three'}>THREE.js XR</button>
  <button on:click={() => activeXR = 'aframe'}>A-Frame XR</button>
  <button on:click={() => activeXR = 'modelviewer'}>ModelViewer XR</button>

  {#if activeXR === 'three'}
    <ThreeXR />
  {:else if activeXR === 'aframe'}
    <AframeXR />
  {:else if activeXR === 'modelviewer'}
    <ModelViewerXR />
  {/if}
</div>

方案二:Vite多入口打包 + 路由隔离

通过Vite多入口配置,为每个XR库生成独立代码chunk,主应用通过路由加载对应chunk,共享代码自动提取为公共模块。

步骤:

  1. 调整项目结构:为每个XR场景创建独立入口文件
src/
├── entry-three.ts
├── entry-aframe.ts
├── entry-modelviewer.ts
├── lib/
│   └── shared-code.ts // 共享代码
└── App.svelte // 主路由组件
  1. Vite多入口配置:在vite.config.ts中配置多入口,Rollup自动提取共享代码
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [svelte()],
  build: {
    rollupOptions: {
      input: {
        three: './src/entry-three.ts',
        aframe: './src/entry-aframe.ts',
        modelviewer: './src/entry-modelviewer.ts',
        app: './src/App.svelte'
      },
      output: {
        manualChunks: {
          'three-latest': ['three'],
          'three-legacy': ['three-legacy'],
          'super-three': ['super-three'],
          'shared': ['./src/lib/shared-code.ts']
        }
      }
    }
  },
  resolve: {
    alias: {
      'three$': 'three-legacy', // ModelViewer用旧版
      'three': 'super-three' // A-Frame用super-three
    }
  }
});
  1. 入口文件编写:每个入口加载对应XR库并挂载到DOM
// entry-three.ts
import * as THREE from 'three';
import { sharedUtils } from './lib/shared-code';
import ThreeXR from './components/ThreeXR.svelte';

new ThreeXR({
  target: document.getElementById('xr-container')!
});
  1. 主应用路由切换:动态加载入口chunk实现无冲突切换
<script lang="ts">
  let xrContainer: HTMLElement;

  async function loadXR(type: string) {
    xrContainer.innerHTML = '';
    switch(type) {
      case 'three':
        await import('./entry-three.ts');
        break;
      case 'aframe':
        await import('./entry-aframe.ts');
        break;
      case 'modelviewer':
        await import('./entry-modelviewer.ts');
        break;
    }
  }
</script>

<div class="app">
  <button on:click={() => loadXR('three')}>THREE.js XR</button>
  <button on:click={() => loadXR('aframe')}>A-Frame XR</button>
  <button on:click={() => loadXR('modelviewer')}>ModelViewer XR</button>
  <div bind:this={xrContainer}></div>
</div>

方案三:Iframe隔离 + 共享模块通信

通过iframe隔离每个XR场景的运行环境,主应用与iframe通过postMessage调用共享代码,彻底避免依赖冲突。

步骤:

  1. 打包共享代码为UMD模块:添加单独的Vite配置打包共享代码
// vite.shared.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    lib: {
      entry: './src/lib/shared-code.ts',
      name: 'SharedXRUtils',
      formats: ['umd'],
      fileName: (format) => `shared-utils.${format}.js`
    }
  }
});

运行vite build --config vite.shared.config.ts,将生成的shared-utils.umd.js放到public目录。

  1. 创建XR iframe页面:每个页面引入对应库和共享UMD模块
<!-- public/aframe-xr.html -->
<!DOCTYPE html>
<html>
<head>
  <script src="./shared-utils.umd.js"></script>
  <script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
</head>
<body>
  <a-scene id="scene"></a-scene>
  <script>
    window.SharedXRUtils.setupCommonAframe(document.getElementById('scene'));
    // 监听主应用消息
    window.addEventListener('message', (e) => {
      if (e.data.type === 'updateModel') {
        window.SharedXRUtils.updateModel(e.data.modelUrl);
      }
    });
  </script>
</body>
</html>
  1. 主应用控制iframe:加载对应iframe并通过postMessage通信
<script lang="ts">
  let iframe: HTMLIFrameElement;
  let activeXR = 'three';

  function switchXR(type: string) {
    activeXR = type;
    iframe.src = `/${type}-xr.html`;
    iframe.onload = () => {
      iframe.contentWindow?.postMessage({ type: 'init', config: {} }, '*');
    };
  }
</script>

<div class="app">
  <button on:click={() => switchXR('three')}>THREE.js XR</button>
  <button on:click={() => switchXR('aframe')}>A-Frame XR</button>
  <button on:click={() => switchXR('modelviewer')}>ModelViewer XR</button>
  <iframe bind:this={iframe} class="xr-iframe"></iframe>
</div>

<style>
  .xr-iframe {
    width: 100%;
    height: 80vh;
    border: none;
  }
</style>

内容的提问来源于stack exchange,提问作者Furious Gamer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:17:44