预取/预加载模块
webpack 4.6.0+增加了对预取和预加载的支持。
在声明导入时使用这些内联指令允许webpack输出“Resource Hint”,它告诉浏览器:
prefetch:将来某些导航可能需要资源
preload:当前导航期间可能需要资源
简单的预取示例可以是一个HomePage
组件,该组件呈现一个LoginButton
组件,然后在LoginModal
点击后按需加载组件。
LoginButton.js
//...
import(/* webpackPrefetch: true */ 'LoginModal');
这将导致<link rel="prefetch" href="login-modal-chunk.js">
被附加在页面的头部,这将指示浏览器在空闲时间预取login-modal-chunk.js
文件。
一旦父块加载,webpack将添加预取提示。
与prefetch相比,Preload指令有许多不同之处:
- 与 prefetch 指令相比,preload 指令有许多不同之处:
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
- 浏览器支持程度不同。
下面这个简单的 preload 示例中,有一个 Component
,依赖于一个较大的 library,所以应该将其分离到一个独立的 chunk 中。
我们假想这里的图表组件 ChartComponent
组件需要依赖一个体积巨大的 ChartingLibrary
库。它会在渲染时显示一个 LoadingIndicator(加载进度条)
组件,然后立即按需导入 ChartingLibrary
:
ChartComponent.js
//...
import(/* webpackPreload: true */ 'ChartingLibrary');
在页面中使用 ChartComponent
时,在请求 ChartComponent.js 的同时,还会通过 <link rel="preload">
请求 charting-library-chunk。假定 page-chunk 体积很小,很快就被加载好,页面此时就会显示 LoadingIndicator(加载进度条)
,等到 charting-library-chunk
请求完成,LoadingIndicator 组件才消失。启动仅需要很少的加载时间,因为只进行单次往返,而不是两次往返。尤其是在高延迟环境下。
错误地使用webpackPreload实际上会损害性能,因此使用它时要小心。
延迟加载
懒惰或“按需”加载是优化网站或应用程序的好方法。这种做法主要涉及在逻辑断点处拆分代码,然后在用户完成需要或将需要新代码块的操作后加载它。这可以加快应用程序的初始负载并减轻其总体重量,因为某些块甚至可能永远不会被加载。
例
让我们以Code Splitting为例,稍微调整一下,以进一步展示这个概念。这些代码确实会导致生成一个单独的块,lodash.bundle.js
并在脚本运行时从技术上“延迟加载”它。问题是加载捆绑包不需要用户交互 - 这意味着每次加载页面时,都会触发请求。这对我们没有多大帮助,并且会对性能产生负面影响。
让我们尝试不同的东西。当用户单击按钮时,我们将添加一个交互以将一些文本记录到控制台。但是,我们将等待加载该代码(print.js
),直到第一次发生交互。为此,我们将返回并重新编写Code Splitting中的最终Dynamic Imports示例,并留在主块中。lodash
webpack-demo
|- package.json
|- webpack.common.js
|- webpack.dev.js
|- webpack.prod.js
|- server.js
|- DEV-server.js
|- /dist
|- /src
|- data.xml
|- icon.png+
|- style.css
|- my-font.woff
|- my-font.woff2
|- print.js
|- index.js
|- math.js
|- /node_modules
// print.js
console.log('The print.js module has loaded! See the network tab in dev tools...');
export default () => {
console.log('Button Clicked: Here\'s "some text"!');
};
// index.js
import _ from 'lodash';
function component() {
var element = document.createElement('div');
var button = document.createElement('button');
var br = document.createElement('br');
button.innerHTML = 'Click me and look at the console!';
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button);
// Note that because a network request is involved, some indication
// of loading would need to be shown in a production-level site/app.
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
var print = module.default;
print();
});
return element;
}
document.body.appendChild(component());
请注意,import()
在ES6模块上使用时,必须引用该.default
属性,因为它module
是解析promise时将返回的实际对象。
构架
许多框架和图书馆都有自己的建议,说明如何在其方法中实现这一目标。这里有一些例子:
React:代码拆分和延迟加载
AngularJS:AngularJS +的WebPack = lazyLoad通过@var_bincom