当前位置:首页 » 《随便一记》 » 正文

react+electron/现成的react项目直接转electron(2)能本地运行,打包,【web层,渲染层,主进程】之间的互相通信交互

12 人参与  2024年02月15日 13:01  分类 : 《随便一记》  评论

点击全文阅读


难点:之前写了一个关于运行electron的文章 《现成的react项目直接转electron(1)能本地运行》后,又接着找打包的,找的是坑真多,全部失败,后来无意中看到 小满zs的B站视频 后,发现这个非常好,然后跟着重构一下,也可以直接看人家写的文章Vue3 Vite electron 开发桌面程序和小满Vue3第三十九章(Vue开发桌面程序Electron)

知识点:人家那是vue的,我这是react的,加减了一些东西,增加了【web层,渲染层,主进程】之间的互相通信交互,话不多说?,接着填坑吧,填了的都是知识点

1.还是先下载依赖

pnpm install -D electron electron-builder

2.先在根目录下创建两个文件夹,每个里面包含两个ts文件

electron-app

        plugins

                vite.electron.build.ts

                vite.electron.dev.ts

        electron

                main.ts

                preload.ts

接下来就一个个讲,干嘛的

2.1 先看看plugins

需要在vite.config.ts中调用这俩文件,以防大家不知道结构,所以索性就都贴上来了,实际上找electron相关就行,其他配置还是照旧(注释掉的是需要去掉的web相关的配置,比如打包的zip,生成压缩包gz的viteCompression,代理我没去掉,估计不会有跨域问题,可以自行试试)

import { fileURLToPath, URL } from "node:url";import { viteElectronDev } from "./plugins/vite.electron.dev";import { viteElectronBuild } from "./plugins/vite.electron.build";import { defineConfig, loadEnv, UserConfigExport, ConfigEnv } from "vite";// import zip from 'vite-plugin-zip';import react from "@vitejs/plugin-react";import ViteRestart from "vite-plugin-restart";import reactRefresh from "@vitejs/plugin-react-refresh";import path from "path";import vitePluginImp from "vite-plugin-imp";// import viteCompression from 'vite-plugin-compression';export default ({ command, mode }: ConfigEnv): UserConfigExport => {  const env = loadEnv(mode, process.cwd()); // 获取.env文件里定义的环境变量  // console.log(env); //变量在命令行里打印出来  return defineConfig({    mode: env.VITE_APP_MODE,    base: "./", // 设置打包路径    server: {      host: "0.0.0.0",      port: 9000, // 设置服务启动端口号      open: false, // 设置服务启动时是否自动打开浏览器      https: false,      cors: true, // 允许跨域      // 设置代理,根据我们项目实际情况配置      proxy: {              },    },    resolve: {      alias: {        "@": fileURLToPath(new URL("./src", import.meta.url)),        // '@': path.resolve(__dirname, './src'),        "~": path.resolve(__dirname, ""),        "#": path.join(__dirname, "types"),      },    },    plugins: [      react(),      reactRefresh(),      viteElectronDev(),      viteElectronBuild(),      vitePluginImp({        // 按需引入        libList: [          {            libName: "antd",            style: (name) => `antd/es/${name}/style`,            libDirectory: "es",          },        ],      }),      // zip({      // // 打包zip      // dir: 'photoSelect',      // outputName: 'photoSelect'      // }),      ViteRestart({        // vite.config.js 和 .env文件后不用重启        restart: ["my.config.[jt]s"],      }),      // viteCompression({      // //生成压缩包gz      // verbose: true,      // disable: false,      // threshold: 512, // 10240      // algorithm: 'gzip',      // ext: '.gz'      // })    ],    css: {      //* css模块化      modules: {        // css模块化 文件以.module.[css|less|scss]结尾        generateScopedName: "[name]__[local]___[hash:base64:5]",        hashPrefix: "prefix",      },      preprocessorOptions: {        less: {          modifyVars: {            // 更改主题在这里            // 'primary-color': '#87ceeb', // 全局主色            "primary-color": "#FF7E00", // 全局主色            "link-color": "#1890ff", // 链接色            "success-color": "#52c41a", // 成功色            "warning-color": "#faad14", // 警告色            "error-color": "#f5222d", // 错误色            "font-size-base": "14px", // 主字号          },          // modifyVars: getThemeVariables({          // // dark: true // 开启暗黑模式          // // compact: true, // 开启紧凑模式          // }),          javascriptEnabled: true,        },      },    },  });};

