构建electron、typescript、react、antd应用

这个研究了很旧,还没研究透,先记录,以免忘记

1. 初始化react项目

1
create-react-app demo --typescript

2. 添加相关依赖, 表如下

包名 介绍 安装方式
electron 关键包,构建electron应用必须 -D
sqlite3 数据库支持,需要存储数据的话,推荐这个 -P
antd 蚂蚁金服提供的React框架 -P
react-router-dom 提供路由支持 -P
typeorm 适用于Typescript的ORM支持 -P
xlsx 读取excel文件,也可以创建excel文件 -P
ejsexcel 通过模板的方式生成excel文件 -P
electron-is-dev 用于在逻辑中判断当前是否开发模式,因为打包后一些路径会变,
这个可以判断当前是否打包以便选择路径
-P
electron-log 日志支持 -P
iconv-lite 添加更多编码支持,window蛋疼的不使用utf-8,
node又不支持GBK,所以需要这个
-P
lodash 著名的javascript工具库 -P
moment 时间处理库 -P
react-app-rewired 因为create-react-app的配置文件都是不对外透明的,
这个可以让create-react-app 加载的时候,额外对现有配置进行修改
-D
customize-cra 配合react-app-rewired 来使用,快速写一下配置 -D
electron-rebuild 一些需要需要根据系统编译的库,比如 sqlite3 如果直接安装会出现不兼容问题,
然后就会死活找不到这个库,通过这个可以根据现有的版本进行重新编译
-D
electron-builder 用于打包项目,生成安装包

以上就是我这个项目用到的包,感谢 GitHub 这个开源平台,感觉包作者的大大们,谢谢!!! :smile:

3. 配置项目需要的基础代码

  1. 添加 electron 的入口文件 /public/main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain } = require('electron');
const isDev = require('electron-is-dev')
const path = require('path')
const log = require('electron-log');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

async function createWindow() {

// Create the browser window.
mainWindow = new BrowserWindow({
width: 1700,
height: 1000,
minHeight: 1000,
minWidth: 1700,
webPreferences: {
nodeIntegration: true,
webSecurity: false,
},
autoHideMenuBar: true,
})

// 这里就是初始化的时候加载的页面,由于打包之后使用的是静态文件,开发时候使用的是服务器,所有这里要分开处理,electron-is-dev 库的重要性
const htmlUrl = `file://${path.join(__dirname, '../build/index.html#login')}`;
const url = isDev ? "http://127.0.0.1:3000#/login" : htmlUrl;
log.info(url);
mainWindow.loadURL(url);
// 因为开发的时候 electron 虽然使用chrome内核,但是不没有任何chrmoe插件。开发React的时候最好有 React dev tools 插件,所以可以通过以下的方式来加载额外的插件,路径是插件安装在chrmoe的路径,有点难找,可以配合 Everything 来查找
if (isDev) {
BrowserWindow.addDevToolsExtension('C:\\Users\\kirno\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions\\fmkadmapgofadopljbjfkapdkoienihi\\3.6.0_0');
}

// Open the DevTools.
// mainWindow.webContents.openDevTools()

// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
// 这里线程间通信用的,electron 主要分为 主线程(浏览器)和渲染线程 (网页) 的形式运行,有些事情在网页里是比较难实现,就可以通过这种方式让浏览器帮你做。下面是实现获取系统打印机列表的功能
ipcMain.on('obtainPrinterList', (event) => {
const printers = mainWindow.webContents.getPrinters();
mainWindow.webContents.send('getPrinterList', printers);
});
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') app.quit()
})

app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) createWindow()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
  1. package.json文件中添加脚本,辅助开发,以下是节选内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"scripts": {
"ele": "electron .",
"pub": "build --publish always",
/* 重新编译 */
"rebuild": "electron-rebuild",
/* 开发运行 */
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject",
"postinstall": "electron-builder install-app-deps",
"buil": "electron-builder install-app-deps",
"preelectron-pack": "yarn build",
/* 打包 */
"electron-dist": "build -w",
/* 打包,但不生成安装包文件 */
"electron-pack": "electron-builder --dir",
"release": "build"
}
}

接下来就可以进行开发了。以下记录几个问题

typeorm 的问题

  1. 无法找到 sqlite3 的包,原因可能有两个,一个是sqlite3直接安装后,和electron的版本不符合。可以通过 electron-build 库解决,重新编译即可;二是因为导入的方式不对,如果使用

    1
    import {xxxx} from 'typeorm'

    的话,由于设计到c这种第三方底层库,然而 electron 的渲染线程是没有办法访问这种底层代码的,所以导致无法typrorm 底层无法调用成功,所以这里要借助 electron 的函数 require 方法来引入,实际上就是让 electron 来帮我们导包。但是如果使用 require 的方法来导包,就会失去 typescript 的类型优势,以下是解决方法:

    1. 新建一个 libary.js 文件

      1
      2
      const { getConnection, getRepository } = window.require('typeorm');
      module.exports = { getConnection, getRepository }
    2. 新建 libary.d.ts 文件

      1
      2
      export declare function getRepository<Entity>(entityClass: ObjectType<Entity> | EntitySchema<Entity> | string, connectionName?: string): Repository<Entity>;
      export declare function getConnection(connectionName?: string): Connection;
    3. 再要使用的地方引入

      1
      import { getRepository, getConnection } from '../libary';

    即可,这里制作了两个常用方法的实例,其他方法也是这样引如。这样就不会丢失 类型。这个方法同样适用于引入 electron 对象。

  2. 打包问题,由于webpack会把所有引入的文件都进行打包,然而typeorm因为 要兼容多种数据库,所以底层会引入多中数据库的包,但是因为我们只用一种数据,其他的没有,所以打包的时候会报错。解决办法就是让 webpack不要打包那些多余没用到的库,在实际要用的时候再引入。

    1. 由于 create-react-app的配置文件是赢藏的,所以通过 react-app-rewiredcustomize-cra 配合修改,新建 /config-overrides.js 文件,以下还提到了两种打包目标模式,具体可以去webpack官网查看

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      const { addLessLoader, override, addWebpackExternals, fixBabelImports } = require('customize-cra');
      module.exports = {
      webpack: override(
      // 添加less支持
      addLessLoader({
      javascriptEnabled: true,
      }),
      // 重点: 取消不必要的库的打包
      addWebpackExternals({
      'pg': true,
      'pg-hstore': true,
      'mysql2': true,
      'react-native-sqlite-storage': true,
      }),
      // 客户端使用 electron-renderer方式渲染
      (config) => {
      config.target = "electron-renderer";
      return config;
      },
      (config) => {
      config.optimization.minimize = false;
      return config;
      },
      ),
      // 服务端使用 electron-main方式渲染
      devServer: (config) => {
      config.target = "electron-main";
      return config;
      }
      }

路径问题

由于打包版本和开发版本存在路径差异,所以需要引入 electron-is-dev包来判断当前的环境。

数据存储路径建议放大 electron.app.getAppPath()