先看效果
功能说明
1、electron开发的程序中在一个modal里实现浏览外部网站
2、实现简单浏览以及简单控制(前进 后退 刷新 最小化 关闭),没有加地址部分的控制
技术
1、webview的嵌入和使用
2、点击链接打开新窗口时如何控制使新页面在当前窗口显示
有三种方式可以让你在electron
的BroweserWindow
里集成(第三方)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种方法也可以实现,大家可以都试试,欢迎指点~