0%

Vue3+Vite+Electron+TypeScript开发App

前端开发技术日新月异,一段时间没碰就会发生不少变化。最近我想做个跨平台的桌面App,经过调研现在比较流行的一种架构是Vue3+Vite+Electron+TypeScript,今天我就来介绍一下我是如何将这几种技术结合到一起。

创建项目

第一步,执行下面的操作创建vite项目:

1
npm create vite

创建项目时,我们可以选择不同的模板来创建不同的项目。这里我们选择 Vue,之后再选择TypeScript,这样我们就创建了一个使用TypeScript作为开发语言的Vue项目,而其打包和测试工具使用的是Vite。

如果你的系统中没有npm命令,则需要先安装nodejs,关于nodejs的安装请自行到网上查阅。

接下来,执行下面的操作:

1
2
3
cd vite-project #进入到项目目录下
npm install #安装依赖包
npm run dev #启动web服务

执行完上面的操作后,你就可以通过访问http://127.0.0.1:5173/地址,看到Demo程序的样子了。

现在我们已经Vue部分构建好了,接下来咱们构建一下Electron相关的部分。执行下面的命令,安装Electron:

1
npm install electron --save-dev

该命令中的--save-dev指明安装的Electron仅用于开发环境。

Electron安装好后,在Vue项目目录下创建electron子目录,并创建两个文件,一个是main.ts另一个是preload.ts。其中main.ts用于创建App窗口,而preload.ts用于预加载一些资源,一般情况下我们不需要预加载任何东西。这两个文件的代码实现如下:

  • main.ts的实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    // main.ts

    // 控制应用生命周期和创建原生浏览器窗口的模组
    import { app, BrowserWindow } from 'electron'
    import path from 'path'

    function createWindow() {
    // 创建浏览器窗口
    const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
    // 引入预加载文件
    preload: path.join(__dirname, "preload.js"),
    },
    });

    // 加载web服务页面
    mainWindow.loadURL("http://localhost:5173");

    }

    // 这段程序将会在 Electron 结束初始化
    // 和创建浏览器窗口的时候调用
    // 部分 API 在 ready 事件触发后才能使用。
    app.whenReady().then(() => {
    createWindow();

    app.on("activate", function () {
    // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
    // 打开的窗口,那么程序会重新创建一个窗口。
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
    });

    // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
    // 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
    app.on("window-all-closed", function () {
    if (process.platform !== "darwin") app.quit();
    });
  • preload.ts的实现
    1
    2
    3
    4
    5
    // preload.ts

    //这里只打印了一行日志
    //如果需要预加载东西时,再在这里添加逻辑
    console.log("preload");

至此,我们就将Vue和Electron两部分的代码实现完成了。其中,Vue相关的代码都是由脚手架实现的,而Electron的代码是从官方Demo中拷贝来的。

几个配置文件及其作用

实际上Electron是由浏览器Chromium改造而来的,因此你可以把它看作是一个浏览器,浏览器上页面展示的内容由HTML编写,样式由CSS控制,而逻辑由Javascript实现。

Electron既然是浏览器,那么这些规则它也要遵守,所以我们编写的TypeScript代码不能在浏览器上运行,同样也不能在Electron上运行。因此,我们需要使用tsc编译器将main.tspreload.ts编译成main.jspreload.js,这样它才能在Electron上正常工作。

当然每次都手动执行tsc显然是不高效的,更好的办法是在启动Electron App时或者编译项目时自动执行该操作,这样更简单、更合理,为此我们需要修改一下相关的配置文件。

package.json该文件的作用是指明项目的依赖库脚本命令。其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "my-vite-app",
"private": true,
"version": "0.0.0",
"main": "main.js", #Electron的入口点
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"electron:dev": "tsc && electron ." #增加启动
},
"dependencies": {
"vue": "^3.4.21"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
"electron": "^29.1.5",
"typescript": "^5.2.2",
"vue-tsc": "^2.0.7"
}
}

在该配置文件中有两个字段需要我们重点关注一下,第一个是main字段,该字段指明了Electron的入口点;第二个字段scripts,该字段中包括好几个子字段,这些子字段指明有哪些npm run指令,比如当我们执行 npm run dev 时,它就会调用vite命令。同时,当我们执行npm run electron:dev时,它会执行tscelectron. 两条命令。

