Code Splitting loads all bundles into html - javascript

How does code splitting know which chunks go with which entry? For example I have two pages, on page uses AgGrid and another page uses ReactTable for example.
I code split and get vendor.1.bundle.js (contains AgGrid) and vendor.2.bundle.js( contains ReactTable)
I set up my entry:
entry: {
index: './src/index.jsx',
review: './src/review.jsx'
},
optimization: {
splitChunks: {
chunks: 'all'
},
// plugins
new HtmlWebpackPlugin({
filename: 'index.html',
inject: true,
excludeChunks: ['review'],
template: 'src/pages/index.html'
}),
new HtmlWebpackPlugin({
filename: 'review.html',
inject: true,
excludeChunks: ['index'],
template: 'src/pages/review.html'
}),
What I end up with is two html files with both vendor files instead of one vendor file containing the deps needed per page.
Shouldn't index which uses AgGrid only load vendor.1.bundle.js and review.html load only vendor.1.html? I thought that was the point of code splitting. Otherwise why not just make 1 huge shared bundle between both pages, I think that would defeat the point. Not sure what I'm doing wrong in this setup however.

Related

Load chunks/bundles as needed (like SystemJS)

Using Webpack, I have multiple chunks/bundles being created so that the entire app is not loaded at once. I've hand-chosen which dependencies I want to be moved into their own chunks. Here is the important part of my config:
module.exports = {
devtool: 'inline-source-map',
mode: process.env.NODE_ENV,
entry: {
main: './src/index.tsx',
},
optimization: {
runtimeChunk: {
name: 'runtime',
},
splitChunks: {
cacheGroups: {
...makeChunkCacheGroup('chunk_1', /\/node_modules\/(... list of deps ...)(\/|$)/),
...makeChunkCacheGroup('chunk_2', /\/node_modules\/(... list of deps ...)(\/|$)/),
},
},
},
// ...
};
function makeChunkCacheGroup(name, ...moduleNameRegexps) {
return {
[name]: {
name,
test: module => moduleNameRegexps.some(pattern => pattern.test(module.context)),
chunks: 'all',
minChunks: 1,
minSize: 0,
maxSize: Infinity,
reuseExistingChunk: true,
enforce: true,
},
};
}
This config gives me runtime, main, chunk_, and chunk_2. However, all of these chunks are injected into index.html, thus they all load during the initial page load instead of dynamically (as I naively expected).
I've used SystemJS in the past to bundle things up into multiple bundles and it would only download a given bundle as it was required by the app. I now realize that Webpack does not work this way.
Is there a way to make Webpack only download the runtime and main bundles initially, and then download the other bundles as they're needed?
Note 1: I realize that I can use dynamic imports e.g. import('some-dep').then(...), but it's not reasonable to do so based on the size of the codebase, and also, I think this sort of thing is better left to configuration (a module shouldn't have to pick and choose which deps it should load dynamically).
Note 2: I did try to specify multiple entry points but never got it working. The app really only has a single entry point. But, for instance, we have multiple directories under src/app/elements/, and it'd be perfect if each of those directories ended up in its own bundle which was then dynamically loaded. I couldn't get this working in an automated/smart way.

Webpack 3 and CommonsChunkPlugin - Exclude specific entry point from requiring Vendor

I'm working on a large piece of business software which is slowly being migrated to use Webpack on all pages. I want to start incorporating some webpack-bundled helper typescript code into pages which haven't yet started using webpack - they include scripts the old-fashioned way.
Here are the relevant parts of the config:
entry: {
vendor: [
// All vendor scripts go here
],
// All entry points for 'webpack' pages...
// A special entry point that I want to inject into non-webpack pages
es5Compatibility: [
'Utility/ES5Compatibility.ts'
]
},
// ...
output: {
path: path.join(__dirname, 'Scripts/bundle'),
filename: '[name].[chunkhash].bundle.js',
chunkFilename: '[id].chunk.js'
},
plugins: [
// ...
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
})
// This should NOT get injected into non-webpack pages, as all of the
// vendor dependencies already exist there
new HtmlWebpackPlugin({
chunks: ['vendor'],
template: 'bundleTemplate.ejs',
inject: false,
filename: '_VendorBundle.html'
}),
// This should get injected into non-webpack pages WITHOUT requiring
// the above bundle to be included
new HtmlWebpackPlugin({
chunks: ['es5Compatibility'],
template: 'bundleTemplate.ejs',
inject: false,
filename: '_ES5CompatibilityBundle.html'
}),
// ...
new webpack.HashedModuleIdsPlugin(),
]
The problem is, when the _ES5CompatibilityBundle.html is included in a page without the VendorBundle.html, I get the following Javascript error as Webpack expects that the vendor bundle was included:
Uncaught ReferenceError: webpackJsonp is not defined
How can I tell Webpack to bundle the ES5 compatibility bundle as a 'self-contained' bundle, while retaining the commons chunk functionality for the webpack pages?

