本章将介绍如何通过vite搭建electron + vue-ts 开发环境。nodejs版本16.16.0,vite版本3.2.0,包管理工具pnpm
# 使用vite vue-ts模板创建项目
pnpm create vite file-manager --template vue-ts
# 进入到项目目录下,安装依赖
cd file-manager
pnpm install
在 src
目录下创建 render
目录,将 src
下原有的目录及文件移入至 render
目录
在 src
目录下创建 main
和 preload
目录
将项目根目录下的 index.html
和 public
目录移入至 render
目录下,修改 index.html
中引入资源的路径
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TStitle>
head>
<body>
<div id="app">div>
<script type="module" src="/main.ts">script>
body>
html>
修改 vite
配置文件 vite.config.ts
需要安装
@types/node
模块用于让nodejs相关api支持ts
import { defineConfig } from "vite";
import type { BuildOptions } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
export default defineConfig(({ mode }) => {
const build: BuildOptions = {
outDir: resolve(__dirname, "./dist/render")
};
return {
base: "./",
root: resolve(__dirname, "./src/render"),
build,
plugins: [vue()]
}
});
修改 package.json
文件
// 修改scripts属性值为如下
"scripts": {
"dev:render": "vite",
"build:render": "vue-tsc && vite build"
}
到此,目录结构修改完毕。
执行
pnpm dev:render
运行web,在浏览器中查看web是否可以正常显示执行
pnpm build:render
打包web,查看打包出的内容路径与vite中配置的outDir
是否一致。以上两步均没问题则说明项目目录修改成功。
目录说明:
main 目录,electron主线程入口目录
preload目录,electron preload 目录(preload中的内容将会挂载到web window对象中,用于web内调用node相关API)
render目录,web目录
在 package.json
中添加配置
{
// ...
"packageManager": "[email protected]",
"engines": {
"node": "16.16.0"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
// ...
}
// ...
}
在项目根目录创建npm配置文件, .npmrc
文件,添加如下内容:
engine-strict = true
使用
eslint
进行代码规范。eslint
用于语法检测及代码风格约束。
安装 eslint
pnpm install -D eslint
初始化 eslint
npx eslint --init
执行eslint初始化目录后,根据项目需要在命令行交互界面选择对应选项即可。
修改 eslint
配置文件
由于我在初始化时选择用JavaScript作为配置文件语言,所以首先要在
env
属性中加入对node的支持,否则配置文件本身会提示异常
"env": {
"browser": true,
"es2021": true,
"node": true,
},
由于使用vue-ts环境作为web开发环境所以还需要增加对vue的支持
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
在配置中存在上述配置,需要将 parser
移入 parserOptions
中,并重新定义 parser
值为 vue-eslint-parser
即可。
parser: "vue-eslint-parser",
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: "@typescript-eslint/parser"
},
安装 vite-plugin-eslint
插件,开启vite对eslint的支持,当使用vite命令时会进行eslint验证
pnpm install -D vite-plugin-eslint
在vite配置文件中引入此插件,并在plugins中声明即可。
import eslintPlugin from "vite-plugin-eslint";
// ...
return {
//...
plugins: [
eslintPlugin()
// ...
]
};
需提前在本地安装
git
git init
husky
用于设置git钩子回调
lint-staged
用于验证暂存区文件
安装依赖
pnpm install -D husky lint-staged
配置 lint-staged
"lint-staged": {
"*.{vue,ts}": "eslint"
}
在执行
npx lint-staged
时会验证暂存区后缀为vue
或ts
的所有文件。
配置 husky
# 安装husky
npx husky install
# 添加git钩子
npx husky add .husky/pre-commit "npx lint-staged"
也就是说在
git commit
前执行,会先执行npx lint-staged
。
安装依赖
pnpm install -D electron
在 src > main
目录中添加入口文件
在该目录下初始化 typescript
。npx tsc --init
// 具体配置如下
{
"compilerOptions": {
"allowJs": true,
"alwaysStrict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "commonjs",
"moduleResolution": "node",
"noEmit": false,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "esnext",
"outDir": "../../dist/main",
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx", "**/*.js"]
}
创建 index.ts
文件,和 main-window.ts
文件。其中 index
作为项目入口,main-window
用于定义创建 BrowserWindow
的相关方法。
// main-window.ts
import { BrowserWindow, app } from "electron";
import { resolve } from "path";
const isDev = !app.isPackaged;
export function createWindow (): BrowserWindow {
const minWidth = 800;
const minHeight = 600;
// const preloadUrl = "";
const window = new BrowserWindow({
minWidth,
minHeight,
frame: false,
backgroundColor: "#ffffff",
// webPreferences: {
// preload: preloadUrl
// }
});
if (isDev) {
window?.loadURL(`http://localhost:${process.env.PORT || 5173}`);
window.webContents.openDevTools();
} else {
window?.loadFile(resolve(__dirname, "../render/index.html"));
window.removeMenu();
}
window.on("closed", () => {
window.destroy();
});
return window;
}
// index.ts
import { app } from "electron";
import { createWindow } from "./main-window";
async function bootstrap () {
app.on("ready", () => {
const window = createWindow();
});
}
bootstrap();
在 package.json
文件中添加启动命令
需要安装
concurrently
依赖,用于启动多个监听服务。(web服务和electron服务)pnpm install -D concurrently
{
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev:render": "vite",
"build:render": "vue-tsc && vite build",
"build:electron": "tsc -p ./src/main",
"dev:electron": "npm run build:electron && electron .",
"dev": "concurrently \"npm run dev:render\" \"npm run dev:electron\""
}
}
在 src > render > index.html
文件中添加 meta标签
设置安全策略,避免 electron
告警 Insecure Content-Security-Policy
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';">
设置 preload
相关内容
在该目录下初始化 typescript
。npx tsc --init
{
"compilerOptions": {
"allowJs": true,
"alwaysStrict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "commonjs",
"moduleResolution": "node",
"noEmit": false,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "esnext",
"outDir": "../../dist/preload",
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx", "**/*.js"]
}
在 src > preload
目录中创建 index.ts
,types.d.ts
文件。其中:index.ts
作为 preload
入口文件;types.d.ts
文件用于定义preload相关类型,并将其挂载于web端window对象上。
// index.ts
import { contextBridge, ipcRenderer } from "electron";
const sayHello = () => {
console.log("hello");
};
const apis = {
sayHello: sayHello
};
export type Apis = typeof apis;
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer);
contextBridge.exposeInMainWorld("Main", apis);
// types.d.ts
import type { ipcRenderer } from "electron";
import type { Apis } from "./index";
declare global {
interface Window {
ipcRenderer: typeof ipcRenderer;
Main: Apis;
}
}
在 src > main > main-window.ts
文件中引入preload并将其挂载于 webPrefrences
上,以达到在web端正常访问 preload
相关api的目的。
// ...
const window = new BrowserWindow({
// ...
webPreferences: {
preload: resolve(__dirname, "../preload/index.js")
}
});
// ...
到此electron以引入完毕,可在web端,可通过如下代码测试
if (window.Main) { window.Main.sayHello(); }
electron-builder
处理打包事宜安装
pnpm install -D electron-builder
配置
productName: 资料管理
nsis:
oneClick: false
shortcutName: 资料管理
allowToChangeInstallationDirectory: true
installerIcon: ./src/render/public/logo.ico
uninstallerIcon: ./src/render/public/logo.ico
installerHeaderIcon: ./src/render/public/logo.ico
files:
- dist
directories:
output: dist
extraResources:
- data
win:
icon: ./src/render/public/logo.ico
target:
- target: nsis
arch:
- x64
artifactName: 资料管理.exe
修改 package.json
文件
// 增加包描述
"description": "本地资料管理平台",
"author": {
"email": "[email protected]",
"name": "wkj"
},
// 设置脚本 (增加 build 、 dist命令,用于构建资源、打包成windows应用)
"scripts": {
"prepare": "husky install",
"preinstall": "npx only-allow pnpm",
"dev:render": "vite",
"build:render": "vue-tsc && vite build",
"dev:electron": "npm run build:electron && electron .",
"build:electron": "npm run build:preload && tsc -p ./src/main",
"build:preload": "tsc -p ./src/preload",
"dev": "concurrently \"npm run dev:render\" \"npm run dev:electron\"",
"build": "npm run build:render && npm run build:electron",
"dist": "electron-builder"
},