在 Electron 渲染器进程中将 nedb 持久化到磁盘(Webpack/Electron/nedb 配置问题)

Persist nedb to disk in Electron renderer process (Webpack/Electron/nedb configuration problem)

问题

我正在尝试使用名为 nedb in an Electron renderer process. It uses the browser field in its package.json 的纯 JS 数据库来交换基于浏览器的存储系统。这导致我的数据库实际上没有保存到文件中。

背景

我正在使用 Next.js 作为我的视图框架,其 Webpack 配置为 "target": "electron-renderer" 用于渲染线程。这显然会导致 Webpack 处理这些浏览器指令,即使渲染器进程应该可以访问浏览器和 Node API。这种行为并没有真正记录下来,所以我不知道如何覆盖它。

我试过的

我已经确认,如果我在 node_modules/nedb/package.json 的本地副本上手动编辑掉 browser 字段,问题就会消失。

作为临时解决方法,我指出了我自己的 nedb 分支可以做到这一点。但这并不令人满意。

其他研究

奇怪的是,这似乎不是 electron-vue 的问题,其文档 explicitly demonstrate use of nedb from a renderer process. That framework does, indeed, appear to use "target": "electron-renderer" in its Webpack config.

是否有解决此问题的方法,也许是通过 Webpack 配置?

您不需要 运行 渲染器进程上的数据库,您可以 运行 其他您想要的数据库,例如 sql、sqlite,mongodb,等等,在主进程上。

如果您不介意切换数据库,请按以下方法实现。在Electron 中存在一个class 叫作ipcMain 和ipcRenderer,这个classes 用来使renderer 进程和主进程通信。您可以 send/receive 使用 ipc 的任何类型的数据。

这是一个例子:

Renderer.js

const btnSave = document.getElementById('btn-save')

// Get any data from forms, etc


btn.addEventListener('click', () => {
     // ipcRender sends the data via 'receive-data-to-save-in-database' channel, you
     // you can send any type of data, and have has many args you want. In this case I 
     // sent a a empty object
     ipcRenderer.send('receive-data-to-save-in-database', {})              
})

Main.js

// ipcMain listens to channel 'receive-data-to-save-in-database'
ipcMain.on('receive-data-to-save-in-database', (event, args) => {
    // Code to save in database
    // The empty object will be received in args parameter
}) 

这不是您想要的,但它是一种解决方法。

想了解更多,建议你去:

ipcRenderer Docs ipcMain Docs

正如您在问题中所述,根据 nedb 包中的 Github issue 问题的根本原因是 webpack 的文件解析过程读取 package.browser 键以便当 target 构建为 browser 或其他一些会导致其检查 package.browser 属性.

的值时,将特定文件路径别名到不同的位置

electron-vue 出现在 sidestep the webpack bundling issue by treating all NPM dependencies as externals 中,这样它们就不会被拉入应用程序包中,而是应该通过其他方式在 global 上定义。您可以类似地将 nedb 指定为您的 webpack 配置中的外部,并通过脚本标签将 Node 版本拉入您的应用程序,或者通过其他方式在 global 上定义对它的引用。

另一种解决方案是创建一个 webpack 解析器插件来覆盖 "./lib/customUtils.js""./lib/storage.js" 的问题要求如何得到解决,绕过检查 package.browser 别名的解析步骤那些文件路径。

请参阅 how to pass a custom resolver plugin in your Webpack config. See the wepback/enhanced-resolve documentation for additional details on how plugins are defined 的 webpack 文档及其工作原理。

本质上,插件是一个带有 apply 方法的对象,该方法采用 resolver 实例并执行文件解析过程的某些步骤。在下面的示例中,我们测试当前正在解析的文件是否在 nedb 包中,以及它是否是两个有问题的浏览器别名之一。如果是这样,我们将使用正确的文件路径退出解析过程。否则我们什么也不做,按照正常的解决流程。

// Prevents nedb from substituting browser storage when running from the
// Electron renderer thread.
const fixNedbForElectronRenderer = {
  apply(resolver) {
    resolver
      // Plug in after the description file (package.json) has been
      // identified for the import, which makes sure we're not getting
      // mixed up with a different package.
      .getHook("beforeDescribed-relative")
      .tapAsync(
        "FixNedbForElectronRenderer",
        (request, resolveContext, callback) => {
          // When a require/import matches the target files, we
          // short-circuit the Webpack resolution process by calling the
          // callback with the finalized request object -- meaning that
          // the `path` is pointing at the file that should be imported.
          const isNedbImport = request.descriptionFileData["name"] === "nedb"

          if (isNedbImport && /storage(\.js)?/.test(request.path)) {
            const newRequest = Object.assign({}, request, {
              path: resolver.join(
                request.descriptionFileRoot,
                "lib/storage.js"
              )
            })
            callback(null, newRequest)
          } else if (
            isNedbImport &&
            /customUtils(\.js)?/.test(request.path)
          ) {
            const newRequest = Object.assign({}, request, {
              path: resolver.join(
                request.descriptionFileRoot,
                "lib/customUtils.js"
              )
            })
            callback(null, newRequest)
          } else {
            // Calling `callback` with no parameters proceeds with the
            // normal resolution process.
            return callback()
          }
        }
      )
  }
}

// Register the resolver plugin in the webpack config
const config = {
  resolve: {
    plugins: [fixNedbForElectronRenderer]
  }
}