webpack 是一个古代 JavaScript
应用程序的动态模块打包器 (module bundler)
。当 webpack 解决应用程序时,它会递归地构建一个依赖关系图 (dependency graph)
,其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle
。
前端工程化演进到明天,webpack 做了很大的奉献。我的项目工程化带来了很多便捷,咱们不再须要手动解决依赖之间的关系,也能够更不便的应用更多好用的框架,咱们能够更关注业务自身,集中精力打造咱们的产品。
在 webpack 中,应用懒加载或者按需加载,是一种很好的优化网页或利用的形式。这种形式实际上是先把你的代码在一些逻辑断点处罚来到,而后在一些代码块中实现某些操作后,立刻援用或行将援用另外一些新的代码块。这样放慢了利用的初始加载速度,加重了它的总体体积,因为某些代码块可能永远不会被加载。
那么,接下来让咱们来探索一下 webpack 对懒加载的模块做了哪些工作吧~
实现背景
咱们先假如咱们在实现一个实在的我的项目,这个我的项目中有上传下载性能。
下载性能个别是关上一个链接,所以咱们间接实现在主包中。而上传性能可能会应用到第三方 sdk,咱们应用懒加载进行加载。只有在用户点击上传时,咱们才会加载这个具备上传性能的包,来进行上传。
上传下载性能可能会应用到一些第三方 sdk,而这些第三方 sdk 的体积往往十分大,并且这个性能所以这个性能做成懒加载实现是正当的。
为了演示差异,咱们这里将“下载”和“上传”两个性能做辨别。
我的项目根底配置
咱们先搭建一个根底的 webpack 配置,让其反对懒加载配置,而后咱们间接通过打包后的代码来看看懒加载实现的成果。咱们须要有个根底目录配置,我的项目 Demo 目录构造如下:
文件 / 目录 | 阐明 |
---|---|
src |
入口文件、下载模块、上传模块 |
index.html |
html 模板文件 |
webpack.config.js |
webpack 配置文件 |
package.json |
我的项目阐明文件 |
性能代码实现
咱们先来看看咱们的性能代码实现吧,别离是 download.js
、upload.js
、index.js
。
// ./src/download.js
const download = () => {console.log("download start");
console.log("schedule download sdk");
console.log("download");
}
export default download;
// ./src/upload.js
const upload = () => {console.log("upload start");
console.log("schedule upload sdk");
console.log("upload");
}
export default upload;
// ./src/index.js
import download from "./download";
console.log("initial page");
async function handlerUploadClick() {
// 动静加载 upload 模块,该模块的 default 属性就是 upload 办法
const {default: upload} = await import("./upload");
// 调用 upload 办法
upload();}
async function handlerDownloadClick() {download();
}
// 点击 upload 按钮时,调用上传办法
document.querySelector("#upload").addEventListener("click", handlerUploadClick, false)
// 点击 download 按钮时,调用下载办法
document.querySelector("#download").addEventListener("click", handlerDownloadClick, false)
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack LazyLoad</title>
</head>
<body>
<section>
<h1>Home</h1>
<button id="upload">Upload</button>
<button id="download">Download</button>
</section>
</body>
</html>
在咱们的性能代码实现中,咱们实现了一个 html
网页,其中有两个按钮,一个是上传按钮,一个是下载按钮。
配置实现
性能实现后,咱们须要配置 webpack,而后打包生成可能间接运行的我的项目。
咱们新建文件 webpack.config.js
进行配置,代码实现如下:
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WebpackChain = require("webpack-chain");
// 应用 webpack chain 组装配置
const chain = new WebpackChain();
// 设置入口文件为 src/index.js
chain.entry("main").add("./src/index.js").end();
// 将构建生成的文件输入到 dist 目录
chain.output.path(path.resolve(__dirname, "./dist")).end();
// 增加 html-webpack-plugin 插件,设置 HTML 模板文件
chain.plugin("html-webpack-plugin").use(HtmlWebpackPlugin, [{template: path.resolve(__dirname, "./index.html")
}]);
// 设置 source map 生成规定
chain.devtool("cheap-source-map").end();
// 将配置转成 webpack 可辨认的配置对象
const config = chain.toConfig();
module.exports = config;
从代码实现中能够看出,咱们的 webpack
配置只是简略配置了入口进口和 html
模板文件。
那么接下来,咱们须要配置 package.json
,在 scripts
中增加启动命令,代码实现如下:
"scripts": {"build": "NODE_ENV=development webpack --config webpack.config.js && cd dist && anywhere"}
anywhere 是一个疾速启动
http
服务的插件,能够应用npm i anywhere -g
进行全局装置。
在配置完了当前,咱们须要运行上面的命令装置一些依赖
npm i webpack webpack-cli webpack-chain html-webpack-plugin anywhere -D
装置依赖后,咱们就能够筹备启动我的项目啦!
运行我的项目
咱们运行 npm build
启动我的项目编译,命令行输入将会是上面这样(如下图)
咱们的我的项目在被 webpack
打包后,输入到了 dist
目录,并且由 anywhere
运行了一个服务,在 8000
端口。
咱们关上浏览器,能够看到上面这个页面。(如下图)
咱们关上控制台,会看到咱们设置的对应输入(如下图)
页面操作
咱们来进行一些页面操作,咱们先点击 Download
按钮,会发现控制台的输入变成了上面这样(如下图)
从上图能够看出 download
办法被调用了,此时执行了一个下载操作(mock 操作)。
那咱们此时再点击一下 Upload
按钮,再察看控制台输入(如下图)
从控制台能够看到咱们的上传操作被调用了,然而仿佛和下载操作没什么区别。
此时,咱们须要切换到 network
控制面板,查看网络申请。(如下图)
咱们从上图能够发现,在调用 upload
办法时,才会加载 upload
办法对应的文件,从而实现懒加载。
这样做的益处在于,能够依据需要无效减小主包的体积,放慢首屏渲染速度,缩小服务器带宽压力。同时也缩小了 JS 解析工夫,晋升页面渲染速度。
懒加载的实现对前端来说,是性能优化专项必修课。能够说,我的项目越简单,那么懒加载带来的益处就越大。
实现剖析
上面咱们能够来看看 webpack
编译后的代码,看看 webpack
是如何实现懒加载的。
首先,咱们查看主包文件,也就是 dist/main.js
。在这个文件里找到咱们在 src/index.js
中实现的初始化页面操作。(如下图)
从上图能够看出,该操作间接被打包进了构建生成的 bundle
文件中。
Download 解析
那咱们再来看看 src/download.js
中实现的下载办法调用(如下图)
从上图能够看出,handlerDownloadClick
最终调用了 _download__WEBPACK_IMPORTED_MODULE_0__.default
办法。
而这个 _download__WEBPACK_IMPORTED_MODULE_0__.default
是什么呢?
在构建后的代码中,找到了对这个对象的赋值操作(如下图)
而 __webpack_require__
函数,其实就是加载 __webpack_modules__
中的对应模块,这里加载的对应模块就是 "./src/download.js"
模块。
最终,咱们在 dist/main.js
中,找到了对该模块的定义。(如下图)
从上图能够看出,对该模块的定义,其实就是 src/download.js
的实现,被打包进了 dist/main.js
中,成为了 __webpack_modules__
对象的一部分。
Upload 解析
看完了 download
办法的打包实现,咱们接下来看看懒加载的 upload
是如何实现的?
咱们先找到 upload
对应的函数调用(如下图)
从上图能够看出,当点击上传按钮时,先应用 __webpack_require__.e
办法进行了一个加载操作,咱们来看看这个办法所做的事件。
该办法先拼接了这个模块对应的绝对路径(如下图)
这个门路的文件其实就是咱们打包后生成在 dist
目录的文件(如下图)
而后应用动静插入 script
标签的形式,将对应的脚本文件插入到文档中。(如下图)
当 upload
对应的文件被插入后,将会主动执行。src_upload_js.js
脚本文件中将会执行 webpackJsonpCallback
办法,执行后将会在 window
的 webpackChunklazyload
数组插入方才懒加载的模块。(如下图)
在执行该函数后,还会把 upload
模块注册在 __webpack_modules__
中,前面的调用流程就和调用 download
一样啦~(如下图)
小结
从一个简略的案例,咱们理解到了 webpack
的懒加载实现。
webpack
的懒加载实现在打包时会将懒加载的代码切割进来独自打包,而后在主包中进行按需加载,最初执行调用。
咱们最初用一张图来梳理一下懒加载的加载执行过程。(如下图)
最初一件事
如果您曾经看到这里了,心愿您还是点个赞再走吧~
您的点赞是对作者的最大激励,也能够让更多人看到本篇文章!
如果感觉本文对您有帮忙,请帮忙在 github 上点亮 star
激励一下吧!