electron之使用webview实现加载第三方网站

先看效果

功能说明

1、electron开发的程序中在一个modal里实现浏览外部网站

2、实现简单浏览以及简单控制(前进 后退 刷新 最小化 关闭),没有加地址部分的控制

技术

1、webview的嵌入和使用

2、点击链接打开新窗口时如何控制使新页面在当前窗口显示

有三种方式可以让你在electronBroweserWindow里集成(第三方)web内容,<iframe> 和, <webview>BrowserViews 每个功能都略有不同,适用于不同的情况 本文使用的是<webview>

webView是一个自定义元素 <webview>,仅在 Electron 内工作。在一个独立的 frame 和进程里显示外部 web 内容,这意味着所有与 <webview> 的通信都是异步使用 IPC 进行的。

启用webview

默认情况下,Electron >= 5禁用 webview 标签。 在构造 BrowserWindow 时,需要通过设置 webviewTag webPreferences选项来启用标签

页面代码

tsx 复制代码
import { useState } from 'react'
import WebviewItem from './components/WebviewItem'
import { Modal } from 'antd'

function App(): JSX.Element {
  const [modalOpen, setModalOpen] = useState(false)
  const [destroyChild, setDestroyChild] = useState(false)
  const url = 'https://juejin.cn/'

  const handleClose = (bol: boolean): void => {
    setModalOpen(false)
    setDestroyChild(bol)
  }
  
  return (
    <div>
       <div className="icon-box" onClick={(): void => setModalOpen(true)}>
          <span>掘金</span>
       </div>
       <Modal
        width={'100vw'}
        open={modalOpen}
        wrapClassName="desktop_setting_modal"
        style={{
          maxWidth: '100vw',
          height: '100vh',
          overflow: 'hidden',
          position: 'fixed',
          top: 0
        }}
        closable={false}
        destroyOnClose={destroyChild} //modal关闭时是否销毁子元素
        footer={null}
       >
        <WebviewItem handleClose={handleClose} src={url} />
      </Modal>
    </div>
  )
}

export default App

App.tsx中通过回调函数传过来的值 来设置Modal destroyOnClose属性值 从而实现网站的关闭 最小化

WebviewItem.tsx 完整代码

tsx 复制代码
import React, { useState, useEffect } from 'react'
import '../styles/WebviewItem.css'

interface IProp {
  src: string
  handleClose: (bol: boolean) => void
}

const WebviewItem: React.FC<IProp> = (props) => {
  const [src, setSrc] = useState(props.src)

  useEffect(() => {
    setSrc(src)
    // 获取webview元素
    const webview = document.getElementById(`webview-box${src}`) as HTMLIFrameElement | any

    if (!webview) return

    const buttons: HTMLButtonElement[] = [
      document.getElementById(`last-btn${src}`) as HTMLButtonElement,
      document.getElementById(`next-btn${src}`) as HTMLButtonElement,
      document.getElementById(`reload-btn${src}`) as HTMLButtonElement,
      document.getElementById(`minimize-btn${src}`) as HTMLButtonElement,
      document.getElementById(`close-btn${src}`) as HTMLButtonElement
    ]

    function handleNewWindow(e): void {
      const urlClass = new URL(e.url)
      const { protocol } = urlClass
      if (protocol === 'http:' || protocol === 'https:') {
        //更新当前url
        webview.src = e.url
      }
    }

    function updateButtonStatus(): void {
      buttons[0].disabled = !webview.canGoBack() // 禁用返回按钮,如果不能后退
      buttons[1].disabled = !webview.canGoForward() // 禁用后退按钮,如果不能前进
    }

    function handleButtonClick(event: Event): void {
      const buttonId = (event.target as HTMLElement).id
      switch (buttonId) {
        case `last-btn${src}`:
          if (webview.canGoBack()) {
            webview.goBack()
          }
          break
        case `next-btn${src}`:
          if (webview.canGoForward()) {
            webview.goForward()
          }
          break
        case `reload-btn${src}`:
          webview.reload()
          break
        case `minimize-btn${src}`:
          //通知父组件需要关闭Modal,但不需要销毁当前组件
          props.handleClose(false)
          break
        case `close-btn${src}`:
          //通知父组件需要关闭Modal,需要销毁当前组件
          props.handleClose(true)
          break
        default:
          break
      }
    }

    //webview 元素必须已被加载
    webview.addEventListener('dom-ready', updateButtonStatus)
    //监听网页打开新窗口
    webview.addEventListener('new-window', handleNewWindow)

    buttons.forEach((button) => {
      //给toolbar 5个按钮添加点击事件
      button.addEventListener('click', handleButtonClick)
    })
  }, [src])

  const customStatusbarHtml = `
    <div id="custom-statusbar">
      <div class="reload-box">
        <button id="last-btn${src}" disabled>回退</button>
        <button id="next-btn${src}" disabled>前进</button>
        <button id="reload-btn${src}">刷新</button>
      </div>
      <div class="button-box">
        <button id="minimize-btn${src}">最小化</button>
        <button id="close-btn${src}">关闭</button>
      </div>
    </div>
    <webview allowpopups src="${src}" id="webview-box${src}" class="webview-box"></webview>
  `
  return (
    <div
      dangerouslySetInnerHTML={{ __html: customStatusbarHtml }}
      style={{ width: '100vw', height: '100vh' }}
    />
  )
}

export default WebviewItem

WebviewItem.tsx代码中 使用了React dangerouslySetInnerHTML显示文本内容
<webview>标签:

1、src属性表示可见网址的string 更新src的值将重新加载页面

2、当我们点击所有 target 为 _blank 的 a 标签点击都没反应: 这是因为 webview 默认不允许打开新窗口,需要设置 allowpopups 属性

3、当我们设置allowpopups属性后,点击target 为 _blank 的 a 标签这时 electron应用程序会自动新开启BrowserWindow, 如果阻止默认行为,则需要在当前窗口主进程中监听web-contents-created事件

ts 复制代码
//阻止打开新窗口
  app.on('web-contents-created', (_, contents) => {
    contents.addListener('new-window', (e) => {
      e.preventDefault()
    })
  })

并且还可以在webview元素上监听新窗口 webview.addEventListener('new-window', handleNewWindow) 拿到新的url 更新(代码32行)

tips:app.on('web-contents-created') 优先webview.addEventListener('new-window')

好啦,功能完成了🍗

另外2种方法也可以实现,大家可以都试试,欢迎指点~

相关推荐
风清扬_jd38 分钟前
Chromium 硬件加速开关c++
java·前端·c++
谢尔登1 小时前
【React】事件机制
前端·javascript·react.js
2401_857622662 小时前
SpringBoot精华:打造高效美容院管理系统
java·前端·spring boot
etsuyou2 小时前
Koa学习
服务器·前端·学习
Easonmax2 小时前
【CSS3】css开篇基础(1)
前端·css
粥里有勺糖3 小时前
视野修炼-技术周刊第104期 | 下一代 JavaScript 工具链
前端·javascript·github
大鱼前端3 小时前
未来前端发展方向:深度探索与技术前瞻
前端
昨天;明天。今天。3 小时前
案例-博客页面简单实现
前端·javascript·css
天上掉下来个程小白3 小时前
请求响应-08.响应-案例
java·服务器·前端·springboot
前端络绎3 小时前
初识 DT-SDK:基于 Cesium 的二三维一体 WebGis 框架
前端