欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Electron 如何调用本地模块的方法

程序员文章站 2022-05-14 15:03:18
electron 结合了 chromium、node.js 和用于调用操作系统本地功能的 api(如打开文件窗口、通知、图标等,基于 electron 的开发,就好像开发一...

electron 结合了 chromium、node.js 和用于调用操作系统本地功能的 api(如打开文件窗口、通知、图标等,基于 electron 的开发,就好像开发一个网页一样,而且能够无缝地使用 node。或者说:就好像构建一个 node app,并通过 html 和 css 构建界面。

那么如何在页面中调用 node api 呢?

碰到了一些坑…

先从页面加载方式说起,electron 中加载页面的方式有两种:
一种是直接加载本地文件,另一种是通过 http 网络请求页面。

//方法1 本地路径
win.loadurl(url.format({
  pathname: path.join(__dirname, '/dist/index.html'),
  protocol: 'file:',
  slashes: true
}));
//方法2 网络路径
win.loadurl('http://localhost:3000');

现在我想要在某个js文件中引用一个本地的 npm 包,其中包含 node api,所以在浏览器中无法使用。

var local = window.noderequire('local');

此时出现一个问题,使用方法1运行正常,但使用方法2时报错,但是如果使用方法1,每次修改完代码都需要先打包一遍,再使用 electron 启动,耗时耗力啊。继续寻找解决方法。

can not find module xxx

调试发现在使用网络文件时,在调用 module.js 中的 module._load 函数时参入的参数 parent 为

Electron 如何调用本地模块的方法

重点在下面两个变量,从 http 加载页面时,由于路径是网络地址,所以 electron 将文件名设置为 electron 安装目录下的 init.js.

filename: "c:\users\asus\appdata\roaming\npm\node_modules\electron\dist\resources\electron.asar\renderer\init.js"
paths: array[0]

而在使用本地 index.html 时,pathname 指向正确的路径,而且 paths 中也包含了多个 node_modules 路径,module在初始化时会将当前路径以及上一级、上上一级…直到根目录的 node_modules 作为搜索路径。

Electron 如何调用本地模块的方法

filename: "e:\webstormworkspace\electron_require\index.html"

从下面 module.js 源码可以看到,文件名解析的时候正式利用了这个 paths 中的路径。因为 paths 中的空的,所以找不到所需要的模块。

其实 electron 是从安全的角度考虑,在从 http 请求中加载网页时,如果能直接调用本地的一些模块,会比较危险。

module._resolvefilename = function(request, parent, ismain) {
 if (nativemodule.noninternalexists(request)) {
  return request;
 }

 var resolvedmodule = module._resolvelookuppaths(request, parent);
 var id = resolvedmodule[0];
 var paths = resolvedmodule[1];

 // look up the filename first, since that's the cache key.
 debug('looking for %j in %j', id, paths);

 var filename = module._findpath(request, paths, ismain);
 if (!filename) {
  var err = new error("cannot find module '" + request + "'");
  err.code = 'module_not_found';
  throw err;
 }
 return filename;
};

此时很自然地想到可以把所需要模块的路径加入到 paths 中去,但这其实是不可行的,electron 包含主进程和渲染进程,主进程就是这里命名main.js 的文件,该文件是每个 electron 应用的入口。它控制了应用的生命周期(从打开到关闭)。它能调用原生元素和创建新的(多个)渲染进程,而且整个 node api 是内置其中的。

渲染进程就是一个浏览器窗口,现在我们的 js 跑在渲染进程里面,所以我们并不能直接在主进程里面修改渲染进程的数据。

electron 提供了 ipc 接口专门用于主进程和渲染进程之间的通信,他提供了同步和异步两种方法,同步方法直接设置 event.returnvalue,异步方法使用 event.sender.send(…).

// in main process.
const {ipcmain} = require('electron')
ipcmain.on('asynchronous-message', (event, arg) => {
 console.log(arg) // prints "ping"
 event.sender.send('asynchronous-reply', 'pong')
})

ipcmain.on('synchronous-message', (event, arg) => {
 console.log(arg) // prints "ping"
 event.returnvalue = 'pong'
})
// in renderer process (web page).
const {ipcrenderer} = require('electron')
console.log(ipcrenderer.sendsync('synchronous-message', 'ping')) // prints "pong"

ipcrenderer.on('asynchronous-reply', (event, arg) => {
 console.log(arg) // prints "pong"
})
ipcrenderer.send('asynchronous-message', 'ping')

但其实有更简单的方法,使用 模块来直接调用:

const remote = window.noderequire('electron').remote;
var local = remote.require('local');

这样子就可以直接使用外部模块了,这里为什么能引用 electron 模块,而其他的不可以呢?

继续看源码, electron 重写了 module._resolvefilename 函数,在 require(‘electron') 时,就直接返回路径,所以就可以找到啦。

// patch module._resolvefilename to always require the electron api when
// require('electron') is done.
const electronpath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js')
const originalresolvefilename = module._resolvefilename
module._resolvefilename = function (request, parent, ismain) {
 if (request === 'electron') {
  return electronpath
 } else {
  return originalresolvefilename(request, parent, ismain)
 }
}

}.call(this, exports, require, module, __filename, __dirname); });

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。