问题描述:

最近做一个软件,用better-sqlite3存储数据用vue3做前端,用IPC进程间通信模拟ajax请求实现前后端分离。

后端简单模仿MVC框架写了一遍,后端在node环境下测试没有问题,但是打包进electron项目中就出现了以下问题

INFO  Launching Electron...
App threw an error during load
TypeError: Cannot read property 'indexOf' of undefined
    at Function.getFileName (webpack:///./node_modules/bindings/bindings.js?:179:16)
    at bindings (webpack:///./node_modules/bindings/bindings.js?:82:48)
    at new Database (webpack:///./node_modules/better-sqlite3/lib/database.js?:48:119)
    at DBUtils.createDatabase (webpack:///./src/backend/utils/DBUtils.js?:37:25)
    at new DBUtils (webpack:///./src/backend/utils/DBUtils.js?:15:18)
    at new PropertyDao (webpack:///./src/backend/dao/PropertyDao.js?:10:29)
    at new InfoPairService (webpack:///./src/backend/service/InfoPairService.js?:27:32)
    at new ArchivesController (webpack:///./src/backend/controller/ArchivesController.js?:12:26)
    at eval (webpack:///./src/backend/server-routes/Routes.js?:8:28)
    at Module../src/backend/server-routes/Routes.js (D:\Code\political_assistant\merge\dist_electron\index.js:2076:1)

原因是使用DBUtils中使用better-sqlite3的new Database方法打开sqlite数据库文件时,调用了bindings库中的getFileName方法,这个方法返回了Undefined。

打开bindings.js这个文件找到此方法:

exports.getFileName = function getFileName(calling_file) {
  var origPST = Error.prepareStackTrace,
    origSTL = Error.stackTraceLimit,
    dummy = {},
    fileName;

  Error.stackTraceLimit = 10;

  Error.prepareStackTrace = function(e, st) {
    for (var i = 0, l = st.length; i < l; i++) {
      fileName = st[i].getFileName();
      if (fileName !== __filename) {
        if (calling_file) {
          if (fileName !== calling_file) {
            return;
          }
        } else {
          return;
        }
      }
    }
  };

  // run the 'prepareStackTrace' function above
  Error.captureStackTrace(dummy);
  dummy.stack;

  // cleanup
  Error.prepareStackTrace = origPST;
  Error.stackTraceLimit = origSTL;

  // handle filename that starts with "file://"
  var fileSchema = 'file://';

  if (fileName.indexOf(fileSchema) === 0) {
    fileName = fileURLToPath(fileName);
  }

  return fileName;
};

从上面的代码发现,报错中的indexOf出现在

// handle filename that starts with "file://"
  var fileSchema = 'file://';

  if (fileName.indexOf(fileSchema) === 0) {
    fileName = fileURLToPath(fileName);
  }

这一部分,fileName的值为undefined原因在于上面的方法

 Error.prepareStackTrace = function(e, st) {
    for (var i = 0, l = st.length; i < l; i++) {
      fileName = st[i].getFileName();
      if (fileName !== __filename) {
        if (calling_file) {
          if (fileName !== calling_file) {
            return;
          }
        } else {
          return;
        }
      }
    }
  };

st[i].getFileName()返回了undefined

解决方法:

到bindings包的github仓库中发现有大佬已经踩过这个坑,并且提交了解决这个BUG的pull request,但是原作者似乎不再维护了,没有把修复bug的pull request合并进去。

大佬的PR地址:

Fix for using bindings.js with eval by pverscha · Pull Request #66 · TooTallNate/node-bindings · GitHub

 进大佬的PR把修复了bug的bindings.js下载下来替换自己项目中的node_modules/bindings/bindings.js文件

再次运行项目,问题解决!

问题复盘:

better-sqlite3需要被编译成.node二进制文件来执行。但是编译后生成的.node文件在不同的环境下会输出到不同的目录,导致在加载.node文件时找不到对应目录。

这个时候使用node-bindings可以自动从包目录出发遍历整个目录来查找编译后的node文件。

但是node-bindings使用chrome V8的Error API获取程序调用栈,来找到调用bindings的函数所在目录。

然而webpack打包后,webpack似乎使用eval方式调用了我的代码,一直调用到better-sqlite3和bindings.js,而bindings存在被eval调用后getFileName返回undefined的BUG,引发了这个BUG。

具体为啥被eval方式调用后出现这个BUG,还没搞清楚。

事实上这个BUG已经被大佬修复,但是原作者似乎弃坑了,没有把修复的PR合并到主线。

参考文章:

关于electron+vue开发:

最简洁Vue+Electron项目搭建教程 - 知乎

Electron + Vue3 + TS 实战 - 掘金

关于进程间通信模拟B/S架构:

关于 Electron 进程间通信的一个小小实践 - 掘金

关于问题定位:

https://github.com/TooTallNate/node-bindings/issues/29

getFileName cannot read property of undefined · Issue #54 · TooTallNate/node-bindings · GitHub

Cannot read property 'indexOf' of undefined at Function.getFileName · Issue #76 · TooTallNate/node-bindings · GitHub

关于问题解决:

Fix for using bindings.js with eval by pverscha · Pull Request #66 · TooTallNate/node-bindings · GitHub

关于node-bindings中使用的chrome V8 的 Error API:

Error 错误 | Node.js API 文档

Stack trace API · V8

Logo

前往低代码交流专区

更多推荐