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

如何在Tauri中加载包含导入语句的独立JS模块?

在Tauri+Vite应用中加载带导入语句的外部JS脚本到Web Worker

我基于Vite构建了一个Tauri应用,希望让用户能够加载自定义的小型JS脚本——理想状态下,用户只需要提供一个index.js作为入口,我通过扫描文件夹就能从这个文件开始加载,并且为了安全与隔离性,这些脚本最好在Web Worker环境中运行。

我尝试过动态加载脚本内容的方式,代码如下:

import { readTextFile } from '@tauri-apps/api/fs';
import { appDataDir } from '@tauri-apps/api/path';

const appDataDirPath = await appDataDir();

const workerScriptPath = `${appDataDirPath}/agents/sample/worker.js`;

const scriptContent = await readTextFile(workerScriptPath);

const blob = new Blob([scriptContent], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);

// 创建Worker实例
// const worker = new Worker(workerUrl);
const worker = new Worker(workerUrl, { type: 'module' });

worker.onmessage = (event) => {
  logi('Message from worker:', event.data);
};

worker.onerror = (event) => {
  loge('[SIDEBAR]', `🟥 Error in worker: ${event.message}`);
  loge('[SIDEBAR]', JSON.stringify(event));
};

worker.postMessage(
  JSON.stringify({ message: 'Hello from main script!', dataDir: appDataDirPath })
);

这种方式能正常加载单文件的index.js,但如果文件内部包含import导入语句就会失效——因为我只是把单个文件的内容传给了Worker,浏览器无法解析外部文件系统中的依赖模块。

下面是我的Worker脚本代码(它不在项目打包目录内,是文件系统中的独立文件):

// 直接导入本地文件不行
// import agentFn from "./index";
// importScripts也不适用,因为需要从非打包目录动态加载
// importScripts("./index.js")

onmessage = async (event) => {
  const parsedEvent = JSON.parse(event.data);

  console.log('Message received from main script:');
  console.log(parsedEvent);

  // 尝试动态加载外部脚本失败
  // const agentFn = await import(`${parsedEvent.dataDir}agents/sample/index.js`);
  console.log(agentFn);
  postMessage('Hello, main script!');
};

请问有没有办法实现需求:将包含导入语句的外部JS文件加载到Tauri的WebView中?


可行解决方案

方案1:主进程构建模块映射,代理Worker加载

浏览器无法直接解析本地文件系统的模块路径,你可以在主进程中提前扫描脚本目录,构建完整的模块依赖映射,再通过代理Worker加载:

  • 步骤1:扫描脚本目录并构建模块映射
    编写逻辑遍历用户提供的脚本文件夹,读取所有.js文件的内容,生成一个键为模块相对路径、值为文件内容的映射对象,比如:

    // 示例模块映射
    const moduleMap = {
      "./index.js": "export default function() { ... }",
      "./utils.js": "export const helper = () => { ... }"
    };
    
  • 步骤2:生成代理Worker脚本
    创建一个代理脚本,接收主进程传递的模块映射,重写模块加载逻辑,然后加载用户的入口文件:

    // 主进程动态生成的代理Worker内容
    const moduleMap = ${JSON.stringify(moduleMap)};
    
    self.loadModule = async (specifier) => {
      if (!moduleMap[specifier]) throw new Error(`Module ${specifier} not found`);
      const blob = new Blob([moduleMap[specifier]], { type: 'application/javascript' });
      const url = URL.createObjectURL(blob);
      const module = await import(url);
      URL.revokeObjectURL(url);
      return module;
    };
    
    // 加载用户入口文件
    const entryModule = await loadModule('./index.js');
    // 这里可以根据需求调用入口模块的方法,或者绑定消息处理逻辑
    

    主进程将这个代理脚本打包成Blob,创建Worker实例。

方案2:注册Tauri自定义协议,直接访问外部模块

Tauri支持注册自定义协议,将用户脚本目录映射到WebView可访问的协议路径,这样就能用标准ES模块语法加载:

  • 步骤1:配置自定义协议
    在tauri.conf.json中添加自定义协议配置,将用户脚本目录(比如$APPDATA/agents)映射到agent://协议:

    {
      "tauri": {
        "allowlist": {
          "protocol": {
            "all": true,
            "custom": ["agent"]
          }
        },
        "protocol": {
          "custom": [
            {
              "name": "agent",
              "scheme": "agent",
              "privileged": true,
              "fs": {
                "scope": ["$APPDATA/agents/**"]
              }
            }
          ]
        }
      }
    }
    
  • 步骤2:在Worker中使用自定义协议导入
    将本地文件路径转换为自定义协议路径,直接用import加载:

    onmessage = async (event) => {
      const { dataDir } = JSON.parse(event.data);
      // 转换本地路径为自定义协议路径
      const entryUrl = `agent://${dataDir.split('agents')[1]}/sample/index.js`;
      const agentModule = await import(entryUrl);
      console.log(agentModule);
      postMessage('Hello, main script!');
    };
    

    这种方式让WebView通过Tauri的权限控制访问本地文件,原生支持ES模块的依赖解析。

方案3:递归解析依赖树,转换为Blob URL链

如果用户脚本的依赖层级不深,可以在主进程中递归解析所有导入依赖,将每个文件转换为Blob URL,替换原文件中的相对导入路径,最后在Worker中加载入口Blob URL:

  • 步骤1:递归解析依赖
    编写函数读取入口文件,解析其中的import语句,递归读取所有依赖文件,将每个文件的相对导入替换为对应的Blob URL。

  • 步骤2:加载入口Blob URL
    主进程将处理后的入口文件Blob URL传给Worker,Worker直接import该URL,此时所有依赖已经被替换为可访问的Blob URL,浏览器能正常解析模块依赖。


内容的提问来源于stack exchange,提问作者Oscar Franco

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:59:59