package.json中命令如下,执行 pnpm run dev 和 pnpm run build 即可

单纯的web项目还是执行pnpm start

{  "scripts": {    "dev": "vite --mode dev",    "build": "tsc && vite build",    "start": "vite --host 0.0.0.0 --mode dev"  }}

要知道,node其实没办法直接执行ts文件,所以执行命令后会在将根目录下electron里面的ts打包一份js在根目录下,生成新的文件,然后electron找的是这些js文件,这个不用管就行

electron-app

        dist

                main.js

                preload.js

2.1.1 vite.electron.build.ts:打包时候走的程序

import type { Plugin } from "vite";import * as electronBuilder from "electron-builder";import path from "path";import fs from "fs";// 导出Vite插件函数export const viteElectronBuild = (): Plugin => {  return {    name: "vite-electron-build",    // closeBundle是Vite的一个插件钩子函数,用于在Vite构建完成后执行一些自定义逻辑。    closeBundle() {      // 定义初始化Electron的函数      const initElectron = () => {        // 使用esbuild编译TypeScript代码为JavaScript        // 主进程        require("esbuild").buildSync({          entryPoints: ["electron/main.ts"],          bundle: true,          outfile: "dist/main.js",          platform: "node",          target: "node12",          external: ["electron"],        });        // 渲染层        require("esbuild").buildSync({          entryPoints: ["electron/preload.ts"],          bundle: true,          outfile: "dist/preload.js",          platform: "node",          target: "node12",          external: ["electron"],        });      };      // 调用初始化Electron函数      initElectron();      // 修改package.json文件的main字段 不然会打包失败      const json = JSON.parse(fs.readFileSync("package.json", "utf-8"));      json.main = "main.js";      fs.writeSync(        fs.openSync("dist/package.json", "w"),        JSON.stringify(json, null, 2)      );      // 创建一个空的node_modules目录 不然会打包失败      fs.mkdirSync(path.join(process.cwd(), "dist/node_modules"));      // 使用electron-builder打包Electron应用程序      electronBuilder.build({        config: {          appId: "com.example.app",          productName: "打包项目名字",          directories: {            output: path.join(process.cwd(), "release"), //输出目录            app: path.join(process.cwd(), "dist"), //app目录          },          asar: true,          nsis: {            installerIcon: 'assets/installer-icon.ico', // 安装程序图标文件的路径            oneClick: false, //取消一键安装          },          mac: {            icon: 'assets/app.icns',          },          dmg: {            //backgroundColor: '#f1f1f6',            background: 'assets/app.png',            icon: 'assets/installer-icon.ico',            iconSize: 160,            window: {              width: 600,              height: 420,            },            contents: [              {                x: 150,                y: 200,              },              {                x: 450,                y: 200,                type: 'link',                path: '/Applications',              },            ],            sign: false,            artifactName: '${productName}_mac_${arch}_${version}(${buildVersion}).${ext}',          },          win: {            icon: 'assets/icon.ico',            target: [              {                target: "nsis",                arch: ["x64", "ia32"],              },            ],          },          linux: {            icon: 'assets/app.icns',          }        },      }).then(() => {        // 在这里执行构建成功后的逻辑        console.log('Electron应用程序构建成功');        fs.rmdirSync('./dist', { recursive: true });      });    },  };};

2.1.2 vite.electron.dev.ts:开发时候走的程序

