Electron结合React和TypeScript进行开发


目录
  • 结合React+TypeScript进行Electron开发
    • 1. electron基本简介
      • 为什么选择electron?
    • 2. 快速上手
      • 2.1 安装React(template为ts)
      • 2.2 快速配置React
      • 2.3 安装electron
      • 2.4 配置main.jspreload.jspackage.json文件
      • 2.5 运行electron项目
      • 2.6 打包项目
    • 3. 自动刷新页面
    • 4. 主进程和渲染进程
    • 5.定义原生菜单、顶部菜单
      • 5.1 自定义菜单
      • 5.2 给菜单定义点击事件
      • 5.3 抽离菜单定义
      • 5.4 自定义顶部菜单
      • 5.5 在渲染进程中使用主进程方法remote和electron(点击创建新窗口)
      • 5.6 点击打开浏览器
    • 6. 打开对话框读取文件
      • 6.1 读取文件
      • 6.2 保存文件
    • 7. 定义快捷键
      • 7.1 主线程定义
      • 7.2在渲染进程中定义
    • 8. 主进程和渲染进程通讯

结合React+TypeScript进行Electron开发

1. electron基本简介

electron是使用JavaScript,HTML和CSS构建跨平台桌面应用程序。我们可以使用一套代码打包成Mac、Windows和Linux的应用,electron比你想象的更简单,如果把你可以建一个网站,你就可以建一个桌面应用程序,我们只需要把精力放在应用的核心上即可。

为什么选择electron?

  • Electron 可以让你使用纯JavaScript调用丰富的原生APIs来创造桌面应用。你可以把它看作是专注于桌面应用。
  • 在PC端桌面应用开发中,nwjs和electron都是可选的方案,它们都是基于Chromium和Node的结合体,而electron相对而言是更好的选择方案,它的社区相对比较活跃,bug比较少,文档相对利索简洁。
  • electron相对来说比nw.js靠谱,有一堆成功的案例:Atom编辑器 Visual Studio Code WordPress等等。
  • Node.js的所有内置模块都在Electron中可用。

2. 快速上手

2.1 安装React(template为ts)

yarn create react-app electron-demo-ts --template typescript

2.2 快速配置React

工程架构

index.html



  
    
    
    
    
    
    
    
    electron App
  
  
    
    

App.tsx

import React from 'react'

export default function App() {
  return (
    
App
) }

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  // 
    
  // 
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

2.3 安装electron

electron包安装到您的应用程序的devDependencies.

// npm
npm install --save-dev electron
// yarn
yarn add --dev electron

2.4 配置main.jspreload.jspackage.json文件

main.js

// 导入app、BrowserWindow模块
// app 控制应用程序的事件生命周期。事件调用app.on('eventName', callback),方法调用app.functionName(arg)
// BrowserWindow 创建和控制浏览器窗口。new BrowserWindow([options]) 事件和方法调用同app
// Electron参考文档 https://www.electronjs.org/docs
const {app, BrowserWindow, nativeImage } = require('electron')
const path = require('path')
// const url = require('url');


function createWindow () {
    // Create the browser window.
    const mainWindow = new BrowserWindow({
        width: 800, // 窗口宽度
        height: 600,  // 窗口高度
        // title: "Electron app", // 窗口标题,如果由loadURL()加载的HTML文件中含有标签,该属性可忽略
        icon: nativeImage.createFromPath('public/favicon.ico'), // "string" || nativeImage.createFromPath('public/favicon.ico')从位于 path 的文件创建新的 NativeImage 实例
        webPreferences: { // 网页功能设置
            webviewTag: true, // 是否使用<webview>标签 在一个独立的 frame 和进程里显示外部 web 内容
            webSecurity: false, // 禁用同源策略
            preload: path.join(__dirname, 'preload.js'),
            nodeIntegration: true // 是否启用node集成 渲染进程的内容有访问node的能力,建议设置为true, 否则在render页面会提示node找不到的错误
        }
    })


    // 加载应用 --打包react应用后,__dirname为当前文件路径
    // mainWindow.loadURL(url.format({
    //   pathname: path.join(__dirname, './build/index.html'),
    //   protocol: 'file:',
    //   slashes: true
    // }));

    
    // 因为我们是加载的react生成的页面,并不是静态页面
    // 所以loadFile换成loadURL。
    // 加载应用 --开发阶段  需要运行 yarn start
    mainWindow.loadURL('http://localhost:3000');

    // 解决应用启动白屏问题
    mainWindow.on('ready-to-show', () => {
        mainWindow.show();
        mainWindow.focus();
    });

    // 当窗口关闭时发出。在你收到这个事件后,你应该删除对窗口的引用,并避免再使用它。
    mainWindow.on('closed', () => {
        mainWindow = null;
    });
    
    // 在启动的时候打开DevTools
    mainWindow.webContents.openDevTools()
}

