diff --git a/antwar.config.js b/antwar.config.js index 79a1db2631d0..14d66547209e 100644 --- a/antwar.config.js +++ b/antwar.config.js @@ -67,6 +67,8 @@ module.exports = { /^\.\/.*\.md$/ ); }, { + 'code-splitting-import': '/guides/code-splitting-async', + 'code-splitting-require': '/guides/code-splitting-async/#require-ensure-', 'why-webpack': '/guides/comparison' } ), diff --git a/content/guides/code-splitting-import.md b/content/guides/code-splitting-async.md similarity index 53% rename from content/guides/code-splitting-import.md rename to content/guides/code-splitting-async.md index 7612c3d4cfe8..1a449a2e4b83 100644 --- a/content/guides/code-splitting-import.md +++ b/content/guides/code-splitting-async.md @@ -1,12 +1,19 @@ --- -title: Code Splitting - Using import() +title: Code Splitting - Async sort: 33 contributors: - simon04 - levy9527 + - pksjce + - rahulcs + - johnstew --- -## Dynamic import +This guide documents how to split your bundle into chunks which can be downloaded asynchronously at a later time. For instance, this allows to serve a minimal bootstrap bundle first and to asynchronously additional features later. + +webpack supports two similar techniques to achieve this goal: using `import()` (preferred, ECMAScript proposal) and `require.ensure()` (legacy, webpack specific). + +## Dynamic import: `import()` Currently, a "function-like" `import()` module loading [syntax proposal](https://github.com/tc39/proposal-dynamic-import) is on the way into ECMAScript. @@ -29,7 +36,7 @@ determineDate(); ``` T> Keep in mind that `import()` path cannot be fully dynamic (e.g., `import(Math.random())`). Rather either completely static (e.g., `import('./locale/de.json')`) or partially static (e.g., `import('./locale/' + language + '.json')`). -## Promise polyfill +### Promise polyfill W> `import()` relies on [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) internally. @@ -49,7 +56,7 @@ if (!window.Promise) { // or ... ``` -## Usage with Babel +### Usage with Babel If you want to use `import` with [Babel](http://babeljs.io/), you'll need to install/add the [`syntax-dynamic-import`](http://babeljs.io/docs/plugins/syntax-dynamic-import/) plugin while it's still Stage 3 to get around the parser error. When the proposal is added to the spec this won't be necessary anymore. @@ -98,7 +105,7 @@ Not using the `syntax-dynamic-import` plugin will fail the build with * `Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level`, or * `Module build failed: SyntaxError: Unexpected token, expected {` -## Usage with Babel and `async`/`await` +### Usage with Babel and `async`/`await` To use ES2017 [`async`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)/[`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) with `import()`: @@ -144,7 +151,7 @@ module.exports = { }; ``` -## `import` supersedes `require.ensure`? +### `import` supersedes `require.ensure`? Good news: Failure to load a chunk can be handled now because they are `Promise` based. @@ -156,14 +163,103 @@ require.ensure([], function(require) { }, "custom-chunk-name"); ``` -## `System.import` is deprecated +### `System.import` is deprecated The use of `System.import` in webpack [did not fit the proposed spec](https://github.com/webpack/webpack/issues/2163), so it was deprecated in [v2.1.0-beta.28](https://github.com/webpack/webpack/releases/tag/v2.1.0-beta.28) in favor of `import()`. +## `require.ensure()` + +W> `require.ensure()` is specific to webpack and superseded by `import()`. + +webpack statically parses for `require.ensure()` in the code while building. Any module that is referenced as a dependency or `require()`d within the callback function, will be added to a new chunk. This new chunk is written to an async bundle that is loaded on demand by webpack through jsonp. + +The syntax is as follows: + +```javascript +require.ensure(dependencies: String[], callback: function(require), chunkName: String) +``` + +* `dependencies` is an array of strings where we can declare all the modules that need to be made available before all the code in the callback function can be executed. +* `callback` is a function that webpack will execute once the dependencies are loaded. An implementation of the `require` function is sent as a parameter to this function. The function body can use this to further `require()` modules it needs for execution. +* `chunkName` is a name given to the chunk created by this particular `require.ensure()`. By passing the same `chunkName` to various `require.ensure()` calls, we can combine their code into a single chunk, resulting in only one bundle that the browser must load. + +Let's reconsider the dynamic import of `moment` from the `import()` section and rewrite it using `require.ensure()`: + +**index.js** +```javascript +function determineDate() { + require.ensure([], function(require) { + var moment = require('moment'); + console.log(moment().format()); + }); +} + +determineDate(); +``` + +Running `webpack index.js bundle.js` generates two files, `bundle.js` and `0.bundle.js`: + +**bundle.js** +```js +// webpack code ... +/***/ (function(module, exports, __webpack_require__) { + +function determineDate() { + __webpack_require__.e/* require.ensure */(0).then((function(require) { + var moment = __webpack_require__(0); + console.log(moment().format()); + }).bind(null, __webpack_require__)).catch(__webpack_require__.oe); +} + +determineDate(); +// webpack code ... +``` + +**0.bundle.js* +```js +webpackJsonp([0],[(function(module, exports, __webpack_require__) { +/* WEBPACK VAR INJECTION */(function(module) { + +//! moment.js + +})]); +``` + +When you add `bundle.js` in your HTML file and open it in your browser, the `0.bundle.js` will be loaded asynchronously by webpack. + +### publicPath + +`output.publicPath` is an important option when using code-splitting, it is used to tell webpack where to load your bundles on-demand, see the [configuration documentation](/configuration/output/#output-publicpath). + +### Empty Array as Parameter + +```javascript +require.ensure([], function(require){ + require('./a.js'); +}); +``` + +The above code ensures that a split point is created and `a.js` is bundled separately by webpack. + +### Dependencies as Parameter + +```javascript +require.ensure(['./b.js'], function(require) { + require('./c.js'); +}); +``` + +In the above code, `b.js` and `c.js` are bundled together and split from the main bundle. But only the contents of `c.js` are executed. The contents of `b.js` are only made available and not executed. +To execute `b.js`, we will have to require it in a sync manner like `require('./b.js')` for the JavaScript to get executed. + ## Examples -* https://github.com/webpack/webpack/tree/master/examples/harmony -* https://github.com/webpack/webpack/tree/master/examples/code-splitting-harmony -* https://github.com/webpack/webpack/tree/master/examples/code-splitting-native-import-context +* `import()` +* * https://github.com/webpack/webpack/tree/master/examples/harmony +* * https://github.com/webpack/webpack/tree/master/examples/code-splitting-harmony +* * https://github.com/webpack/webpack/tree/master/examples/code-splitting-native-import-context +* `require.ensure()` +* * https://github.com/webpack/webpack/tree/master/examples/code-splitting +* * https://github.com/webpack/webpack/tree/master/examples/named-chunks – illustrates the use of `chunkName` ## Weblinks * [Lazy Loading ES2015 Modules in the Browser](https://dzone.com/articles/lazy-loading-es2015-modules-in-the-browser) diff --git a/content/guides/code-splitting-require.md b/content/guides/code-splitting-require.md deleted file mode 100644 index 27d125bbfda9..000000000000 --- a/content/guides/code-splitting-require.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -title: Code Splitting - Using require.ensure -sort: 33 -contributors: - - pksjce - - rahulcs - - johnstew ---- - -In this section, we will discuss how webpack splits code using `require.ensure()`. - -W> `require.ensure` is specific to webpack, see [`import()`](/guides/code-splitting-import) for a proposal for ECMAScript. - -## `require.ensure()` - -webpack statically parses for `require.ensure()` in the code while building. Any module that is referenced as a dependency or `require()`d within the callback function, will be added to a new chunk. This new chunk is written to an async bundle that is loaded on demand by webpack through jsonp. - -The syntax is as follows: - -```javascript -require.ensure(dependencies: String[], callback: function(require), chunkName: String) -``` - -#### dependencies -This is an array of strings where we can declare all the modules that need to be made available before all the code in the callback function can be executed. - -#### callback -This is the callback function that webpack will execute once the dependencies are loaded. An implementation of the `require` function is sent as a parameter to this function. The function body can use this to further `require()` modules it needs for execution. - -#### chunkName -The `chunkName` is a name given to the chunk created by this particular `require.ensure()`. By passing the same `chunkName` to various `require.ensure()` calls, we can combine their code into a single chunk, resulting in only one bundle that the browser must load. - -## Example - -Let us consider the following file structure: - -```bash -. -├── dist -├── js -│   ├── a.js -│   ├── b.js -│   ├── c.js -│   └── entry.js -└── webpack.config.js -``` - -**entry.js** - -```javascript -require('./a'); -require.ensure(['./b'], function(require){ - require('./c'); - console.log('done!'); -}); -``` - -**a.js** - -```javascript -console.log('***** I AM a *****'); -``` - -**b.js** - -```javascript -console.log('***** I AM b *****'); -``` - -**c.js** - -```javascript -console.log('***** I AM c *****'); -``` - -**webpack.config.js** - -```javascript -var path = require('path'); - -module.exports = function(env) { - return { - entry: './js/entry.js', - output: { - filename: 'bundle.js', - path: path.resolve(__dirname, 'dist'), - publicPath: 'https://cdn.example.com/assets/', - // tell webpack where to load the on-demand bundles. - - pathinfo: true, - // show comments in bundles, just to beautify the output of this example. - // should not be used for production. - } - } -} - -``` - -T> `output.publicPath` is an important option when using code-splitting, it is used to tell webpack where to load your bundles on-demand, see the [configuration documentation](/configuration/output/#output-publicpath). - -On running webpack on this project, we find that webpack has created two new bundles, `bundle.js` and `0.bundle.js`. - -`entry.js` and `a.js` are bundled in `bundle.js`. - -**bundle.js** - -```javascript -/******/ (function(modules) { // webpackBootstrap -//webpack bootstrap code... - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "https://cdn.example.com/assets/"; - -// webpack bootstrap code... -/******/ }) -/******/ ([ -/* 0 */ -/* unknown exports provided */ -/* all exports used */ -/*!*****************!*\ - !*** ./js/a.js ***! - \*****************/ -/***/ (function(module, exports) { - -console.log('***** I AM a *****'); - - -/***/ }), -/* 1 */, -/* 2 */, -/* 3 */ -/* unknown exports provided */ -/* all exports used */ -/*!*********************!*\ - !*** ./js/entry.js ***! - \*********************/ -/***/ (function(module, exports, __webpack_require__) { - -__webpack_require__(/*! ./a */ 0); -__webpack_require__.e/* require.ensure */(0).then((function(require){ - __webpack_require__(/*! ./c */ 2); - console.log('done!'); -}).bind(null, __webpack_require__)).catch(__webpack_require__.oe); - - -/***/ }) -/******/ ]); -``` - -T> We can see the specified **webpack public path** on `__webpack_require__.p` in the bootstrap code, it corresponds to our `output.publicPath` configuration on above. - -`b.js` and `c.js` are bundled in `0.bundle.js`. - -**0.bundle.js** -```javascript -webpackJsonp([0],[ -/* 0 */, -/* 1 */ -/* unknown exports provided */ -/* all exports used */ -/*!*****************!*\ - !*** ./js/b.js ***! - \*****************/ -/***/ (function(module, exports) { - -console.log('***** I AM b *****'); - - -/***/ }), -/* 2 */ -/* unknown exports provided */ -/* all exports used */ -/*!*****************!*\ - !*** ./js/c.js ***! - \*****************/ -/***/ (function(module, exports) { - -console.log('***** I AM c *****'); - - - -/***/ }) -]); -``` - -Now just add `bundle.js` in your HTML file and open it in your broswer, the `0.bundle.js` will be loaded on demand (from `https://cdn.example.com/assets/0.bundle.js`) by webpack. - -**More examples** -* https://github.com/webpack/webpack/tree/master/examples/code-splitting -* https://github.com/webpack/webpack/tree/master/examples/named-chunks – illustrates the use of `chunkName` - -## Promise polyfill - -W> `require.ensure` relies on [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) internally. See [this section](/guides/code-splitting-import#promise-polyfill) for possible polyfills. - -## Gotchas for `require.ensure()` - -### Empty Array as Parameter - -```javascript -require.ensure([], function(require){ - require('./a.js'); -}); -``` - -The above code ensures that a split point is created and `a.js` is bundled separately by webpack. - -### Dependencies as Parameter - -```javascript -require.ensure(['./b.js'], function(require) { - require('./c.js'); -}); -``` - -In the above code, `b.js` and `c.js` are bundled together and split from the main bundle. But only the contents of `c.js` are executed. The contents of `b.js` are only made available and not executed. -To execute `b.js`, we will have to require it in a sync manner like `require('./b.js')` for the JavaScript to get executed. diff --git a/content/guides/code-splitting.md b/content/guides/code-splitting.md index 6daaf8e1dbe4..909f10a8717e 100644 --- a/content/guides/code-splitting.md +++ b/content/guides/code-splitting.md @@ -34,5 +34,4 @@ While resource splitting of the previous kind requires the user to specify the s This can be used for more granular chunking of code, for example, per our application routes or as per predicted user behaviour. This allows the user to load non-essential assets on demand. -* [Code Splitting - Using `import()`](/guides/code-splitting-import) – proposal for ECMAScript -* [Code Splitting - Using `require.ensure`](/guides/code-splitting-require) – CommonJS way +Learn [how to split on demand](/guides/code-splitting-async) using `import()` or `require.ensure()`. diff --git a/content/guides/migrating.md b/content/guides/migrating.md index e0188bf466fe..75bbb1baf2fa 100644 --- a/content/guides/migrating.md +++ b/content/guides/migrating.md @@ -427,7 +427,7 @@ To keep compatibility with old loaders, loaders can be switched to debug mode vi ## Code Splitting with ES2015 -In webpack 1, you could use [`require.ensure`](/guides/code-splitting-require) as a method to lazily-load chunks for your application: +In webpack 1, you could use [`require.ensure()`](/guides/code-splitting-async/#require-ensure-) as a method to lazily-load chunks for your application: ```javascript require.ensure([], function(require) { @@ -435,7 +435,7 @@ require.ensure([], function(require) { }); ``` -The ES2015 Loader spec defines [`import()`](/guides/code-splitting-import) as method to load ES2015 Modules dynamically on runtime. +The ES2015 Loader spec defines [`import()`](/guides/code-splitting-async) as method to load ES2015 Modules dynamically on runtime. webpack treats `import()` as a split-point and puts the requested module in a separate chunk. `import()` takes the module name as argument and returns a Promise.