Electron Forge + Vite 项目 Electron 静态资源处理

1. 教程前提

该教程依赖于这篇教程:Electron Forge + Vite + Vue3 + Ts 搭建 Electron 项目到应用打包

2. 参考资料

npm官网: rollup-plugin-copy

3. 静态资源处理机制差异

Vite 在打包应用时主要处理的是基于 Web 技术的项目,其工作方式和 Electron 在处理文件路径上有所不同。这种差异主要体现在以下几个方面:

1. 路径解析机制的差异
  • Vite: 作为一个为现代 Web 应用设计的构建工具,Vite 通常处理的是基于 HTTP 服务器的资源路径。它假设所有资源都可以通过相对于应用的基本 URL 或端口的路径来访问。这意味着,Vite 会扫描源代码中的导入语句,查找以 HTTP URL 形式指定的资源。
  • Electron: Electron 应用通常会访问文件系统中的资源,使用的是文件系统的绝对路径或相对于当前文件的路径。这使得 Electron 可以加载本地的文件和资源,而不依赖于 Web 服务器。
2. 资源引用和打包
  • Vite 打包时,它只会包括那些被静态分析到的资源文件(例如 JavaScript、CSS、图片文件等),这些文件通常是通过相对路径或模块解析方式引用的。
  • Electron 中使用的大多都是系统路径(例如通过 __dirname 或 process.resourcesPath 生成的路径),这些路径在 Vite 的打包过程中是无法被正确识别的。

Vite 不支持指定将某个目录打包至指定编译目录的情况下,问题逐渐变得复杂化(Forge 也没找到相关的功能)。

4. 解决办法

  1. public 公共目录
    public 是一个特殊的目录,Vite 会将这个目录里的静态资源一起输出到 web 编译目录与 Electron 编译目录,资源双份,但不失为一种解决办法。
    不是很推荐这个办法,所以我直接摸了。

  2. rollup-plugin-copy 插件
    这是一个比较推荐的方案,“复制文件和文件夹”

    a. 安装 rollup-plugin-copy

    npm install rollup-plugin-copy -D

    b. 配置 vite.main.config.ts 文件

    import copy from 'rollup-plugin-copy'
    
    plugins: [
    	copy({
        	targets: [
           		{
           			// 要进行复制的目录或文件
           			src: 'static', 
           			// 目标位置
           			dest: '.vite/build'
           		},
         	]
       })
    ]
    

    vite.main.config.ts 文件完整内容:

    import type { ConfigEnv, UserConfig } from 'vite';
    import { defineConfig, mergeConfig } from 'vite';
    import { getBuildConfig, getBuildDefine, external, pluginHotRestart } from './vite.base.config';
    
    // 引入 rollup-plugin-copy 插件
    import copy from 'rollup-plugin-copy'
    
    // https://vitejs.dev/config
    export default defineConfig((env) => {
      const forgeEnv = env as ConfigEnv<'build'>;
      const { forgeConfigSelf } = forgeEnv;
      const define = getBuildDefine(forgeEnv);
      const config: UserConfig = {
        build: {
          lib: {
            entry: forgeConfigSelf.entry!,
            fileName: () => '[name].js',
            formats: ['cjs'],
          },
          rollupOptions: {
            external,
          }
        },
        plugins: [
          pluginHotRestart('restart'),
          copy({
            targets: [
              { 
                // 要进行复制的目录或文件
                src: 'static',
                // 目标位置
                dest: '.vite/build' 
              },
            ]
          })
        ],
        define,
        resolve: {
          // Load the Node.js entry.
          mainFields: ['module', 'jsnext:main', 'jsnext'],
        },
      };
    
      return mergeConfig(getBuildConfig(forgeEnv), config);
    });
    

    这样 static 静态资源目录就会被打包到 Electron 的编译目录内,也不会在 web 编译目录打包它。

    在这里插入图片描述

    c. 编写系统托盘,测试结果
    在项目根目录创建一个 electron 目录,在这个目录里创建一个 Tray.ts 文件,用于编写系统托盘按钮逻辑,在 src/main.ts 中引用托盘内容。Electron Forge 在编译后会将 Tray.ts 合并到 main.js 中,所以我们就以 main.js 的编译位置为路径锚点,进入与其保持同级的 static 目录里获取静态资源。

    在项目根目录创建一个 static 文件夹,在里面放置图片文件,例如 electron.png

    在这里插入图片描述

    Tray.ts 内容:

    import { app, BrowserWindow, Tray, Menu, nativeImage } from 'electron';
    import path from 'path';
    
    // 窗体对象
    let win: BrowserWindow;
    
    // 托盘菜单
    const contextMenu = Menu.buildFromTemplate([
        { label: '显示主界面', click: () => { win.show() } },
        { label: '退出应用', click: () => { app.quit() } }
    ])
    // 创建系统托盘
    function createTray(w: BrowserWindow) {
    	// 获取窗体对象
        win = w;
        // 获取 static 下的图标文件,创建图标对象
        const icon = nativeImage.createFromPath(path.join(__dirname, 'static', 'electron.png'));
        // 系统托盘
        const concrete = new Tray(icon);
        concrete.setContextMenu(contextMenu);
        concrete.setToolTip('工具组');
        // 点击托盘图标显示主窗口
        concrete.on('click', () => {
            win.show();
        });
        return concrete;
    }
    
    export default createTray;
    

    main.ts 内容:
    main.ts 位于 src 目录下,Tray.ts 位于 src 同级目录的 electron 目录下。

    import { app, BrowserWindow, Tray } from 'electron';
    import path from 'path';
    // 这个用于存放 ipcMain 进程通信,在这篇教程中并没有进行描述
    import ipceventmount from '../electron/Ipcevent'
    // 引入 Tray.ts 系统托盘
    import createTray from '../electron/Tray'
    
    // 窗体对象
    let win: BrowserWindow;
    // 系统托盘
    let concrete: Tray;
    
    if (require('electron-squirrel-startup')) {
      app.quit();
    }
    
    const createWindow = () => {
      // 创建并获取 BrowserWindow 对象
      win = new BrowserWindow({
        autoHideMenuBar: true,
        frame: false,
        width: 1000,
        height: 600,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js')
        }
      })
      
      if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
        win.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
      } else {
        win.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
      }
    }
    
    app.whenReady().then(() => {
      createWindow();
        app.on('activate', () => {
            if (BrowserWindow.getAllWindows().length === 0) {
                createWindow();
                ipceventmount(win);
            }
        })
        if (require('electron-squirrel-startup')) app.quit();
        ipceventmount(win);
    }).then(() => {
      /*
      在这里创建系统托盘
      */
      concrete = createTray(win);
    })
    
    // 全部窗口关闭时,退出应用
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') {
        app.quit();
      }
    });
    
    app.on('activate', () => {
      if (BrowserWindow.getAllWindows().length === 0) {
        createWindow();
      }
    });
    

    app.whenReady() 得到一个当 Electron 已初始化后 fulfillPromise 对象,
    在第一个 .then 里创建好 BrowserWindow 对象,并将其赋值给 win 对象,
    在第二个 .then 里执行 concrete = createTray(win); 创建并获取到系统托盘对象。

    d. 启动测试

    npm start

    在这里插入图片描述

    e. 目录结构

    root
       L .vite    # 编译目录
       |   L build
       |       L main.js
       |       L preload.js
       |       L static
       |             L electron.png
       L electron
       |   L Tray.ts
       |   L Ipcevent.ts
       L src
       |   L main.ts
       |   L preload.ts
       |   L renderer.ts
       L static
       |   L electron.png
    
  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值