tsconfig.json,该文件是编译器tsc的配置文件,其作用是指定源文件位置及目标文件的输出位置。每当执行tsc命令时,tsc就会加载tsconfig.json文件,并从中读取配置项,tsconfig.json内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false,
"jsx": "preserve",
"outDir": "dist",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["electron/**/*.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

这个配置文件中也有两个我们需要特别注意的项,一个是outDir,指明目标文件输出到哪个目录下;另一个是include,指明源文件所在路径。

除了上面的两个重要的配置文件外,还有一个与vite相关的配置文件,即vite.config.ts。它的作用是告诉vite如何编译Vue程序,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { builtinModules } from 'module';

export default defineConfig({
base: './',
plugins: [vue()],
root: __dirname, // 指向渲染进程目录
build: {
outDir: 'dist/renderer', // 指定构建输出目录
assetsDir: '',
rollupOptions: {
output: {
format: 'cjs', // 设置输出格式为CommonJS
},
external: ['electron', ...builtinModules], // 排除electron和内建模块
},
},
optimizeDeps: {
exclude: ['electron'], // 排除electron
},
});

在这个文件的defineConfig()函数中,指明了Vue的输出目录为dist/renderer,编译后的JavaScript格式为cjs,也就是Commond JS,并将electron相关的代码排除在外。

至此我就将项目中的几个配置文件及其作用向你介绍清楚了,接下来咱们来看看Electron的运行模式。

运行模式

Electron有两种运行模式,一种是从Web服务器加载页面的工作方式,这种工作方式的好外是容易更新升级,当服务端的页面发生变化时,客户端重新启动后会立即更新,而其缺点是必须要有网络,加载速度相对较慢;第二种是从本地加载页面,其好处是加载速度快,缺点是升级困难。

一般情况下我们使用Electron开发的桌面App都采用第一种方案。 面我们来看一下如何加载在线页面。

加载在线页面模式

其实让Electron加载在线页面非常容易,只需在main.ts中执行一行代码即可,如下:

1
2
3
4
...
// 加载web服务页面
mainWindow.loadURL("http://localhost:5173");
...

通过上面的代码就可以将URL地址中的index.html加载进Electron。

加载本地页面模式

同时,想加载本地页面也很容易,只需将**loadRUL()函数修改为loadFile()**即可,如下:

1
2
3
...
mainWindow.loadFile("dist/renderer/index.html")
...

需要注意的是loadFile函数中的输入参数一定要与package.json中设置的输出路径一致,即从dist目录开始,直到index.html结束。

编译并运行Electron App

现在我们就可以编译并运行Electron App了,为此我们要执行下面的命令。

  • 首先编译Vue程序,如下:
    1
    2
    3
    npm run build #用于加载本地页面
    #
    npm run dev #用于加载Web服务页面
  • 之后启动Electron App,如下:
    1
    npm run electron:dev
    这样我们就可以将Electron App运行起来了。

调试

编写代码就绕不开调试,对于Electron App的调试有两种方式,一种是通过浏览器调试,对于Web前端开发人员来说,比较喜欢这种方式;另一种是使用VSCode进行调试。

下面我分别对这两种方式进行一个介绍

浏览器方式

使用浏览器对Electron App调试又分为两种,一种是对Electron App的调试,另一种是对Electron App所显示的页面的调试,咱们一项项来说。

首先看如何对页面调试,实际上调试Electron App的页面与浏览器下调试页面是一样的,只不过打开调试工具的方式不同罢了。

对于Chrome浏览器我们可以通过鼠标右键->检查来打开调试器,而Electron App则不能这么做,正确的打开方式是通过Option+Command+ICtrl+Alt+I这个组合键打开调试器。

如果想调试Electron App,我们需要在启动Electron App时加上–inspect参数,即将它添加到package.json的electron:dev这条命令上,如下所示:

1
2
3
...
"electron:dev": "tsc && electron . --inspect=1234"
...

inspect=1234中的1234指明开启的调试端口号是多少。有了这个端口号,我们就可以在Chome浏览器的输入框中输入chome://inspect,然后指定好端口号,就可以对Electron进行调试了。

使用VSCode调试

我们同样可以使用VSCode对Electron App进行调试,只不过需要添加几个配置文件。

第一个,修改tsconfig.json文件,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"compilerOptions": {
"target": "ES2020", // 指定 ECMAScript 目标版本
//======= 指定输出的模块系统 ==============
"module": "ESNext",

"lib": ["ES2020", "DOM"], // 包含的库文件
"allowJs": true, // 允许编译 JavaScript 文件
"checkJs": false, // 不检查 JavaScript 文件中的错误
"jsx": "react", // 支持 JSX 语法
"declaration": true, // 生成 .d.ts 声明文件
//======= sourceMap 很重要 ==============
"sourceMap": true, // 生成源码映射文件 (.map) 以支持调试

"outDir": "dist", // 指定输出目录
"strict": true, // 启用所有严格类型检查选项
"esModuleInterop": true, // 允许默认导入非 ES 模块
"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
"resolveJsonModule": true, // 允许导入 JSON 模块
"isolatedModules": false, // 确保每个文件可以独立编译
"noEmitOnError": true, // 发生错误时不输出文件
"noImplicitAny": true, // 不允许隐式的 any 类型
"noImplicitReturns": true, // 不允许函数没有返回值
"noUnusedLocals": true, // 报告未使用的局部变量
"noUnusedParameters": true, // 报告未使用的参数
"removeComments": false // 不移除注释
},
"include": ["src/**/*.ts"], // 包含的文件
"exclude": ["node_modules", "dist"] // 排除的文件
}

最重要的是要为ts代码生成.map文件,这样才能对TypeScript进行调试

之后,添加tasks.json文件,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"version": "2.0.0",
"tasks": [
{
"label": "tsc: build - tsconfig.json",
"type": "typescript",
"tsconfig": "tsconfig.json",
"problemMatcher": [
"$tsc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

该配置文件用于编译TypeScript代码,它在开启调试器是被调用。

第三个,添加launch.json文件,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable":"${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args" : ["--inspect=9229", "${workspaceFolder}/dist/main.js"],
"preLaunchTask": "tsc: build - tsconfig.json",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/dist/**/*.js",
]
}
]
}

当打开调试器时,调试器会读取该文件,并执行该文件中定义的preLaunchTask任务。该任务就是tasks.json中定义的任务。之后启动Electron App,并打开9229调试端口。

调试器通过9229向Electron App发调试指令,如我们在main.ts中设置了断点,调试器就会通过该端口向断点发送给Electron App,当程序运行时,断点就会停留在指定的行处。

当然Electron App执行的是main.js中的代码,它与main.ts之间的对应关系就是通过sourceMaps生成的.map文件找到的。

小结

至此,我们就使用Vue3+Vite+Electron+TypeScript实现了一个最简单的Electron App。其功能非常简单,就是在Electron中将Vue的Demo页面显示出来。

这只是我们使用Vue3+Vite+Electron+TypeScript开发Electron App的第一步,后面有时间我会较弱输出一些更高阶的知识。

未完待续

  1. 如何在VSCode中实现对Vue代码的调试

欢迎关注我的其它发布渠道