app.allowRendererProcessReuse =true;

// 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.whenReady().then(() =>{
    console.log('qpp---whenready');
    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
    console.log('window-all-closed');
    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 (BrowserWindow.getAllWindows().length === 0) 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.
</code></pre>
<p><strong>package.json</strong></p>
<p>这时候我们来修改<code>package.json</code>文件。</p>
<ol>
<li>配置启动文件,添加<code>main</code>字段,我们这里也就是main.js文件。如果没有添加,Electron 将尝试加载包含在<code>package.json</code>文件目录中的<code>index.js</code>文件。</li>
<li>配置运行命令,使用<strong>"electron": "electron ."</strong> 区别于react的启动命令<strong>"start": "react-scripts start",</strong></li>
<li>安装concurrently: <code>yarn add concurrently</code></li>
</ol>
<pre><code class="language-json">{
    ...
    "main": "main.js", // 配置启动文件
  	"homepage": ".", // 设置应用打包的根路径
    "scripts": {
        "start": "react-scripts start",  // react 启动命令
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject",
        "electron": "electron .",  // electron 启动命令
        "dev": "concurrently \"npm run start\" \"npm run electron\""
    },
}
</code></pre>
<p><strong>preload.js</strong></p>
<pre><code class="language-js">window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
        const element = document.getElementById(selector)
            if (element) element.innerText = text
    }
  
    for (const dependency of ['chrome', 'node', 'electron']) {
        replaceText(`${dependency}-version`, process.versions[dependency])
    }
})
</code></pre>
<p><strong>此时的工程架构</strong></p>
<p></p>
<h3 id="25-运行electron项目">2.5 运行electron项目</h3>
<ol>
<li>先<code>yarn start</code> 然后再开一个终端<code>yarn electron</code></li>
<li>或者是<code>npm run dev</code></li>
</ol>
<p></p>
<p>其实我们就可以看出Electron就是一个应用套了一个谷歌浏览器壳子,然后里面是前端页面。</p>
<h3 id="26-打包项目">2.6 打包项目</h3>
<p>使用<strong>electron-packager</strong>依赖:</p>
<pre><code>yarn add --dev electron-packager
</code></pre>
<p>package.json配置打包命令:</p>
<pre><code class="language-json">"package": "electron-packager . bleak-electron-app --platform=win32 --arch=x64 --overwrite --electron-version=18.1.0 --icon=./public/favicon.ico"
</code></pre>
<p>配置解释:</p>
<pre><code>electron-packager <应用目录> <应用名称> <打包平台> <架构x86 还是 x64> <架构> <electron版本> <图标>
overwrite 如果输出目录已经存在,替换它
</code></pre>
<p>然后运行命令:</p>
<pre><code>yarn package
</code></pre>
<p>打包时间慢的话可按照下面两种方式优化:</p>
<pre><code>方法1:
在执行electron-packager前先运行set ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
方法2:
在electron-packager命令行加入参数--download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/
(Windows x64)完整版如下:
electron-packager . bleak-electron-app --platform=win32 --arch=x64 --overwrite --electron-version=18.0.4 --download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/
</code></pre>
<p>然后运行<code>bleak-electron-app-win32-x64</code>里面的exe文件就可以了。</p>
<p></p>
<h2 id="3-自动刷新页面">3. 自动刷新页面</h2>
<p>当你用react开发的时候,网页内容会自动热更新,但是electron窗口的main.js中代码发生变化时不能热加载。</p>
<p>安装插件electron-reloader:</p>
<pre><code>yarn add --dev electron-reloader
npm install --save-develectron-reloader
</code></pre>
<p>然后在路口引用插件:</p>
<pre><code class="language-js">const reloader = require('electron-reloader')
reloader(module)
</code></pre>
<p>就可以实现electron插件热更新。</p>
<h2 id="4-主进程和渲染进程">4. 主进程和渲染进程</h2>
<p>Electron运行package.json的main脚本的进程称为<strong>主进程</strong>。在主进程中运行的脚本通过创建web页面来展示用户节面,一个Electron应用总是有且只有一个主进程。</p>
<p>由于Electron使用了Chromium来展示web页面,所以Chromium的多进程架构也被使用到,每个Electron钟大哥web页面运行在它的叫<strong>渲染进程</strong>的进程中。</p>
<p>在普通的浏览器中,web页面无法访问操作系统的原生资源。然而Electron的用户在Node.js的API支持下可以在页面中和操作系统进行一些底层交互。</p>
<p>ctrl + shift + i 打开渲染进程调试(devtools)</p>
<p>默认打开调试:</p>
<pre><code class="language-tsx">// 在启动的时候打开DevTools
mainWindow.webContents.openDevTools()
</code></pre>
<h2 id="5定义原生菜单顶部菜单">5.定义原生菜单、顶部菜单</h2>
<h3 id="51-自定义菜单">5.1 自定义菜单</h3>
<p>可以使用Menu菜单来创建原生应用菜单和上下文菜单。</p>
<ol>
<li>首先判断是什么平台,是mac还是其他:</li>
</ol>
<pre><code class="language-js">const isMac = process.platform === 'darwin'
</code></pre>
<ol start="2">
<li>创建菜单模板:</li>
</ol>
<p>其是由一个个MenuItem组成的,可以在菜单项官网API查看。</p>
<pre><code class="language-js">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: [
      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: '关闭' }
      ])
    ]
  },
  {
    role: 'help',
    label: '帮助',
    submenu: [
      {
        label: '从Electron官网学习更多',
        click: async () => {
          const { shell } = require('electron')
          await shell.openExternal('https://electronjs.org')
        }
      }
    ]
  }
]
</code></pre>
<ol start="3">
<li>根据模板创建menu:</li>
</ol>
<pre><code class="language-js">const menu = Menu.buildFromTemplate(template)
</code></pre>
<ol start="4">
<li>设置菜单:</li>
</ol>
<pre><code class="language-js">Menu.setApplicationMenu(menu)
</code></pre>
<h3 id="52-给菜单定义点击事件">5.2 给菜单定义点击事件</h3>
<p>可以通过<code>click</code>属性来设置点击事件</p>
<h3 id="53-抽离菜单定义">5.3 抽离菜单定义</h3>
<p>创建一个menu.js:</p>
<pre><code class="language-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: [
      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: '关闭' }
      ])
    ]
  },
  {
    role: 'help',
    label: '帮助',
    submenu: [
      {
        label: '从Electron官网学习更多',
        click: async () => {
          const { shell } = require('electron')
          await shell.openExternal('https://electronjs.org')
        }
      }
    ]
  }
]

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
</code></pre>
<p>然后在<code>main.js</code>中<code>createWindow</code>的方法使用<code>require</code>调用:</p>
<pre><code class="language-js">const createWindow = () => {
    ......
    require('./menu')
    ......
}
</code></pre>
<h3 id="54-自定义顶部菜单">5.4 自定义顶部菜单</h3>
<p>我们可以自定义顶部菜单,通过以下两个步骤进行:</p>
<ul>
<li>先通过frame创建无边框窗口。</li>
</ul>
<pre><code class="language-js">function createWindow () {
    const mainWindow = new BrowserWindow({
        ......
        frame: false
    })    
}
</code></pre>
<ul>
<li>然后再通过前端页面布局设置顶部菜单</li>
</ul>
<p>如果想让顶部菜单支持拖拽,可以加如下css:</p>
<pre><code>-webkit-app-region: drag;
</code></pre>
<h3 id="55-在渲染进程中使用主进程方法remote和electron点击创建新窗口">5.5 在渲染进程中使用主进程方法remote和electron(点击创建新窗口)</h3>
<p>我们想要通过<strong>remote</strong>来使用主进程方法和功能。</p>
<ol>
<li>首先要安装<code>@electron/remote</code></li>
</ol>
<pre><code class="language-tsx">yarn add @electron/remote
</code></pre>
<ol start="2">
<li>在主进程main.js中配置remote:</li>
</ol>
<pre><code class="language-tsx">const remote = require("@electron/remote/main")
remote.initialize()

