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


Posted in Javascript onFebruary 01, 2019

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')

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

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); });

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

Javascript 相关文章推荐
IE 上下滚动展示模仿Marquee机制
Dec 20 Javascript
无闪烁更新网页内容JS实现
Dec 19 Javascript
使用JS或jQuery模拟鼠标点击a标签事件代码
Mar 10 Javascript
javascript生成随机数的方法
May 16 Javascript
Jquery Mobile 自定义按钮图标
Nov 18 Javascript
Extjs实现下拉菜单效果
Apr 01 Javascript
web前端页面生成exe可执行文件的方法
Feb 08 Javascript
Vue 中使用 CSS Modules优雅方法
Apr 09 Javascript
详解node字体压缩插件font-spider的用法
Sep 28 Javascript
JavaScript 面向对象基础简单示例
Oct 02 Javascript
基于纯JS实现多张图片的懒加载Lazy过程解析
Oct 14 Javascript
微信小程序基础教程之echart的使用
Jun 01 Javascript
详解使用webpack+electron+reactJs开发windows桌面应用
Feb 01 #Javascript
原来JS还可以这样拆箱转换详解
Feb 01 #Javascript
微信小程序拍照和摄像功能实现方法示例
Feb 01 #Javascript
微信小程序常用简易小函数总结
Feb 01 #Javascript
使用Angular自定义字段校验指令的方法示例
Feb 01 #Javascript
angular中如何绑定iframe中src的方法
Feb 01 #Javascript
Javascript迭代、递推、穷举、递归常用算法实例讲解
Feb 01 #Javascript
You might like
7个超级实用的PHP代码片段
2011/07/11 PHP
php强制更新图片缓存的方法
2015/02/11 PHP
PHP数组与对象之间使用递归实现转换的方法
2015/06/24 PHP
php上传大文件失败的原因及应对策略
2015/10/20 PHP
php模仿qq空间或朋友圈发布动态、评论动态、回复评论、删除动态或评论的功能(中)
2017/06/11 PHP
php设计模式之适配器模式原理、用法及注意事项详解
2019/09/24 PHP
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
javascript 写类方式之九
2009/07/05 Javascript
JQuery操作tr和td内容的方法实例
2013/03/06 Javascript
jquery ajax post提交数据乱码
2013/11/05 Javascript
js冒泡、捕获事件及阻止冒泡方法详细总结
2014/05/08 Javascript
选择复选框按钮置灰否则按钮可用
2014/05/22 Javascript
Js实现自定义右键行为
2015/03/26 Javascript
如何解决谷歌浏览器下jquery无法获取图片的尺寸
2015/09/10 Javascript
jQuery EasyUI中DataGird动态生成列的方法
2016/04/05 Javascript
使用CSS+JavaScript或纯js实现半透明遮罩效果的实例分享
2016/05/09 Javascript
Node.js环境下JavaScript实现单链表与双链表结构
2016/06/12 Javascript
javascript 定时器工作原理分析
2016/12/03 Javascript
利用select实现年月日三级联动的日期选择效果【推荐】
2016/12/13 Javascript
JavaScript中的工厂函数(推荐)
2017/03/08 Javascript
vue和webpack打包项目相对路径修改的方法
2018/06/15 Javascript
javascript实现导航栏分页效果
2019/06/27 Javascript
[48:54]VGJ.T vs infamous Supermajor小组赛D组败者组第一轮 BO3 第二场 6.3
2018/06/04 DOTA
python SSH模块登录,远程机执行shell命令实例解析
2018/01/12 Python
python3+PyQt5使用数据库表视图
2018/04/24 Python
Python常见的pandas用法demo示例
2019/03/16 Python
pytorch 实现cross entropy损失函数计算方式
2020/01/02 Python
Django实现微信小程序支付的示例代码
2020/09/03 Python
关于Python3的import问题(pycharm可以运行命令行import错误)
2020/11/18 Python
python 三种方法提取pdf中的图片
2021/02/07 Python
排序都有哪几种方法?请列举。用JAVA实现一个快速排序
2014/02/16 面试题
国贸专业自荐信范文
2014/03/02 职场文书
小班开学寄语
2014/04/04 职场文书
劳动者解除劳动合同通知书
2015/04/16 职场文书
结婚喜宴迎宾词
2015/08/10 职场文书
Java由浅入深通关抽象类与接口(下篇)
2022/04/26 Java/Android