How to create a webpack chunk for a dependency of a single angular component

I am building an angular app, which contains a single component which depends on a library in my node_modules. I would like to create a single chunk specifically for the dependency so that my users are able to cache it, as the component may change regularly, but the dependency will only be updated every few weeks (whenever it has an update).
I have tried various splits in my webpack config, which always resulted in either all node_modules in a single chunk, or the the component and its dependency together in one chunk.
Is there a way to configure webpack to always split a vendor into its own chunk if it is not loaded initially?
I achieved the desired effect with the following config:
optimization: {
splitChunks: {
cacheGroups: {
initVendors: {
chunks: 'initial',
test: /[\\/]node_modules[\\/]/,
},
asyncVendors: {
chunks: 'async',
test: /[\\/]node_modules[\\/]/,
},
},
},
},
initVendors
With chunks: 'initial' we advise webpack to only group modules that are required for the initial pageload.
initVendors: {
chunks: 'initial',
test: /[\\/]node_modules[\\/]/,
}
asyncVendors
With chunks: 'async' we advise wepack to only group modules that can be loaded into the page async, while browsing, for features that are not required on pageload.
This will also place these modules into their own chunks, instead of bundling them with the code in your application that is using them. This allows long term caching of a chunk.
asyncVendors: {
chunks: 'async',
test: /[\\/]node_modules[\\/]/,
},

How to add a js file with webpack?

I was reading this webpack tutorial:
https://webpack.github.io/docs/usage.html
It says it bundles the src files and node_modules. If I want to add another .js file there, how can I do this? This is a thirdpartyjs file that is not part of the source and not part of the node_modules files. This is my current webpack.config.js:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./app/app.js'
],
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/dist/",
filename: "dist.js",
sourceMapFilename: "dist.map"
},
devtool: 'source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
],
module: {
loaders: [{
loader: 'babel',
exclude: /node_modules/
}]
},
devServer: {
inline: true
},
node: {
fs: "empty"
},
watch: false
}
The start point for code is the entry field in config. In your config entry point is the list of files. Webpack gets all, resolve their dependencies and output in one file.
You have two options for adding third party script:
add the file path to entry list before app.js
require this file from app.js
In response to Dmitry's answer:
add the file path to entry list before app.js
This has the effect that you will get a bundled .js file for each entry point, which you might not want.
require this file from app.js
You might not have access to app.js if it is written dynamically, or for whatever reason you might not want to edit app.js.
Another option:
You can use webpack-inject-plugin to inject any JS code as string into the resulting .js bundle created by webpack. This way you can read the File you want to inject as a string (e.g. fs.readFile in nodejs) and inject it with the plugin.
Another solution but without using any extra plugins:
//Webpack.config.js
entry: {
main: './src/index',
/**
/* object is passed to load script at global scope and exec immediately
/* but if you don't need then simply do:
/* myCustomScriptEntry: './src/myCustomScript'
*/
myCustomScriptEntry: {
import: './src/myCustomScript',
library: {
name: 'myCustomScriptEntry',
type: 'var',
},
},
},
new HtmlWebpackPlugin({
template: './public/index.html',
excludeChunks: ['myCustomScriptEntry'], //exclude it from being autoreferenced in script tag
favicon: './public/favicon.svg',
title: 'Alida',
}),
and
//index.html
<script type="text/javascript" src="<%= compilation.namedChunks.get('myCustomScriptEntry').files[0] %>"></script>

How to polyfill fetch and promise with webpack 2?

How to polyfill fetch and promise for Webpack 2?
I have a lot of entry points, so Webpack 1-way to add them before each entry point is not desired solution.
Regardless of how many entry points you have, you should have a separate file for your vendor files, such as frameworks (react, angular, whatevs) and any libraries you always need but are rarely going to change. You want those as a separate bundle so you can cache it. That bundle should always be loaded. Anything you include in that bundle will always be available but never repeated in your chunks if you use it with the commonChunksPlugin.
Here's a sample from an app I've done (just showing relevant config options):
module.exports = {
entry: {
client: 'client',
vendor: [
'react',
'react-addons-shallow-compare',
'react-addons-transition-group',
'react-dom',
'whatwg-fetch'
]
},
output: {
path: `${__dirname}/dist`,
filename: '[name].js',
publicPath: '/build/'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest']
})
]
}
Maybe I'm not understanding correctly, but couldn't you just add babel-polyfill before the rest of your entry points in your webpack config?
module.exports = {
entry: ['babel-polyfill', './app/js', '/app/js/whatever']
};

Categories

Resources