const createWindow = () => {
    let mainWindow = new BrowserWindow({
        ......
        webPreferences: { // 网页功能设置
        	......
            nodeIntegration: true, // 是否启用node集成 渲染进程的内容有访问node的能力,建议设置为true, 否则在render页面会提示node找不到的错误
            contextIsolation : false, //允许渲染进程使用Nodejs
        }
    })
    remote.enable(mainWindow.webContents)
}
</code></pre>
<ol start="3">
<li>在渲染进程中使用remote的BrowserWindow:App.tsx</li>
</ol>
<pre><code class="language-tsx">import React from 'react'
// 使用electron的功能
// const electron = window.require('electron')
// 使用remote
const { BrowserWindow } = window.require("@electron/remote")

export default function App() {
  const openNewWindow = () => {
    new BrowserWindow({
      width:500,
      height:500
    })
  }

  return (
    <div>
      App
      <div>
        <button onClick={openNewWindow}>点我开启新窗口</button>
      </div>
    </div>
  )
}
</code></pre>
<p>我们想要通过使用electron提供给渲染进程的API:</p>
<pre><code class="language-tsx">const electron = window.require('electron')
</code></pre>
<p>然后从electron中提取方法。</p>
<h3 id="56-点击打开浏览器">5.6 点击打开浏览器</h3>
<p>使用electron中的shell可以实现此功能:</p>
<pre><code class="language-tsx">import React from 'react'
// 使用electron的功能
// const electron = window.require('electron')
// 使用remote
// const { BrowserWindow } = window.require("@electron/remote")
// 使用shell
const { shell } = window.require('electron')

