前言
很多情况下嵌套页面内容我们可通过webview、iframe来实现
webview
内嵌的浏览器组件(如Android的Webview,IOS的WKWebView或Electron),它内置了:
- HTML 解析器
- CSS渲染引擎
- JavaScript引擎
- 网络栈
因此,它可以像完整浏览器一样渲染 HTML、CSS 和 JavaScript。
iframe
浏览器提供的内嵌框架标签,其 src 属性可以指向:
- 远程url
- 本地文件
- 相对路径
但由于浏览器的一些安全限制,通常会禁用file://协议加载本地iframe,会需要做一定的配置。
electron
过往electron中使用的就是webview,但在≥5的版本中,electron webvie 默认情况下已经被弃用,转而提出的是BrowserView + WebContents的方式,虽然不是字面上的webview,但功能类似。
渲染单页应用
单页应用结构
前端项目(如react/vue)打包后通常会生成:
- index.html
- bundle.js
- *.css
- 静态资源
这些文件是标准的web资源,而要实现渲染便是加载入口文件(即index.html)
react
这里的例子将直接用dist作为项目名演示,实际情况改成项目名做区分更稳妥,以下是目录结构
Plain1electron-app/ 2├── main.js 3├── preload.js 4├── dist/ 5│ ├── index.html 6│ ├── bundle.js 7│ └── style.css 8└── package.json
这里我们需要初始化一个BrowserWindow,BrowserWindow创建:
JavaScript1const mainWindow = new BrowserWindow({
2 width: 1200,
3 height: 800,
4 webPreferences: {
5 nodeIntegration: false,
6 contextIsolation: true,
7 webSecurity: true,
8 },
9 autoHideMenuBar: true,
10})
然后使用window.loadUrl加载静态资源即可,但这里有个注意点,就是你的html在去拉css、js文件时需要获取的路径是/dist下的,否则就相当于直接拉的/目录下的文件
这个时候就可以用到protocol 来注册自定义协议:
JavaScript1protocol.registerSchemesAsPrivileged([
2 {
3 scheme: 'app',
4 privileges: {
5 standard: true,
6 secure: true,
7 supportFetchAPI: true,
8 corsEnabled: true,
9 allowServiceWorkers: true,
10 },
11 },
12])
JavaScript1protocol.handle('app', (request) => {
2 let url = request.url.replace('app://', '')
3
4 url = url.split('?')[0].split('#')[0]
5
6 if (url.startsWith('./')) {
7 url = url.substring(2)
8 }
9
10 if (url.startsWith('/dist/')) {
11 url = url.replace('/dist/', '')
12 } else if (url.startsWith('dist/')) {
13 url = url.replace('dist/', '')
14 }
15
16 if (url.startsWith('/')) {
17 url = url.substring(1)
18 }
19
20 if (url === '' || url.endsWith('/')) {
21 url = url + 'index.html'
22 }
23
24 const filePath = path.join(__dirname, 'dist', url)
25 return net.fetch('file://' + filePath)
26})
这样当我们访问app://下就能直接转到dist下了,而我们loadUrl也就成了:
JavaScript1mainWindow.loadURL('app://./index.html')
服务端渲染框架
nextjs
对于服务端渲染的框架,实现的路径会有些许不同,这里的例子将用nextjs选择standalone打包出来的应用,目录结构同上,dist目录换成nextjs默认打包的app
但由于选择在本地启动这个服务加载链接的形式,我们反而不用太在意文件路径了,核心的启动方法:
JavaScript1async function startNextServer() {
2 const nextDir = path.join(__dirname, 'app', 'server.js')
3 try {
4 nextProcess = fork(nextDir, [], {
5 cwd: __dirname,
6 silent: false,
7 env: {
8 ...process.env,
9 NODE_ENV: 'production',
10 },
11 })
12
13 nextProcess.on('close', (code) => {
14 logToFile(`Next.js 进程退出,代码 ${code}`)
15 })
16 nextProcess.on('error', (error) => {
17 logToFile('Next.js 进程启动失败:', error)
18 })
19 } catch (error) {
20 logToFile('Next.js 进程启动失败:', error)
21 }
22}
加载的端口也就取决于你启动的端口了,这里是默认:
JavaScript1await startNextServer() 2await mainWindow.loadURL('http://localhost:3000')