如何在Rails 3.1中使用Sprockets编写DRY、模块化的Coffeescript?

15

我正在尝试编写一些合理的 JavaScript,希望尽可能地使用命名空间来避免全局变量,但仍然能够访问在各处声明的函数。但是,我不想在函数定义中过于冗长。

我的理想 CoffeeScript 代码应该是这样的:

class @MyApp
  @myClassMethod = ->
    console.log 'This is MyApp.myClassMethod()'

  class @Module1
    @moduleMethod = ->
      console.log 'This is MyApp.Module1.moduleMethod()'

你明白了。这样我就避免每次想要正确地定义一个命名空间函数时都要写MyApp.Module.submoduleMethod = ->。使用@并在类定义中定义事物可以使事情变得简短。

这一切都很顺利,直到我想将我的功能拆分成多个CoffeeScript文件。然后我真正想要的是这样的东西:

// application.js
class @MyApp
  //= require 'module1'
  //= require 'module2'

// module1.js
class @Module1
  @moduleMethod = ->
    console.log 'This is STILL MyApp.Module1.moduleMethod()'

看起来Sprockets似乎无法做到这一点。

是否有一种明智的方法可以在我的容器文件中正确地引用我的CoffeeScript文件?或者另一种方法来编写将代码分成单独文件的模块化代码,使用CoffeeScript、Sprockets和Rails 3.1?


1
我认为这个问题需要进一步研究 - 下面的答案不够好,尤其是因为CoffeeScript的创建者已经删除了“易于模块化”的页面,因为这种技术很差。 - dmonopoly
3个回答

4
只需保留module1.js不变,然后使application.js看起来像这样:
//= require 'module1'

class @MyApp
  ...

  @Module1 = Module1

这将奏效,因为您已将Module1设置为全局的(声明class @Module1等同于编写@Module1 = class Module1,在该上下文中@指向window),而在class @MyApp体内,@指向类本身。
如果要在附加后使Module1仅成为全局MyApp类的属性,则可以添加以下行:
delete window.Module1

好主意!如果我的模块名称与页面上其他脚本中的名称冲突,那么仍然会存在问题,对吧?我很挑剔,因为我正在编写代码,这些代码可能会使用书签注入到其他页面中。 - nfm
啊,在这种情况下,我会只定义一个全局变量,然后将其附加到其他所有东西上。为此,请翻转您的依赖关系顺序:在application.js中,要求module1.js,而在module1.js中,要求myapp.js,以便首先定义全局变量MyApp。然后您可以编写class MyApp.Module... - Trevor Burnham
我的上面的答案解决了名称冲突的问题。使用类很好,但它不像Ruby中可以进行Monkey Patching。它会清除任何先前定义的类。https://dev59.com/Smw15IYBdhLWcg3wIYK_#6826293 解决了这个问题。 - bradgonesurfing

3

我有一个模块解决方案,我在我的代码中使用。

我定义我的模块如下:

@module "foo", ->
    @module "bar", ->
        class @Amazing
            toString: "ain't it"

Amazing可以作为

foo.bar.Amazing

实现@module辅助程序是。
window.module = (name, fn)->
  if not @[name]?
    this[name] = {}
  if not @[name].module?
    @[name].module = window.module
  fn.apply(this[name], [])

这篇文章介绍了如何使用CoffeeScript轻松创建模块。具体信息请查看这里


1
谢谢提供链接。你有什么想法可以扩展它,使其在多个文件中运行得很好吗? - nfm
它可以跨多个文件工作。这就是它的全部意义所在。你所说的“好”是什么意思? - bradgonesurfing
我的意思是,不需要在每个文件中定义所有父模块。虽然这可能比我最初想象的要少烦人。 - nfm
你把 @module "foo" -> 代码放在哪里?可以放在 application.js 中吗?我认为它需要一个 .coffee 扩展名... - dmonopoly
@bradgonesurfing:CoffeeScript的创建者删除了链接-他显然认为它不太好...那么有没有正确的方法来组织JS文件?例如,我无法使用曾经在损坏的链接中提供的方法定义常量或“实例变量”以在“类”中全局使用... - dmonopoly

1
这是我用于使用Sprockets管理Coffeescript的模块化模式(也适用于Rails 4):
  # utils.js.coffee

  class Utils
    constructor: ->

    foo: ->
      alert('bar!!!')

    # private methods should be prefixed with an underscore
    _privateFoo: ->
      alert('private methods should not be exposed')

  instance = new Utils()

  # only expose the methods you need to.
  # because this is outside of the class,
  # you can use coffee's sugar to define on window

  @utils = foo: instance.foo

  # otherscript.js.coffee 

  //= require utils
  class OtherScript
    constructor: ->
      @utils.foo()         # alerts bar!!!
      @utils._privateFoo() # undefined method error

这种方法的一个缺点是你会将对象暴露在窗口上。根据您的需求,添加模块加载器或采用一些新的 ES 模块语法可能是一个不错的选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接