webview使用

webview开启

BrowserWindow实例化时,在webPreferences配置中配置webviewTag:true

webview初始化

webview标签使用时,应使用默认src属性为about:blank这样不会使webview因为没有初始化造成报错。随后可以根据src属性动态修改指向。

webview预加载

我们可以通过预加载preload的方式,通过注入js文件到webview中,从而达到干预内部网页行为的目的

A String that specifies a script that will be loaded before other scripts run in the guest page. 该脚本的URL的协议必须是 file: asar:二者之一,因为在访客页中,它是通过“内部”的 require 去加载的 当访客页没有 node integration ,这个脚本仍然有能力去访问所有的 Node APIs, 但是当这个脚本执行执行完成之后,通过Node 注入的全局对象(global objects)将会被删除。 Note: This option will appear as preloadURL (not preload) in the webPreferences specified to the will-attach-webview event.

这里我们通过配置来进行预加载,这里有四点需要注意

  1. maclinux系统需要加上file://这样的protocol
  2. 在配置中我们需要改变的字段是preloadURL而不是preload
  3. 需要将需要预加载的js文件放到插件为我们暴露的__static路径中,他指向了public/
  4. 事件的注入钩子,需要使用will-attacj-webview这个钩子中响应

image.png

webview预加载JS与渲染进程通信

我们通过外部注入webview中的网页需要和渲染进程通信,得益于electron的强大功能,在预加载的JS中,我们可以通过其内部的require方法,拿到ipcRenderer以及其他我们可以使用的API,但是这些都会在加载网页完成后被移除。

该脚本的URL的协议必须是 file: asar:二者之一,因为在访客页中,它是通过“内部”的 require 去加载的

当访客页没有 node integration ,这个脚本仍然有能力去访问所有的 Node APIs, 但是当这个脚本执行执行完成之后,通过Node 注入的全局对象(global objects)将会被删除。

具体内容,请参考这里。这里,一个小例子,我们使用 ipcRenderer.sendToHost发送事件,修改webview嵌入页面的跳转事件。

  1. //preload.js
  2. const { ipcRenderer } = require('electron')
  3. document.addEventListener('DOMContentLoaded', () => {
  4. blanketHrefOriginalBehavior()
  5. })
  6. // 修改A标签原有href,代理点击事件到外层通信
  7. // 阻止默认跳转事件,改由IPC代理至渲染进程处理
  8. const blanketHrefOriginalBehavior = () => {
  9. const aTag = document.querySelectorAll('a')
  10. Array.from(aTag).forEach(tag => {
  11. const href = tag.getAttribute('href')
  12. const title = tag.getAttribute('title')
  13. if (href && !href.includes('#') && title) {
  14. tag.addEventListener('click', e => {
  15. e.preventDefault()
  16. ipcRenderer.sendToHost('navigate-in-webview', title)
  17. })
  18. }
  19. })
  20. }

在外部渲染进程的页面中,我们使用webview.addEventListeners('ipc-message')来接收。然后发送这个事件到vue的组件中

  1. webviewController.js
  2. this.instance.addEventListener('ipc-message', event => {
  3. const [keyWord] = event.args
  4. this._dispatch('navigate-in-webview', keyWord)
  5. })
  1. webview.vue
  2. this.webviewController.registerEvent('navigate-in-webview', (...args) => {
  3. const [targetId] = args
  4. // search from cache catalog and then navigate
  5. const { linkId } = this.catalogFlatten.find(item =>
  6. item.linkId.includes(targetId)
  7. )
  8. this.getCatalogById(linkId).then(res => {
  9. if (res) {
  10. // controll side behavior the same to located node
  11. this.$emit('sync-side-bar', linkId)
  12. }
  13. })
  14. })

视频文件加载错误问题

使用vue-cli-plugin-electron-builder插件时,会造成视频文件加载错误,这里我们通过配置一个代理的静态端口 protocal,由主进程来处理。vue-cli-plugin-electron-builder中protocol来加载静态图片的例子

  1. background.js
  2. function registerLocalVideoProtocol () {
  3. protocol.registerFileProtocol('local-video', (request, callback) => {
  4. const url = request.url.replace(/^local-video:\/\//, '')
  5. const decodedURL = decodeURI(url)
  6. try {
  7. // eslint-disable-next-line no-undef
  8. return callback(path.join(__static, decodedURL))
  9. } catch (error) {
  10. console.error('could not get file path:', error)
  11. }
  12. })
  13. }

.然后在视频文件上,使用local-video://即可。github上作者的issues