import type { Plugin } from "vite";import type { AddressInfo } from "net";import { spawn } from "child_process";import fs from "fs";import os from "os";function getLocalIpAddress() {  const interfaces = os.networkInterfaces();  for (const interfaceName in interfaces) {    const addresses = interfaces[interfaceName];    for (const address of addresses) {      if (address.family === "IPv4" && !address.internal) {        return address.address;      }    }  }  return null;}const ipAddress = getLocalIpAddress();// 导出Vite插件函数export const viteElectronDev = (): Plugin => {  return {    name: "vite-electron-dev",    // 在configureServer中实现插件的逻辑    configureServer(server) {      // 定义初始化Electron的函数      const initElectron = () => {        // 使用esbuild编译TypeScript代码为JavaScript        require("esbuild").buildSync({          entryPoints: ["electron/main.ts"],          bundle: true,          outfile: "dist/main.js",          platform: "node",          target: "node12",          external: ["electron"],        });        require("esbuild").buildSync({          entryPoints: ["electron/preload.ts"],          bundle: true,          outfile: "dist/preload.js",          platform: "node",          target: "node12",          external: ["electron"],        });      };      // 调用初始化Electron函数      initElectron();      // 监听Vite的HTTP服务器的listening事件      server?.httpServer?.once("listening", () => {        // 获取HTTP服务器的监听地址和端口号        const addressInfo = server?.httpServer?.address() as AddressInfo;        // console.log(addressInfo);        const IP = `http://${ipAddress}:${addressInfo.port}`;        console.log(`本地IP地址:${IP}`);        // 启动Electron进程        let electronProcess = spawn("electron", ["dist/main.js", IP]);        const fsWatchFile = (fileName) => {          fs.watchFile(`electron/${fileName}.ts`, () => {            // 杀死当前的Electron进程            electronProcess.kill();            // 重新编译主进程代码并重新启动Electron进程            initElectron();            electronProcess = spawn("electron", [`dist/main.js`, IP]);          });        }        // 监听主进程代码的更改        fsWatchFile('main');        fsWatchFile('preload');        // console.log(electronProcess);        // 监听Electron进程的stdout输出        electronProcess.stdout?.on("data", (data) => {          console.log(`日志: ${data}`);        });      });    },  };};

2.2 electron 主进程和渲染层还有web层的互相通信交互

2.2.1 main.ts 主进程

import { app, BrowserWindow, ipcMain, dialog } from "electron";import path from "path";app.whenReady().then(async () => {  const win = await new BrowserWindow({    // 窗口的宽高    width: 1800,    height: 900,    // 配置窗口的WebPreferences选项,用于控制渲染进程的行为    webPreferences: {      nodeIntegration: true,      contextIsolation: true,      preload: path.join(__dirname, "preload.js"), // 渲染进程,能满足交互作用    },  });  // 根据命令行参数加载URL或本地文件 配置好的开发环境,看情况而定  if (process.argv[2]) {    win.loadURL(process.argv[2]);    win.webContents.openDevTools(); // 开发时候使用 打开web的ip或者本地 可以热更新  } else {    win.loadFile("index.html"); // 打包时候使用 打开的是本地的html  }  // web层借助渲染层和主进程的交互  ipcMain.on("open-dialog", (event) => {    dialog      .showOpenDialog({        properties: ["openDirectory"],      })      .then((result) => {        event.reply("open-dialog-reply", result);      });  });  ipcMain.on('openFlyCar',()=>{    console.log('收到')  })});app.on("window-all-closed", () => {  if (process.platform !== "darwin") app.quit();});

2.2.2 preload.ts 渲染层

import { contextBridge, ipcRenderer, shell } from "electron";import path from 'path';// 和web端的交互contextBridge.exposeInMainWorld('electronAPI', {  // 打开浏览器窗口  openExternal(url: string){    shell.openExternal(url);  },  // 打开文件夹  async openPath() {    const folderPath: string = "本地的地址";    shell.openPath(folderPath);  },  // web层借助渲染层和主进程的交互  send: (channel, data) => ipcRenderer.send(channel, data),  // web层借助渲染层和主进程的交互,切有回调  on: (channel, callback) => ipcRenderer.on(channel, (event, ...args) => callback(...args))});渲染层和主进程的交互const open = () => {  ipcRenderer.send('openFlyCar')}open();

2.2.3 react中tsx web层

// 最外部写入let win: any = window;// 随便在按钮的点击事件中写即可// web层和渲染层交互通信win['electronAPI'].openExternal("http://www.baidu.com/"); // 唤起浏览器打开百度// web层借助渲染层和主进程交互通信win['electronAPI'].send('open-dialog'); // 打开资源管理器win['electronAPI'].on('open-dialog-reply', (result: any) => {if (!result.canceled) {        // 选择了什么地址        // ...}});

3.暂时就这些了,估计会有一定的问题,如果有问题可以评论和私信,我会经常看的,可以互相交流学习,其中node可以在electron中随便使用,后续我会接着更新一些关于electron的方法,希望大家共同进步,共同成长,加油!


点击全文阅读


本文链接:http://zhangshiyu.com/post/68561.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1