1.在成都
在成都用Electron的公司很少,都是些写小程序或者H5,移动端的很多,然后加上后台。一般vue3和TS就足够用了,万万没想到我去了一家用Electron的公司。如果有说的不对的地方希望大佬多多指正
2.electron用来干啥的
Electron其实就是一个跨平台解决方案,和Flutter,react native一样,当然这两个我还不会哈。我希望有机会能学习下,还有three.主要使用来开发桌面程序的。可以结合vue和react来使用它,vue的组件页面就相当于是渲染进程。他的Demo学完很快,网上的教程很多,如果和我一样的朋友们,我建议对许多的功能自己多敲,多实现,用Demo先测试实现一次,再运用再稍微复杂的项目环境中,用了几次你会发现,你越用越快,越用越快。我还记得一位朋友的一句话,你的开发效率就是你摸鱼的时间,哈哈真的是你做的越快摸鱼的事件越多。
3.我遇到那些问题
讲我遇到的一些初级的问题和知识点,可能对大佬们来说都太简单了。可能我都懂得东西本身也就是皮毛。我觉得常用的就是嵌入网页,插件;还有electron如何结合vue;以及主进程和渲染进程之间的通信。当然还有很多小功能,托盘,创建新页面,拖拽,很多不一一赘述哈。
3-1网页和插件
我遇到了需要嵌入插件和网页的需求 采用的解决方案:
1. iframe
这个是很老的一个解决方案,不过也有很多边缘问题需要解决
2. webview插入网页
<webview
src="http://www.baidu.com"
rel="external nofollow"
plugins
style="
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex !important;
"
></webview>
plugin设置可以引入插件样,不过我自己还没实现
在background.js文件中打开
webPreferences: {
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
webviewTag: true,
webSecurity: false,
plugins: true,//这里
},
const { session } = require('electron');
const path = require('path');
const pluginPath = path.join(__dirname, '<插件扩展程序目录>');
session.defaultSession.loadExtension(pluginPath);
其中,<插件扩展程序目录> 是插件的扩展程序目录路径,通常是插件的根目录。
注意,由于 Chrome 插件和 Electron 应用程序使用的是不同的上下文环境,因此在插件和应用程序之间通信时需要使用 Electron 提供的 IPC(进程间通信)机制,例如使用 ipcRenderer 和 ipcMain 对象。
3 BrowserView
这一种给我自己还没实现,写上来供大家参考哈
const { BrowserView } = require('electron');
const view = new BrowserView();
mainWindow.setBrowserView(view);
view.setBounds({ x: 0, y: 50, width: 800, height: 600 });
view.setAutoResize({ width: true, height: true });
view.webContents.loadURL('chrome-extension://<插件 ID>/<插件内容页路径>');
其中,mainWindow 是 Electron 应用程序的主窗口对象;<插件 ID> 和 <插件内容页路径> 分别是要加载的 Chrome 插件的唯一标识符和内容页面路径。
这里推荐哈官网和W3c的属性和事件,标签讲解挺详细的:www.w3cschool.cn/electronman… www.electronjs.org/zh/docs/lat…
3-2electron结合vue
为了效率,我是直接拿模板过来改的,模板加UI框架,开发效率必备。
1 使用 vue cli 创建vue项目
npm install -g @vue/cli //全局安装
vue create electron-test //创建项目
2. 安装插件 vue-cli-plugin-electron-builder
vue add electron-builder
会多出来几个东西,非常重要,bacjground.js
我当时参考这个大佬的juejin.cn/post/703853…
多了个background.js文件。 package.json新增了几个scripts。
{
"electron:build": "vue-cli-service electron:build platform=win32",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps",
"b32": "vue-cli-service electron:build platform=win32",
"b": "vue-cli-service electron:build"
},
"main": "background.js",
3-3主进程和渲染进程之间的通信
这是今天没时间我还得忙着学哈浏览器插件,就简单记录一下我觉得重要的点
3-3-1需要声明ipcRener全局使用
就是在Vue原型上面挂载ipcRenderer
注意:他是主进程操作node,渲染进程操作DOM元素
3-3-2渲染进程的操作
//监听主进程发送过来的消息
ipcRenderer.on('gsh-active',(evevt,arg)=>{
console.log(event)
console.log(event)
}
//渲染进程发送消息给主进程
var btn =document.queryselector('#btn')
btn.onclick=function(){
ipcRenderer.send('openNewWindow','这里还可以携带数据')
}
//在渲染进程操作主进程使用node方法
const dialog = require("@electron/remote").dialog;
// Electron的内置方法,选择多个文件
const res = dialog.showOpenDialogSync({
title: "读取文件",
buttonLabel: '读取',
filters: [{
name: 'Custom File Type',
extensions: ['js']
}],
})
remote在主进程background.js中设置
// 解决remote使用它主进程问题
require("@electron/remote/main").initialize();
require("@electron/remote/main").enable(mainWindow.webContents);
3-3-3主进程的操作
//监听渲染进程发送过来的消息
ipcMain.on('openNewWindow',(evevt,arg)=>{
event.reply('gsh-active','这里是主进程的答复')
//还可以写业务逻辑,比如打开新窗口,还可以设置网页
}
//主进程可以主动发送消息,不过在创建过程最好用定时器延迟2秒
setTimeout(()=>{
mainWindow.webContents.send('gsh-active','创建窗口之后,延时三秒主进程主动发数据给渲染进程')
},3000)
未完待续。。。。。
4.我开发过程中遇到的问题
1.报错require未定义的错误
然后我把electron的9版本升到13,出现了报错require未定义的错误。
本来排查以为是node和electron版本不兼容的问题,因为electron13需要node16才支持。
结果不是,配置了一下主进程就可以了,但是治标不治本,遇到必须开contextIsolation的时候还是会报错, 希望谁解决了可以私信或者评论告诉我一下
webPreferences: {
nodeIntegration: true,
// 官网似乎说是默认false,但是这里必须设置contextIsolation
contextIsolation: false
}
2.需要chrome的版本60+
这里说一下需要注意node,electron和chrome内核版本需要对应
举个栗子:Chrome 112 对应的 Electron 版本是 18.0.0,对应的 Nodejs 版本是 16.1.0
这里有个可以查chrome对应版本的链接:juejin.cn/post/702637…
最后解决方案:我是electron的版本对了的,但是webview的chrome和electron的版本不一样,所以需要专门设置下 webview的useragent属性
<webview
src="https://web.whatsapp.com/"
useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
style="width: 100%; height: 100%; display: flex !important"
></webview>
wenview提供很多属性和方法,还有事件
useragent 修改请求头的UA,设置用户代理
preload 规定预加载脚本的url须如 `file:` 或者 `asar:`,因为它在是 guest page 中通过通过 `require` 命令加载的。
nodeintegration 并且拥有可以使用系统底层的资源,例如 `require` 和 `process`
src 填写链接地址
更多的话可以查询链接:www.w3cschool.cn/electronman…
3.我遇到个不断重启webview网页的问题
在 WebView 初始化时设置 CacheMode、JavaScriptEnabled、MixedContentMode 等选项(缓存模式,加载混合内容)。 示例代码:
skype.addEventListener("did-stop-loading", () => {
//注入js脚本
const skypes=skype.getWebContents()
// 设置 WebView 的缓存模式
skypes.session.setCacheMode('prefer-cache')
// 允许 WebView 加载混合内容
skypes.session.setCertificateVerifyProc((request, callback) => {
callback(0) // 返回 0 表示信任所有证书
})
})
5.常用命令行开关
5-1.chromium命令行开关
Electron 中的 chromium 内核支撑了窗口的创建和内容的渲染,而 chromium 内核对网页的渲染运行有很多默认设置,比如处于网页内容调用摄像头需要用户授权、音视频自动播放。
那么怎么知道自己什么时候需要去找这些命令行参数来辅助开发呢?
只要是窗口页面的功能不符合浏览器的设置或标准,都可以尝试去找相对应的 chromium 命令开关来解决实际问题。
这里引用大佬:链接:juejin.cn/post/685457…
详细齐全的命令行链接:shaozhuqing.com/?p=5326
//触摸屏上会出现点击范围扩大的问题,这个可以解决
app.commandLine.appendSwitch('disable-touch-adjustment', true); //chrome 66以上版本屏蔽自动播放限制设置,音视频随心放
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); //触摸屏上禁用双指缩放
app.commandLine.appendSwitch('disable-pinch', true); //关闭网络代理
app.commandLine.appendSwitch('no-proxy-server', true); //关闭浏览器的证书拦截
app.commandLine.appendSwitch('ignore-certificate-errors', true);
5-2. webPreferences命令的配置
nodeIntegration: true,//是否开启node
webviewTag: true, //允许webview
webSecurity: false,//允许跨域,上线时删除此配置
plugins: true, //允许插件
contextIsolation: false,//解决require报错问题,我也不知道为啥
5-3. 基本的一些小操作
1.设置快捷键
const {
app,
BrowserWindow,
globalShortcut,
ipcMain
} = require('electron')
globalShortcut.register('CommandOrControl+M',()=>{
mainWindow.maximize()
})
globalShortcut.register('CommandOrControl+T',()=>{
mainWindow.unmaximize()
})
globalShortcut.register('CommandOrControl+H',()=>{
mainWindow.close()
})
2. 设置热加载
// 热加载
const reloader=require('electron-reloader')
reloader(module)
//在on(ready)生命周期内部
// 自动打开开发者调试工具
mainWindow.webContents.openDevTools()
3.两种加载页面html区别
//用于加载文件
mainWindow.loadFile('./src/index.html')
//用于加载html,也可以加载外部网页
mainWindow.loadURL(startPage);
4.preload预加载脚本,沙盒化进程
沙盒模式就是渲染进程没有办法操作node,就像普通浏览器一样,相当于把每个渲染进程当成一个独立的空间
5.remote渲染进程和主进程可以相互访问对象和方法
Remote 模块允许主进程和渲染进程之间相互访问对象和方法。
//主进程中初始化
import * as remoteMain from '@electron/remote/main';
remoteMain.initialize();
//渲染进程中
const BrowserWindow = require("@electron/remote").BrowserWindow;
//通过remote拿到
const dialog = require("@electron/remote").dialog;
const globalShortcut = require("@electron/remote").globalShortcut;
// Electron的内置方法,选择多个文件
//使用主进程dialog方法
const res = dialog.showOpenDialogSync({
title: "读取文件",
buttonLabel: '读取',
filters: [{
name: 'Custom File Type',
extensions: ['js']
}],
})
//使用主进程globalShortcut方法
globalShortcut.register('Ctrl+A', () => {
console.log('ctrl+o')
})
6.操作webview里面嵌入的网页
有两种方法:webview有注入js和css的方法,还可以使用预加载
//开始加载事件监听
webview.addEventListener("did-start-loading", ()=> {
console.log("did-start-loading...");
})
//停止加载事件监听
webview.addEventListener("did-stop-loading", ()=> {
console.log("did-stop-loading...");
注入css
webview.insertCSS(`
.title-blog {
background: red !important;
}
`)
注入js脚本
webview.executeJavaScript(`
setTimeout(()=>{
alert("内容是:"+document.querySelector('._21AquerySelectorhp').innerHTML);
}, 2000);
`)
/这里app的id能拿到标签,说明是能够操作嵌入的
打开调试工具
webview.openDevTools();
})
7.设置桌面置顶的功能
// 开启桌面文件置顶功能
win.setAlwaysOnTop(true);
8.使桌面可以自定义拖拽大小
// 创建初始窗口
win = new BrowserWindow({
width: 500,
height: 600,
webPreferences: {
nodeIntegration: true,//是否开启node
webviewTag: true, //允许webview
webSecurity: false,//允许跨域,上线时删除此配置
plugins: true, //允许插件
resizable: true,//可以拖拽窗口大小
},
});
win.setResizable(true);//设置可以拖拽窗口大小
9.可以打开嵌入webviewd的对应网页的控制台,可以看到网页结构,控制台
还是位置和数据模式
生命周期mounted中
const skype = this.$refs.skype;//拿到对应网页的webview,通过ref
skype.addEventListener("dom-ready", () => {
skype.openDevTools({
mode: "detach",
});
});
//监听页面加载完毕,打开配置可以打开嵌入的网页控制台,默认关闭了
10.监听webview嵌入网页的变化,mutationObserver监听页面变化
1.这个是js的原生方法,使用就是监听谁,开启什么监听,把这个两个参数传过去就行了
const chatList = document.querySelector(".css-1dbjc4n .r-13awgt0"); // 获取聊天消息列表
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.type === "childList") {
console.log(123,'内容变化来了');
//alert('内容是'+chatList.innerHTML)
alert(mutation.target.textContent)
//mutation.target可以拿到变动的节点,textContent可以拿到变动节点的内容
}
}
});
observer.observe(chatList, { childList: true ,subtree:true});//这里开启所有的后代DOM节点监听subtree,childList会自动变成监听子孙节点
未完待续,欢迎私信我交流,共同进步