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 相关文章推荐
为JavaScript类型增加方法的实现代码(增加功能)
Dec 29 Javascript
可在线编辑网页文字效果代码(单击)
Mar 02 Javascript
基于React.js实现原生js拖拽效果引发的思考
Mar 30 Javascript
AngularJS基础 ng-cloak 指令简单示例
Aug 01 Javascript
Angular4如何自定义首屏的加载动画详解
Jul 26 Javascript
解决html-jquery/js引用外部图片时遇到看不了或出现403的问题
Sep 22 jQuery
javascript 开发之网页兼容各种浏览器
Sep 28 Javascript
jQuery中的类名选择器(.class)用法简单示例
May 14 jQuery
jQuery.extend 与 jQuery.fn.extend的用法及区别实例分析
Jul 25 jQuery
JavaScript数组、json对象、eval()函数用法实例分析
Feb 21 Javascript
jQuery实现滑动星星评分效果(每日分享)
Nov 13 jQuery
vue 使用原生组件上传图片的实例
Sep 08 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
深入php-fpm的两种进程管理模式详解
2013/06/03 PHP
解析PHP留言本模块主要功能的函数说明(代码可实现)
2013/06/25 PHP
php命令行(cli)模式下报require 加载路径错误的解决方法
2015/11/23 PHP
php实现微信扫码自动登陆与注册功能
2016/09/22 PHP
PHP-FPM的配置与优化讲解
2019/03/15 PHP
PHP迭代器和生成器用法实例分析
2019/09/28 PHP
模拟用户操作Input元素,不会触发相应事件
2007/05/11 Javascript
转自Jquery官方 jQuery1.1.3发布,速度提升800%,体积保持20K
2007/08/19 Javascript
javascript中substr,substring,slice.splice的区别说明
2010/11/25 Javascript
javascript中万恶的function实例分析
2011/05/25 Javascript
jQuery EasyUI API 中文文档 - ComboGrid 组合表格
2011/10/13 Javascript
JS的replace方法详细介绍
2012/11/09 Javascript
jquery ajax实现下拉框三级无刷新联动,且保存保持选中值状态
2013/10/29 Javascript
javascript if条件判断方法小结
2014/05/17 Javascript
JavaScript中获取Radio被选中的值
2015/11/11 Javascript
javascript HTML5 canvas实现打砖块游戏
2020/06/18 Javascript
webpack学习教程之前端性能优化总结
2017/12/05 Javascript
bootstrap-table formatter 使用vue组件的方法
2019/05/09 Javascript
富文本编辑器vue2-editor实现全屏功能
2019/05/26 Javascript
js实现登录拖拽窗口
2020/02/10 Javascript
微信小程序实现倒计时功能
2020/11/19 Javascript
Python的Django框架中自定义模版标签的示例
2015/07/20 Python
Mac 上切换Python多版本
2017/06/17 Python
Python调用系统底层API播放wav文件的方法
2017/08/11 Python
使用python socket分发大文件的实现方法
2019/07/08 Python
python 基于dlib库的人脸检测的实现
2019/11/08 Python
python数据爬下来保存的位置
2020/02/17 Python
解决django xadmin主题不显示和只显示bootstrap2的问题
2020/03/30 Python
关于CSS Tooltips(鼠标经过时显示)的效果
2013/04/10 HTML / CSS
css3实现六边形边框的实例代码
2019/05/24 HTML / CSS
临床医学专业毕业生的自我评价
2013/10/17 职场文书
转预备党员政审材料
2014/02/06 职场文书
违反交通安全法检讨书
2014/10/24 职场文书
小学英语新课改心得体会
2016/01/22 职场文书
python opencv检测直线 cv2.HoughLinesP的实现
2021/06/18 Python
Win10开机修复磁盘错误怎么跳过?Win10关闭开机磁盘检查的方法
2022/09/23 数码科技