Webpack с небольшим начальным script и асинхронной загрузкой всех других скриптов

Я начал использовать Webpack при разработке обычных веб-сайтов, состоящих из числовых страниц и разных типов страниц. Я привык к загрузчику RequireJs script, который загружает все зависимости по требованию, когда это необходимо. Только небольшая часть javascript загружается при загрузке страницы.

Я хочу достичь этого:

  • Небольшой начальный файл javascript, который загружает зависимости асинхронно
  • Каждый тип страницы может иметь свой собственный javascript, который также может иметь зависимости.
  • Общие модули, скрипты поставщиков должны быть объединены в общие скрипты

Я пробовал много конфигураций для достижения этого, но без успеха.

entry: {
    main: 'main.js', //Used on all pages, e.g. mobile menu
    'standard-page': 'pages/standard-page.js',
    'start-page': 'pages/start-page.js',
    'vendor': ['jquery']
},
alias: {
    jquery: 'jquery/dist/jquery.js'
},
plugins: [
    new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
    new webpack.optimize.CommonsChunkPlugin('common.js')
]

Ответы

Ответ 1

Решение этой проблемы двукратно:

Разделение кода

Прежде чем приступать к использованию webpack, вам нужно отучиться от зависимости от конфигурации. Require.js - это все файлы конфигурации. Этот менталитет затруднил переход к webpack, который более точно моделируется после CommonJS в node.js, который не использует никакой конфигурации.

С учетом этого рассмотрим следующее. Если у вас есть приложение, и вы хотите, чтобы он асинхронно загружал некоторые другие части javascript, вам нужно использовать одну из следующих парадигм.

Require.ensure

require.ensure - это один из способов создания "точки разделения" в вашем приложении. Опять же, вы, возможно, подумали, что вам нужно будет сделать это с настройкой, но это не так. В примере, когда я нажал require.ensure, в моем файле webpack автоматически создаст второй комплект и загрузит его по требованию. Любой код, выполняемый внутри этой разделенной точки, будет объединен в отдельный файл.

require.ensure(['jquery'], function() {
    var $ = require('jquery');
    /* ... */
});

Require ([])

Вы также можете добиться того же, что и с AMD-версией require(), той, которая принимает массив зависимостей. Это также создаст ту же точку разделения:

require(['jquery'], function($) {
    /* ... */
});

Общие пакеты

В приведенном выше примере вы используете entry для создания пакета vendor с jQuery. Вам не нужно вручную указывать эти пакеты зависимостей. Вместо этого, используя точки разделения выше вас, webpack автоматически сгенерирует это.

Используйте entry только для отдельных тегов <script>, которые вы хотите на своих страницах.

Теперь, когда вы сделали все это, вы можете использовать CommonsChunkPlugin для дополнительной оптимизации ваших кусков, но опять-таки большая часть волшебства сделана для вас и вне определения того, какие зависимости должны быть разделены, вам не нужно будет делать что-нибудь еще. webpack автоматически вытащит общие куски, не требуя дополнительных тегов <script> или entry.

Заключение

Сценарий, который вы описываете (несколько тегов <script>), может быть не таким, каким вы хотите. С помощью webpack все зависимости и связки могут управляться автоматически, начиная только с одного тега <script>. Пройдя несколько итераций рефакторинга из require.js в webpack, я обнаружил, что обычно это самый простой и лучший способ управлять вашими зависимостями.

Все самое лучшее!

Ответ 2

Вот решение, которое я придумал.

Сначала экспортируйте эти две функции в window.* - вы захотите их в браузере.

export function requireAsync(module) {
    return new Promise((resolve, reject) => require(`bundle!./pages/${module}`)(resolve));
}

export function runAsync(moduleName, data={}) {
    return requireAsync(moduleName).then(module => {
        if(module.__esModule) {
            // if it an es6 module, then the default function should be exported as module.default
            if(_.isFunction(module.default)) {
                return module.default(data);
            }
        } else if(_.isFunction(module)) {
            // if it not an es6 module, then the module itself should be the function
            return module(data);
        }
    })
}

Затем, когда вы хотите включить один из ваших скриптов на странице, просто добавьте это в свой HTML:

<script>requireAsync('script_name.js')</script>

Теперь все в каталоге pages/ будет предварительно скомпилировано в отдельный фрагмент, который может быть асинхронно загружен во время выполнения, только когда это необходимо.

Кроме того, используя вышеописанные функции, теперь у вас есть удобный способ передачи данных на стороне сервера в ваши клиентские скрипты:

<script>runAsync('script_that_needs_data', {my:'data',wow:'much excite'})</script>

Теперь вы можете получить к нему доступ:

// script_that_needs_data.js
export default function({my,wow}) {
    console.log(my,wow);
}