本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
Electron 简介
Electron(原名为Atom Shell)是GitHub开发的一个开源框架。它通过使用Node.js(作为后端)和Chromium的渲染引擎(作为前端)完成跨平台的桌面GUI应用程序的开发。Electron现已被多个开源Web应用程序用于前端与后端的开发,著名项目包括GitHub的Atom和微软的Visual Studio Code。
Electron 报错症状
上手一个 Electron 项目开始学习写业务代码时,碰到了这样的一个问题。
Node 终端编译报错如下:
Module not found: Error: Can't resolve 'fs' in 'D:\electron\xxx\node_modules\electron'
代码之事,先行谷歌,寻得有两个方案。
方案1,修改 webpack target 配置项
方案出自链接 typescript - Electron 和 typescript : 'fs' can't be resolved,该文作者碰到的报错和本人一模一样。
该文的作者也挺用心的,排版什么的也挺好看的,不晓得颜值咋样。请好好写文章不要多想
方案简而言之,是修改 webpack 的 target
配置项。
要么
module.exports = {
// ...其他内容略
{
// for files that should be compiled for electron main process
target: 'electron-main'
}
// ...其他内容略
}
要么
{
// for files that should be compiled for electron renderer process
target: 'electron-renderer'
}
兴致冲冲的去寻找,想去对应修改,发现项目使用的模板 electron-react-boilerplate,其实本身已经配置了该 target
值。 有点撩起来,却又爽约的无奈感。
方案2:修改 webpack externals 配置项
总而言之,也是修改 webpack 的配置项 externals
module.exports = {
//...其他内容略
"externals": {
"fs": 'require("fs")',
},
//...其他内容略
}
方案链接出自
- electron Error: Can't resolve 'fs'(编译报错)
- # [v6] Electron: Module not found: Error: Can't resolve 'fs'
这个改了以后,的确没有报错了。但又产生了一个新的报错。
Module not found: Error: Can't resolve 'path' in 'D:\electron\xxx\node_modules\electron'
很熟悉有没有,看我习得 ctrl+v 大法。
"externals": {
"fs": 'require("fs")',
"path": 'require("path")',
},
终于守得云开见月明。Node 侧的构建没有报错了。但浏览器端开始报错了。
Cannot read property 'join' of undefined
Call Stack
./node_modules/electron/index.js
renderer.dev.js:23020:23
Object.options.factory
renderer.dev.js:69814:31
报错行见:
const pathFile = path.join(__dirname, 'path.txt');
这个报错倒提醒我了,为啥子 node 端的代码会在浏览器侧执行,是不是哪里姿势不对哦。
最终方案: contextBridge.exposeInMainWorld
找到幺蛾子,原来我直接在 render 进程里面的 js 直接引入了 electron。
const { ipcRenderer, shell, remote } = require('electron');
因为 electron 有很多 node 模块,当然会在浏览器端出错。 这个时候,我们应该用到可 node 可 浏览器端执行的 preload
配置项。
- 首先:
main.js
配置提前加载preload.js
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
}
preload.js
内引入electron
并通过contextBridge
暴露给window
const { contextBridge, ipcRenderer, remote, shell } = require('electron');
contextBridge.exposeInMainWorld('electron', {
ipcRenderer,
remote,
shell,
});
- render 进程的 js 修改引入方式,通过 window.electron 获取相应的模块
// 注释错误的引入方式
//const { ipcRenderer, shell, remote } = require('electron');
const { shell, remote, ipcRenderer } = window.electron;
- 重启项目,重新刷新页面即可。
写在最后
总而言之,出这个问题,还是比较大意。自己没有任何项目经验,自然忽视了 main 主进程(node 端)和 render 渲染进程 (浏览器端)执行环境的差异。看到官网说 ipcRenderer
是渲染进程的模块,就直接在浏览器侧的 js 中直接引入模块了。
记住:如果想在前端环境中使用某些模块,一定要在 preload
选项配置的 js 中,通过 contextBridge.exposeInMainWorld
暴露给 window
给前端使用。
这个出错其实有点像 SSR 的某些类型报错,不该在 node 执行的代码,执行在 node 层了,比如在 node 侧获取 window ,都是执行环境的不匹配导致出错。
当然,这个可能是我的个人情况,不具备通配性。希望对大家有所帮助。
原创不易,感谢大家阅读,感恩点赞支持。
另
在配置 preload 的时候,碰到了另外一个问题,# Electron App : Unable to load preload script 的解决方式,有兴趣的读者可以阅读。正所谓坑太多了…说多了都是泪。