electron+vue+better-sqlite3构建报错:Cannot read property ‘indexOf‘ of undefined

问题描述:

最近做一个软件,用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

### 使用 ElectronVue3 构建项目的设置与配置 #### 项目初始化 为了创建一个基于 ElectronVue3 的应用,首先需要安装必要的依赖项。通过 `electron-builder` 可以简化打包过程并支持跨平台发布。 ```bash npm init @vue/cli my-electron-app cd my-electron-app ``` 这会启动 Vue CLI 来引导新项目的建立[^1]。 #### 安装 Electronelectron-builder 接下来,在项目根目录下执行命令来添加 Electron构建工具: ```bash npm install --save-dev electron electron-builder ``` 此操作引入了开发环境所需的包以便于后续集成工作[^2]。 #### 修改 package.json 文件 编辑 `package.json` 添加脚本用于启动和打包应用程序: ```json { "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", "electron:build": "vue-cli-service build && electron-builder", "electron:start": "vue-cli-service build && electron ." }, ... } ``` 这些自定义指令允许开发者轻松地运行或编译整个解决方案。 #### 配置主进程文件 (main.js) 在 src 目录内新建 main.js 作为 Electron 应用程序入口点,并编写如下代码片段: ```javascript const { app, BrowserWindow } = require(&#39;electron&#39;) const path = require(&#39;path&#39;) function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, &#39;preload.js&#39;), nodeIntegration: true } }) win.loadFile(&#39;dist/index.html&#39;) // 加载Vue前端页面 } app.whenReady().then(() => { createWindow() app.on(&#39;activate&#39;, () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on(&#39;window-all-closed&#39;, () => { if (process.platform !== &#39;darwin&#39;) { app.quit() } }) ``` 这段 JavaScript 设置了一个基本窗口实例化逻辑以及事件监听器处理不同操作系统下的行为差异。 #### 创建预加载脚本(preload.js) 在同一级位置增加 preload.js 提供安全上下文之间的桥梁功能: ```javascript // All of the Node.js APIs are available in the preloader process. window.addEventListener(&#39;DOMContentLoaded&#39;, () => { const replaceText = (selector, text) => { const element = document.getElementById(selector); if (element) element.innerText = text; } for (api in window.electronAPI){ console.log(`${api} is available`); } }); ``` 该部分实现了 DOMContentLoaded 后的操作接口暴露给渲染进程中使用。 完成上述步骤之后就可以利用 npm run electron:start 开始调试模式下的本地测试;当准备部署时则采用 npm run electron:build 命令来进行最终版本制作。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值