【问题标题】:How to solve a conflict between browserify (Backbone based app) and require.js on the same page?如何解决 browserify(基于 Backbone 的应用程序)和 require.js 在同一页面上的冲突?
【发布时间】:2025-12-01 22:25:01
【问题描述】:

我有一个主干应用程序启动并正常运行。它旨在用作第三页中的小部件。不幸的是,我刚刚意识到其中一个页面已经加载了 Backbone / underscore。

我收到如下错误:

Uncaught TypeError: Cannot read property 'extend' of undefined 

通常在之前未加载下划线时出现。

我的典型视图是这样的:(正常的 Backbone 视图)

./view1.js

var Backbone = require('backbone')
var _ = require('underscore')
var $ = require('jquery')
Backbone.$ = $

module.exports = Backbone.View.extend({

  events: {

  },

  initialize: function () {

  },

  render: function () {

  }
})

那我只需要调用它:

var View1 = require('view1')
var view1Instance = new View1(...)

感谢您的帮助:)

调查后编辑: 通过调试器运行时,Backbone 变量似乎是一个空对象,而不是 Backbone。好像require('backbone') 刚刚返回了{}

编辑2: 好像和这个问题有关:https://github.com/substack/node-browserify/issues/790

【问题讨论】:

  • 你能给我们提供更多关于这个错误的细节吗?是 Backbone.View.extend 失败了吗?
  • @ben 我提到的错误发生在module.exports = Backbone.View.extend(我也编辑了问题)
  • 当你有循环依赖时,通常会出现空对象。但是在需要 Backbone 时不应该发生这种情况。此外,noConflict 应该无关紧要,因为从技术上讲,捆绑包中的所有内容都与环境的其余部分隔离。
  • 会不会和他们使用require.js有关?

标签: backbone.js requirejs underscore.js browserify


【解决方案1】:

Backbone 和 Requirejs 的问题 (as indicated by thlorenz here)

  • backbone 在查找 module.exports 之前查找 define
  • requirejs 与全局变量一起工作,这不是一个好的做法,在这种情况下会导致问题,因为它会影响在同一环境中运行的所有内容 浏览器标签

他建议将所有内容包装在一个范围内并隐藏define 函数。 他还有一个工具可以做到这一点。browserify-shim

但是我没有使用它,而是 Browserify 的 postBundleCB 选项:(感谢同事)。

在我的Gruntfile:

browserify: {
  dist: {
    src: ['src/app/app.js'],
    dest: 'www/app.js',
    options: {
      postBundleCB: function (err, src, next) {
        return next(null, '(function () { var define = undefined; '+src+' })();')
      }
    }
  }
},

这解决了我的问题:)

没有尝试browserify-shim,所以不太了解。

【讨论】:

  • 这是迄今为止我修复 requirejs 与我的库冲突的最简单方法(已浏览)。 @balazs 解决方案也有效,但我喜欢包装我的代码而不是在它之前放置一些东西的方法(尽管如果你的代码中已经有一个自执行匿名函数,这可能是重复的)。
  • 谢谢。我得到Fatal error: The "list[1]" argument must be one of type Array, Buffer, or Uint8Array. Received type string 将代码包装在缓冲区中为我解决了这个问题return next(null, Buffer.from('(function () { var define = undefined; '+src.toString()+' })();'))
【解决方案2】:

如果有人使用 gulp 和官方 browserify(因此 postBundleCB 不可用),我使用 gulp-wrap 来包装“定义”定义:

var wrap = require('gulp-wrap');

[...]

bundler.bundle()
  .on('error', swallowError)
  .pipe(source(file))
  .pipe(wrap('(function () { var define = undefined; <%=contents%> })();'))
  .pipe(rename(renameOptions))
  .pipe(gulp.dest(argv.output?argv.output:'./dist'));

【讨论】:

    【解决方案3】:

    另一种解决方法是使用这样的自定义前奏:

    // modules are defined as an array
    // [ module function, map of requireuires ]
    //
    // map of requireuires is short require name -> numeric require
    //
    // anything defined in a previous bundle is accessed via the
    // orig method which is the requireuire for previous bundles
    
    (function outer (modules, cache, entry) {
        // Save the require from previous bundle to this closure if any
        var previousRequire = typeof require == "function" && require;
    
        function newRequire(name, jumped){
            var oldDefine = window.define;
            window.define = undefined;
            if(!cache[name]) {
                if(!modules[name]) {
                    // if we cannot find the the module within our internal map or
                    // cache jump to the current global require ie. the last bundle
                    // that was added to the page.
                    var currentRequire = typeof require == "function" && require;
                    if (!jumped && currentRequire) return currentRequire(name, true);
    
                    // If there are other bundles on this page the require from the
                    // previous one is saved to 'previousRequire'. Repeat this as
                    // many times as there are bundles until the module is found or
                    // we exhaust the require chain.
                    if (previousRequire) return previousRequire(name, true);
                    var err = new Error('Cannot find module \'' + name + '\'');
                    err.code = 'MODULE_NOT_FOUND';
                    throw err;
                }
                var m = cache[name] = {exports:{}};
                modules[name][0].call(m.exports, function(x){
                    var id = modules[name][1][x];
                    return newRequire(id ? id : x);
                },m,m.exports,outer,modules,cache,entry);
            }
            window.define = oldDefine;
            return cache[name].exports;
        }
        for(var i=0;i<entry.length;i++) newRequire(entry[i]);
    
        // Override the current require with this new one
        return newRequire;
    })
    

    你可以像这样为 browserify 提供这个前奏:

    browserify({
      prelude: fs.readFileSync(__dirname + '/src/js/prelude.js', 'utf-8'),
      ...
    })
    

    【讨论】:

      【解决方案4】:

      这可能与在具有 requirejs 的站点中使用 browserify 的人有关。临时解决方案可以是,而不是:

      `.pipe(wrap('(function () { var define = undefined; <%=contents%> })();'))`
      

      对于直接使用 browserify API 的人,可以在加载我们的包之前添加这段代码:

      (function() {
      if (typeof define === "function" && define.amd) {
          define.amd= false; 
      }})();
      

      这将允许我们继续执行,而不会破坏客户端的代码。

      我希望这很有用。

      【讨论】:

        最近更新 更多