【前言】此文档是本人在实际开发中所遇到的问题,且已经解决的,因为技术有限,可能有很多地方,很多代码可能都还存在漏洞,如有疑问或者有更好的解决方式,以及有代码可以优化的地方,欢迎留言,多谢大神指点。
ElectronBuild开发问题浅解
开机自启
之所以将这一项放在最前面,是因为这一点很重要,当需要主进程执行
app.setLoginItemSettings
开机自启操作时,必须要将vue.config.js
中的"requestedExecutionLevel": "highestAvailable",
清除,否则将会导致app.setLoginItemSettings
失效
至于为什么要在主进程的监听中加入
event.sender.send('setting-auto-start', true)
看似在重复发送通信,是因为在Electron API文档中有提到,因此个人认为加上也无妨
/** 设置开机自启 **/
ipcMain.on('setting-auto-start', async (event, flag) => {
if (flag) {
app.setLoginItemSettings({
openAtLogin: true,
openAsHidden: true,
path: process.execPath,
args: [
'--hideWindow', '"true"'
]
})
event.sender.send('setting-auto-start', true)
} else {
app.setLoginItemSettings({
openAtLogin: false,
openAsHidden: false,
path: process.execPath,
args: []
})
event.sender.send('setting-auto-start', false)
}
})
有关Webview
在使用
webview
的preload时,需要注意的一点是preload需要传入的路径是一个协议路径,比如file://
、http://
等,而由于这里的path.join
获取的是编译后的路径,所以需要与当前环境区分开来,当然也可以将webview
封装成一个公共的组件,使用传值的方式加载相对应的src
和preload
<template>
<webview class="webview" :src="weburl" :preload="filepath" :title="title" nodeintegrationinsubframes disablewebsecurity></webview>
</template>
<script>
export default {
props: ['weburl', 'filepath', 'title']
}
</script>
if (process.env.NODE_ENV == 'production') {
this.info.preloadUrl = path.join(__dirname, 'preload.js')
} else {
this.info.preloadUrl = require('path').resolve('./src/utils/electron/preload.js')
}
在开发过程中,我们最有可能遇到的就是要对
webview
访客界面中的事件、元素属性等进行操作。此时,我们可以通过使用webview.executeJavaScript()
函数将JS注入到访客页面中,当然这些也仅仅只是针对于一些公共的处理方法,如果有极个别的访客界面不需要这些公共的操作,那就需要进行特殊处理
比如这里需要对访客界面的所有超链接标签进行监听,点击超链接在客户端中新建一个tab打开标签中的链接,而其中也针对访客页面中的iframe界面进行处理:
const webview = document.querySelector('.webview')
// 在加载中添加监听
webview.addEventListener("did-start-loading", () => {
webview.openDevTools()
webview.executeJavaScript(`
setTimeout(() => {
if (document) {
let nodeList = []
let type = 'default'
let tabContent = document.querySelectorAll('.el-tab-pane')
if (tabContent && tabContent.length) {
tabContent.forEach(item => {
if (item.firstChild.contentWindow) {
type = 'tdd'
nodeList = item.firstChild.contentWindow.document.querySelectorAll('body a')
}
})
} else {
nodeList = document.querySelectorAll('body a')
}
nodeList.length && nodeList.forEach(item => {
let attr_type = item.getAttribute('target')
if (attr_type != '_blank') return false
item.addEventListener('click', (event) => {
let info = {
title: type == 'tdd' ? '涂多多商城-' + event.srcElement.innerText : event.srcElement.innerText,
src: event.srcElement.href || event.srcElement.parentNode.href,
type: 'webview'
}
window.electron.send('ibi_href_click', info)
}, false)
})
}
}, 1500)
`)
})
有关webview用户登录
- 由于
webview
的监听事件都是在加载完访客链接地址后才能够执行,而在此之前我们需要将客户端登录的token
设置在访客页面的Cookie中,因此不能再使用webview.executeJavaScript()
的方式注入JS,这时,我们所引入的preload.js
就起来作用 - 因为每个
webview
都可以理解为每个单独的浏览器,相互之间的Cookie等数据都不能共同享用,因此为了能够达到所有的访客加载之前都能获取到客户端的token
,我个人这里是使用延迟加载的方式,将token
设置到访客额Cookie中,从而使访客可以在第一时间拿到token
进行登录后的操作
const electronStore = require('electron-store')
const electronCookie = new electronStore()
setTimeout(() => {
if (electronCookie.get('userInfo')) {
if (electronCookie.get('userInfo').access_token && document) {
document.cookie = 'token=' + electronCookie.get('userInfo').access_token
}
}
})
webview中调用ipcRenderer
当访客端需要向客户端发送通信指令时,可以在预加载文件(preload.js)中将
electron
挂载到window上,从而使访客端可以直接通过window操作ipcRenderer
向客户端发送通信,同时也可以在preload
中设置相应的方法,以供访客端可以通过调用获取到客户端的数据信息
- 客户端设置
preload
console.log('preload引入成功') const {ipcRenderer, contextBridge} = require('electron') const electronStore = require('electron-store') const electronCookie = new electronStore() setTimeout(() => { if (electronCookie.get('userInfo')) { if (electronCookie.get('userInfo').access_token && document) { document.cookie = 'token=' + electronCookie.get('userInfo').access_token } } }) contextBridge.exposeInMainWorld( "electron", { ipcRenderer: { send: (channel, data) => { ipcRenderer.send(channel, data); }, }, send: (channel, data) => { ipcRenderer.send(channel, data); }, on: (channel, callback) => { const newCallback = (_, data) => callback(_, data); ipcRenderer.on(channel, newCallback); }, once: (channel, callback) => { const newCallback = (_, data) => callback(_, data); ipcRenderer.once(channel, newCallback); }, // 获取用户 token getToken: () => { return electronCookie.get('userInfo').access_token }, // 设置用户 token setUserToken: () => { document.cookie = 'token=' + electronCookie.get('userInfo').access_token } } );
- 访客端调用
preload
if (window.electron) { // 发送通信 window.electron.send('check-login') window.electron.ipcRenderer.send('check-login') // 接收通信 window.electron.on('check-login') // 获取用户信息 const token = window.electron.getToken() }
封装进程监听
在实际开发中,我们可能会经常操作主进程和渲染进程之间的相互通信,而通信越多写的代码就会越多,如果不对这些监听进行分类和封装,久而久之会导致项目维护起来很痛苦,每次都要先去找到监听指令在哪,找到以后可能也并不知道这个指令是在操作下载还是用户退出,或者是链接跳转,因此,这里我个人是通过类的方式,将不同类型的通信处理区分开,这样就会一目了然,看网上有很多大佬都是通过js导入的方式,都是可以的
-
主进程封装
const { app, ipcMain } = require('electron') exports.initCommon = function (win) { /** 设置开机自启 **/ ipcMain.on('setting-auto-start', async (event, flag) => { if (flag) { app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true, path: process.execPath, args: [ '--hideWindow', '"true"' ] }) event.sender.send('setting-auto-start', true) } else { app.setLoginItemSettings({ openAtLogin: false, openAsHidden: false, path: process.execPath, args: [] }) event.sender.send('setting-auto-start', false) } }) }
-
渲染进程
import { ipcRenderer } from 'electron' import $public from '../public' // 封装的公共函数 export default class CheckLogin { constructor ($bus) { this.about($bus) // 使用 $bus 在页面中进行数据接收 } about ($bus) { ipcRenderer.on('check-login', () => {}) } }
<script> import OtherRender from './utils/renders/otherRender' export default { name: 'app', mounted() { // 其他渲染进程通信 new OtherRender(this.$bus) // 下载渲染进程通信 new DownloadRender(this.$bus) } } </script>
项目中使用 PubSub
如果在项目中使用
PubSub
你就会发现这里面有一个坑,就是当触发PubSub.publish
发布消息时,实际上PubSub.subscribe
可能会被多次订阅监听,因此,遇到这种问题是,应该先取消订阅
// 发布消息
PubSub.publish('check-login')
// 订阅消息
PubSub.unsubscribe('check-login')
PubSub.subscribe('check-login', () => {
...
})
防止多任务
正常情况下,Electron允许打包后使用快捷方式开启多任务,而我们的需求是只能开启一个客户端端,并且不影响托盘,可以使用一下参考:
if (win) {
const isAppInstance = app.requestSingleInstanceLock()
// 当检测到开机多任务时,关闭当前进程
if (!isAppInstance) {
app.quit()
} else {
app.on('second-instance', (event, argv, workingDirectory, additionalData, ackCallback) => {
if (win) {
if (win.isMinimized()) win.restore()
win.focus()
win.show()
}
})
}
setTimeout(() => {
// 托盘设置,因为只需要在Windows系统上设置,所以需要排除mac系统
if (!$platform.is_mac()) {
let iconImage;
const iconUrl = process.env.NODE_ENV === "development" ? path.join(__dirname, '..', "./src/assets/favicon.ico") : path.join(__dirname, "favicon.ico")
appTray = new Tray(nativeImage.createFromPath(iconUrl))
appTray.setToolTip(process.env.VUE_APP_NAME)
// 单击托盘展示窗口,仅针对Windows系统
appTray.on('click', () => {
win.isVisible() ? win.hide() : win.show()
})
const contextMenu = Menu.buildFromTemplate([
{
label: '打开窗口',
click: () => {
win.show()
},
},
{
label: '退出',
click: () => {
process.platform === 'darwin' ? app.quit() : win.destroy()
},
},
])
// 设置上下文菜单
appTray.setContextMenu(contextMenu)
}
}, 500)
}
ElectronBuild打包问题浅解
修改文件入口
一般情况下
electron-build
的打包入口默认为background.js
,但如果有需求要修改入口文件的话需要在vue.config.js
中修改pluginOptions.electronBuilder.mainProcessFile
的配置
注意: 设置自定义入口文件以后,
package.json
中的入口文件不需要改变,还是设置background.js
作为入口就好,否则在打包时程序会报错找不到background.js
,原因是因为vue-cli-plugin-electron-builder
在打包时,默认从package.json
中寻找入口文件,而vue-cli-plugin-electron-builder
的默认入口文件还是background.js
,具体可以到node_modules
中的vue-cli-plugin-electron-builder
查看源码
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
mainProcessFile: 'src/index.js' // 设置自定义入口文件
}
}
}
}
{
"name": "demo",
"version": "1.0.0",
"private": true,
"description": "This is Desccript",
"main": "background.js",
"author": "Me",
"scripts": {
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
}
}
修改输出文件
默认情况下
electron-build
打包好的文件都会生成在dist_electron
中,而在实际开发中,我们也可以通过pluginOptions.electronBuilder.builderOptions.outputDir
来自定义打包文件,就像Vue-CLI
的outputDir
那样
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
"directories": {
"output": "dist" // 设置自定义输出文件
}
}
}
}
}
图标设置
在打包项目是通常会遇到,明明图标是设置了的,打包后却没有生效,或者打包报错、失败,是因为在打包时,Windows要求打包的
ico
文件的尺寸需要在256*256
,同时ico
文件不能直接有png
等图标格式文件修改后缀名得出,而是需要通过ico
处理的网站得出
打包生成安装文件
众所周知,我们使用
electron
或者electron-bhild
最终目的就是要将项目打包成各个系统可以安装,并使用的安装文件,而这些也可以在pluginOptions.electronBuilder.builderOptions
中来客制化
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
enableRemoteModule: true,
removeElectronJunk: false,
mainProcessFile: 'src/index.js',
builderOptions: {
"appId": "com.example.app",
"productName": process.env.VUE_APP_NAME, // 项目名,也是生成的安装文件名,即 aDemo.exe
"copyright": "Copyright © 2022", // 版权信息
"directories": {
"output": "dist" // 输出文件路径
},
"win": { // win相关配置
"icon": "./public/favicon.ico", // 图标,当前图标在根目录下,注意这里有两个坑
"target": [
{
"target": "nsis", // 利用nsis制作安装程序
"arch": [
"ia32", // 32位
"x64", // 64位
]
}
]
},
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "./src/assets/images/icon.icns"
},
"linux": {
"icon": "./src/assets/images/icon.icns"
}
}
}
}
}
原文地址:https://blog.csdn.net/snail767/article/details/124711326
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_8501.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!