Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】
背景
目前社区两大Vue+Electron的脚手架:electron-vue和vue-cli-plugin-electron-builder,
都有这样那样的问题,且都还不支持Vue3,然而Vue3已是大势所趋,
Vite势必也将成为官方Vue脚手架,
下图是尤雨溪在开发好Vite之后与webpack之父的对话
所以开发一个Vite+Vue3+Electron的脚手架的需求日趋强烈
我前段时间做了一个,
但是发现了一些与Vite有关的问题,
比如:Vite会把开发环境的process对象吃掉的问题
这对于web项目来说问题不大,但对于我们的Electron项目来说,就影响很大了
今天我就把这个思路和实现方式的关键代码发出来供大家参考,
同时也希望Vue社区的贡献者们,能注意到这个问题
(给Vue官方的各个项目提issue真的是太难了,Electron官方项目在这方面就做的很好,很open、很包容)
环境
先用Vite创建一个Vue3的工程,这就是你的实际项目工程
接着安装几个Electron相关的依赖,最终我的工程下的依赖情况如下:
"@vue/compiler-sfc": "^3.0.0",
"vite": "^1.0.0-rc.9",
"vue": "^3.0.2",
"vue-router": "^4.0.0-rc.1",
"electron": "^11.0.2",
"electron-builder": "^22.9.1",
"electron-updater": "^4.3.5",
注意:这些依赖全部安装在devDependencies下
各个库的版本发文时应该是最新的了,不过如果有更新的版本,你完全可以用,没影响。
工程的目录结构大概是如下这样:
[yourProject]
node_modules 依赖包
public vite创建的目录,为vue服务的,实际没多大用
release 打包后编译输出的目录,该目录的根目录下存放打包后的安装包
bundled 该目录存放vue打包后的文件(html js css img等)
win-unpacked 该目录存放编译后生成的可执行文件及相关的dll,不包含安装包
resource 资源目录
unrelease 该目录存放编译期需要的资源
release 该目录存放编译后需要随安装包分发给客户的资源
script 此目录存放各种脚本,比如编译脚本,启动脚本,签名脚本等
src 源码目录
render 渲染进程源码目录
main 主进程源码目录
common 两个进程都会用到的共用源码目录
package.json 项目配置文件
index.html vue3的入口页面
.gitignore
接着在package.json中,增加两个命令:
"scripts": {
"start": "node ./script/dev.js",
"release": "node ./script/release.js"
},
同时在script目录下创建相应的文件,接着我们就开始撰写者两个文件的代码了
调试脚本
通过Vite启动Web项目
调试脚本首先要做的工作就是启动Vue项目
让它跑在http://localhost下,这样我们修改渲染进程的代码时,
会通过Vite的热更新机制实时反馈到界面上
Vite除了提供cli的指令启动项目外,也提供了API,我这里就是直接调它的API来启动项目的
关键代码如下:
let vite = require("vite")
createServer () {
return new Promise((resolve, reject) => {
let options = {
root:process.cwd(),
enableEsbuild: true
};
this.server = vite.createServer(options);
this.server.on("error", (e) => this.serverOnErr(e));
this.server.on("data", (e) => console.log(e.toString()));
this.server.listen(this.serverPort, () => {
console.log(`http://localhost:${this.serverPort}`);
resolve();
});
});
},
其中this.serverPort是绑定在当前对象上的一个变量,意义是指定vite项目启动时使用的端口号
启动成功后http server对象绑定到当前对象的server变量上
如果启动过程中报错,则很有可能是端口占用,将执行如下逻辑:
serverOnErr (err) {
if (err.code === "EADDRINUSE") {
console.log(
`Port ${this.viteServerPort} is in use, trying another one...`
);
setTimeout(() => {
this.server.close();
this.serverPort += 1;
this.server.listen(this.viteServerPort);
}, 100);
} else {
console.error(chalk.red(`[vite] server error:`));
console.error(err);
}
},
这段逻辑就是递增端口号,再次尝试启动http server
设置环境变量
往往每个开发人员的环境变量都是不一样的
有的开发人员需要连开发服务器A,有的开发人员需要连开发服务器B
而且开发环境的环境变量、测试环境、生产环境的环境变量也不一样
所以我把环境变量设置到几个单独的文件中
方便区分不同的环境,也方便gitignore,避免不同开发人员的环境变量互相冲突
开发环境的环境变量保存在src/script/dev.env.js中
let env = require("./dev.env.js")
生产环境的环境变量则为release.env.js
这个文件的代码非常简单,如下:
module.exports = {
APP_VERSION: require("../package.json").version,
ENV_NOW: "dev",
PROTOBUF_SERVER: "******.com",
SENTRY_SERVICE: "https://******.com/34",
ELECTRON_DISABLE_SECURITY_WARNINGS: true
}
需要注意的是:ELECTRON_DISABLE_SECURITY_WARNINGS,
这个环境变量是为了屏蔽Electron开发者调试工具那一大堆警告的
(你如果开发过Electron应用,你应该知道我说的是什么)
APP_VERSION是从项目的package.json中取的版本号,
你当然可以不设置这个环境变量,通过Electron的API获取版本号
app.getVersion() //主进程可用
但通过ElectronAPI获取到的版本号,在开发环境下,是Electron.exe的版本号,不是你的项目的版本号
打包编译后,这个问题是不存在的。
ENV_NOW是当前的环境,开发环境下它的值为dev,打包编译后的生产环境它的值应为product,
因为现在我们是讲如何构建开发环境,引用的是dev.env.js,
等下一篇文章讲如何构建编译环境时,引用的就是release.env.js了,
编译主进程代码
Vite之所以快,有一个很重要的原因是它使用了esbuild模块来编译代码
这里我们也使用esbuild来编译我们的主进程的代码
前面说了主进程是放在src/main/目录下的
这里我使用的是TypeScript开发,入口程序是app.ts,你完全可以使用Js开发,文件名也随你自定义
buildMain () {
let outfile = path.join(this.bundledDir, "entry.js");
let entryFilePath = path.join(process.cwd(), "src/main/app.ts");
//这个方法得到的结果:{outputFiles: [ { contents: [Uint8Array], path: '<stdout>' } ]}
esbuild.buildSync({
entryPoints: [entryFilePath],
outfile,
minify: false,
bundle: true,
platform: "node",
sourcemap: false,
external: ["electron"],
});
env.WEB_PORT = this.serverPort;
let envScript = `process.env={...process.env,...${JSON.stringify(env)}};`
let js = `${envScript}${os.EOL}${fs.readFileSync(outfile)}`;
fs.writeFileSync(outfile, js)
},
esbuild会自动查找app.ts引用的其他代码,
还有treeshaking机制保证你不会把无用的代码打包到输出目录
我把sourcemap关掉了,因为调试主进程很困难,
基本都是手动console.log信息调试的,朋友们有好的建议请赐教一下
platform要指定成node,要不然esbuild会尝试帮你去找node.js内置的包,肯定找不到,就报错了
同理,还要把electron设置成external
在上一节设置的环境变量的基础上
我们又增加了一个WEB_PORT的环境变量,
Electron启动后,要根据这个变量去加载localhost的页面,
这个变量是应用启动时确定的,是动态的,所以没办法设置到dev.env.js中
输出代码前,我们把环境变量的值也附加在输出代码中了
这样Electron进程启动时,会先设置好环境变量,再执行具体的业务代码
(我们当然也可以通过其他方式设置环境变量,但这样做主要是为了和生产环境保持一致,看到下一篇文章你就会知道了)
最终生成的代码会被输出到这个目录下面:
bundledDir: path.join(process.cwd(), "release/bundled")
稍后我们启动Electron时,也会让Electron加载这个目录下的入口程序。
启动Electron
Electron的node module并没有提供API给开发者调用以启动进程
所以我们只能通过node的child_process模块来启动Electron的进程
代码如下:
createElectronProcess () {
this.electronProcess = spawn(
require("electron").toString(),
[path.join(this.bundledDir, "entry.js")],
{
cwd: process.cwd(),
env,
}
);
this.electronProcess.on("close", () => {
this.server.close();
process.exit();
});
this.electronProcess.stdout.on("data", (data) => {
data = data.toString();
console.log(data);
});
},
require("electron").toString()得到的是Electron的可执行文件的路径
Windows环境下为:node_modules\electron\dist\electron.exe
Mac环境下为:node_modules/electron/dist/Electron.app/Contents/MacOS/Electron
path.join(this.bundledDir, "entry.js")为Electron进程指定了入口程序文件的地址
cwd: process.cwd()是为Electron指定当前工作目录(此处又为Electron指定了一次环境变量,其实不指定也没关系)
当Electron进程退出时,我们也关闭了Vite创建的http server
主进程加载渲染进程页面
此处最关键的逻辑就是这一句
if (process.env.ENV_NOW === "dev") {
await win.loadURL(`http://localhost:${process.env.WEB_PORT}/`);
}
process.env.WEB_PORT就是我们上文中设置的WEB_PORT变量
这个逻辑当然还有else分支,那是下一篇博文的内容了
敬请期待!
Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】的更多相关文章
- 基于 vagrant搭建移动端的开发环境
# 后端开发环境Homestead启动 Homestead 之前,确保 VirtualBox .Vagrant.Git 软件己安装. ## 安装 laravel/homesteadvagrant bo ...
- 基于全志H3芯片的ARM开发环境搭建
基于全志H3芯片的ARM开发环境搭建 最近买了个友善之臂的NanoPi M1板子,又在网上申请了个NanoPi NEO板子,这两个都是基于全志H3芯片的Crotex-A7四核ARM开发板,两个板子可以 ...
- 如何基于Jupyter notebook搭建Spark集群开发环境
摘要:本文介绍如何基于Jupyter notebook搭建Spark集群开发环境. 本文分享自华为云社区<基于Jupyter Notebook 搭建Spark集群开发环境>,作者:apr鹏 ...
- 搭建 AngularJS+Ionic+Cordova 开发环境并运行一个demo
目前的手机APP有三类:原生APP,WebAPP,HybridApp:HybridApp结合了前两类APP各自的优点,越来越流行. Cordova就是一个中间件,让我们把WebAPP打包成Hybrid ...
- 极其简单的搭建eclipse的android开发环境
这篇博客是关于如何搭建eclipse的android开发环境, 与网上的其他博客不同,我的方法比他们简单的多,所 以推荐给大家. 搭建eclipse的android开发环境步骤: 1.配置JDK(Ja ...
- 基于Eclipse的Go语言可视化开发环境
http://jingyan.baidu.com/article/d7130635032e2f13fdf475b8.html 基于Eclipse的Go语言可视化开发环境 | 浏览:2924 | 更新: ...
- 搭建Eclipse C/C++开发环境
搭建eclipse C/C++开发环境: 1.下载并安装Eclipse for C++:http://www.eclipse.org.最新版是基于Eclipse 3.5 galileo,文件名 ...
- 轻松搭建Windows8云平台开发环境
原文:轻松搭建Windows8云平台开发环境 Windows Store应用是基于Windows 8操作系统的新一代Windows应用程序,其开发平台以及运行模式和以往传统平台略有不同.为了帮助更多开 ...
- (cljs/run-at (JSVM. :browser) "搭建刚好可用的开发环境!")
前言 书接上一回,在了解cljs基本语法后并在clojurescript.net的奇特错误提示后,我们必须痛定思痛地搭建一个本地的开发环境,以便后续深入地学习cljs. 现有的构建工具 由于浏览器 ...
- Grunt搭建自动化web前端开发环境--完整流程
Grunt搭建自动化web前端开发环境-完整流程 jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用! 1. 前言 各位web前 ...
随机推荐
- Android Studio的第一次经历
第一个简单APP的制作是从xml开始的,通过在java新建一个empty activity,并在layout里找到对应的xml文件进行编写.每编写一个xml就要事先新建 一个对应的empty ac ...
- insert into select 和select into from 备份表
一 insert into select要求表必须存在 INSERTINTO order_record SELECT * FROM order_today FORCEINDEX (idx_pay_su ...
- 快快使用ModelArts,零基础小白也能玩转AI!
摘要: 走过路过不要错过,看Copy攻城狮如何借力华为云ModelArts玩转AI. "自2018年10月发布以来,ModelArts累计服务了众多行业十几万开发者,通过基础平台的完备性和面 ...
- python开发基础(二)运算符以及数据类型之bool(布尔值))
# encoding: utf-8 # module builtins # from (built-in) # by generator 1.147 """ Built- ...
- Kafka_2.12-2.5.1集群搭建与参数调优
Kafka是目前业界使用最广泛的消息队列.数据流转常见这样的业务场景,客户端把采集到的日志推送给Kafka,业务方可以消费Kafka的数据落地HDFS,用于离线分析,也可以使用Spark或Flink消 ...
- LR-demo
from __future__ import print_function # 导入相关python库 import os import numpy as np import pandas as ...
- 面经手册 · 第17篇《码农会锁,ReentrantLock之AQS原理分析和实践使用》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 如果你相信你做什么都能成,你会自信的多! 千万不要总自我否定,尤其是职场的打工人.如 ...
- 论文解读 - MaskGAN:BETTER TEXT GENERATION VIA FILLING IN THE _____
1 简介 文本生成是自然语言处理中一个重要的研究领域,具有广阔的应用前景.当前主流的用来进行文本生成的模型主要是Seq2Seq模型,通常利用maximum likelihood和teacher for ...
- IDEA与Eclipse创建struts项目
1.IDEA创建struts项目 这里再构建struts项目是选择jar包出问题了,可以重新配置 创建页面和action配置struts.xml 启动tomcat,浏览器中运行 具体参考: https ...
- http 响应 ngx_http_send_header ngx_http_output_filter
在解析完 http 请求报文后, 需要发出响应报文, 那么ngx 框架 提供了那些通用接口呢?如果自己设计将所用的模块的响应接口合并起来 你会怎么设计呢?? 响应头过滤函数主要的用处就是处理HTTP ...