Unable to load jQuery plugins when using requirejs

2019-04-26 18:42发布

问题:

I'm having a bit of trouble with my the requirejs optimizer. After I run the optimizer, I get a few error messages in my build/compiled file. When running my web application without the optimize step I do not have any errors.

This is my client.js file (contains config) (coffeescript)

requirejs.config
  baseUrl: '/source/'
  paths:
    text:                 'lib/text'
    io:                   'lib/socket.io'
    underscore:           'lib/underscore'
    backbone:             'lib/backbone'
    jquery:               'lib/jquery'
#    almond:               'lib/almond'
    bootstrap:            'lib/bootstrap'
    bootstrapFileUpload:  'lib/bootstrap-fileupload'
    jqueryUniform:        'lib/jquery.uniform'
    jqueryBrowser:        'lib/jquery.browser'
    datatables:           'lib/jquery.dataTables'
    datatables_bootstrap: 'lib/DT_bootstrap'
  shim:
    io:
      exports: 'io'
    jquery:
      exports: 'jQuery'
    jqueryBrowser:
      deps:    ['jquery']
    jqueryUniform:
      deps:    ['jqueryBrowser', 'jquery']
    underscore:
      exports: '_'
    backbone:
      deps:    ['underscore', 'jquery']
      exports: 'Backbone'
    datatables_bootstrap:
      deps:    ['jquery', 'datatables']
    datatables:
      deps:    ['jquery']


require ['routers/router', 'backbone'], (Router, Backbone) ->
  MainRouter = new Router()
  Backbone.history.start()

And here is my config for the optimizer. I run the optimizer from nodejs after requiring 'requirejs' as a module.

  config =
    baseUrl: __dirname + '/../client/source'
    name:    'lib/almond'
    include: './client'
    optimize: 'none'
    out:     __dirname + '/../client/' + hash + '.js'
    paths:
      text:                 'lib/text'
      io:                   'lib/socket.io'
      underscore:           'lib/underscore'
      backbone:             'lib/backbone'
      jquery:               'lib/jquery'
      bootstrap:            'lib/bootstrap'
      bootstrapFileUpload:  'lib/bootstrap-fileupload'
      jqueryUniform:        'lib/jquery.uniform'
      jqueryBrowser:        'lib/jquery.browser'
      datatables:           'lib/jquery.dataTables'
      datatables_bootstrap: 'lib/DT_bootstrap'
    shim:
      bootstrap:
        exports: 'bootstrap'
      bootstrapFileUpload:
        exports: 'bootstrapUpload'
      io:
        exports: 'io'
      jquery:
        exports: 'jQuery'
      jqueryBrowser:
        deps:    ['jquery']
      jqueryUniform:
        deps:    ['jqueryBrowser', 'jquery']
      underscore:
        exports: '_'
      backbone:
        deps:    ['underscore', 'jquery']
        exports: 'Backbone'
      datatables:
        deps:    ['jquery']
      datatables_bootstrap:
        deps:    ['jquery', 'datatables']



  requirejs.optimize config, (buildResponse) ->
    js = true
    if js && css
      require './server'
  , (err) ->
    console.log 'requirejs err'
    console.log err

The specific error I'm seeing in chrome is: "Uncaught TypeError: Cannot read property 'defaults' of undefined"

Which correlates to this snippet:

/* Set the defaults for DataTables initialisation */
$.extend( true, $.fn.dataTable.defaults, {

Any idea what might be going wrong? Thanks!

回答1:

I encountered the same issue. I think the reason this error occurs is because DT_bootstrap.js is not a AMD module while it depends on the side effects of one. In this case jquery.dataTables.js.

When RequireJS optimizer combines all the modules you reference into one big JS file, the raw DT_bootstrap.js is somewhere in the middle of it, some place after jquery.dataTables.js. The problem is that DT_bootstrap.js is evaluated immediately when your combined js file is loaded. It wants $.fn.dataTable to be defined when it encounters the line:

$.extend( true, $.fn.dataTable.defaults, {

Since jquery.dataTables.js is a AMD module it has been compiled but not evaluated yet. Only in later code where it is required as a dependency will it be evaluated, and only then will it define $.fn.dataTable.

I worked around this by wrapping 'DT_bootstrap.js' in a AMD module definition, like is done here: https://github.com/amdjs/backbone/blob/master/backbone.js#L8-L24

For example:

(function(root, factory) {
  // Set up DT_bootstrap appropriately for the environment.
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['jquery', 'datatables', 'bootstrap'], function($) {
      factory($);
    });
  } else {
    // Browser globals
    factory(root.jQuery);
  }
}(this, function($) {
    // <--- original DT_bootstrap.js goes here 
}));

It solved the issue for me.



回答2:

Peter is almost correct. The only thing he missed was that the defines must match Casey's config. So in the above answer, instead of:

define(['jquery', 'dataTable', 'bootstrap'], function($) ...

it would need to be:

define(['jquery', 'datatables', 'bootstrap'], function($) ...

Otherwise require js will look for the file dataTable.js and not the one it needs to retrieve.



回答3:

Since require 2.1.11 the wrapShim option handle this problem, so you can keep the original source file.