前言
先从官网教程开始搭建一个简单的Electron,
然后通过vite脚手架重新搭建一个 vite + electron + vue3 + UI的项目。
原生态入门
初始化环境
Windows下搭建一个Electron,先贴一个官方网站Electron (electronjs.org)
检查 Node.js 是否已被安装
没安装的官网下载 Node.js (nodejs.org) 长期稳定版LTS即可
$ node -v
v16.14.2
$ npm -v
8.7.0
初始化项目
1. 创建目录并初始化项目
mkdir ele-app && cd ele-app
npm init
2. 添加electron依赖
npm install electron --save-dev
可能出现electron安装不下来,那么,换源
npm config set registry https://registry.npmmirror.com
npm config set electron_mirror https://cdn.npmmirror.com/binaries/electron/
npm config set electron_builder_binaries_mirror https://npmmirror.com/mirrors/electron-builder-binaries/
3. 添加启动入口
安装完electron后,package.json的内容如下,
注意"main": "index.js",(官网文档为main.js)以实际为准,可以改成和官方一致,方便后续编码。
{
"name": "ele-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "honyee",
"license": "ISC",
"dependencies": {
"electron": "^32.1.2"
}
}
修改package.json,scripts中添加"start": "electron .",用于启动项目。
其中.表示当前目录中寻找入口点,就是上述的index.js。
也可以指定完整的入口位置,例如:"start": "electron ./index.js。
修改完后如下
{
"name": "ele-app",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "honyee",
"license": "ISC",
"dependencies": {
"electron": "^32.1.2"
}
}
创建main.js文件,填充点内容
console.log('Hello from Electron');
目前结果如下
4. 启动项目
npm run start
创建窗体
创建您的第一个应用程序 | Electron (electronjs.org)
在 Electron 中,每个窗口展示一个页面,后者可以来自本地的 HTML,也可以来自远程 URL。
1. 创建一个 index.html 文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"/>
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'"/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>
2. 将 index.js 中的内容替换成下列代码
const {
app,
BrowserWindow
} = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
app.on('window-all-closed', () => {
// 终止app
app.quit();
})
3. 再次运行
打开开发者工具
开发中需要调试,免不了要要使用开发者工具
在main.js中添加代码
win.webContents.openDevTools();
启动时会出现如下错误,是因为nodejs新版本不支持自动填充,不影响使用,忽略即可
[23020:1010/094855.972:ERROR:CONSOLE(1)] "Request Autofill.enable failed. {"code":-32601,"message":"'Autofill.enable' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
[23020:1010/094855.972:ERROR:CONSOLE(1)] "Request Autofill.setAddresses failed. {"code":-32601,"message":"'Autofill.setAddresses' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
给页面添加个按钮
创建一个index.js,并在index.html中引入
<script src="index.js"></script>
方式1 (推荐)
通过addEventListener添加click事件
<button id="myBtn">click me</button>
给按钮加个点击监听
window.onload = function () {
document.getElementById("myBtn").addEventListener("click",
() => {
console.log('just do it');
});
}
方式2(不推荐)
直接在标签上写onclick事件
<button onclick="clickMe()">click me</button>
function clickMe() {
console.log('just do it');
}
在点击时会出现错误
Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.
因为index.html中添加了安全策略,防止跨站脚本攻击等
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"/>
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'"/>
事件介绍
ready 应用准备完成事件,下面两种方式都可以
// 方式1
app.on('ready', ()=>{
});
// 方式2
app.whenReady().then(() => {
})
activate 应用被激活
触发场景:
- 当应用从没有窗口打开的状态变为有窗口打开时,比如用户点击应用图标或通过其他方式激活应用。
- 在 macOS 上,当用户点击 dock 中的应用图标且应用已经在运行但没有可见窗口时,也会触发这个事件。
app.on('activate', () => {
})
ready-to-show 准备展示时
win.once('ready-to-show', () => {
});
close 窗口关闭事件
win.on('closed', () => {
})
window-all-closed 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
})
上下文隔离
官方推荐上下文隔离,通过预加载preload暴露api,渲染进程通过api执行electron的方法。
从 Electron 20 开始,预加载脚本默认沙盒化 ,不再拥有完整 Node.js 环境的访问权。
为简化代码,下面都是通过关闭上下文隔离的方式示例代码
预加载(推荐)
- 创建
preload.js提供暴露的方法
const {contextBridge} = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {
print: (content) => console.log('content', content),
})
main.js中指定preload
const {
app, ipcMain,
BrowserWindow
} = require('electron')
const {join} = require("node:path");
let win;
const createWindow = () => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: join(__dirname, 'preload.js')
}
})
win.loadFile('index.html');
win.webContents.openDevTools();
}
app.whenReady().then(createWindow);
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
app.quit();
})
index.js中调用暴露的API
window.myAPI.print('print test')
关闭上下文隔离(不推荐)
BrowserWindow配置项说明:
contextIsolation:用于控制渲染进程的上下文隔离,当设置为false时,渲染进程可以直接访问主进程的全局对象,例如require('electron').remotenodeIntegration:用于控制渲染进程中是否启用 Node.js 集成,当设置为true时才可以使用如require此类方法,且contextIsolation必须为falseenableRemoteModule:用于控制是否启用remote模块。
进程间通信
进程间通信 | Electron (electronjs.org)
在electron中,把main.js称为主进程,每个打开的BrowserWindow称为渲染进程。
electron 进程间通信的方式主要有两种,ipc和remote
ipc
ipc分别有ipcMain和ipcRenderer,ipcMain用于主进程,ipcRenderer用于渲染进程。
- 模式 1:渲染器进程到主进程(单向),即没有返回值,使用
ipcRenderer.send发送消息,ipcMain.on接收消息 - 模式 2:渲染器进程到主进程(双向),即有返回值,使用
ipcRenderer.invoke与ipcMain.handle - 模式 3:主进程到渲染器进程,使用
WebContents.send发送消息,ipcRenderer.on接收消息 - 模式 4:渲染器进程到渲染器进程,没有直接的方法
模式1 渲染器进程到主进程(单向)
main.js完整代码
const {
app, ipcMain,
BrowserWindow
} = require('electron')
let win;
const createWindow = () => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
})
win.loadFile('index.html');
// 打开控制台,查看打印内容
win.webContents.openDevTools();
// 监听渲染进程发送的消息
ipcMain.on('async-message', (event, arg) => {
console.log('args: ', arg);
// 发送消息到主进程
event.sender.send('async-reply', 'Main has received');
});
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
// 终止app
app.quit();
})
index.js完整代码
const {ipcRenderer} = require('electron')
window.onload = function () {
document.getElementById("myBtn").addEventListener("click",
() => {
// 使用 ipcRenderer.send 向主进程发送消息。
ipcRenderer.send('async-message', 'Child call Main');
});
// 监听主进程返回的消息
ipcRenderer.on('async-reply', function (event, arg) {
console.log('Child receive Main Message:',arg);
});
}
模式 2:渲染器进程到主进程(双向)
main.js完整代码
const {
app, ipcMain,
BrowserWindow
} = require('electron')
let win;
const createWindow = () => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false, // 控制渲染进程的上下文隔离,false表示能直接访问主进程的内容
nodeIntegration: true, // 控制渲染进程中是否启用 Node.js 集成
}
})
win.loadFile('index.html');
win.webContents.openDevTools();
// 监听渲染进程发送的消息
ipcMain.handle('invoke-message', (event, arg1, arg2) => {
console.log('args: ', arg1, arg2);
return 'main result'
});
}
app.whenReady().then(createWindow);
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
app.quit();
})
index.js完整代码
const {ipcRenderer} = require('electron')
window.onload = function () {
document.getElementById("myBtn").addEventListener("click",
() => {
ipcRenderer.invoke('invoke-message', 'a', 'b')
.then(res => console.log('res', res))
});
}
模式 3:主进程到渲染器进程
main.js完整代码
const {
app, ipcMain,
BrowserWindow
} = require('electron')
let win;
const createWindow = () => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false, // 控制渲染进程的上下文隔离,false表示能直接访问主进程的内容
nodeIntegration: true, // 控制渲染进程中是否启用 Node.js 集成
}
})
win.loadFile('index.html');
win.webContents.openDevTools();
// 发送消息
setTimeout(()=>{
win.webContents.send('notify-message', 'a', 'b');
},1000)
}
app.whenReady().then(createWindow);
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
app.quit();
})
index.js完整代码
const {ipcRenderer} = require('electron')
window.onload = function () {
ipcRenderer.on('notify-message', (event, arg1, arg2) => {
console.log('args', arg1, arg2);
})
}
模式 4:渲染器进程到渲染器进程
没有直接的方法可以使用 ipcMain 和 ipcRenderer 模块在 Electron 中的渲染器进程之间发送消息。 为此,您有两种选择:
- 将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
- 从主进程将一个 MessagePort 传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。
remote
使用remote可以直接调用主进程对象的方法,已不推荐使用
从 Electron 10 开始,remote模块默认被禁用以提高安全性。从Electron 14以后需要单独安装引入
- 仅需启用即可的使用:
main.js中
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false,
enableRemoteModule: true
}
})
index.js中
const {remote} = require('electron')
const remote = require('electron').remote
const {app, BrowserWindow} = require('electron').remote
- 单独安装引入后的使用:
安装依赖
npm install --save @electron/remote
main.js中启用
const remote = require('@electron/remote/main')
remote.initialize()
remote.enable(win.webContents);
index.js中调用
const remote = require("@electron/remote")
打包应用
打包您的应用程序 | Electron (electronjs.org)
官方文档使用 Electron Forge打包
npm install --save-dev @electron-forge/cli
npx electron-forge import
创建一个可分发版本,注意,package.json中的author和description不能为空(串),打包完后输出到项目底下的out目录中,近300MB,真就打包了一个浏览器进去
npm run make
发布和更新
发布和更新 | Electron (electronjs.org)
将发布应用到 GitHub 版本中心,并将自动更新功能整合到应用代码中。
使用vite构建
上面的流程是js + electron,现在用 ts + electron + vite + vue3 重新搭建一次
什么是vite
简单讲,vite和webpack一样是一个前端开发与构建工具,方便开发和打包。然后直接开整……
初始化项目
为了方便,直接用electron-vite搭建,手动配置vite + electron 太麻烦……
npm create electron-vite@latest
- 填入项目名称
- 选择Vue
- cd到刚创建的目录里
npm install
关于type
package.json中的type属性
module: Node.js 将以 ES 模块(ECMAScript modules)的方式加载模块,在代码中使用import和export语句来导入和导出模块。
import { someFunction } from './someModule.js';
export const anotherFunction = () => {
// 函数实现
};
commonjs:Node.js 将以 CommonJS 的方式加载模块,使用require和module.exports来导入和导出模块。
const someModule = require('./someModule');
module.exports = {
anotherFunction: () => {
// 函数实现
}
};
如果type配置与实际使用不符合,运行项目会出现如下错误:
了解目录和文件配置
electron存放electron的代码public存放公共资源,如图片这些release默认打包后的输出目录,可在electron-builder.json5中修改src存放vue代码electron-builder.json5打包配置index.html首页入口package.json依赖和项目配置tsconfig.json配置typescripttsconfig.node.json配置TypeScript 在 Node.js 环境中的编译和开发vite.config.ts配置vite
打包输出结果
package.json 说明
通过electron-vite安装的electron版本可能不是最新,例如这里是^30.0.1,
本来想使用npm update electron更新版本,发现不起作用,原因未知,所以可以考虑手动改版本号……
scripts:
- dev : 启动electron
- build:打包,会输出到release,生成exe安装包,建议关闭部分代码检查,否则可能会出现
xxx is declared but its value is never read.之类的错误,关闭方法在下面tsconfig.json说明 - preview:预览,可以在浏览器中访问,而不是通过electron
{
"name": "vit-ele-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build && electron-builder",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.21"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
"typescript": "^5.2.2",
"vite": "^5.1.6",
"vue-tsc": "^2.0.26",
"electron": "^30.0.1",
"electron-builder": "^24.13.3",
"vite-plugin-electron": "^0.28.6",
"vite-plugin-electron-renderer": "^0.14.5"
},
"main": "dist-electron/main.js"
}
tsconfig.json说明
strict:严格检查模式
noUnusedLocals:检查未使用的变量,建议改为false
noUnusedParameters:检查未使用的函数参数,建议改为false
noFallthroughCasesInSwitch:检查switch的case是否使用break、return、throw来明确终止
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "electron"],
"references": [{ "path": "./tsconfig.node.json" }]
}
引入UI
以下几款UI,这里以 Element+ 为例
安装依赖
npm install element-plus --save
使用依赖
/src/main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(ElementPlus).mount('#app') // 省略其他部分
使用组件
在/src/App.vue中随便写个组件试试有没有生效
<el-button type="primary">Primary</el-button>
发起http请求
引入axios
npm i axios -S
electron默认开启站点安全机制,只允许主进程发起http请求,渲染进程发起的http请求皆视为跨域CORS。
解决方式:
- 方式1:通过preload间接调用
在/electron/main.ts中引入axios,并通过ipcMain双向通信暴露接口
import axios from 'axios'
//...忽略其他的代码
ipcMain.handle('http-get', async (_event, url, params) => {
const response = await axios.get(url, params)
console.log('http-get', url, params,response.data)
return response.data
});
ipcMain.handle('http-post', async (_event, url, params) => {
const response = await axios.post(url, params)
console.log('http-post', url, params,response.data)
return response.data
});
在/electron/preload.ts中暴露接口
contextBridge.exposeInMainWorld('http', {
get<T>(url: string, params?: object): Promise<T> {
return ipcRenderer.invoke('http-get', url, params);
},
post<T>(url: string, params?: object): Promise<T> {
return ipcRenderer.invoke('http-post', url, params);
}
})
在渲染进程中调用接口,例如 HelloWorld.vue
window.http.get('http://localhost:1222/api/test/get')
.then(res => {
console.log(res)
})
- 方式2:禁用web安全性
webSecurity: false
好处是可以直接在渲染进程请求http,且开发者工具的NetWork可以直接查看请求
坏处是安全性降低
/electron/main.ts
win = new BrowserWindow({
icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'),
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
webSecurity: false // 禁用web安全性
},
})
/src/components/HelloWorld.vue
import axios from "axios";
axios.get('http://localhost:1222/api/test/get')
.then(res => {
console.log(res)
})
使用Sass
Sass 是一个 CSS 预处理器。
Sass 是 CSS 扩展语言,可以帮助我们减少 CSS 重复的代码,节省开发时间。
Sass 完全兼容所有版本的 CSS。
- 安装Sass,网上默认推荐
-g全局安装,但是这里使用局部安装
npm i sass -D
-
将
/src/main.css改为/src/main/scss,注意是scss而不是sass -
使用Sass,比如使用变量
--el-color-success,该变量是element-plus已经定义的颜色
@import "../../node_modules/element-plus/dist/index";
.use-color {
color: var(--el-color-success);
}
- 启动项目时,会提示API已过期,可以添加如下配置
vite.config.ts
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
api: "modern-compiler" // or 'modern'
}
}
},
});
打包
由于引入了element-plus,在打包时,会提示存在index-xxxxx-.js体积过大,解决方式如下
方式1: 修改全局的包大小告警阈值
export default defineConfig({
build: {
chunkSizeWarningLimit: 800, // 构建时的 chunk 大小警告阈值,默认500kb
}
});
方式2: 自定义分包规则
import {ManualChunkMeta} from "rollup";
export default defineConfig({
build: {
chunkSizeWarningLimit: 800, // 构建时的 chunk 大小警告阈值
rollupOptions: {
output: {
manualChunks(id: string, meta: ManualChunkMeta) {
// 未配置时默认直接返回index,所以打包后文件名是index-xxxxx-.js
// return 'index'
// 根据id分包
if (id.includes('node_modules')) {
const arr = id.toString().split('node_modules')[1].split('/');
// 单独将大包分离出来
if (arr[1].includes('element-plus')) {
return '__vendor_' + arr[1];
}
}
return 'index'
},
}
}
},
});
解决一些环境、编译问题
控制台的中文乱码
如下图,中文乱码
解决方式: 修改注册列表
Windows+R,输入regedit打开注册列表- 来到
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor - 添加一个字符串值,名称为:
autorun数据为:chcp 65001
implicitly has an 'any' type
error TS7016: Could not find a declaration file for module .... implicitly has an 'any' type.
因为是typescript,存在未指定类型代码,可以强制关闭该校验
tsconfig.json
{
"compilerOptions": {
"noImplicitAny": false, // 强制关闭该校验
}
}
Electron Security Warning
这个警告,是上面提到过的安全策略问题
Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
this app to unnecessary security risks.
在/index.html中加入如下内容即可
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';">
完整版
<meta http-equiv="Content-Security-Policy" content="default-src *; connect-src * ws://* wss://*; style-src * 'unsafe-inline' 'unsafe-eval'; media-src * ; img-src * data:; font-src * ; script-src * 'unsafe-inline' 'unsafe-eval';" />
打包时找不到preload中暴露的API
通过electron-vite初始化项目时,已经自动初始化了如下代码
/electron/preload.ts
// --------- Expose some API to the Renderer process ---------
contextBridge.exposeInMainWorld("ipcRenderer", {
on(...args: Parameters<typeof ipcRenderer.on>) {
const [channel, listener] = args;
return ipcRenderer.on(channel, (event, ...args) =>
listener(event, ...args),
);
},
off(...args: Parameters<typeof ipcRenderer.off>) {
const [channel, ...omit] = args;
return ipcRenderer.off(channel, ...omit);
},
send(...args: Parameters<typeof ipcRenderer.send>) {
const [channel, ...omit] = args;
return ipcRenderer.send(channel, ...omit);
},
invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
const [channel, ...omit] = args;
return ipcRenderer.invoke(channel, ...omit);
},
// You can expose other APTs you need here.
// ...
});
/electron/electron-env.d.ts
// Used in Renderer process, expose in `preload.ts`
interface Window {
ipcRenderer: import("electron").IpcRenderer;
}
在/electron/preload.ts中的接口的实现,如果在打包build时没有在/electron/electron-env.d.ts中声明暴露的API,会出现错误:
error TS2339: Property 'ipcRenderer' does not exist on type 'Window & typeof globalThis'.
如果要新增一个自定义API,
/electron/preload.ts编写接口
contextBridge.exposeInMainWorld("myApi", {
print() {
console.log('myApi print')
},
});
- 新建一个接口文件
/electron/preload-interface.ts·
interface MyApi {
print();
}
- 暴露接口
/electron/electron-env.d.ts
interface Window {
myApi: import("./preload-interface.ts").MyApi;
}
- 在渲染进程中使用
window.myApi.print();
组件被强制居中了
因为style.css中#app的样式默认居中了,可以在使用其他组件时同时设置自身text-align
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
令组件充满窗口
修改style.css,
#app {
width: 100vw; /*占满可视宽度的100%*/
height: 90vh; /*占满可视高度的90%*/
margin: 0 auto;
padding: 2rem;
text-align: center;
}
窗口设置
最大化
win.maximize();
无边框
win = new BrowserWindow({
frame: false,
})
隐藏菜单栏
import { Menu } from "electron";
Menu.setApplicationMenu(null);
使用dialog的问题
我要调用系统的文件操作窗口,例如保存文件,会出现dialog出现后,依旧可以点击其他窗体的问题。
因为dialog.showSaveDialogSync([window, ]options)中的window是可选参数,不传递则不会“硬控”
ipcMain.on('dialog-save', (_event, base64ImageData) => {
// 提取图像格式
const extendName = base64ImageData.split(';')[0].split('/')[1];
// 打开保存对话框获取保存路径
const filePath = dialog.showSaveDialogSync(win, {
filters: [
{name: 'Images', extensions: [extendName]},
],
});
if (filePath) {
// 去除 Base64 数据的头部信息
const base64Data = base64ImageData.split(',')[1];
// 将 Base64 数据转换为 Buffer 对象并保存为文件
fs.writeFileSync(filePath, Buffer.from(base64Data, 'base64'));
}
})
路径别名
原先使用 src/components/hello/world/
采用路径别名后 @components/hello/world
添加如下配置:
vite.config.ts
import path from 'node:path'
export default defineConfig({
resolve: {
alias: {
'@': path.join(__dirname, 'src'),
'@lib': path.join(__dirname, 'src/lib'),
'@components': path.join(__dirname, 'src/components'),
},
},
});
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@lib/*": ["src/lib/*"],
"@components/*": ["src/components/*"],
},
}
关闭Linting校验
打包时会检查变量是否使用、是否严格模式等
tsconfig.json
{
"compilerOptions": {
{
/* Linting */
"strict": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": false
}
}
拼写检查
使用element-plus的input->textarea,会出现红色波浪号,这是因为默认会进行拼写检查,在el-input关闭即可
:spellcheck="false"
Unable to move the cache: 拒绝访问
打开了不止一个Electron
[7992:1022/085703.670:ERROR:cache_util_win.cc(20)] Unable to move the cache: 拒绝访问。 (0x5)
[7992:1022/085703.670:ERROR:cache_util_win.cc(20)] Unable to move the cache: 拒绝访问。 (0x5)
[7992:1022/085703.670:ERROR:cache_util_win.cc(20)] Unable to move the cache: 拒绝访问。 (0x5)
[7992:1022/085703.673:ERROR:disk_cache.cc(208)] Unable to create cache
[7992:1022/085703.673:ERROR:gpu_disk_cache.cc(711)] Gpu Cache Creation failed: -2
[7992:1022/085703.673:ERROR:disk_cache.cc(208)] Unable to create cache
[7992:1022/085703.673:ERROR:gpu_disk_cache.cc(711)] Gpu Cache Creation failed: -2
[7992:1022/085703.673:ERROR:disk_cache.cc(208)] Unable to create cache
[7992:1022/085703.674:ERROR:gpu_disk_cache.cc(711)] Gpu Cache Creation failed: -2