1. Webpack是什么?

从官网的定义上可以看到:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。换而言之,我们可以将Webpack理解成为一个“箱子”,我们可以把不同的文件或者不同的文件依赖放入这个“箱子”,它会根据我们的入口索引来构建一个视图—即每个文件之间的引用关系,同时将他们“打包”在“箱子”里。在“打包”结束后,它会把打包后的资源(bundle)进行输出。

2. Why Webpack?

  1. 然而我们为什么需要使用到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

  1. entry(入口)指示 webpack 应该使用哪个模块,来作为构建其内部_依赖图_的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。而Webpack的入口又分为单入口与多入口,单入口多用于构建单页面应用(SPA),而多入口则用于构建多页应用(MPA)
  1. //常见的单入口配置
  2. const config = {
  3. entry: './yourFilePath'
  4. };
  5. module.exports = config;
  1. //多入口配置--entry以对象形式进行配置
  2. entry: {
  3. app: './src/app.js',
  4. app2: './src/app2.js'
  5. }

3.2 output

  1. output(出口)告诉 webpack 在哪里输出它所创建的 _bundles_,以及如何命名这些文件。基本上,整个应用程序结构,都会被编译到指定的输出路径的文件夹中。可以将其理解成为货物打包后所发送到的目的地
  1. // 常见的单入口时output配置
  2. const config = {
  3. output: {
  4. filename: 'bundle.js',
  5. path: '/home/proj/public/assets'
  6. }
  7. };
  8. module.exports = config;
  1. // 多入口时output配置
  2. output: {
  3. filename: '[name].js',
  4. path: __dirname + '/dist'
  5. }

3.3 loader

  1. 由于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 能够产生额外的任意文件
    1. //常用的loader配置
    2. rules: [{
    3. // ts文件
    4. test: /\.ts$/,
    5. loader: 'ts-loader',
    6. options: { appendTsSuffixTo: [/\.vue$/] }
    7. }, {
    8. // vue文件
    9. // 避免[VueLoaderPlugin Error] No matching rule for .vue files found.
    10. test: /\.vue$/,
    11. loader: 'vue-loader'
    12. }, {
    13. // css文件
    14. test: /\.css$/,
    15. use: ['style-loader', 'css-loader'] // 可以是数组的格式,指定需要的loader,这里顺序需要注意一下,执行的时候是先执行 css-loader -> style-laoder
    16. // 这里也可以传入参数
    17. }, {
    18. // scss\sass 文件
    19. test: /\.s[ac]ss$/i,
    20. use: [
    21. // 将 JS 字符串生成为 style 节点
    22. 'style-loader',
    23. // 将 CSS 转化成 CommonJS 模块
    24. 'css-loader',
    25. // 将 Sass 编译成 CSS
    26. 'sass-loader'
    27. ]
    28. }, {
    29. // 处理 字体文件的 loader
    30. test: /\.(ttf|eot|svg|woff|woff2)$/,
    31. use: 'url-loader'
    32. }, {
    33. // eslint
    34. enforce: 'pre',
    35. test: /\.(js|vue)$/,
    36. loader: 'eslint-loader',
    37. exclude: /node_modules/
    38. }]

    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

  1. plugins(插件)的作用丝毫不逊色于loader,可以用于执行范围更广的任务。它的核心本质在于围绕着资源打包来拓展Webpack的功能,如打包优化和压缩。它可以用来处理各种各样的任务。
  1. // plugins使用---具体plugin的options需要自行查看文档
  2. module.exports = {
  3. plugins: [
  4. new HtmlWebpackPlugin({
  5. template: './src/index.html'
  6. })
  7. ]
  8. }

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. 针对项目的常用配置与特性

  1. // externals可以忽略第三方包打包,配合htmlWebpackPlugin与CDN注入可以减少包体
  2. const externals = [
  3. 'https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js',
  4. 'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js'
  5. ]
  6. externals: {
  7. 'MTD': 'MTD',
  8. 'vue': 'Vue',
  9. 'vue-router': 'VueRouter'
  10. },
  11. plugins: [
  12. new HtmlWebpackPlugin({
  13. template: './public/index.html',
  14. cdn: externals
  15. })
  16. ]
  17. //在index.html中
  18. <% for(var js of htmlWebpackPlugin.options.cdn) { %>
  19. <script src="<%=js%>"></script>
  20. <% } %>
  1. // alias别名配置,可以方便找到文件
  2. alias: {
  3. '@/components': path.resolve(__dirname, './src/common/components'),
  4. '@/utils': path.resolve(__dirname, './src/common/utils')
  5. }
  6. // 值得注意的是在VScode编译器中,假设你使用JS进行开发需要配置jsconfig.json才能显式的提醒你文件路径否则无法提示,
  7. // 同样TS进行开发时也需要对tsconfig.json进行baseUrl与path的配置,否则整个项目将报错
  8. //jsconfig.json配置识别webpack alias
  9. {
  10. "compilerOptions": {
  11. "baseUrl": ".",
  12. "paths": {
  13. "@/*": ["src/*"]
  14. },
  15. "target": "ES6",
  16. "module": "commonjs",
  17. "allowSyntheticDefaultImports": true
  18. },
  19. "include": ["src/**/*"],
  20. "exclude": ["node_modules"]
  21. }
  22. // tsconfig.json
  23. "baseUrl": ".",
  24. "paths": {
  25. "@/components": [
  26. "src/common/components/index.ts"
  27. ],
  28. "@/utils": [
  29. "src/common/utils/index.ts"
  30. ],
  31. },
  1. // require.context() api可以方便的帮助我们文件批量引入
  2. const files = require.context('./modules', false, /\.js$/)

5. 展望未来,Webpack 5 带来的是什么?

  1. 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 新特性 - 模块联邦》