diff --git a/blog/1.js.md b/blog/1.js.md deleted file mode 100644 index 83532d8..0000000 --- a/blog/1.js.md +++ /dev/null @@ -1,313 +0,0 @@ -- 伴随着 `vue3` 的发布,vue 全家桶又添新成成员 ——`vite` 脚手架工具;相比 `@vue/cli` 基于浏览器内置的 ES module 极快的冷启动速度、基于 `Rollup` 打包的配置更简单(确实简单); -`vite` 底层原理网上已有好多文章,本文主要讲使用 - -- 公司的项目用的整合方案是 `umi` + `electron` ,umi 是我用过目前最傻瓜化的框架了,你能想到的她都做了而且还是自动化的 👍 -唯一一点不大好的 umi 的构建速度较慢; -vite 冷启动速度确实让人眼前一亮,索性拿来和 electron 集成一波;算是一个技术备选方案 `^_^` - -> 毕竟 ****尤大出品,必是精品**** 定律嘛! - -[完整代码 https://github.com/caoxiemeihao/electron-vue-vite](https://github.com/caoxiemeihao/electron-vue-vite) - -**`喜欢的(づ ̄3 ̄)づ╭❤~给点个start哦~`** - -#### 划重点 (踩坑记录) -- import { write } from 'fs' 的这种形式会被 vite 编译成 /@modules/fs?import -- const { write } = require('fs') 这种形式就能用了 😉 -- const { ipcRenderer } = require('electron') 同理 -- 虽然开发期可以用 require 避开 vite 的编译问题,但是打包时候 rollup 那边又出了问题; - * 拿 require('electron-store') 举例,在 vite.config.ts 中通过 **`自定义 rollup-plugin`** 转换成 EMS 形式即可 - * `const Store = require('electron-store')` >> `import Store from 'electron-store'` - -![800x600.png](https://upload-images.jianshu.io/upload_images/6263326-76f23d47dea57638.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -#### 准备材料 -- `vue3` -- `vite` vue3 脚手架 -- `electron` -- `electron-builder` electron 构建工具 -- `electron-connect` 主进程代码修改热重启 -- `rollup` 之前写过一个偏文章 [Electron、webpack、react、typescript 从零开始搭积木](https://www.jianshu.com/p/3aafab67ff70) - * 一来能少装个包,vite 用的也是 rollup —— 入乡随俗 - * 二来 rollup 的配置比 webpack 简单又专注于 js 的打包;只是用来打包 electron 主进程代码确实方便 -- `concurrently` 同时启动 vite(渲染进程)、electron(主进程) -- `wait-on` 用于监听 vite 启动,随后拉起 electron 启动 -- `chalk` 命令行文字颜色、背景色 -- `ora` 命令行友好提示 -- `minimist` 命令行参数解析 -- `dotenv` 解析 .env 文件 - -#### 先用创建一个 vite 工程 -- 官方命令: [https://github.com/vitejs/vite#getting-started](https://github.com/vitejs/vite#getting-started) - -```bash -yarn create vite-app -``` - -#### 然后调整目录结构 -```tree -. -├─dist 打包后的文件夹 -├─screenshot -├─script -│ ├─build.js 主进程构建文件 -│ └─rollup.config.js 主进程构建配置 -└─src - ├─main - │ └─index.ts 主进程入口文件 - └─render - ├─assets - ├─components - ├─dist vue 打包后目录 - │ _assets - ├─public - ├─App.vue - ├─index.css - ├─index.html - └─main.js 渲染进程入口文件 -└─.env 配置文件 -``` - -#### 主进程构建配置 - -> script/rollup.config.js - -- `@rollup/plugin-node-resolve` 支持引入 `node_modules` 模块 -- `@rollup/plugin-commonjs` 支持 `require`、`module.exports` 写法 -- `@rollup/plugin-typescript` 支持 `typescript` - -```javascript -const path = require('path'); -const { nodeResolve } = require('@rollup/plugin-node-resolve'); -const commonjs = require('@rollup/plugin-commonjs'); -const typescript = require('@rollup/plugin-typescript'); - -module.exports = (env = 'production') => { - return { - input: path.join(__dirname, '../src/main/index.ts'), // 入口文件 - output: { - file: path.join(__dirname, '../src/main/_.js'), // 输出目标 - format: 'cjs', // CommonJs 格式 - name: 'ElectronMainBundle', // 模块名称(可选) - sourcemap: true, - }, - plugins: [ - nodeResolve({ jsnext: true, preferBuiltins: true, browser: true }), // 支持引入 node_modules 模块 - commonjs(), // 支持 CommonJs 规范 - typescript(), // 支持 TypeScript - ], - external: [ - // 告诉 rollup 碰到下面模块时候不要去打包 - 'fs', - 'path', - 'http', - 'https', - 'child_process', - 'os', - 'electron', - ], - } -}; -``` - -#### 主进程构建脚本 - -> script/build.js - -- 实现思路: 使用 `wait-on` 监听 `vite` 启动,然后拉起 `electron` 打开渲染进程加载 vue 应用 -- `rollup` 使用比较简单 - * 如果是开发模式用 `rollup.watch(options)` 会监听文件变化,文件改变自动重新编译 - * 如果只是构建使用 `rollup.rollup(options)` 只会执行一次构建 - -```javascript -/** - * electron 打包 - */ -const path = require('path'); -const rollup = require('rollup'); -const argv = require('minimist')(process.argv.slice(2)); -const chalk = require('chalk'); -const ora = require('ora'); -const waitOn = require('wait-on'); -const electron = require('electron-connect').server.create({ stopOnClose: true }); // 表示要操作主进程端(对应的还是渲染进程端;渲染进程用的 vite 热更新) -require('dotenv').config({ path: path.join(__dirname, '../.env') }); // 解析项目根目录下的 .env 文件 -const options = require('./rollup.config'); // 引入 rollup 配置 - -const opt = options(argv.env); -const TAG = '[script/build.js]'; -const spinner = ora(`${TAG} Electron build...`); - -if (argv.watch) { // 开发模式 (命令行传入 --watch 标识) - waitOn({ - resources: [`http://localhost:${process.env.PORT}`], // 等待 vite 服务器启动,然后拉起 electron - log: false, - }, err => { - if (err) { - console.log(err); - process.exit(1); - } - - // once here, all resources are available - const watcher = rollup.watch(opt); - watcher.on('change', filename => { - const log = chalk.green(`change -- ${filename}`); - console.log(TAG, log); - }); - watcher.on('event', ev => { - if (ev.code === 'END') { - // init-未启动、started-第一次启动、restarted-重新启动 - electron.electronState === 'init' ? electron.start() : electron.restart(); - } - }); - }); -} else { // 构建模式 - spinner.start(); - rollup.rollup(opt) - .then(build => { - spinner.stop(); - console.log(TAG, chalk.green('Electron build successed.')); - build.write(opt.output); - }) - .catch(error => { - spinner.stop(); - console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n'); - }); -} -``` - -#### 渲染进程 vite 配置 - -> vite.config.ts - -- 需要改变 `vite` 的构建路径,因为 vue 代码移动到了 `src/render` 下 -- 通过 `.env` 配置启动端口,配合主进程监听端口启动 - -```typescript -/** - * 参考链接: https://github.com/vitejs/vite/blob/master/src/node/config.ts - */ -import { join } from 'path' -import { UserConfig } from 'vite' -import dotenv from 'dotenv' - -dotenv.config({ path: join(__dirname, '.env') }) -const root = join(__dirname, 'src/render') - -const config: UserConfig = { - root, - port: +process.env.PORT, - base: './', - outDir: join(__dirname, 'dist/render'), - alias: { - // 别名必须以 / 开头、结尾 - // '/@/': root, -- vite 内部在用,这里不能用了 - // '/root/': __dirname, -- vite 内部在用,这里不能用了 - '/assets/': join(__dirname, 'src/render/assets'), - '/components/': join(__dirname, 'src/render/components'), - '/lib/': join(__dirname, 'src/render/lib'), - '/utils/': join(__dirname, 'src/render/utils'), - '/views/': join(__dirname, 'src/render/views'), - }, - optimizeDeps: { - // 这里不加也没事,用 require 的形式就能避开 import 被编译成 /@modules/fs?import - // allowNodeBuiltins: ['electron-is-dev', 'electron-store', 'electron'] - }, - rollupInputOptions: { - external: [ - 'crypto', - 'assert', - 'fs', - 'util', - 'os', - 'events', - 'child_process', - 'http', - 'https', - 'path', - 'electron', - ], - plugins: [ - { - name: '@rollup/plugin-cjs2esm', - transform(code, filename) { - if (filename.includes('/node_modules/')) { - return code - } - - const cjsRegexp = /(const|let|var)[\n\s]+(\w+)[\n\s]*=[\n\s]*require\(["|'](.+)["|']\)/g - const res = code.match(cjsRegexp) - if (res) { - // const Store = require('electron-store') -> import Store from 'electron-store' - code = code.replace(cjsRegexp, `import $2 from '$3'`) - } - return code - }, - } - ], - }, - rollupOutputOptions: { - format: 'commonjs', - }, -} - -export default config -``` - -#### 启动脚本配置 - -```json -{ - "main": "src/main/_.js", - "scripts": { - "dev": "npm run dev:all", - "dev:all": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", - "dev:vue": "vite", - "dev:ele": "node script/build --env=development --watch", - "build:vue": "vite build", - "build:ele": "node script/build --env=production", - "build": "npm run build:vue && npm run build:ele && electron-builder" - } -} -``` - -- main `electron` 启动后会加载 main 配置的文件 -- dev:all 使用 `concurrently` 同时启动 `dev:vue`、`dev:ele` -- dev:ele `electron` 开发模式脚本,通过传入 `--watch` 表示 rollup 监听主进程变化自动编译 -- build 使用 `electron-builder` 打包 - -#### 启动一下试试 -```bash -$ yarn dev - -yarn run v1.22.4 -$ npm run dev:all - -> electron-vue@0.0.1 dev:all D:\github\electron-vue-vite -> concurrently -n=vue,ele -c=green,blue "npm run dev:vue" "npm run dev:ele" - -[ele] -[ele] > electron-vue@0.0.1 dev:ele D:\github\electron-vue-vite -[ele] > node script/build --env=development --watch -[ele] -[vue] -[vue] > electron-vue@0.0.1 dev:vue D:\github\electron-vue-vite -[vue] > vite -[vue] -[vue] vite v1.0.0-rc.4 -[vue] -[vue] Dev server running at: -[vue] > Network: http://192.168.1.9:3344/ -[vue] > Network: http://192.168.119.1:3344/ -[vue] > Network: http://10.0.60.32:3344/ -[vue] > Local: http://localhost:3344/ -[vue] -[ele] [2020-08-17T08:57:11.850Z] [electron-connect] [server] started electron process: 1488 -[ele] [2020-08-17T08:57:11.851Z] [electron-connect] [server] server created and listening on 30080 -[ele] -``` - -#### 尾巴 -- 2019 款 13 寸 mac-pro 启动速度 4秒 左右 -- 奔腾 G4560 台机 CUP 神舟笔记本启动速度 6 秒左右 -- 毋庸置疑 vite 的方案比起 @vue/cli、umi、create-react-app 这类基于 webpack 的脚手架启动这块的优势大的多滴多 -- 技术总是飞快的迭代、进步,目的都是解决一些已经存在、或即将到来的问题;继续治疗、学习起来、加油哇~ diff --git a/blog/1.md b/blog/1.md deleted file mode 100644 index 9511942..0000000 --- a/blog/1.md +++ /dev/null @@ -1,249 +0,0 @@ -# Electron + vue3 + vite 整合 - -- 伴随着 `vue3` 的发布,vue 全家桶又添新成成员 ——`vite` 脚手架工具;相比 `@vue/cli` 基于浏览器内置的 ES module 极快的冷启动速度、基于 `Rollup` 打包的配置更简单(确实简单); -`vite` 底层原理网上已有好多文章,本文主要讲使用 - -- 公司的项目用的整合方案是 `umi` + `electron` ,umi 是我用过目前最傻瓜化的框架了,你能想到的她都做了而且还是自动化的 👍 -唯一一点不大好的 umi 的构建速度较慢; -vite 冷启动速度确实让人眼前一亮,索性拿来和 electron 集成一波;算是一个技术备选方案 `^_^` - -> 毕竟 ****尤大出品,必是精品**** 定律嘛! - -![](https://raw.githubusercontent.com/caoxiemeihao/electron-vue-vite/master/screenshot/800x600.png) - - -#### 准备材料 -- `vue3` -- `vite` vue3 脚手架 -- `electron` -- `electron-builder` electron 构建工具 -- `electron-connect` 主进程代码修改热重启 -- `rollup` 之前写过一个偏文章 [Electron、webpack、react、typescript 从零开始搭积木](https://www.jianshu.com/p/3aafab67ff70) - * 一来能少装个包,vite 用的也是 rollup —— 入乡随俗 - * 二来 rollup 的配置比 webpack 简单又专注于 js 的打包;只是用来打包 electron 主进程代码确实方便 -- `concurrently` 同时启动 vite(渲染进程)、electron(主进程) -- `wait-on` 用于监听 vite 启动,随后拉起 electron 启动 -- `chalk` 命令行文字颜色、背景色 -- `ora` 命令行友好提示 -- `minimist` 命令行参数解析 -- `dotenv` 解析 .env 文件 - -#### 目录结构 -```tree -. -├─dist 打包后的文件夹 -├─screenshot -├─script -│ ├─build.js 主进程构建文件 -│ └─rollup.config.js 主进程构建配置 -└─src - ├─main - │ └─index.ts 主进程入口文件 - └─render - ├─assets - ├─components - ├─dist vue 打包后目录 - │ _assets - ├─public - ├─App.vue - ├─index.css - ├─index.html - └─main.js 渲染进程入口文件 -└─.env 配置文件 -``` - -#### 主进程构建配置 - -> script/rollup.config.js - -- `@rollup/plugin-node-resolve` 支持引入 `node_modules` 模块 -- `@rollup/plugin-commonjs` 支持 `require`、`module.exports` 写法 -- `@rollup/plugin-typescript` 支持 `typescript` - -```javascript -const path = require('path'); -const { nodeResolve } = require('@rollup/plugin-node-resolve'); -const commonjs = require('@rollup/plugin-commonjs'); -const typescript = require('@rollup/plugin-typescript'); - -module.exports = (env = 'production') => { - return { - input: path.join(__dirname, '../src/main/index.ts'), // 入口文件 - output: { - file: path.join(__dirname, '../src/main/_.js'), // 输出目标 - format: 'cjs', // CommonJs 格式 - name: 'ElectronMainBundle', // 模块名称(可选) - sourcemap: true, - }, - plugins: [ - nodeResolve({ jsnext: true, preferBuiltins: true, browser: true }), // 支持引入 node_modules 模块 - commonjs(), // 支持 CommonJs 规范 - typescript(), // 支持 TypeScript - ], - external: [ - // 告诉 rollup 碰到下面模块时候不要去打包 - 'fs', - 'path', - 'http', - 'https', - 'child_process', - 'os', - 'electron', - ], - } -}; -``` - -#### 主进程构建脚本 - -> script/build.js - -- 实现思路: 使用 `wait-on` 监听 `vite` 启动,然后拉起 `electron` 打开渲染进程加载 vue 应用 -- `rollup` 使用比较简单 - * 如果是开发模式用 `rollup.watch(options)` 会监听文件变化,文件改变自动重新编译 - * 如果只是构建使用 `rollup.rollup(options)` 只会执行一次构建 - -```javascript -/** - * electron 打包 - */ -const path = require('path'); -const rollup = require('rollup'); -const argv = require('minimist')(process.argv.slice(2)); -const chalk = require('chalk'); -const ora = require('ora'); -const waitOn = require('wait-on'); -const electron = require('electron-connect').server.create({ stopOnClose: true }); // 表示要操作主进程端(对应的还是渲染进程端;渲染进程用的 vite 热更新) -require('dotenv').config({ path: path.join(__dirname, '../.env') }); // 解析项目根目录下的 .env 文件 -const options = require('./rollup.config'); // 引入 rollup 配置 - -const opt = options(argv.env); -const TAG = '[script/build.js]'; -const spinner = ora(`${TAG} Electron build...`); - -if (argv.watch) { // 开发模式 (命令行传入 --watch 标识) - waitOn({ - resources: [`http://localhost:${process.env.PORT}`], // 等待 vite 服务器启动,然后拉起 electron - log: false, - }, err => { - if (err) { - console.log(err); - process.exit(1); - } - - // once here, all resources are available - const watcher = rollup.watch(opt); - watcher.on('change', filename => { - const log = chalk.green(`change -- ${filename}`); - console.log(TAG, log); - }); - watcher.on('event', ev => { - if (ev.code === 'END') { - // init-未启动、started-第一次启动、restarted-重新启动 - electron.electronState === 'init' ? electron.start() : electron.restart(); - } - }); - }); -} else { // 构建模式 - spinner.start(); - rollup.rollup(opt) - .then(build => { - spinner.stop(); - console.log(TAG, chalk.green('Electron build successed.')); - build.write(opt.output); - }) - .catch(error => { - spinner.stop(); - console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n'); - }); -} -``` - -#### 渲染进程 vite 配置 - -> vite.config.ts - -- 需要改变 `vite` 的构建路径,因为 vue 代码移动到了 `src/render` 下 -- 通过 `.env` 配置启动端口,配合主进程监听端口启动 - -```typescript -/** - * 参考链接: https://github.com/vitejs/vite/blob/master/src/node/config.ts - */ -import { join } from 'path' -import { UserConfig } from 'vite' -import dotenv from 'dotenv' - -dotenv.config({ path: join(__dirname, '.env') }) - -const config: UserConfig = { - // vite 项目编译、启动的根目录 - // 默认指向项目跟目录,我们把 vue(渲染进程) 代码全部搬到 src/render 下 - root: join(__dirname, 'src/render'), - // 通过 .env 配置端口,能够让 electron 加载到正确的地址 - port: +process.env.PORT, - // 打包后的 vue 项目引入 js、css、favicon 等资源路径 - base: './', -} - -export default config -``` - -#### 启动脚本配置 - -```json -{ - "main": "src/main/_.js", - "scripts": { - "dev": "npm run dev:all", - "dev:all": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", - "dev:vue": "vite", - "dev:ele": "node script/build --env=development --watch", - "build:vue": "vite build", - "build:ele": "node script/build --env=production", - "build": "npm run build:vue && npm run build:ele && electron-builder" - } -} -``` - -- main `electron` 启动后会加载 main 配置的文件 -- dev:all 使用 `concurrently` 同时启动 `dev:vue`、`dev:ele` -- dev:ele `electron` 开发模式脚本,通过传入 `--watch` 表示 rollup 监听主进程变化自动编译 -- build 使用 `electron-builder` 打包 - -#### 启动一下试试 -```bash -$ yarn dev - -yarn run v1.22.4 -$ npm run dev:all - -> electron-vue@0.0.1 dev:all D:\github\electron-vue-vite -> concurrently -n=vue,ele -c=green,blue "npm run dev:vue" "npm run dev:ele" - -[ele] -[ele] > electron-vue@0.0.1 dev:ele D:\github\electron-vue-vite -[ele] > node script/build --env=development --watch -[ele] -[vue] -[vue] > electron-vue@0.0.1 dev:vue D:\github\electron-vue-vite -[vue] > vite -[vue] -[vue] vite v1.0.0-rc.4 -[vue] -[vue] Dev server running at: -[vue] > Network: http://192.168.1.9:3344/ -[vue] > Network: http://192.168.119.1:3344/ -[vue] > Network: http://10.0.60.32:3344/ -[vue] > Local: http://localhost:3344/ -[vue] -[ele] [2020-08-17T08:57:11.850Z] [electron-connect] [server] started electron process: 1488 -[ele] [2020-08-17T08:57:11.851Z] [electron-connect] [server] server created and listening on 30080 -[ele] -``` - -#### 尾巴 -- 2019 款 13 寸 mac-pro 启动速度 4秒 左右 -- 奔腾 G4560 台机 CUP 神舟笔记本启动速度 6 秒左右 -- 毋庸置疑 vite 的方案比起 @vue/cli、umi、create-react-app 这类基于 webpack 的脚手架启动这块的优势大的多滴多 -- 技术总是飞快的迭代、进步,目的都是解决一些已经存在、或即将到来的问题;继续治疗、学习起来、加油哇~ diff --git a/blog/2.md b/blog/2.md deleted file mode 100644 index 9198fe8..0000000 --- a/blog/2.md +++ /dev/null @@ -1,70 +0,0 @@ -# electron-vue-vite -`Electron` + `vue3` + `vite` 整合 - -#### [简书地址](https://www.jianshu.com/p/ee5ec23d4716) - -## How and Why -- 写这个 Demo 项目主要有两个目的 - 1. `vue@3.x` 发布了,想试试新功能 - 2. 工作中用的 `umi`+`electron` 项目大了,启动速度并不理想; - 用 `vite` 试试,算一个储备方案 ^_^ - -## Command -- npm run dev -- npm run build - -## Note `踩坑记` -- import { write } from 'fs' 的这种形式会被 vite 编译成 /@modules/fs?import -- const { write } = require('fs') 这种形式就能用了 😉 -- const { ipcRenderer } = require('electron') 同理 -- 虽然开发期可以用 require 避开 vite 的编译问题,但是打包时候 rollup 那边又出了问题; - * 拿 const Store = require('electron-store') 举例,在 vite.config.ts 中通过自定义 rollup 插件转换成 EMS 形式即可 - ```javascript - // vite.config.ts -> rollupInputOptions -> plugins - plugins: [ - { - name: '@rollup/plugin-cjs2esm', - transform(code, filename) { - if (filename.includes('/node_modules/')) { - return code - } - - const cjsRegexp = /(const|let|var)[\n\s]+(\w+)[\n\s]*=[\n\s]*require\(["|'](.+)["|']\)/g - const res = code.match(cjsRegexp) - if (res) { - // const Store = require('electron-store') -> import Store from 'electron-store' - code = code.replace(cjsRegexp, `import $2 from '$3'`) - } - return code - }, - } - ], - ``` -- "rollup-plugin-esbuild": "^2.4.2", 有 BUG `21-02-18` -- **tsconfig.json中不能有多余的逗号,不然有如下警告** `21-02-18` - ```bash - SyntaxError: Unexpected token ] in JSON at position 428 - at JSON.parse () - at Object.load (/Users/caoxie/Desktop/github/electron-vue-vite2/node_modules/rollup-plugin-esbuild/dist/index.js:21:17) { - code: 'PLUGIN_ERROR', - plugin: 'esbuild', - hook: 'transform', - id: '/Users/caoxie/Desktop/github/electron-vue-vite2/src/main/index.ts', - watchFiles: [ - '/Users/caoxie/Desktop/github/electron-vue-vite2/src/main/index.ts' - ] - } - ``` -- main 进程中暂时无法用 require,打包后会导致模块找不到 `21-02-18` -- `"asar": false` 这样可以保障 `"extraResources"` 能够正常搬运到文件夹中 `21-02-18` - -## 总结 - -- 2019 款 13 寸 mac-pro 启动速度 4秒 左右 -- 奔腾 G4560 台机 CUP 神舟笔记本启动速度 6 秒左右 -- 毋庸置疑 vite 的方案比起 @vue/cli、umi、create-react-app 这类基于 webpack 的脚手架启动这块的优势大的多滴多 -- 技术总是飞快的迭代、进步,目的都是解决一些已经存在、或即将到来的问题;继续治疗、学习起来、加油哇~ - ---- - -![](https://raw.githubusercontent.com/caoxiemeihao/electron-vue-vite/master/screenshot/800x600-2.png) diff --git a/blog/3.md b/blog/3.md deleted file mode 100644 index f839c7a..0000000 --- a/blog/3.md +++ /dev/null @@ -1,402 +0,0 @@ -# Vite 整合 Electron 总结 - -## 前言 - -- Vite 是面向未来几年的构建工具,很有必要在各个场景下都试试集成进来 -- Electron 作为前端标配的桌面开发工具,官方并有脚手架也没和哪个框架整合 -- `@vue/cli` 官方有给出模板;但是 Vite 这块并没提供,毕竟人家定位是和 `webpack` 那样的通用构建工具 - 甚至连 Vue 都没集成 🖖 那么我们尝试来做下这件事儿 - 按照 Vue 在 Vite 中的集成风格 Electron 这块应当写一个插件! - -## 注意 📢 -- 这里假定你简单的知道一些 Vite 的工作原理,这种文章网上有好多的 -- 同时也假定你使用或者上手过 Electron;上手非常简单,直接看官网即可 -- 项目整体所有代码在这 [https://github.com/caoxiemeihao/electron-vue-vite](https://github.com/caoxiemeihao/electron-vue-vite) 可以直接 **用于生产** (亲点个 start 呗 😘) - -## 目录结构设计 - -```tree -· -├── script 项目脚本目录 -├── src -| ├── main Electron 主进程代码 -| ├── preload Electron 预加载目录 -| ├── render Electron 渲染进程 - 既 Vite 代码 -| -├── vite.config.ts Vite 配置文件 -``` - -## vite.config.ts 配置 - -- Electron 支持全量的 NodeJs API 渲染进程难免会用到 -- Vite 基于的 Rollup 构建工程,所以我们改造 Rollup 部分配置,输出 CommonJs 格式 -- 目录结构相比于 Vite 提供的默认结构有所不同,也需要配置一番才能按预期工作 - -```ts -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import { join } from 'path' - -export default defineConfig((env) => ({ - plugins: [ - vue(), // 开启 Vue 支持 - ], - root: join(__dirname, 'src/render'), // 指向渲染进程目录 - base: './', // index.html 中静态资源加载位置 - build: { - outDir: join(__dirname, 'dist/render'), - assetsDir: '', // 相对路径 加载问题 - rollupOptions: { - output: { - format: 'cjs', // 配置 Rollup 打包输出 CommonJs 格式 - }, - external: ['electron'], // 告诉 Rollup 不要去打包 electron - }, - }, - optimizeDeps: { - exclude: ['electron'], // 告诉 Vite 不要转换 electron 模块 - }, - // 其他配置略... -})) -``` - -## 启动脚本分析 - -- 我们先出个结论 - Electron 的启动与 NodeJs 相比行为几乎是一致的 - `可执行程序` + `入口文件` - -```shell -# 全局目录安装的 NodeJs -node path/filename.js - -# 项目目录安装的 Electron -node_modules/.bin/electron path/filename.js -``` -- 如果我们把 Electron 的启动设计到 `npm` 的 `scripts` 中,它还可以更简单 `npm run electron` - -```json -{ - "scripts": { - "electron": "electron path/filename.js" - } -} -``` -- 🤔 思考一下,`npm` 只有一个启动命令 Vite、Electron 各需要一个,加一起就是两个启动命令了这里我们借助 `concurrently` 来同时启动 Vite、Electron - -```json -{ - "scripts": { - "dev": "concurrently \"npm run vite\" \"npm run electron\"", - "vite": "vite", - "electron": "electron path/filename.js" - } -} -``` -**看起来不错!** - -- 不过我们再思考下关于 Electron 启动的问题 - 1. 开发环境下 Electron 应该加载 Vite 启动的开发服务器,生产环境下启动一个具体的文件 - 2. 那么这里就会出现一个等待的情况,就是 Electron 要等待 Vite 启动后再启动 - -## 启动脚本设计 - -- 我们需要监听 Vite 的启动后拉起 Electron,这块我们考虑用轮训监听端口的方式 -- 监听到 Vite 启动后 Electron 我们用 NodeJs 的子进程 API `child_process.spawn()` 拉起 -- 我们将 `npm scripts` 做下改动,方便我们知道脚本是干啥的,**相对上面的脚本这里重命名一下** - `concurrently` 也加入一些命令行参数,能得好更友好的控制台输出 - -```json -{ - "scripts": { - "dev": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", - "dev:vite": "vite", - "dev:electron": "node -r ts-node/register script/build-main --env=development --watch" - } -} -``` - -- 由于我们要控制的是 Electron 的启动,为此我们单独写个脚本(script/build-main.ts)来控制,包括下面两个功能点 - 1. 启动时机控制 - 监听 Vite 已经启动 - 2. 主进程代码使用使用 typescript 开发 - 加入 Rollup 编译、打包 - -#### script/build-main.ts - -```ts -import { join } from 'path' -import { get } from 'http' -import { spawn, ChildProcess } from 'child_process' -import { watch } from 'rollup' -import minimist from 'minimist' -import electron from 'electron' -import options from './rollup.config' -import { main } from '../package.json' - -/** - * 1. 监听 vite 启动 - */ -function waitOn(arg0: { port: string | number; interval?: number; }) { - return new Promise(resolve => { - const { port, interval = 149 } = arg0 - const url = `http://localhost:${port}` - - // 通过定时器轮训向 Vite 服务器请求 - const timer: NodeJS.Timer = setInterval(() => { - get( - `http://localhost:${port}`, // 指向 Vite 开发服务器 - res => { - clearInterval(timer) - resolve(res.statusCode) - } - ) - }, interval) - }) -} - -/** - * 2. 控制 Electron 启动时机,编译 typescript - */ -waitOn({ port: '3000' }).then(msg => { - // 解析 npm script 的命令行参数 - const argv = minimist(process.argv.slice(2)) - - // 加载 rollup 配置 - const opts = options(argv.env) - - // Vite 启动后以监听模式开启 Rollup 编译 Electron 主进程代码 - const watcher = watch(opts) - - let child: ChildProcess - - watcher.on('event', ev => { - if (ev.code === 'END') { - // 保证只启动一个 Electron 个程序 - if (child) child.kill() - - // 使用 NodeJs 子进程能力拉起 Electron 程序 - child = spawn( - // 这里 electron 本质上只是一个字符串;指向 Electron 可执行程序的绝对路径 - electron as any, - - // 指定 Electron 主进程入口文件;既 Rollup 编译后输出文件的路径 - [join(__dirname, `../${main}`)], { stdio: 'inherit' } - ) - } - }) -}) - -``` - -#### script/rollup.config - -```ts -import { builtinModules } from 'module' -import { join } from 'path' -import { RollupOptions } from 'rollup' -import nodeResolve from '@rollup/plugin-node-resolve' -import commonjs from '@rollup/plugin-commonjs' -import typescript from '@rollup/plugin-typescript' -import json from '@rollup/plugin-json' - -/** node.js builtins module */ -const builtins = () => builtinModules.filter(x => !/^_|^(internal|v8|node-inspect)\/|\//.test(x)) - -export default (env = 'production') => { - const options: RollupOptions = { - input: join(__dirname, '../src/main/index.ts'), - output: { - file: join(__dirname, '../dist/main/index.js'), - format: 'cjs', // 使用 CommonJs 模块化 - }, - plugins: [ - nodeResolve(), // 支持 node_modules 下面的包查找 - commonjs(), // 支持 CommonJs 模块 - json(), // 支持引入 json 文件 - typescript({ - module: 'ESNext', // 支持 typescript - }), - ], - external: [ - // 打包避开内置模块 - ...builtins(), - 'electron', - ], - } - - return options -} -``` - -**到这为止,项目应该是能跑起来的状态了;但是还不能使用 Electron、NodeJs 相关的 API** - -> 这里简单的改了下 App.vue、HelloWorld.vue 中的一些文案,没做逻辑修改;就不贴出代码了 - -![import-electron.jpg](./images/main.jpg) - -## 加入 Electron API - -- 渲染和主进程通信是个十分常用的功能;我试着从 `electron` 导出 `ipcRenderer` - -```ts -// src/render/main.ts -import { createApp } from 'vue' -import App from './App.vue' -import { ipcRenderer } from 'electron' - -console.log('ipcRenderer:', ipcRenderer) - -createApp(App).mount('#app') -``` - -![import-electron.jpg](./images/import-electron.jpg) - -- **报错了!默认情况下直接用 import 语法会被 Rollup 编译** -- 事实上在 `'electron'` 在 Electron 运行环境中是一个 **内置模块** 你可以在控制台中试试下这段代码 - -> *注意这里先不要引入 Electron 相关的包,保障项目能跑起来* - -```js -require.resolve('electron') -"electron" // 将会输出 -``` - -- 既然 Electron 本就支持全量的 NodeJs API 我们不妨直接在在代码中直接写成 - -```diff -// src/render/main.ts -import { createApp } from 'vue' -import App from './App.vue' -- import { ipcRenderer } from 'electron' -+ const { ipcRenderer } = require('electron') - -console.log('ipcRenderer:', ipcRenderer) - -createApp(App).mount('#app') -``` - -**项目确实跑起来了,我们还可以用这个办法进一步验证其他的模块** - -![require-electron.jpg](./images/require-electron.jpg) - -## 插件设计分析 - -- 可以打包票说,这个可以用!(仅限开发期);但是这个会带来两个问题 - 1. 编码风格不统一,人家都在用 `ESModule` 混入 `CommonJs` 确实不好看 - 2. `require('xxxx')` 在打包期间如果不做些处理,并不会被 Rollup 处理 (这里只`.ts`文件,有大神知道怎么对付这种情况的请指点下小弟) - 如果你引入的是 `node_modules` 中的包那可就惨了;比如 `require('electron-store')` 这种会原样输出;打包后的程序开起来会找不到 `'electron-store'` 这个模块,铁定报错! - -- 我们知道 `ESModule` 写法在开发期运行会报错,但是还是要写; -如果我们在运行的前一刻将 ESModule 转换成 NodeJs 内置的 CommonJs 岂不是两全其美的好事; -甚至只要有关 NodeJs API 的包我们都可以转化,毕竟开发期项目根目录是有 `node_modules` 这个 **NodeJs包仓库** 给你用的! - -**分析至此,我们该动动手写个插件了;让插件去自动化完成 - ESModule to CommonJs** - -## vitejs-plugin-electron - -- Vite 插件上手教程请看官网 [https://vitejs.dev/guide/api-plugin.html](https://vitejs.dev/guide/api-plugin.html) (我个人觉得比 `webpack` 那边的插件要好写) -- 为了支持传参方便日后扩展,我们把它成一个 Function 并返回一个插件 -- 代码处理这块,我们需要一个 AST 工具帮忙 - `yarn add acorn` - -```ts -import * as acorn from 'acorn' -import { Plugin as VitePlugin } from 'vite' - -const extensions = ['.js', '.jsx', '.ts', '.tsx', '.vue'] // 需要处理的文件后缀 - -export interface Esm2cjsOptions { - excludes?: string[] // 需要被转换的模块 -} - -export default function esm2cjs(options?: Esm2cjsOptions): VitePlugin { - const opts: Esm2cjsOptions = { - // 默认我们转换 electron、electron-store 两个模块 - excludes: [ - 'electron', - 'electron-store', - ], - ...options - } - - return { - name: 'vitejs-plugin-electron', // 这个 name 就是插件名字 - transform(code, id) { - const parsed = path.parse(id) // 解析引入模块的路径,id 即引入文件完整路径 - if (!extensions.includes(parsed.ext)) return // 只处理需要处理的文件后缀 - - const node: any = acorn.parse(code, { // 使用 acorn 解析 ESTree - ecmaVersion: 'latest', // 指定按照最新的 es 模块标准解析 - sourceType: 'module', // 指定按照模块进行解析 - }) - - let codeRet = code - node.body.reverse().forEach((item) => { - if (item.type !== 'ImportDeclaration') return // 跳过非 import 语句 - if (!opts.excludes.includes(item.source.value)) return // 跳过不要转换的模块 - - /** - * 下面这些 const 声明用来确定 import 的写法 - */ - const statr = codeRet.substring(0, item.start) - const end = codeRet.substring(item.end) - const deft = item.specifiers.find(({ type }) => type === 'ImportDefaultSpecifier') - const deftModule = deft ? deft.local.name : '' - const nameAs = item.specifiers.find(({ type }) => type === 'ImportNamespaceSpecifier') - const nameAsModule = nameAs ? nameAs.local.name : '' - const modules = item. - specifiers - .filter((({ type }) => type === 'ImportSpecifier')) - .reduce((acc, cur) => acc.concat(cur.imported.name), []) - - /** - * 这里开始根据各种 import 语法做转换 - */ - if (nameAsModule) { - // import * as name from - codeRet = `${statr}const ${nameAsModule} = require(${item.source.raw})${end}` - } else if (deftModule && !modules.length) { - // import name from 'mod' - codeRet = `${statr}const ${deftModule} = require(${item.source.raw})${end}` - } else if (deftModule && modules.length) { - // import name, { name2, name3 } from 'mod' - codeRet = `${statr}const ${deftModule} = require(${item.source.raw}) - const { ${modules.join(', ')} } = ${deftModule}${end}` - } else { - // import { name1, name2 } from 'mod' - codeRet = `${statr}const { ${modules.join(', ')} } = require(${item.source.raw})${end}` - } - }) - - return codeRet - }, - } -} - -``` - -- 在 `vite.config.ts` 中使用 `vitejs-plugin-electron` - -```ts -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import electron from 'vitejs-plugin-electron' - -export default defineConfig((env) => ({ - plugins: [ - vue(), - electron(), - ], - // 其他配置略... -})) -``` - -- 再次运行下项目 -![plugin-electron.jpg](./images/plugin-electron.jpg) - -- **It's Worked!** 🎉 🎉 🎉 - -- 好了,这个插件可以用了;再想想上面的关于 Rollup 的配置其实我们完全可以集成到 `vitejs-plugin-electron` 中的,这样会使 `vite.config.ts` 文件更少、更清晰;具体代码就不演示了,自己拉代码看看吧 🚀 -- [https://github.com/caoxiemeihao/vitejs-plugins/tree/main/electron](https://github.com/caoxiemeihao/vitejs-plugins/tree/main/electron) - -## 总结 - -- Vite 个人觉得是个不错的方案,毕竟打包工具早晚会推出历史舞台;Vite 往前又迈了 `0.5步` -- Electron 的集成只是一个案例,从一个案例出发到写一个插件,你会更好的理解 Vite 设计、思想 -- 最后,不能什么都站在客观的角度去等待,更需要我们主动的去**建设** diff --git a/blog/images/import-electron.jpg b/blog/images/import-electron.jpg deleted file mode 100644 index e0e1f0f..0000000 Binary files a/blog/images/import-electron.jpg and /dev/null differ diff --git a/blog/images/main.jpg b/blog/images/main.jpg deleted file mode 100644 index 7f38160..0000000 Binary files a/blog/images/main.jpg and /dev/null differ diff --git a/blog/images/plugin-electron.jpg b/blog/images/plugin-electron.jpg deleted file mode 100644 index 95f52e9..0000000 Binary files a/blog/images/plugin-electron.jpg and /dev/null differ diff --git a/blog/images/require-electron.jpg b/blog/images/require-electron.jpg deleted file mode 100644 index 52a30d5..0000000 Binary files a/blog/images/require-electron.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210627.jpg b/blog/wx/qrcode-210627.jpg deleted file mode 100644 index 970c5fb..0000000 Binary files a/blog/wx/qrcode-210627.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210707.jpg b/blog/wx/qrcode-210707.jpg deleted file mode 100644 index 01842b2..0000000 Binary files a/blog/wx/qrcode-210707.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210714.jpg b/blog/wx/qrcode-210714.jpg deleted file mode 100644 index 766db21..0000000 Binary files a/blog/wx/qrcode-210714.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210721.jpg b/blog/wx/qrcode-210721.jpg deleted file mode 100644 index 8703a42..0000000 Binary files a/blog/wx/qrcode-210721.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210728.jpg b/blog/wx/qrcode-210728.jpg deleted file mode 100644 index 56548f9..0000000 Binary files a/blog/wx/qrcode-210728.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210804.jpg b/blog/wx/qrcode-210804.jpg deleted file mode 100644 index 26262a8..0000000 Binary files a/blog/wx/qrcode-210804.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210812.jpg b/blog/wx/qrcode-210812.jpg deleted file mode 100644 index e1c0df4..0000000 Binary files a/blog/wx/qrcode-210812.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210823.jpg b/blog/wx/qrcode-210823.jpg deleted file mode 100644 index 7a77293..0000000 Binary files a/blog/wx/qrcode-210823.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210831.jpg b/blog/wx/qrcode-210831.jpg deleted file mode 100644 index 793a201..0000000 Binary files a/blog/wx/qrcode-210831.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210908.jpg b/blog/wx/qrcode-210908.jpg deleted file mode 100644 index 5642f74..0000000 Binary files a/blog/wx/qrcode-210908.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210916.jpg b/blog/wx/qrcode-210916.jpg deleted file mode 100644 index 80bad1f..0000000 Binary files a/blog/wx/qrcode-210916.jpg and /dev/null differ diff --git a/blog/wx/qrcode-210924.jpg b/blog/wx/qrcode-210924.jpg deleted file mode 100644 index b688dd8..0000000 Binary files a/blog/wx/qrcode-210924.jpg and /dev/null differ diff --git a/blog/wx/qrcode-211001.jpg b/blog/wx/qrcode-211001.jpg deleted file mode 100644 index 9f4138f..0000000 Binary files a/blog/wx/qrcode-211001.jpg and /dev/null differ diff --git a/blog/wx/qrcode-211015.jpg b/blog/wx/qrcode-211015.jpg deleted file mode 100644 index 16329a6..0000000 Binary files a/blog/wx/qrcode-211015.jpg and /dev/null differ diff --git a/blog/wx/qrcode-211023.jpg b/blog/wx/qrcode-211023.jpg deleted file mode 100644 index 7393ff1..0000000 Binary files a/blog/wx/qrcode-211023.jpg and /dev/null differ diff --git a/blog/wx/qrcode-211101.jpg b/blog/wx/qrcode-211101.jpg deleted file mode 100644 index bb1d964..0000000 Binary files a/blog/wx/qrcode-211101.jpg and /dev/null differ diff --git a/blog/wx/qrcode.jpg b/blog/wx/qrcode.jpg deleted file mode 100644 index f55b6e1..0000000 Binary files a/blog/wx/qrcode.jpg and /dev/null differ