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 相关文章推荐
ppk谈JavaScript style属性
Oct 10 Javascript
超轻量级的基于jquery的三级展开列表
Apr 26 Javascript
javascript设计模式之解释器模式详解
Jun 05 Javascript
c#+jquery实现获取radio和checkbox的值
Sep 12 Javascript
JavaScript阻止回车提交表单的方法
Dec 30 Javascript
基于jQuery的select下拉框选择触发事件实例分析
Nov 18 Javascript
学习vue.js表单控件绑定操作
Dec 05 Javascript
使用jQuery监听扫码枪输入并禁止手动输入的实现方法(推荐)
Mar 21 jQuery
webpack学习教程之前端性能优化总结
Dec 05 Javascript
javscript 数组扁平化的实现
Feb 03 Javascript
基于JS实现操作成功之后自动跳转页面
Sep 25 Javascript
使用Vant完成通知栏Notify的提示操作
Nov 11 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
德劲1103二次变频版的打磨
2021/03/02 无线电
基于文本的搜索
2006/10/09 PHP
ThinkPHP CURD方法之where方法详解
2014/06/18 PHP
php实现网页缓存的工具类分享
2015/07/14 PHP
详解 PHP加密解密字符串函数附源码下载
2015/12/18 PHP
PHP简单数据库操作类实例【支持增删改查及链式操作】
2016/10/10 PHP
PHP迭代器接口Iterator用法分析
2017/12/28 PHP
js 变量类型转换常用函数与代码[比较全]
2009/12/01 Javascript
qTip2 精致的基于jQuery提示信息插件
2012/02/17 Javascript
详解AngularJS过滤器的使用
2016/03/11 Javascript
Document.body.scrollTop的值总为零的快速解决办法
2016/06/09 Javascript
微信小程序进行微信支付的步骤昂述
2016/12/01 Javascript
js学习总结之DOM2兼容处理this问题的解决方法
2017/07/27 Javascript
Node.js dgram模块实现UDP通信示例代码
2017/09/26 Javascript
使用Vuex实现一个笔记应用的方法
2018/03/13 Javascript
用jQuery将JavaScript对象转换为querystring查询字符串的方法
2018/11/12 jQuery
Vue实现商品详情页的评价列表功能
2019/09/04 Javascript
Python使用scrapy抓取网站sitemap信息的方法
2015/04/08 Python
python用10行代码实现对黄色图片的检测功能
2015/08/10 Python
Python 爬虫学习笔记之多线程爬虫
2016/09/21 Python
Python Requests模拟登录实现图书馆座位自动预约
2018/04/27 Python
基于python 等频分箱qcut问题的解决
2020/03/03 Python
python爬虫beautifulsoup解析html方法
2020/12/07 Python
html5拖曳操作 HTML5实现网页元素的拖放操作
2013/01/02 HTML / CSS
乌克兰品牌化妆品和香水在线商店:Bomond
2020/01/14 全球购物
CHARLES & KEITH加拿大官网:新加坡时尚品牌
2020/03/26 全球购物
会计岗位职责
2013/11/08 职场文书
中学生英语演讲稿
2014/04/26 职场文书
优秀驾驶员先进事迹材料
2014/05/04 职场文书
2014年班干部工作总结
2014/11/25 职场文书
专业技术职务聘任证明
2015/03/02 职场文书
小学教师工作总结2015
2015/04/07 职场文书
小兵张嘎电影观后感
2015/06/03 职场文书
学习雷锋主题班会
2015/08/14 职场文书
初三化学教学反思
2016/02/22 职场文书
Redis命令处理过程源码解析
2022/02/12 Redis