我正在将一个较旧的应用程序升级到Rails 6,它使用webpacker进行所有JS Assets 管理。

我正在使用pikaday日历库,并已通过yarn add pikaday添加了它,并验证它是否显示在packages.json中,然后通过app/javascript/packs/application.js将其添加到了我的require("pikaday")中。

我有一个称为Datepicker的JS类,用于抽象实际的pikaday日历。之所以这样做,是因为有一天我可能会更改datepicker的实现,并且这种方式只需要更改一个类,而不用更新所有的pikaday调用即可。

但是,是否在require("pikaday")包文件中使用application.js似乎无关紧要;只要我在要引用它的类中使用import Pikaday from "pikaday",它就没有区别。

问题

我试图了解正在发生的事情。

  • 是否需要在require("pikaday")文件中添加import Pikaday from "pikaday"app/javascript/pack/application.js?为什么或者为什么不?
  • 我熟悉全局变量不好并且应避免使用的原理,但是有没有一种方法可以避免必须在引用它的每个JS文件上使用import CLASS from "class_file"?在我的示例中,我想在多个地方使用Datepicker类。我问的原因是因为我有10多个这样的类,在每个要使用它们的JS文件的顶部都有10多个import语句有点令人讨厌。我总是想访问某些类。我已经使用了webpacker ProvidePlugin功能,但是它抱怨Datepicker不是构造函数,因此我可能缺少了一些东西,但是我没有足够的知识来知道什么。

  • app / javascript / custom / datepicker.js
    import Pikaday from "pikaday"
    
    export default class Datepicker {
      constructor(element, options) {
        if (options == null) { options = {} }
    
        // Store DOM element for reference
        this.element = element
    
        // Do not re-run on elements that already have datepickers
        if (this.element.datepicker === undefined) {
          options = Object.assign({},
            this.defaultOptions(),
            options
          )
    
          const picker = new Pikaday(options)
    
          // Store picker on element for reference
          this.element.datepicker = picker
    
          return picker
        } else {
          console.log("Datepicker already attached")
          return
        }
      }
    
      // Overridden by `options` in constructor
      defaultOptions() {
        return {
          field: this.element,
          format: "M/D/YYYY",
          bound: true,
          keyboardInput: false,
          showDaysInNextAndPreviousMonths: true
        }
      }
    }
    
    

    app / javascript / packs / application.js
    require("@rails/ujs").start()
    require("turbolinks").start()
    
    require("moment")
    
    // Note: if using `@rails/ujs`, you do not need to use `jquery-ujs`.
    import "jquery"
    
    // Does not matter if I require this or not, as long as it is imported in the
    // class file, I can remove this require statement and everything still works.
    require("pikaday")
    
    // StimulusJS
    // Webpack's `require` looks for `controllers/index.js` by default
    require("controllers")
    
    require("custom/datepicker")
    

    最佳答案

    您问了几个问题:

  • 在引用导入变量import Pikaday from 'pikaday'的文件中只需要Pikaday即可。在这种情况下,只需要在自定义datepicker模块中进行此导入。您可以从application.js包文件中删除require("pikaday")

    原因是Webpack将把application.js包作为依赖关系图的入口;从那里开始,它将递归地遍历每个必需/导入的模块,找到那些模块的依赖关系,依此类推,直到 bundle 包中包含所有声明的模块。由于您已经在application.js包中声明了import 'custom/datepicker',并且自定义datepicker导入了pikaday,它将作为依赖项包含在 bundle 软件中。
  • 因为您正在使用ES模块语法Datepicker,所以您的自定义export default ...被编译为ES模块(而是Webpack的ES模块的实现)。这对于ProvidePlugin的工作方式很重要。从Webpack 4 documentation of ProvidePlugin :



    这意味着您的Datepicker插件条目的Webpack配置如下所示(使用Rails Webpacker环境api):

    const { environment } = require('@rails/webpacker')
    const webpack = require('webpack')
    const {resolve} = require('path');
    
    environment.plugins.append('Provide', new webpack.ProvidePlugin({
      Datepicker: [resolve('app/javascript/custom/datepicker'), 'default']
    }))
    
    module.exports = environment
    

    意见:就是说,我鼓励您明确输入,例如在每个模块中引用了import Datepicker from 'custom/datepicker'Datepicker。即使是重复的,与诸如ESlint之类的工具进行集成也将变得更加容易,该工具可以通过某些代码编辑器提供有关编译错误的内联反馈-更加容易在每个模块中声明的显式依赖项进行设置。

  • 我将您的自定义Datepicker与ProvidePlugin结合在一起使用了Pikaday的工作演示:https://github.com/rossta/rails6-webpacker-demo/commit/be3d20107c2b19baa8b9560bce05e0559f90086d

    09-25 18:36