export default function App() {
  const openNewWindow = () => {
    shell.openExternal('https://www.baidu.com')
  }

  return (
    <div>
      App
      <div>
        <button onClick={openNewWindow}>点我开启新窗口打开百度</button>
      </div>
    </div>
  )
}
</code></pre>
<h2 id="6-打开对话框读取文件">6. 打开对话框读取文件</h2>
<h3 id="61-读取文件">6.1 读取文件</h3>
<p>主进程中的dialog模块可以显示用于打开和保存文件、警报等的本机系统对话框。</p>
<p>因为dialog模块属于主进程,如果我们在渲染进程中需要使用则需要使用remote模块。</p>
<p><strong>App.tsx</strong></p>
<pre><code class="language-tsx">import React,{ useRef } from 'react'
// 使用electron的功能
// const electron = window.require('electron')

// 使用remote
// const remote = window.require('@electron/remote')
// const { BrowserWindow } = window.require("@electron/remote")
const { dialog } = window.require("@electron/remote")

// 使用shell
const { shell } = window.require('electron')

// 使用fs
const fs = window.require('fs')

export default function App() {
  // ref 
  const textRef = useRef<HTMLTextAreaElement | null>(null)

  const openNewWindow = () => {
    shell.openExternal('https://www.baidu.com')
  }

  const openFile = () => {
    const res = dialog.showOpenDialogSync({
      title: '读取文件', // 对话框窗口的标题
      buttonLabel: "读取", // 按钮的自定义标签, 当为空时, 将使用默认标签。
      filters: [ // 用于规定用户可见或可选的特定类型范围
        //{ name: 'Images', extensions: ['jpg', 'png', 'gif', 'jpeg', 'webp'] },
        //{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
        { name: 'Custom File Type', extensions: ['js'] },
        { name: 'All Files', extensions: ['*'] },
      ]
    })
    const fileContent:string  = fs.readFileSync(res[0]).toString();
    (textRef.current as HTMLTextAreaElement).value = fileContent
  }

  return (
    <div>
      App Test
      <div>
        <button onClick={openNewWindow}>点我开启新窗口打开百度</button>
      </div>
      <div>
        <button onClick={openFile}>打开文件</button>
        <textarea ref={textRef}></textarea>
      </div>
    </div>
  )
}
</code></pre>
<h3 id="62-保存文件">6.2 保存文件</h3>
<p>保存文件需要使用dialog函数里的<code>showSaveDialogSync</code>,与之前的读取文件所用到的<code>showOpenDialogSync</code>类似:</p>
<pre><code class="language-tsx">const saveFile = () => {
    const res = dialog.showSaveDialogSync({
        title:'保存文件',
        buttonLable: "保存",
        filters: [
            { name: 'index', extensions: ['js']}
        ]
    })
    fs.writeFileSync(res, textRef.current?.value)
}
</code></pre>
<h2 id="7-定义快捷键">7. 定义快捷键</h2>
<h3 id="71-主线程定义">7.1 主线程定义</h3>
<p>引入<code>globalShortcut</code></p>
<pre><code class="language-js">const {app, BrowserWindow, nativeImage, globalShortcut } = require('electron')
</code></pre>
<p>注册快捷键打印字符串、窗口最大化、窗口最小化、关闭窗口。</p>
<pre><code class="language-js">const createWindow = () => {
    ......
    // 注册快捷键
    globalShortcut.register('CommandOrControl+X', () => {
        console.log('CommandOrControl + X is pressed')
    })

    globalShortcut.register('CommandOrControl+M', () => {
        mainWindow.maximize()
    })

    globalShortcut.register('CommandOrControl+T', () => {
        mainWindow.unmaximize()
    })

    globalShortcut.register('CommandOrControl+H', () => {
        mainWindow.close()
    })

    // 检查快捷键是否注册成功
    // console.log(globalShortcut.isRegistered('CommandOrControl+X'))
}

