React19+Electron聊天室|electron41跨平台仿微信EXE客户端|react聊天程序

举报
yxybox 发表于 2026/08/17 11:54:35 2026/08/17
【摘要】 最新款electron41结合react 19+arco-design构建高性能跨平台应用的聊天系统前端解决方案。

历经两周潜心研发,最新原创基于vite8+react19+electron41.1+zustand+arco-design纯手搓仿微信/QQ界面桌面聊天客户端Exe项目,正式完结了。


未标题-3.png

p6.gif


项目使用技术

  • 编辑器:vscode
  • 跨平台技术框架:electron41.1.0
  • 前端框架:react^19.2.8+react-router-dom^7.18.2
  • 组件库:@arco-design/web-react^2.66.16
  • 状态管理:zustand^5.0.14
  • 打包管理:electron-builder^26.15.3
  • 前端构建工具:vite^8.2.0
  • 样式处理:sass^1.102.0
  • electron嵌入react插件:vite-plugin-electron^1.1.1


未标题-1.png

p3.gif

p2.gif


项目目录框架

使用最新react 19整合electron 41搭建项目框架页面。


360截图20260814230626520.png


001360截图20260815002223109.png

p1.gif


项目页面布局


360截图20260814230626523.png


<div className='ra__container flex-c'>
  <div className='ra__layout flexbox flex-col'>
    <div className='ra__layout-body flex1 flexbox'>
      {/* 菜单栏 */}
      <Menubar />

      {/* 侧边栏 */}
      <Sidebar />
      
      {/* 主内容区 */}
      <div className='ra__layout-main flex1 flexbox flex-col'>
        <Toolbar />
        { Lazyload(<Outlet />) }
      </div>
    </div>
  </div>
</div>


360截图20260814231223045.png

360截图20260814231324782.png

002360截图20260815002434144.png

002360截图20260815002520302.png

360截图20260814232312430.png

360截图20260814232903568.png

360截图20260814233912243.png


前端入口配置main.jsx

import '@arco-design/web-react/es/_util/react-19-adapter'
import { createRoot } from 'react-dom/client'
import './style.scss'
import App from './App.jsx'
import '@arco-design/web-react/dist/css/arco.css'

import { launchApp } from '@/windows/actions'

launchApp().then(config => {
  if(config) {
    // 存储窗口配置
    window.config = config
  }

  createRoot(document.getElementById('root')).render(
    <App />
  )
})

Electron进程配置/预加载文件

/**
 * electron主进程配置
 * @author andy
 */

import { app, BrowserWindow } from 'electron'

import { WindowManager } from '../src/windows/index.js'

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true

const createWindow = () => {
  let win = new WindowManager()
  win.create({isMajor: true})
  // 系统托盘管理
  win.trayManager()
  // 监听ipcMain事件
  win.ipcManager()
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if(BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', () => {
  if(process.platform !== 'darwin') app.quit()
})


未标题-1.png

/**
 * electron预加载文件
 * @author andy
 */

const { contextBridge, ipcRenderer } = require('electron')
// MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
ipcRenderer.setMaxListeners(0)

contextBridge.exposeInMainWorld(
  'electron',
  {
    // 通过 channel 向主进程发送异步消息。主进程使用 ipcMain.on() 监听 channel
    send: (channel, args) => {
      ipcRenderer.send(channel, args)
    },

    // 通过 channel 向主进程发送消息,并异步等待结果。主进程应该使用 ipcMain.handle() 监听 channel
    invoke: (channel, args) => {
      return new Promise(resolve => ipcRenderer.invoke(channel, args).then(data => resolve(data)).catch(e => console.log(e)))
    },

    // 监听 channel 事件
    on: (channel, func) => {
      console.log('receive event')
      ipcRenderer.on(channel, (event, ...args) => func(event, ...args))
    },

    // 一次性监听事件
    once: (channel, func) => {
      ipcRenderer.once(channel, (event, ...args) => func(event, ...args))
    },

    // 浏览器打开链接
    openExternal: (url) => ipcRenderer.send('open-external', url)
  }
)


002360截图20260815002613273.png

003360截图20260815003139958.png

003360截图20260815003336410.png

004360截图20260815003458278.png

004360截图20260815003645188.png

005360截图20260815003919701.png

005360截图20260815004006135.png

005360截图20260815004148906.png

005360截图20260815004216566.png

005360截图20260815004254113.png

006360截图20260815004348692.png

007360截图20260815004537970.png

007360截图20260815004735861.png

360截图20260814235338481.png

360截图20260815000329335.png

360截图20260815001116383.png

008360截图20260815094608269.png

008360截图20260815094817124.png

008360截图20260815094850662.png

008360截图20260815094913551.png

008360截图20260815095406352.png

008360截图20260815100349569.png

008360截图20260815100501589.png

008360截图20260815100604628.png

008360截图20260815100649220.png

008360截图20260815100939035.png

009360截图20260815101030806.png

009360截图20260815101917800.png

010360截图20260815101933424.png


Electron/React自定义无边框窗口拖拽/系统最小化/最大化/关闭

