Crie pacotes individuais de SPAs com o Webpack
Como uso o Webpack para criar pacotes independentes de SPA que podem ou não ser carregados dinamicamente quando meu usuário navega no meu SPA?
Eu tenho um módulo de contatos e um módulo de tarefas. Ambos têm duas dependências. Quero que o WebPack crie pacotes para cada um que é carregado quando (e se) for necessário.
O código está abaixo. O problema parece ser que cada uma dessas entradas está sendo vista como pontos de entrada do aplicativo e, portanto, o código de inicialização do webpack é inserido nele.
Eu já vi vários exemplos comCommonsChunkPlugin
mas não consigo encontrar uma referência / documentação da API e, pelo que posso supor, não é isso que eu quero.
Editar - encontrou esses documentosaquie adicionamos uma tentativa com esse plug-in abaixo em minha edição.
Configuração atual
module.exports = {
entry: {
contacts: './contacts',
tasks: './tasks'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name]-bundle.js'
}
};
Contacts.js
define(['./ca', './cb'], function(ca, cb){
var name = 'Contacts';
alert(ca + ' ' + cb);
});
Tasks.js
define(['./ta', './tb'], function(ta, tb){
var name = 'TASKS Main';
alert(ta + ' ' + tb);
});
tasks-bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(3), __webpack_require__(4)], __WEBPACK_AMD_DEFINE_RESULT__ = function(ta, tb){
var name = 'TASKS Main';
alert(ta + ' ' + tb);
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ },
/* 1 */,
/* 2 */,
/* 3 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
var name = 'TASKS - A';
alert('ta');
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
var name = 'TASKS - B';
alert('tb');
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ }
/******/ ]);
EDITAR
Aqui está minha tentativa número 2 com o CommonsChunkPlugin. Eu criei um app.js fictício
app.js
var module = window.location.hash.split('/')[0];
alert(module);
Em seguida, movi todos os meus arquivos de contatos e tarefas para uma pasta de componentes, mas os deixei em paz. Minha nova configuração:
module.exports = {
entry: {
app: './app'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name]-bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: './components/contacts',
filename: 'contacts-component-bundle.js'
}),
new webpack.optimize.CommonsChunkPlugin({
name: './components/tasks',
filename: 'tasks-component-bundle.js'
})
]
};
Estranhamente, agoraapp-bundle.js parece não ter nenhum código de inicialização do Webpack
webpackJsonp([0,1,2],[
/* 0 */
/***/ function(module, exports) {
var module = window.location.hash.split('/')[0];
alert(module);
/***/ }
]);
contatos-componentes-pacote.js agora só tem isso
webpackJsonp([1,2],[]);
etask-components-bundle.js parece ter todo o meu código de inicialização do webpack
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ var parentJsonpFunction = window["webpackJsonp"];
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, callbacks = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId])
/******/ callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/ while(callbacks.length)
/******/ callbacks.shift().call(null, __webpack_require__);
/******/ if(moreModules[0]) {
/******/ installedModules[0] = 0;
/******/ return __webpack_require__(0);
/******/ }
/******/ };
/******/ // The module cache
/******/ var installedModules = {};
/******/ // object to store loaded and loading chunks
/******/ // "0" means "already loaded"
/******/ // Array means "loading", array contains callbacks
/******/ var installedChunks = {
/******/ 2:0,
/******/ 1:0
/******/ };
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = function requireEnsure(chunkId, callback) {
/******/ // "0" is the signal for "already loaded"
/******/ if(installedChunks[chunkId] === 0)
/******/ return callback.call(null, __webpack_require__);
/******/ // an array means "currently loading".
/******/ if(installedChunks[chunkId] !== undefined) {
/******/ installedChunks[chunkId].push(callback);
/******/ } else {
/******/ // start chunk loading
/******/ installedChunks[chunkId] = [callback];
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
/******/ script.type = 'text/javascript';
/******/ script.charset = 'utf-8';
/******/ script.async = true;
/******/ script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"app","1":"./components/contacts"}[chunkId]||chunkId) + "-bundle.js";
/******/ head.appendChild(script);
/******/ }
/******/ };
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ })
/************************************************************************/
/******/ ([]);
Novamente, estou apenas tentando usar o Webpack para obter uma prova de conceito do SPA em funcionamento, com algum tipo de ponto de entrada raiz app.js e, em seguida, algum número arbitrário de módulos / componentes que são carregados sob demanda. Isso é trivialmente fácil com o requirejs, então eu tenho que imaginar que estou perdendo algo importante aqui, especialmente com todos os artigos que vi falando sobre o quão bom é o Webpack para SPAs.
EDIT 2
Pela resposta de bebraw abaixo, tentei o seguinte:
app.js
var mod = window.location.hash.split('/')[0];
alert(mod);
require.ensure([], function() {
require('./components/' + mod).show();
});
webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
app: './app'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name]-bundle.js'
}
};
E então, na minha pasta de compilação, fico com app-bundle.js, que possui todo o meu código de bootstrap e meu código app.js, e então 1.1-bundle.js, que possuitudo do meu código de tarefas e contatos.
Eu tenhoAlém disso tentei isso
module.exports = {
entry: {
app: './app'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name]-bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: './components/contacts',
filename: 'contacts-component-bundle.js',
children: true
}),
new webpack.optimize.CommonsChunkPlugin({
name: './components/tasks',
filename: 'tasks-component-bundle.js',
children: true
})
]
};
Que produz o mesmo que o acima, mas agoraAlém disso possui task-component-bundle.js e contatos-component-bundle.js, ambos comsó algum código de inicialização do webpack; o código de tarefas e contatos ainda está no pacote 1.1.
Mais uma vez, eu simplesmente quero poder dizer ao Webpack, de uma forma ou de outra, para agrupar módulos individuais e suas dependências para subsequente carregamento lento e assíncrono, quando necessário.
A resposta final foi dada por Tobias - criador do Webpack - abaixo, que colocarei aqui para a posteridade.
Verdadeiramente dinâmico não é possível. O webpack (em construção para require.js) compila seu aplicativo antes de executá-lo e não tem acesso às informações de tempo de execução. A dinâmica requer mergulho na Webpack em todas as pastas possíveis, desde que sua expressão dinâmica não contenha ... Você deve ser capaz de configurá-la para usar mod + '/' + mod com o ContextReplacementPlugin e um pouco de magia RegExp (use as referências anteriores em o RegExp). Por padrão, incluiria muitos módulos.