// 将要退出时的生命周期,注销快捷键
app.on('will-quit', () => {
    // 注销快捷键
    globalShortcut.unregister('CommandOrControl+X')
    // 注销所有快捷键
    globalShortcut.unregisterAll()
})
</code></pre>
<h3 id="72在渲染进程中定义">7.2在渲染进程中定义</h3>
<p>通过retmote来定义</p>
<pre><code class="language-tsx">const { globalShortcut } = window.require("@electron/remote")


globalShortcut.register("Ctrl+O", () => {
    console.log('ctrl+O is pressed.')
})
</code></pre>
<h2 id="8-主进程和渲染进程通讯">8. 主进程和渲染进程通讯</h2>
<p>在渲染进程使用ipcRenderer,主进程使用ipcMain,可以实现主进程和渲染进程的通讯:</p>
<p>App.tsx</p>
<pre><code class="language-tsx">......
import React,{ useState, useRef } from 'react'
const { shell, ipcRenderer } = window.require('electron')
export default function App() {
    // state
  	const [windowSize, setWindowSize] = useState('max-window')
    ......
    // 传参
    const maxWindow = () => {
        ipcRenderer.send('max-window', windowSize);
        if(windowSize === 'max-window') {
            setWindowSize('unmax-window')
        } else {
            setWindowSize('max-window')
        }
    }
        ......
        return (
            <div>
                    <div>
                        <button onClick={maxWindow}>与主进程进行通讯,窗口最大化或取消窗口最大化</button>
                    </div>
                </div>
            </div>
        )
}
</code></pre>
<p>main.js</p>
<pre><code class="language-js">const {app, BrowserWindow, nativeImage, globalShortcut, ipcMain } = require('electron')
const createWindow = () => {
    let mainWindow = ......
    ......
    // 定义通讯事件
    ipcMain.on('max-window', (event, arg) => {
        if(arg === 'max-window') {
            mainWindow.maximize()
        } else if (arg === 'unmax-window') {
            mainWindow.unmaximize()
        }
    })
    ......
}
......
</code></pre>
						  
					  </div>
						<!--conend-->
							<div class="p-2"></div>

						<div class="arcinfo my-3 fs-7 text-center">
							
							
			<a href='/t/etagid89-0.html' class='tagbtn' target='_blank'>react</a><a href='/t/etagid1916-0.html' class='tagbtn' target='_blank'>TypeScript</a><a href='/t/etagid2273-0.html' class='tagbtn' target='_blank'>electron</a><a href='/t/etagid1537-0.html' class='tagbtn' target='_blank'>NodeJS</a>							
						



						</div>
						
						<div class="p-2"></div>

						

						
					</div>
					<div class="p-2"></div>
					<!--xg-->
					<div class="lbox p-4 shadow-sm rounded-3">
						<div class="boxtitle"><h2 class="fs-4">相关</h2></div>
						
