1. Webpack是什么?
从官网的定义上可以看到:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。换而言之,我们可以将Webpack理解成为一个“箱子”,我们可以把不同的文件或者不同的文件依赖放入这个“箱子”,它会根据我们的入口索引来构建一个视图—即每个文件之间的引用关系,同时将他们“打包”在“箱子”里。在“打包”结束后,它会把打包后的资源(bundle)进行输出。
2. Why Webpack?
然而我们为什么需要使用到Webpack?正是由于前端的迅猛发展,前端项目体积过大且拥有着复杂的JavaScript代码和一大堆依赖包。而为了解决这一问题,社区中便出现了模块化解决方案,模块化构建工具便应运而生。同时对比其他的构建工具Webpack所提供的功能更为全面,对比如下:
| 构建工具 | 优点 | 缺点 |
|---|---|---|
| Rollup | 1. 打包资源更小,速度更快 2. 基于模块化打包 |
1. 生态不完善 2. 仅支持JS文件打包编译 |
| Grunt | 1. 基于文件媒介 2. 利于MPA实现 |
1. 配置繁琐 |
| Gulp | 1. 代码驱动,灵活性更高 2. 利于MPA实现 |
1. 集成度较低 |
| Webpack | 1. 社区建设良好,生态完善 2. 模块化管理 3. 按需打包、按需加载 4. 支持多类型文件的打包编译 |
1. 配置繁琐 2. 打包后体积偏大 |
| 特性 | Webpack | rollup | requireJS |
|---|---|---|---|
| 按需加载 | 支持 | 无 | 支持 |
| 独立成包 | 支持 | 支持 | 支持 |
| 压缩 | 支持 | 支持 | 支持 |
| 插件 | 支持 | 支持 | 支持 |
| 热更新 | 支持 | 支持 | 无 |
| CommonJS | 支持 | 支持 | 仅支持定义 |
| 调试支持 | 支持 | 支持 | 无 |
| 编译非JS文件 | 支持 | 无 | 无 |
3. 核心概念
3.1 entry
entry(入口)指示 webpack 应该使用哪个模块,来作为构建其内部_依赖图_的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。而Webpack的入口又分为单入口与多入口,单入口多用于构建单页面应用(SPA),而多入口则用于构建多页应用(MPA)
//常见的单入口配置const config = {entry: './yourFilePath'};module.exports = config;
//多入口配置--entry以对象形式进行配置entry: {app: './src/app.js',app2: './src/app2.js'}
3.2 output
output(出口)告诉 webpack 在哪里输出它所创建的 _bundles_,以及如何命名这些文件。基本上,整个应用程序结构,都会被编译到指定的输出路径的文件夹中。可以将其理解成为货物打包后所发送到的目的地
// 常见的单入口时output配置const config = {output: {filename: 'bundle.js',path: '/home/proj/public/assets'}};module.exports = config;
// 多入口时output配置output: {filename: '[name].js',path: __dirname + '/dist'}
3.3 loader
由于webpack 只能解析 JavaScript,而当我们需要对不同文件资源进行打包时就需要使用loader(解析器)来让 webpack 去处理那些非 JavaScript 文件。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后我们就可以利用 webpack 的打包能力,对它们进行处理。<br />** 值得注意的loader特性:**
- loader 支持链式传递。loader将会按照rules的定义顺序执行,当上一个loader执行完毕后返回值给下一个 loader
- loader 可以是同步的,也可以是异步的
- loader 运行在 Node.js 中,并且能够执行任何可能的操作(loader实质上是使用loader api 导出的一个执行函数,所以它可以运行在Node.js中)
- loader 接收查询参数。用于对 loader 传递配置。例如loader: “url-loader?mimetype=image/png”或{ test: /.png$/, loader: “url-loader”, query: { mimetype: “image/png” } }
- loader 也能够使用 options 对象进行配置
- 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段
- 插件(plugin)可以为 loader 带来更多特性
- loader 能够产生额外的任意文件
//常用的loader配置rules: [{// ts文件test: /\.ts$/,loader: 'ts-loader',options: { appendTsSuffixTo: [/\.vue$/] }}, {// vue文件// 避免[VueLoaderPlugin Error] No matching rule for .vue files found.test: /\.vue$/,loader: 'vue-loader'}, {// css文件test: /\.css$/,use: ['style-loader', 'css-loader'] // 可以是数组的格式,指定需要的loader,这里顺序需要注意一下,执行的时候是先执行 css-loader -> style-laoder// 这里也可以传入参数}, {// scss\sass 文件test: /\.s[ac]ss$/i,use: [// 将 JS 字符串生成为 style 节点'style-loader',// 将 CSS 转化成 CommonJS 模块'css-loader',// 将 Sass 编译成 CSS'sass-loader']}, {// 处理 字体文件的 loadertest: /\.(ttf|eot|svg|woff|woff2)$/,use: 'url-loader'}, {// eslintenforce: 'pre',test: /\.(js|vue)$/,loader: 'eslint-loader',exclude: /node_modules/}]
3.3.1 常用loader
| 名称 | 描述 | | —- | —- | | babel-loader | 转换ES6+等JS新特性语法 | | ts-loader | 将TS转换成JS | | css-loader | 支持.css文件的加载和解析 | | style-loader | 将css代码注入到JS中 | | sass-loader | 将sass/scss文件转换成css | | file-loader | 进行图片、字体等的打包 | | url-loader | 将小文件以base64的方式注入到代码中 | | raw-loader | 将文件以字符串的形式导入 | | thread-loader | 多进程打包JS和CSS | | vue-loader | 识别.Vue内的template语法 |
3.4 plugins
plugins(插件)的作用丝毫不逊色于loader,可以用于执行范围更广的任务。它的核心本质在于围绕着资源打包来拓展Webpack的功能,如打包优化和压缩。它可以用来处理各种各样的任务。
// plugins使用---具体plugin的options需要自行查看文档module.exports = {plugins: [new HtmlWebpackPlugin({template: './src/index.html'})]}
3.4.1 常用plugins
| 名称 | 描述 |
|---|---|
| CommonsChunkPlugin | 将chunks相同的模块代码提取成公共js |
| CleanWebpackPlugin | 清空构建目录 |
| ExtractTextWebpackPlugin | 将CSS从bunlde文件里提取成一个独立的CSS文件 |
| CopyWebpackPlugin | 将文件或者文件夹拷贝到构建的输出目录 |
| HtmlWebpackPlugin | 创建html文件去承载输出的bundle |
| UglifyjsWebpackPlugin | 压缩JS |
| ZipWebpackPlugin | 将打包出的资源生成一个zip包 |
| HotModuleReplacementPlugin | 启用模块热替换 |
| CompressionWebpackPlugin | 开启Gzip压缩 |
| BundleAnalyzerPlugin | 开启Bundle分析 |
| MinChunkSizePlugin | 确保 chunk 大小超过指定限制 |
| DllPlugin | 为了极大减少构建时间,进行分离打包 |
4. 针对项目的常用配置与特性
// externals可以忽略第三方包打包,配合htmlWebpackPlugin与CDN注入可以减少包体const externals = ['https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js','https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js']externals: {'MTD': 'MTD','vue': 'Vue','vue-router': 'VueRouter'},plugins: [new HtmlWebpackPlugin({template: './public/index.html',cdn: externals})]//在index.html中<% for(var js of htmlWebpackPlugin.options.cdn) { %><script src="<%=js%>"></script><% } %>
// alias别名配置,可以方便找到文件alias: {'@/components': path.resolve(__dirname, './src/common/components'),'@/utils': path.resolve(__dirname, './src/common/utils')}// 值得注意的是在VScode编译器中,假设你使用JS进行开发需要配置jsconfig.json才能显式的提醒你文件路径否则无法提示,// 同样TS进行开发时也需要对tsconfig.json进行baseUrl与path的配置,否则整个项目将报错//jsconfig.json配置识别webpack alias{"compilerOptions": {"baseUrl": ".","paths": {"@/*": ["src/*"]},"target": "ES6","module": "commonjs","allowSyntheticDefaultImports": true},"include": ["src/**/*"],"exclude": ["node_modules"]}// tsconfig.json"baseUrl": ".","paths": {"@/components": ["src/common/components/index.ts"],"@/utils": ["src/common/utils/index.ts"],},
// require.context() api可以方便的帮助我们文件批量引入const files = require.context('./modules', false, /\.js$/)
5. 展望未来,Webpack 5 带来的是什么?
从Wepack3时复杂繁琐的配置到Webpack4发布时号称零配置开箱即用,再到如今面向未来的Webpack5,作为项目构建工具的Webpack在不断的给我们制造惊喜。如今Webpack版本已经升级到V5.11.0,对比于Wepack4它具有以下的特性:
- 通过持久化缓存提高性能
- 采用更好的持久化缓存算法和默认行为
- 通过优化 Tree Shaking 和代码生成来减小Bundle体积
- 提高 Web 平台的兼容性
- 清除之前为了实现 Webpack4 没有不兼容性变更导致的不合理 state
尝试现在引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 Webpack 5
微前端是目前解决多项目共存的流行方案,而Webpack5中 Module Federation给我们提供一种新式的多项目间模块共享的解决思路。对比于目前使用的方法如npm包引入、CDN源加载,它更为可控同时也能避免依赖重复的问题。相信在今后的发展中Webpack依旧可以占据一席之位。
参考资料:
1.Webpack中文官方文档
2.深入Webpack-编写Loader
3.Web全栈技术笔记
4.Webpack5特性介绍
5.精读《Webpack5 新特性 - 模块联邦》
