Simple solution to share modules loaded via NPM across multiple Browserify or Webpack bundles

后端 未结 3 1112
Happy的楠姐
Happy的楠姐 2021-02-04 05:36

Pulling my hair out here looking for a simple solution to share code, required via NPM, across multiple Browserify or Webpack bundles. Thinking, is there such a thing

相关标签:
3条回答
  • 2021-02-04 05:55

    With webpack you'd use multiple entry points and the CommonChunkPlugin.

    Taken from the webpack docs:


    To split your app into 2 files, say app.js and vendor.js, you can require the vendor files in vendor.js. Then pass this name to the CommonChunkPlugin as shown below.

    module.exports = {
      entry: {
        app: "./app.js",
        vendor: ["jquery", "underscore", ...],
      },
      output: {
        filename: "bundle.js"
      },
      plugins: [
        new webpack.optimize.CommonsChunkPlugin(
            /* chunkName= */"vendor",
            /* filename= */"vendor.bundle.js"
        )
      ]
    };
    

    This will remove all modules in the vendor chunk from the app chunk. The bundle.js will now contain just your app code, without any of it’s dependencies. These are in vendor.bundle.js.

    In your HTML page load vendor.bundle.js before bundle.js.

    <script src="vendor.bundle.js"></script>
    <script src="bundle.js"></script>
    

    0 讨论(0)
  • 2021-02-04 06:06

    Listing all the vendor files/modules and using CommonChunkPlugin is indeed the recommended way. This gets pretty tedious though, and error prone.

    Consider these NPM modules: fastclick and mprogress. Since they have not adopted the CommonJS module format, you need to give webpack a hand, like this:

    require('imports?define=>false!fastclick')(document.body);
    require('mprogress/mprogress.min.css');
    var Mprogress = require('mprogress/mprogress.min.js'),
    

    Now assuming you would want both fastclick and mprogress in your vendor chunk, you would probably try this:

    module.exports = {
      entry: {
        app: "./app.js",
        vendor: ["fastclick", "mprogress", ...]
    

    Alas, it doesn't work. You need to match the calls to require():

    module.exports = {
      entry: {
        app: "./app.js",
        vendor: [
          "imports?define=>false!fastclick", 
          "mprogress/mprogress.min.css", 
          "mprogress/mprogress.min.js", 
          ...]
    

    It gets old, even with some resolve.alias trickery. Here is my workaround. CommonChunkPlugin lets you specify a callback that will return whether or not you want a module to be included in the vendor chunk. If your own source code is in a specific src directory, and the rest is in the node_modules directory, just reject the modules based on their path:

    var node_modules_dir = path.join(__dirname, 'node_modules'),
        app_dir          = path.join(__dirname, 'src');
    
    module.exports = {
      entry: {
        app: "./app.js",
      },
      output: {
        filename: "bundle.js"
      },
      plugins: [
        new webpack.optimize.CommonsChunkPlugin(
          /* chunkName= */"vendor",
          /* filename= */"vendor.bundle.js"
          function (module, count) {
           return module.resource && module.resource.indexOf(app_dir) === -1;
          }
        )
      ]
    };
    

    Where module.resource is the path to the module being considered. You could also do the opposite, and include only the module if it is inside node_modules_dir, i.e.:

           return module.resource && module.resource.indexOf(node_modules_dir) === 0;
    

    but in my situation, I'd rather say: "put everything that is not in my source source tree in a vendor chunk".

    Hope that helps.

    0 讨论(0)
  • 2021-02-04 06:07
    // vendor anything coming from node_modules
    minChunks: module => /node_modules/.test(module.resource)
    

    Source: https://github.com/webpack/webpack/issues/2372#issuecomment-213149173

    0 讨论(0)
提交回复
热议问题