Electron集成NestJs方案

1,616 阅读3分钟

这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

1.问题场景/应用场景

首先第一个,为什么electron集成NestJs。

这样做最主要的目的是:

  • 第一个避免通过electron主进程或通过渲染进程,开放其自身的node能力去进行文件啥的操作。
  • 第二一个就是使得基于electron开发的这个应用可以做正向或者反向代理。
  • 第三一个就是NestJS集成到electron,这样electron自身就是完全的一个套壳,写ui,内置的数据库操作啥的都交给NestJS来操作,通过NestJS,electron中实现文件上传,下载等功能将更为方便。

2. NestJs集成操作

这里的集成操作,ui界面是基于Umijs的,其他的前端UI界面的集成方式大同小异,本文均具有参考价值

2.1 UmiJs下配置electron

这里我没有采用Umi的第三方关于Electron的集成插件,而是使用的自己的配置方案

  1. 首先按Umijs的文档创建一个项目,test
  2. 安装electron相关依赖包electron electron-builder electron-reloader 安装在devDependencies下.
  3. 创建一个desk.ts和一个tsconfig.main.json用于开发electron主进程相关逻辑

desk.ts基本内容如下

import { BrowserWindow, app } from 'electron';
import path from 'path';
import child_process from 'child_process';
const exec = child_process.exec;
let openExec;
type MyWindow =
  | (BrowserWindow & {
      rootPath?: string;
      dirPath?: string;
    })
  | null
  | BrowserWindow;
let window: MyWindow;

const initWindow = {
  width: 1050,
  height: 700,
};

const isDevelopment = process.env.NODE_ENV !== 'production';
function createWindow() {
  console.log(__dirname);
  window = new BrowserWindow({
    ...initWindow,
    webPreferences: {
      javascript: true,
      plugins: true,
      webSecurity: false,
      contextIsolation: false,
      nodeIntegration: true,
      preload: path.join(__dirname, './preload.js'),
    },
    frame: false,
    transparent: true,
  });

  if (isDevelopment) {
    try {
        require('electron-reloader')(module, {});
    } catch (_) {}
    window.webContents.openDevTools();
    window.loadURL('http://localhost:8001/');
  } else {
    window.loadURL('app://./index.html');
  }

  return window;
}
let serverApp;

app.on('ready', () => {
  process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'; //关闭web安全警告
  createWindow();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
    if (!openExec) {
    } else {
      exec('taskkill /f /t /im node.exe', function (error, stdout, stderr) {
        if (error) {
          console.log(error.stack);
          console.log('Error code: ' + error.code);
          return;
        }
        console.log('使用exec方法输出: ' + stdout);
        console.log(`stderr: ${stderr}`);
      });
    }
  }
});

从上面代码可以看到有这样一段代码 preload: path.join(__dirname, './preload.js'),这是很关键的,因为umijs,渲染进程中没法直接使用electron提供的能力,需要用这样一个文件进行预加载,因此在desk.ts同级下创建preload.ts文件

import electron, { ipcRenderer } from 'electron';
global.electron = electron;

(window as any).ipcRenderer = ipcRenderer;

其主要作用就是把electron能力挂载到window对象属性上,做全局属性或方法 然后就是修改一下项目启动命令在package.json

...
"start:main": "tsc -w --p tsconfig.main.json",
"start:electron": "electron ./dist/desk.js",
"build:electron": "electron-builder --win",
"start:umi": "umi dev",
"build:umi": "umi build",
// electron 打包配置
"build": {
    "extraResources": {
      "from": "./resources/",
      "to": ""
    },
    "productName": "test",
    "appId": "test",
    "win": {
      "icon": "./resources/icon.ico",
      "target": [
        "nsis",
        "zip"
      ]
    },
    "nsis": {
      "oneClick": false,
      "perMachine": true,
      "allowElevation": true,
      "allowToChangeInstallationDirectory": true,
      "createDesktopShortcut": true,
      "createStartMenuShortcut": true,
      "shortcutName": "icon",
      "installerIcon": "./resources/icon.ico",
      "uninstallerIcon": "./resources/icon.ico",
      "installerHeaderIcon": "./resources/icon.ico"
    }
  },

2.2 集成NestJs

  1. 安装Nestjs相关依赖包,请注意,这里骚操作就来了
    • Nestjs相关的依赖包是安装在dependencies,umijs的包全部转移到devDependencies
    • 原因的话就是Nestjs打包出来并不会吧node_moudules模块加载进去,所以需要通过这样的配置,在打包electron的时候,将这些依赖打进去
  2. 在项目根目录下创建一个server文件夹,然后就是tsconfig.build.json,修改package.json
  "start:nest": "nest start --watch",
  "build:nest": "nest build",

tsconfig.build.json这个文件很重要,这个是运行yarn start:nest命令所识别的tsconfig

{
  "exclude": [
    "node_modules",
    "test",
    "dist",
    "server/dist",
    "./src",
    "deskapp",
    "mock",
    "**/*spec.ts"
  ],
  "compilerOptions": {
    "strict": false,
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./server/dist",
    "baseUrl": "./server",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}
  1. 创建nest-cli.json,修改命令的基本路径指向,在本示例中代码如下:
{
  "collection": "@nestjs/schematics",
  "sourceRoot": "./server/src"
}

这样一个基本的基于electron 集成nestjs能力的项目就完成了

注备一个大坑

如果electron集成了nestjs,nestjs要进行文件读写操作的话,就不能开启asar,在打包build的时候,缺点就是暴露了源码...,目前还没找到一个比较合适的方式处理该问题,探索中,后面进一步补充

  • (2022-02-07)关于asar,还是选择开启,不然包太大,东西太多,然后在asarUnpack当中选择不进行压缩的文件

详情示例项目欢迎参考本人个人项目