Простое решение для обмена модулями, загружаемыми через NPM через несколько пакетов Browserify или Webpack

Вытягивая мои волосы здесь, ища простое решение для совместного использования кода, требуемого через NPM, через несколько пакетов Browserify или Webpack. Думаете, есть ли такая вещь, как файл "мост"?

Это не из-за времени компиляции (я знаю, что смотрю), а скорее из желания извлечь все мои специфические для своего поставщика библиотеки в vendor.js, чтобы сохранить размер файла app.js и не сбрасывать браузер с массивными исходными картами. Кроме того, я нахожу его более чистым, если возникнет необходимость в просмотре скомпилированных js. И так:

// vendor.js

require('react');
require('lodash');
require('other-npm-module');
require('another-npm-module');

Очень важно, чтобы код загружался из NPM, а не Bower, или сохранялся в каком-то каталоге "vendor", чтобы импортироваться через относительный путь и идентифицироваться с помощью прокладки. Я бы хотел, чтобы каждая ссылка библиотеки была загружена через 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? Я знаю, что создаю пакет с внешними файлами, а затем ссылаюсь на прямой файл (в, скажем, node_modules) через псевдоним, но я надеюсь найти более автоматическое решение и меньше "Require.js",

В принципе, мне интересно, возможно ли перевести два, чтобы app.js мог заглянуть внутрь vendor.js, чтобы разрешить зависимости. Это кажется простой и простой операцией, но я не могу найти ответ нигде в этой широкой широкой сети.

Спасибо!

Ответы

Ответ 1

С webpack вы будете использовать несколько точек входа и CommonChunkPlugin.

Взято из документов в веб-пакете:


Чтобы разделить ваше приложение на 2 файла, скажем app.js и vendor.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 vendor.bundle.js до bundle.js.

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

Ответ 2

Список всех файлов/модулей поставщика и CommonChunkPlugin действительно рекомендуется. Это становится довольно утомительным, хотя и склонным к ошибкам.

Рассмотрим эти модули NPM: fastclick и mprogress. Поскольку они не приняли формат CommonJS, вам нужно предоставить webpack руку, например:

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

Теперь, предполагая, что вам нужно как fastclick, так и mprogress в вашем блоке поставщика, вы, вероятно, попробуете это:

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;

но в моей ситуации я бы сказал: "Поместите все, что не в исходное исходное дерево в куске поставщика".

Надеюсь, что это поможет.