Skip to content
扫码联系作者

环境&工程搭建

为什么用 Vite

https://cn.vitejs.dev/guide/why.html已经说得非常清楚了,这里不做过多赘述,在开始用 Vite 构建 Electorn 的项目之前,我也用过 Webpack 来构建,但是从开发体验来说,Vite 给人的感觉就是爽,没啥说的,爽就完了 😊。

创建一个 Vite+React 的项目

用 Vite 官方的指引创建一个 vite+react 的项目

bash
pnpm create vite

选择 React,选择 Typescript + SWC,SWC 是什么——>https://swc.rs

修改项目结构

我们把 src 下创建一个 render 目录和 main 目录,故名思义就是渲染进程和主进程,然后改变 index.html 中 script 的引入<script type="module" src="/src/render/main.tsx"></script>,这样极简的项目结构就出现了

自定义开发脚本

到现在为止,我们还是一个普通的 web 项目,现在我们需要自定义开发脚本来融合主进程和渲染进程的启动。

首先实现 render 进程的启动,删除vite.config.ts,添加scripts目录,然后加入dev.js,用来启动开发服务,第一阶段我们先完成渲染进程的启动配置,添加config/vite文件目录,加入render.js,然后在dev.js中使用config/vite/render.js作为配置文件,用来启动服务,核心代码如下

js
import path from "path";
import electronPath from "electron";
import { spawn } from "child_process";
import { createServer, build } from "vite";
import { fileURLToPath } from "url";

const __dirname = fileURLToPath(new URL(".", import.meta.url));

const sharedOptions = {
  mode: "dev",
  build: {
    watch: {},
  },
};

const renderDev = {
  async createRenderServer() {
    const options = {
      ...sharedOptions,
      configFile: path.resolve(__dirname, "../config/vite/render.js"),
    };
    this.server = await createServer(options);
    await this.server.listen();
    this.server.printUrls();
    return this.server;
  },
};

const initDev = async () => {
  try {
    await renderDev.createRenderServer();
  } catch (err) {
    console.error(err);
  }
};

initDev();

现阶段的目录结构如下:

现在render进程的脚本基本差不多了,下一步就是要把 Electron 集成进去。

集成 Electorn 进程

下面对config/vite/main.jssrc/main/index.ts以及scripts/dev.js进行编码。config/vite/main.js主要是为了给主进程使用的 Vite 配置文件,src/main/index.ts主要是 Electron 进程的入口文件,最后在scripts/dev.js中去启动服务。

编写代码之前我们先下载一下 Electron 的依赖。

tips:如果下载 Electron 慢的话,可以进行一下下载源的配置。

bash
pnpm config set registry https://registry.npm.taobao.org

pnpm config set electron_mirror https://npm.taobao.org/mirrors/electron/

主要是需要改造scripts/dev.js使其支持启动 electron 进程服务,然后将渲染进程和 Electron 进程合并,这样就可以构造一个最基础的 Electron 开发环境了,现在我们来尝试启动 Electron 进程。

核心代码如下

js
let spawnProcess = null;

const mainDev = {
  async createMainServer(renderDevServer) {
    const protocol = `http${renderDevServer.config.server.https ? "s" : ""}:`;
    const host = renderDevServer.config.server.host || "localhost";
    const port = renderDevServer.config.server.port;
    process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}/`;
    process.env.VITE_CURRENT_RUN_MODE = "main";
    const options = {
      ...sharedOptions,
      configFile: path.resolve(__dirname, "../config/vite/main.js"),
    };
    return build({
      ...options,
      plugins: [
        {
          name: "reload-app-on-main-package-change",
          writeBundle() {
            if (spawnProcess !== null) {
              spawnProcess.kill("SIGINT");
              spawnProcess = null;
            }

            spawnProcess = spawn(String(electronPath), ["."]);

            spawnProcess.stdout.on("data", (d) => {
              const data = d.toString().trim();
              console.log(data);
            });

            spawnProcess.stderr.on("data", (data) => {
              console.error(`stderr: ${data}`);
            });
          },
        },
      ],
    });
  },
};

const initDev = async () => {
  try {
    const renderDevServer = await renderDev.createRenderServer();
    await mainDev.createMainServer(renderDevServer);
  } catch (err) {
    console.error(err);
  }
};

initDev();

注意几个点

1.我们这里利用了writeBundle,就是等chunk都写入文件后,再启动 Electron 进程。

2.这里没有利用 Electron 的命令启动,而是通过 Node.js 的child_process模块的spawn方法启动 Electron 子进程,主要是因为我们需要依赖开发环境的渲染进程。

3.另外就是config/vite/main.js中需要对rollupOptionsexternal进行 electron 的配置,把导入包转成外部依赖,不然在启动 Electron 会找不到 Electron 的路径。

4.在createMainServer中我们注入了全局可使用的变量,以便 Electorn 加载页面的时候可以使用这些变量

现阶段的代码结构如下:

不出意外pnpm dev会出现下面的界面。

到现在,一个最简单的 Electron 开发环境搭建好了。