function Winbtns(props) {
  const {
    color,
    // 窗口是否可以最小化
    minimizable = true,
    // 窗口是否可以最大化
    maximizable = true,
    // 窗口是否可关闭
    closable = true,
    // 设置层叠
    zIndex = 2026,
    style,
    ...rest
  } = props

  const appState = appStore()

  const [hasMaximized, setHasMaximized] = useState(false)
  const [isResizable, setIsResizable] = useState(true)
  const [isMaximizable, setIsMaximizable] = useState(true)

  useEffect(() => {
    // 用户是否可以手动调整窗口大小
    window.electron.invoke('win-isResizable').then(res => {
      setIsResizable(res)
    })

    // 窗口是否可以最大化
    window.electron.invoke('win-isMaximizable').then(res => {
      setIsMaximizable(res)
    })

    // 初始窗口是否最大化
    window.electron.invoke('win-isMaximized').then(res => {
      setHasMaximized(res)
    })

    // 实时监听窗口是否最大化
    window.electron.on('win-maximized', (e, data) => {
      setHasMaximized(data)
    })
  }, [])

  // 最小化
  const handleWinMin = () => {
    window.electron.invoke('win-min')
  }
  // 最大化/还原
  const handleWinMax2Min = () => {
    window.electron.invoke('win-toggle').then(res => {
      setHasMaximized(res)
    })
  }
  // 关闭
  const handleWinClose = () => {
    if(window.config.isMajor) {
      const modal = Modal.confirm({
        icon: null,
        content: <div style={{fontSize: 15}}>是否最小化到托盘,不退出程序?</div>,
        focusLock: false,
        style: {width: 275, borderRadius: 12, padding: '10px 25px 20px'},
        footer: <div style={{textAlign: 'right'}}>
          <Button type='text' shape='round' size='small' onClick={() => {
            modal.close()
            setTimeout(() => {
              winSet('hide', window.config.id)
            }, 150)
          }}>最小化至托盘</Button>
          <Button type='text' status='danger' shape='round' size='small' onClick={() => {
            appState.logout()
            winSet('close')
          }}>退出</Button>
        </div>
      })
    } else {
      winSet('close', window.config.id)
    }
  }

  return (
    <>
      <div {...rest} className="ra__winbtns flexbox flex-alignc" style={{ zIndex: zIndex, ...style }}>
        <div className="ra__winbtns-actions flexbox flex-alignc" style={{ color: color }}>
          {isTrue(minimizable) && <a className="min" title="最小化" onClick={handleWinMin}><i className="elec-icon elec-icon-min"></i></a>}
          {(isTrue(maximizable) && isResizable && isMaximizable) &&
            <a className="max" title={hasMaximized ? '向下还原' : '最大化'} onClick={handleWinMax2Min}>
              {hasMaximized ? <i className="wicon elec-icon elec-icon-restore"></i> : <i className="elec-icon elec-icon-max"></i>}
            </a>
          }
          {isTrue(closable) && <a className="close" title="关闭" onClick={handleWinClose}><i className="elec-icon elec-icon-quit"></i></a>}
        </div>
      </div>
    </>
  )
}

Electron自定义托盘消息/图标闪烁


p7.gif

011360截图20260815102032020.png

011360截图20260815102417293.png

/** 
 * 系统托盘图标
 */
trayManager() {
  if(this.tray) return
  const trayMenu = Menu.buildFromTemplate([
    {
      label: '打开主界面',
      icon: join(__root, 'resources/tray-win.png'),
      click: () => {
        this.winMain.restore()
        this.winMain.show()
      }
    },
    {
      label: '设置',
      icon: join(__root, 'resources/tray-setting.png'),
      click: () => this.sendByMainWin('win-ipcdata', {type: 'WINIPC_SETTINGWIN', value: null})
    },
    {
      label: '锁定系统',
      click: () => null,
    },
    {
      label: '关闭托盘闪烁',
      click: () => this.trayFlash(false)
    },
    {
      label: '关于',
      click: () => this.sendByMainWin('win-ipcdata', {type: 'WINIPC_ABOUTWIN', value: null})
    },
    {
      label: '退出聊天室',
      icon: join(__root, 'resources/tray-exit.png'),
      click: () => {
        dialog.showMessageBox(this.winMain, {
          title: '提示',
          message: '确定要退出聊天程序吗?',
          buttons: ['取消', '最小化托盘', '确认退出'],
          type: 'error',
          noLink: false,
          cancelId: 0,
        }).then(res => {
          // console.log(res)
          const index = res.response
          if(index == 0) {
            console.log('用户取消操作')
          }else if(index == 1) {
            console.log('最小化到托盘')
            this.winMain.hide()
          }else if(index == 2) {
            console.log('退出程序')
            this.sendByMainWin('win-ipcdata', {type: 'WINIPC_LOGOUT', value: null})
            app.quit()
          }
        })
      }
    }
  ])
  this.tray = new Tray(this.trayIcon)
  this.tray.setContextMenu(trayMenu)
  this.tray.setToolTip(app.name)
  this.tray.on('double-click', () => {
    console.log('tray double clicked!')
    this.winMain.restore()
    this.winMain.show()
  })
  this.tray.on('mouse-enter', (event, position) => {
    // console.log('鼠标划入', position)
    if(!this.hasFlash) return
    this.sendByMainWin('win-ipcdata', {type: 'WINIPC_MESSAGEWIN', value: position})
    // 简略消息通知
    /* this.tray.displayBalloon({
      iconType: 'none',
      title: 'Electron38研发组',
      content: 'Electron38+Vite7仿微信客户端聊天。'
    }) */
  })
  this.tray.on('mouse-leave', (event, position) => {
    // console.log('鼠标离开')
  })
}

好了,综上就是electron41结合react 19构建桌面电脑版聊天应用的一些实战分享。

bilibili-mall.png

扫一扫,发现更多实战进阶项目

Electron38.2实战客户端OS系统|vite7+vue3+electron仿mac/wins桌面

2025最新款Electron38+Vite7+Vue3+ElementPlus桌面客户端后台管理系统Exe

最新研发vite7.0+electron38客户端仿微信EXE聊天软件

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。