1. 概述
本文介绍Electron项目开发的一些经验和坑,主要是基于Electron的IM项目。
2. 开发基础
2.1. Vue和React脚手架
vue-cli-plugin-electron-builder good
https://github.com/nklayman/vue-cli-plugin-electron-builderelectron-vite-react-starter good
https://github.com/Harhao/electron-vite-react-starter
2.2. Electron Store
- config with encryptionKey
https://github.com/sindresorhus/electron-store
2.3. Electron多窗口 + 托盘 + 截图
https://www.cnblogs.com/xiaoyan2017/p/14403820.html
https://github.com/nashaofu/dingtalk
https://www.cnblogs.com/xiaoyan2017/p/14454624.html
https://www.cnblogs.com/xiaoyan2017
2.4. Default Path
自动升级的安装包下载路径
C:\Users\Administrator\AppData\Local\foxchat-updaterelectron-log的默认路径
C:\Users\Administrator\AppData\Roaming\FoxChat\logs
3. Electron samples
3.1. fsharechat
商业项目,基于Vue的UI,看着不错
还有webrtc功能,做音视频时可以参考
https://github.com/fsharechat/vue-chat
https://github.com/han960619/Vue-chat
https://han960619.github.io/Vue-chat/#/chat
登录功能
https://github.com/fsharechat/vue-chat/blob/master/src/page/login/login.vue侧边栏sidebar
https://github.com/fsharechat/vue-chat/blob/master/src/components/mycard/mycard.vue会话列表
https://github.com/fsharechat/vue-chat/blob/master/src/components/chatlist/chatlist.vue聊天窗口,包含单聊、群聊、输入框
https://github.com/fsharechat/vue-chat/blob/master/src/components/message/message.vue
https://github.com/fsharechat/vue-chat/blob/master/src/components/text/text.vue好友页面
https://github.com/fsharechat/vue-chat/blob/master/src/page/friend/friend.vue登录页面
https://github.com/fsharechat/vue-chat/blob/master/src/page/login/login.vue
3.2. aermin
react的im,不错的项目,可以重点学习,有正在运行的实例
没有用第三方组件库,而是react原生的组件
https://github.com/aermin/ghChat
https://im.aermin.top
单聊框
https://github.com/aermin/ghChat/blob/7be727183a/src/components/PrivateChat/index.js群聊框
https://github.com/aermin/ghChat/blob/7be727183a/src/components/GroupChat/index.js
3.3. chat-react
可以跑起来,前端react,后端go,数据库用mysql
UI一般,没有太多参考的价值
后端用Go写的,有空可以学习下
go run .
yarn start
https://gitee.com/ltly/chat-react
https://gitee.com/ltly/chat-online
4. Feature
开发IM客户端时,实现Feature过程的思考
4.1. emoji feature
emoji-mart
https://github.com/missive/emoji-mart
优点:表情很丰富,功能也很全,支持自定义emoji
缺点:
- 单个emoji的展示,需要自己完成
- 加载很多emoji时候,很卡,尤其是第一次
- emoji不会动,不美观
third party: rich editor
https://ant.design/docs/react/recommendation
https://github.com/zenoamaro/react-quill
https://github.com/margox/braft-editor
功能都挺全,用在IM里面有点重(不用支持表情和文本即可,不用加粗、斜体、字号等),所以暂且舍弃
react-contenteditable
https://github.com/lovasoa/react-contenteditable
https://codesandbox.io/s/l91xvkox9l
符合IM当前需求,可以自定义支持的tag,很赞~
渲染前,通知sanitize-html
消一下毒
https://github.com/apostrophecms/sanitize-html
渲染时候,通过dangerouslySetInnerHTML
渲染即可
https://www.pluralsight.com/guides/how-to-render-html-in-state-string
4.2. catch chat input focus
方法1,removeAllRanges之后,lastRange又回到了开始位置0,怀疑是引用
导致,在失去焦点时候,改成document.getSelection().getRangeAt(0).cloneRange()
也不行
if (this.state.lastRange) {
selection.removeAllRanges()
selection.addRange(this.state.lastRange)
}
但是,下面这个例子是work的。很奇怪,有时间再深入研究
https://segmentfault.com/a/1190000005869372
方法2,步骤和优缺点如下
- 失去焦点onBlur时候,记录range.endOffset
const lr = document.getSelection().getRangeAt(0)
lastRangeEndOffset = lr.endOffset // number
- 获取到焦点时候,设置光标位置
const obj = document.getElementById("inputbox")
selection?.collapse(obj, lastRangeEndOffset)
优缺点
优点:记录input失去焦点是光标位置,下次获取焦点,能够成功插入,在两个场景上体验较好:input没有获取焦点时,点击表情面板并输入表情;input没有获取焦点时,键盘输入可见字符时。
缺点:当text和emoji混合时,记录的光标位置,可能是基于Text的,也可能是基于整个nodeList的,所以,恢复焦点时,设置光标位置,就不能简单的如上操作。会导致光标位置不正确,或者js报错(当lastRangeEndOffset > nodeList.length时)
方法3,input非焦点时,点击输入emoji或者键盘输入可见字符时,直接插入到最后即可
var selection = document.getSelection()
const obj = document.getElementById("inputbox")
selection?.collapse(obj, obj.childNodes.length)
飞书的做法:只支持emoji输入到既定光标
上。当键盘输入字符时,默认插入到最后,而且当输入中文时,第一个字母会丢失(原因是用于获取focus,上述3个方法同样存在)。
综合对比,采用方法3,逻辑清晰,体验还好。
5. 坑
5.1. change BrowserRouter to HashRouter
React assets图片,在build之后,经过react-route之后,会报错404,解决办法:
- import
import { Image } from 'antd';
import imgUrl from '../../assets/images/coding.png'
- change BrowserRouter to HashRouter
https://github.com/csepulv/electron-with-create-react-app/issues/19#issuecomment-498101077
5.2. ipcMain.handle
同一个channel只能被ipcMain.handle只能注册一次,第二次注册会被block住(莫名其妙,还不错报个错),如果想第二次注册成功,需要先调用ipcMain.removeHandler
https://www.electronjs.org/docs/api/ipc-main#ipcmainremovehandlerchannel
5.3. auto-updater取错本地版本号
electron-updater自身的bug,会去取Electron的版本
解决方案: 修改electron-updater中appUpdater.js中isUpdateAvailable函数代码
const pkg=require('../../../package.json');
const isLatestVersionNewer = (0, _semver().gt)(latestVersion, pkg.version);
5.4. Proxy
Linux的proxy,只支持10.0.0.0/8,不支持10.*
Windows下,通过cygwin64,也应该如下配置no_proxy,否则通过yarn electron-builder --publish always
,也会发布失败。例如:
$ export no_proxy=localhost,10.0.0.0/8,127.0.0.1,::1