简体   繁体   English

在电子渲染器过程中将nedb坚持到磁盘(Webpack / Electron / nedb配置问题)

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

Problem 问题

I'm trying to use a pure-JS database called nedb in an Electron renderer process. 我正在尝试在Electron渲染器过程中使用名为nedb的纯JS数据库。 It uses the browser field in its package.json to swap in a browser-based storage system. 它使用package.jsonbrowser字段交换基于浏览器的存储系统。 This is causing my database to not actually be persisted to file. 这导致我的数据库实际上未持久保存到文件中。

Background 背景

I'm using Next.js as my view framework, and its Webpack is configured for "target": "electron-renderer" for the rendering thread. 我将Next.js用作视图框架,并将其Webpack配置为呈现线程的"target": "electron-renderer" Which apparently causes Webpack to process those browser directives, even though renderer processes should have access to both browser and Node APIs. 即使渲染器进程应该有权访问浏览器和Node API,这显然也会导致Webpack处理那些浏览器指令。 This behavior isn't really documented, so I don't know how to override it. 此行为并未真正记录,因此我不知道如何覆盖它。

What I have tried 我尝试过的

I have confirmed that if I manually edit out the browser field on the local copy of node_modules/nedb/package.json , the problem goes away. 我已经确认,如果我手动编辑出node_modules/nedb/package.json的本地副本上的browser字段,问题就会消失。

As a temporary workaround, I've pointed to my own fork of nedb that does this. 作为临时的解决方法,我已经指出了我自己的nedb分支。 But this is pretty unsatisfactory. 但这是非常不令人满意的。

Other research 其他研究

Curiously, this doesn't seem to be an issue for electron-vue, whose docs explicitly demonstrate use of nedb from a renderer process. 奇怪的是,这似乎不是电子nedb的问题,电子nedb的文档明确地演示了渲染程序中对nedb使用。 That framework does, indeed, appear to use "target": "electron-renderer" in its Webpack config . 该框架确实确实在其Webpack配置中似乎使用了"target": "electron-renderer"

Is there a solution to this problem, perhaps by Webpack configuration? 是否可以通过Webpack配置解决此问题?

You don't need to run the database on the renderer process, instead of that you could run other database that you would like, like sql, sqlite, mongodb, etc, on main process. 您不需要在渲染器进程上运行数据库,而是可以在主进程上运行其他想要的数据库,例如sql,sqlite,mongodb等。

If you don't mind switching database, here is how you could achieve this. 如果您不介意切换数据库,可以通过以下方法实现此目的。 In Electron exist a class callled ipcMain and ipcRenderer, this classes are used to make renderer process and main process comunicate. 在Electron中存在一个名为ipcMain和ipcRenderer的类,该类用于使渲染器进程和主进程通信。 You can send/receive any type of data with ipc. 您可以使用ipc发送/接收任何类型的数据。

Here is an example: 这是一个例子:

Renderer.js 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 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
}) 

It's not what you wanted, but is a workaround. 这不是您想要的,而是一种解决方法。

For more information, I suggest you go: 有关更多信息,我建议您去:

ipcRenderer Docs ipcMain Docs ipcRenderer文档 ipcMain文档

As you stated in your question and per this Github issue on the nedb package the root cause of your issue is that webpack's file resolution process reads the package.browser key in order to alias specific file paths to a different location when the target build is browser or some other value that will cause it to inspect the package.browser property. 正如您在问题中以及nedb软件包上的这个Github问题中所述, 问题的根本原因是webpack的文件解析过程读取package.browser键,以便在target版本为browser时将特定文件路径别名到另一个位置或其他将导致其检查package.browser属性的值。

electron-vue appears to sidestep the webpack bundling issue by treating all NPM dependencies as externals so that they don't get pulled into the application bundle and instead are expected to be defined on global by some other means. electron-vue似乎通过将所有NPM依赖项视为externals避开webpack捆绑问题 ,这样它们就不会被拉入应用程序包,而是希望通过其他某种方式在global上进行定义。 You could similarly designate nedb as an external in your webpack config and pull the Node version into your application via script tag or defining a reference to it on global some other way. 您可以类似地在Webpack配置nedb指定为外部,然后通过脚本标签或通过其他global方式定义对它的引用,将Node版本拉入您的应用程序。

Another solution would be to create a webpack resolver plugin to override how the problematic requires for "./lib/customUtils.js" and "./lib/storage.js" get resolved, bypassing the resolution step that inspects package.browser for aliases for those file paths. 另一个解决方案是创建一个webpack解析器插件,以覆盖解决"./lib/customUtils.js""./lib/storage.js"问题的方式,从而绕过检查package.browser别名的解析步骤。这些文件路径。

See the webpack documentation for how to pass a custom resolver plugin in your Webpack config. 有关如何在Webpack配置中传递自定义解析器插件的信息 ,请参阅webpack文档。 See the wepback/enhanced-resolve documentation for additional details on how plugins are defined and how they work. 有关如何定义插件以及它们如何工作的更多详细信息,请参见wepback/enhanced-resolve文档。

Essentially, a plugin is an object with an apply method that takes a resolver instance and performs some step of the file resolution process. 本质上,插件是一个带有apply方法的对象,该对象采用一个resolver实例并执行文件解析过程的某些步骤。 In the example below, we test to see whether the current file being resolved is in the nedb package and whether it's one of the two problematic browser aliases. 在下面的示例中,我们测试以查看当前要解析的文件是否在nedb软件包中,以及它是否是两个有问题的浏览器别名之一。 If so, we exit the resolution process with the correct paths to the files. 如果是这样,我们将使用正确的文件路径退出解析过程。 Otherwise we do nothing and defer to the normal resolution process. 否则,我们将不执行任何操作并遵守正常的解决程序。

// 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]
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM