使用umi+electron实现web端和桌面端同步开发
首先确定我们的目的是实现以下内容
在umi项目中,以安装electron的形式,实现web端及桌面端同步开发,实现一套代码多端使用的目的。 需实现支持打包成win64位及win32位及mac的安装包。
确定目的后我们开撸代码
一 使用umi初始化项目
1. 工程创建
1. 初始化项目
yarn create @umijs/umi-app
2. 安装依赖
yarn
3. 启动项目
yarn start:dev
2.配置项目
1. 配置多环境
config.local.ts // 本地临时配置文件 (后期看是否需要)
config.dev.ts // 开发环境配置文件
config.prod.ts // 正式环境配置文件 (后期看是否需要)
config.ts // 项目公共配置文件
// config.ts
import { defineConfig } from 'umi';
import routes from './routes';
import proxy from './proxy';
const { REACT_APP_ENV } = process.env;
console.log(REACT_APP_ENV);
export default defineConfig({
routes,
proxy: proxy[REACT_APP_ENV || 'dev'],
favicon: '/logo.svg',
locale: { // 配置国际化
default: 'zh-CN',
antd: true,
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
baseSeparator: '-',
},
history: {
type: 'hash',
},
publicPath: './',
outputPath: 'app/build', // 为了打包后electron可使用
});
2.路由配置
route.ts // 路由文件
const route = [
{
path: '/',
redirect: '/login',
},
{
name: '登录',
path: '/login',
component: '@/pages/Login/Login',
icon: 'home',
},
{
name: '主页',
path: '/designTool',
component: '@/pages/DesignTool/DesiginToolIndex/DesiginToolIndex',
icon: 'home',
// hideChildrenInMenu: true,
// routes: [
// {
// name: '详情',
// path: '/designTool/detail',
// component: '@/pages/DesignTool/DesiginToolDetail/DesiginToolDetail',
// },
// {
// name: '编辑',
// path: '/designTool/edit',
// component: '@/pages/DesignTool/DesignToolEdit/DesignToolEdit',
// },
// {
// redirect: '/404',
// },
// ],
},
{
name: 'electron操作页',
path: '/electronPage',
component: '@/pages/ElectronPage/ElectronHandel',
},
];
export default route;
3.配置代理
proxy.ts // 代理文件
// 地址
const devUrl = 'http://192.168.1.1:8080';
export default {
dev: {
'/api/': {
target: devUrl,
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
};
4.封装请求
5.统一处理请求
6.umi项目目录
二 接入electron
首先根目录下创建app文件夹,用于管理electron文件
1.根目录下配置
1.安装electron依赖
yarn add electron -S
2.package.json下配置
- scripts 下新增electron启动命令
"electron": "electron app/main.js",
"electron-dev": "concurrently \"cross-env BROWSER=none yarn start:dev\" \"wait-on http://localhost:8001 && cross-env NODE_ENV=development electron app/main.js\"",
"electron-start": "yarn build && cross-env NODE_ENV=production electron app/main.js"
其中 electron-dev 内的concurrently
cross-env
wait-on
需要使用yarn安装对应包,过程不再赘述。
- 配置主文件入口
"main": "app/main.js",
以下是我的package.json 配置
// package.json
{
"name": "electron_project",
"version": "0.0.1",
"description": "description",
"author": "huangjinhui",
"private": true,
"main": "app/main.js",
"scripts": {
"build": "umi build",
"start": "cross-env PORT=8000 umi dev",
"start:dev": "cross-env REACT_APP_ENV=dev PORT=8001 UMI_ENV=dev umi dev",
"start:prod": "cross-env REACT_APP_ENV=prod PORT=8002 UMI_ENV=prod umi dev",
"build:dev": "cross-env UMI_ENV=dev umi build",
"build:prod": "cross-env REACT_APP_ENV=prod UMI_ENV=prod umi build",
"serve": "serve ./dist",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
"test:coverage": "umi-test --coverage",
"electron": "electron app/main.js",
"electron-dev": "concurrently \"cross-env BROWSER=none yarn start:dev\" \"wait-on http://localhost:8001 && cross-env NODE_ENV=development electron app/main.js\"",
"electron-start": "yarn build && cross-env NODE_ENV=production electron app/main.js"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,less,md,json}": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
},
"dependencies": {
"@ant-design/pro-layout": "^6.5.0",
"@monaco-editor/react": "^4.5.1",
"@types/md5": "^2.3.2",
"ahooks": "^3.7.7",
"antd": "5.x",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"react": "17.x",
"react-dom": "17.x",
"umi": "^3.5.41"
},
"devDependencies": {
"@types/lodash": "^4.14.195",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@umijs/fabric": "^4.0.1",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.5.41",
"concurrently": "^8.1.0",
"cross-env": "^7.0.3",
"electron": "^25.1.0",
"lint-staged": "^10.0.7",
"prettier": "^2.2.0",
"typescript": "^4.1.2",
"wait-on": "^7.0.1",
"yorkie": "^2.0.0"
}
}
2.app文件夹下配置
1.创建main.js主文件
// electron打包配置
const {
app,
BrowserWindow,
globalShortcut,
// dialog
} = require('electron');
const path = require('path');
const isPro = process.env.NODE_ENV !== 'development';
const remote = require('@electron/remote/main');
remote.initialize();
// window对象的全局引用
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
minWidth: 800, // 最小宽度
minHeight: 600, // 最小高度
width: 1000,
heigth: 800,
title: '标题',
// autoHideMenuBar: true, // 关闭工具栏
// frame: false, // 设置为false后为无边框窗口,即无法拖拽,拉伸窗体大小,没有菜单项
icon: path.join(__dirname, './assets/logo.ico'),
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 预加载文件
// 是否启用Node integration
nodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false
// 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本.默认为 true
contextIsolation: false, // Electron 12 版本之后它将被默认true
},
});
remote.enable(mainWindow.webContents);
// 注册快捷键
globalShortcut.register('CommandOrControl+M', () => {
mainWindow.maximize();
});
globalShortcut.register('CommandOrControl+T', () => {
mainWindow.unmaximize();
});
globalShortcut.register('CommandOrControl+H', () => {
mainWindow.close();
});
// 引入目录
require(path.join(__dirname, 'menu.js'));
if (isPro) {
// 生产环境
mainWindow.loadFile(`${__dirname}/build/index.html`);
// mainWindow.loadURL(`http://192.168.10.15:30102`);
} else {
mainWindow.loadURL('http://localhost:8001/');
// 打开开发者工具
mainWindow.webContents.openDevTools();
}
// 解决应用启动白屏问题
mainWindow.on('ready-to-show', () => {
mainWindow.show();
mainWindow.focus();
});
// // 关闭窗口弹框确认
// mainWindow.on("close", (e) => {
// e.preventDefault();
// dialog.showMessageBox(mainWindow, {
// type: "warning",
// title: "关闭",
// message: "是否要关闭窗口",
// buttons: ["取消", "确定"],
// }).then((index) => {
// if (index.response === 1) {
// app.exit();
// }
// })
// })
// 关闭时触发下列事件
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// 是否允许打开多个窗口
// const gotTheLock = app.requestSingleInstanceLock()
// if (!gotTheLock) {
// // 检测到本次未取得锁,即有已存在的实例在运行,则本次启动立即退出,不重复启动。
// app.quit()
// } else {
// app.on('second-instance', (event, commandLine, workingDirectory) => {
// // 监听到第二个实例被启动时,检测当前实例的主窗口,并显示出来取得焦点
// if (mainWindow) {
// if (mainWindow.isMinimized()) mainWindow.restore()
// mainWindow.focus()
// }
// })
// }
app.on('ready', createWindow);
// 热加载
try {
require('electron-reloader')(module, {});
} catch (_) {}
// 所有窗口关闭时退出应用
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if ((mainWindow = null)) {
createWindow();
}
});
// app.on('before-quit', (event) => {
// dialog.showOpenDialog({
// })
// // 询问用户是否退出
// event.preventDefault() // 阻止本次退出
// })
2.创建目录文件menu.js
const { app, Menu } = require('electron');
const isMac = process.platform === 'darwin';
const template = [
// { role: 'appMenu' } // 如果是mac系统才有
...(isMac
? [
{
label: app.name,
submenu: [
{
role: 'about',
},
{
type: 'separator',
},
{
role: 'services',
},
{
type: 'separator',
},
{
role: 'hide',
},
{
role: 'hideOthers',
},
{
role: 'unhide',
},
{
type: 'separator',
},
{
role: 'quit',
},
],
},
]
: []),
// { role: 'fileMenu' }
{
label: '文件',
submenu: [
{
label: '新建',
accelerator: 'CmdOrCtrl+N',
click: () => {},
},
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: () => {},
},
{
label: '保存',
accelerator: 'CmdOrCtrl+S',
click: () => {},
},
{
type: 'separator',
}, // 分割线
,
isMac
? {
role: 'close',
}
: {
role: 'quit',
label: '退出',
},
],
},
// { role: 'editMenu' }
{
label: '编辑',
submenu: [
{
role: 'undo',
label: '撤消',
},
{
role: 'redo',
label: '恢复',
},
{
type: 'separator',
},
{
role: 'cut',
label: '剪切',
},
{
role: 'copy',
label: '复制',
},
{
role: 'paste',
label: '粘贴',
},
...(isMac
? [
{
role: 'pasteAndMatchStyle',
},
{
role: 'delete',
},
{
role: 'selectAll',
},
{
type: 'separator',
},
{
label: 'Speech',
submenu: [
{
role: 'startSpeaking',
},
{
role: 'stopSpeaking',
},
],
},
]
: [
{
role: 'delete',
label: '删除',
},
{
type: 'separator',
},
{
role: 'selectAll',
label: '全选',
},
]),
],
},
// { role: 'viewMenu' }
{
label: '查看',
submenu: [
{
role: 'reload',
label: '重新加载',
},
{
role: 'forceReload',
label: '强制重新加载',
},
{
role: 'toggleDevTools',
label: '切换开发工具栏',
},
{
type: 'separator',
},
{
role: 'resetZoom',
label: '原始开发工具栏窗口大小',
},
{
role: 'zoomIn',
label: '放大开发工具栏窗口',
},
{
role: 'zoomOut',
label: '缩小开发工具栏窗口',
},
{
type: 'separator',
},
{
role: 'togglefullscreen',
label: '切换开发工具栏全屏',
},
],
},
// { role: 'windowMenu' }
{
label: '窗口',
submenu: [
{
role: 'minimize',
label: '最小化',
},
...(isMac
? [
{
type: 'separator',
},
{
role: 'front',
},
{
type: 'separator',
},
{
role: 'window',
},
]
: [
{
role: 'close',
label: '关闭',
},
]),
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
3.app文件夹下创建package.json
涉及到三个安装包
electron
electron-builder
用于打包electron项目
electron-reloader
用于编写electron时,代码保存可以热更新
scripts下配置打包命令 dist-win32
dist-win64
dist-mac
以下为package.json详细内容,可以直接复制到你的项目中,然后再app文件夹下执行yarn安装
// package.json
{
"name": "electron_project",
"version": "0.0.1",
"description": "description",
"author": "huangjinhui",
"main": "main.js",
"scripts": {
"dist-win32": "electron-builder --win --ia32",
"dist-win64": "electron-builder --win --x64",
"dist-mac": " electron-builder --mac"
},
"dependencies": {
"@electron/remote": "^2.0.10"
},
"devDependencies": {
"electron": "^25.1.0",
"electron-builder": "^23.6.0", // 用于打包
"electron-reloader": "^1.2.3" // 用于编写electron时,代码保存可以热更新
},
"build": { // electron打包配置
"productName": "electron_project",
"appId": "com.example.app",
"directories": {
"output": "dist"
},
"files": [ // 打包时需要保存的文件
"build/**/*",
"main.js",
"menu.js",
"preload.js",
],
"extraMetadata": {
"main": "./main.js"
},
"win": {
"icon": "./assets/logo.ico"
},
"mac": {
"icon": "./assets/logo.ico"
},
"linux":{
"icon": "./assets/logo.ico"
}
}
}
最终项目目录
三 运行及打包
- app/main.js为electron主配置文件
- app/package.json为electron项目打包所需相关依赖。
- 打包electron容器需在app文件夹下打包
- 打包web应用在根目录下打包
1.运行流程
- 根目录下执行
yarn electron-dev
即可实现先运行web端,再运行electron桌面端应用
2.打包流程
打包需要分两步:1.打包web端文件;2打包electron容器
- 使用webpack打包web应用。在./package.json中执行 yarn build:prod 命令。
- 使用electron-builder打包桌面应用。在./app/package.json中执行 dist-win32/dist-win64 命令。
最终输出到app/dist 文件夹
以上为使用umi+electron实现web端和桌面端同步开发整个流程,过程中有什么问题,可以留言沟通。
转载自:https://juejin.cn/post/7246581605769527353