<hr>				
						
			<div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-566383.html">在虚拟机(centos)上安装或卸载nodejs</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-564770.html">react dom移动算法</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-563694.html">nodejs 一个简单的web服务器,用于前端自己快速测试部署</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-562681.html">React组件重构:嵌套+继承 与 高阶组件</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-562967.html">React中嵌套组件与被嵌套组件的通信</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-561843.html">AntDesign(React)学习-15 组件定义、connect、interface</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-556788.html">从源码解析Electron的安装为什么这么慢</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-556539.html">初学ReactJS,写了一个RadioButtonList组件</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-554177.html">快速搭建react项目骨架(按需加载、redux、axios、项目级目录等等)</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-554505.html">react传值名称使用value可能出现死循环</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-546475.html">初探Typescript</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div><div class="row g-0 py-2 border-bottom align-items-center">
																
								<div class="col-7 col-lg-11 border-lg-end">
										<h3 class="fs-6 mb-0 mb-lg-2"><a href="/a/1-543897.html">React Native bind函数</a></h3>
									
									<div class="ltag fs-8 d-none d-lg-block">
								 

        </div>
								</div>
							
							</div>            
            
            <!---->
                                    
           <!---->
  			
						

					</div>
					<!--xgend-->
				</div>

				<div class="col-lg-3 col-12 p-0 ps-lg-2">
					<!--box-->									
					<!--boxend-->
					<!--<div class="p-2"></div>-->

					<!--box-->
									<div class="lbox p-4 shadow-sm rounded-3">
					
									   <div class="boxtitle pb-2"><h2 class="fs-4"><a href="#">标签</a></h2></div>
										<div class="clearfix"></div>
										<ul class="m-0 p-0 fs-7 r-tag">
										</ul>
									

										
										<div class="clearfix"></div>
									</div>
					<!--box end-->

					
				</div>

			</div>
		
		
		
		</div>	

</main>
						<div class="p-2"></div>
<footer>
<div class="container-fluid p-0 bg-black">
	<div class="container p-0  fs-8">
	<p class="text-center m-0 py-2 text-white-50">一品网 <a class="text-white-50" href="https://beian.miit.gov.cn/" target="_blank">冀ICP备14022925号-6</a></p>
	</div>	
</div>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?6e3dd49b5f14d985cc4c6bdb9248f52b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
</footer>
		
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"></script>

</body>
</html>