在多个Browserify或Webpack捆绑包中共享通过NPM加载的模块的简单解决方案

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

本文关键字:NPM 加载 共享 简单 模块 解决方案 包中 Browserify Webpack      更新时间:2023-09-26

我在这里寻找一个简单的解决方案,通过NPM在多个Browserify或Webpack捆绑包中共享代码。思考,有没有文件"桥"这样的东西?

这并不是因为编译时(我知道watchify),而是希望将所有特定于供应商的库提取到vendor.js中,这样可以降低app.js的文件大小,并且不会因为大量的源映射而导致浏览器崩溃。此外,如果需要查看编译后的js,我发现它会更干净。因此:

// vendor.js
require('react');
require('lodash');
require('other-npm-module');
require('another-npm-module');

与Bower相反,从NPM加载代码,或者将代码保存到某个"供应商"目录中,以便通过相对路径导入并通过填充程序进行识别,这一点非常重要。除了我的实际应用程序源之外,我想保留通过NPM提取的每个库引用。

app.js中,我保留了所有的源代码,并通过externals数组将上面列出的供应商库排除在编译之外:

// app.js 
var React = require('react');
var _     = require('lodash');
var Component = React.createClass()
// ...

然后在index.html中,我需要两个文件

// index.html
<script src='vendor.js'></script>
<script src='app.js'></script>

使用Browserify或Webpack,如何使app.js能够"看到"通过npm加载的模块?我知道创建一个带有externals的bundle,然后通过别名引用直接文件(比如node_modules),但我希望找到一个更自动、不那么像"Require.js"的解决方案。

基本上,我想知道是否有可能将两者桥接,以便app.js可以查看vendor.js内部以解决依赖关系。这似乎是一个简单、直接的操作,但我似乎在这个广阔的网络上找不到答案。

谢谢!

列出所有供应商文件/模块并使用CommonChunkPlugin确实是推荐的方法。不过,这会变得非常乏味,而且容易出错。

考虑这些NPM模块:fastclickmprogress。由于他们没有采用CommonJS模块格式,您需要帮助webpack,如下所示:

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

现在假设您想在供应商区块中同时使用fastclickmprogress,您可能会尝试以下操作:

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

唉,它不起作用。您需要将呼叫匹配到require():

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

它变老了,即使有一些resolve.alias的把戏。这是我的变通方法。CommonChunkPlugin允许您指定一个回调,该回调将返回您是否希望模块包含在供应商区块中。如果你自己的源代码在一个特定的src目录中,其余的在node_modules目录中,只需根据它们的路径拒绝模块:

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;
      }
    )
  ]
};

其中,module.resource是到正在考虑的模块的路径。你也可以做相反的事情,如果模块在node_modules_dir内,只包括它,即:

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

但在我的情况下,我宁愿说:"将不在我的源代码树中的所有内容放在供应商块中"。

希望能有所帮助。

使用webpack,您将使用多个入口点和CommonChunkPlugin。

取自webpack文档:


要将应用程序拆分为两个文件,例如app.jsvendor.js,您可以在vendor.js中要求供应商文件。然后将此名称传递给CommonChunkPlugin,如下所示。

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

这将从应用程序块中删除供应商块中的所有模块。bundle.js现在只包含您的应用程序代码,没有任何依赖项。这些在vendor.bundle.js中。

在HTML页面中,在bundle.js之前加载vendor.bundle.js

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

// vendor anything coming from node_modules
minChunks: module => /node_modules/.test(module.resource)

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