How do I properly separate my app.js and vendor.js

2019-05-16 10:10发布

问题:

My goals are to include the following in my HTML file and have them all work properly:

index.html:

<script type="text/javascript" src="./vendor.js"></script>
<script type="text/javascript" src="./app.js"></script>
$(document).ready(function() {
    // jQuery should be available as `window.$`
    $(".myclass").doSomethingWithJquery(); 
}
<div class="row">
    <h1 class="col-md-3 col-md-offset-3">
        I should be able to use bootstrap, including bootstrap's javascript libraries, in my templates
    </h1>
</div>
<!-- I should be able to use app.js, with its various require('module') 
statements and attach rendered HTML to the DOM (I'm using React) -->
<div id="attach-app-js"></div>

Therefore:

  1. jQuery should be available as a global $ variable.
  2. Bootstrap's javascript functions should also work, as they're part of vendor.js
  3. My app.js should also work and not re-declare variables like $ that is already declared under global scope via vendor.js.

See the files below. My current solution errors where I have $(".myclass").doSomethingWithJquery(); When my internet is off, I get the error:

Uncaught ReferenceError: $ is not defined

...which makes me think that (maybe) snackbarjs or another module is leaking jQuery to global scope.

Then, when I change the line in vendor.js to: var $ = jQuery = require('jquery'); I get the error:

Uncaught ReferenceError: $ is not defined

but now it's getting called on the line $(document).ready(function.....

Third, if I comment out vendor.js entirely, I get both errors:

Uncaught ReferenceError: $ is not defined

Uncaught ReferenceError: jQuery is not defined

How can I get this frontend setup to correctly shim variables when necessary in the global scope, and not if it's not necessary??

gulpfile.js:

.task('vendor', ['clean'], function() {
    var b = browserify(package.paths.vendor);
    var packages = getBowerPackageIds();

    packages.forEach(function (id) {

      var resolvedPath = bowerResolve.fastReadSync(id);

      b.require(resolvedPath, {

        // exposes the package id, so that we can require() from our code.
        // for eg:
        // require('./vendor/angular/angular.js', {expose: 'angular'}) enables require('angular');
        // for more information: https://github.com/substack/node-browserify#brequirefile-opts
        expose: id

      });
    });
    return b
    .bundle()
    .pipe(source(package.dest.vendor))
    .pipe(gulp.dest(package.dest.dist));
  })

.task('js', ['clean', 'install'], function() {
    var b = browserify(package.paths.app);
    // mark vendor libraries defined in bower.json as an external library,
    // so that it does not get bundled with app.js.
    // instead, we will load vendor libraries from vendor.js bundle
    getBowerPackageIds().forEach(function (lib) {
      b.external(lib);
    });

    var w = watchify(b, watchify.args);
    var file = package.dest.app,
        dest = package.dest.dist;

    w = w
      .transform(reactify)
      .transform(browserifyShim);

    w.on('update', rebundle);

    function rebundle() {
        return w.
            bundle()
            .on('error', errorHandler)
            .pipe(source(file))
            .pipe(gulp.dest(dest))
            .pipe(shell([
              'python manage.py collectstatic --noinput'
            ], {
                cwd: '../'
            }))
            // TODO: Do I need this?
            .pipe(browserSync.reload({stream: true}));
    }
    return rebundle();
  })
/**
   * Running livereload server
   */
  .task('server', ['clean', 'install', 'vendor', 'js', 'less'], function() {
    browserSync({
      proxy: "localhost:8000"
    });
  })
/**
   * Compiling resources and serving application
   */
  .task('serve', ['install', 'backup', 'clean', 'lint', 'less', 'vendor', 'js', 'server'], function() {
    return gulp.watch([
      package.paths.js,
      package.paths.app,
      package.paths.html,
      package.paths.less,
      package.paths.python
    ], [
     'lint', 'less', browserSync.reload
    ]);
  })

vendor.js:

$ = jQuery = require('jquery');
require('bootstrap');
require('snackbarjs');

package.json (browserify-shim config):

  "browserify-shim": {
    "jquery": "$",
    "bootstrap": {
      "depends": [
        "jquery:jQuery"
      ],  
      "exports": "bootstrap"
    },  
    "jquery-cookie": {
      "depends": [
        "jquery:jQuery"
      ]   
    },  
    "eonasdan-bootstrap-datetimepicker": {
      "depends": [
        "jquery:jQuery",
        "moment:moment",
        "bootstrap:bootstrap"
      ],  
      "exports": "$.fn.datetimepicker"
    },  
    "image-picker": {
      "depends": [
        "jquery:jQuery"
      ],  
      "exports": "$.fn.imagepicker"
    },  
    "raven-js": {
      "depends": [
        "jquery:jQuery"
      ],  
      "exports": "raven-js"
    },  
    "parsleyjs": {
      "depends": [
        "jquery:jQuery"
      ],  
      "exports": "parsleyjs"
    }   
  },  
  "browser": {
    "jquery": "./bower_components/jquery/dist/jquery.js",
    "jquery-cookie": "./bower_components/jquery-cookie/jquery.cookie.js",
    "image-picker": "./bower_components/image-picker/image-picker/image-picker.js",
    "eonasdan-bootstrap-datetimepicker": "./bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js",
    "moment": "./bower_components/moment/moment.js",
    "bootstrap": "./bower_components/bootstrap/dist/js/bootstrap.js",
    "raven-js": "./bower_components/raven-js/dist/raven.js",
    "parsleyjs": "./bower_components/parsleyjs/dist/parsley.js"
  },