webpack基础配置

安装

  1. mkdir webpack-demo
  2. cd webpack-demo
  3. yarn init
  4. yarn add webpack webpack-cli -D

文件目录结构

  1. webpack/
  2. |- /src
  3. |- index.js
  4. |- index.html
  5. |- package.json

默认文件配置

在文件根目录下创建:

  1. touch webpack.config.js
  2. 复制代码

webpack.config.js

  1. // webpack 配置文件
  2. const path = require('path'); // node.js 的路径模块
  3. module.exports = {
  4. // entry: './src/index.js', // 入口文件(简写形式)
  5. entry: {
  6. main: './src/index.js',
  7. },
  8. output: {
  9. path: path.resolve(__dirname, 'dist'), // 打包后的路径
  10. filename: 'bundle.js', // 打包后的文件名
  11. }
  12. }
  13. 复制代码

以上是简单的打包配置,配置项包括入口文件、打包路径、打包文件名。

简化打包流程

在 package.json 中的 scripts 字段来编写运行脚本,通过脚本进行打包。

  1. "scripts": {
  2. "bundle": "webpack"
  3. },

这个时候只需要运行 yarn build就可打包了。

71 webpack基础配置 - 图1

打包模式

  1. // webpack.config.js
  2. const path = require('path');
  3. module.exports = {
  4. mode: 'development', // 'development' | 'production'
  5. // entry: './src/index.js',
  6. entry: {
  7. main: './src/index.js',
  8. },
  9. output: {
  10. path: path.resolve(__dirname, 'dist'),
  11. filename: 'bundle.js'
  12. },
  13. }

可以配置为开发模式或者生产模式:

  • 开发模式:本地环境
  • 生产模式:线上环境(代码是压缩的)

具体有何不同,在打包后,打开 dist 文件夹下的 main.js,便可一目了然,此处不再赘述。

静态资源处理

  • 样式文件(CSS)
  • 图片(Images)
  • 字体(Fonts)
  • 数据文件(Data)

Asset Modules — 静态资源模块

根据 Webpack5 的文档,它简化了之前版本对于文件方面的配置,提供了 Asset Modules(静态资源模块),替代了之前常用的 raw-loader、url-loader、file-loader。

也就是说不用再安装这几个 loader 了,直接通过一行type: 'asset' 搞定,书写更加方便!(注意 module 中 type 的设置)

  1. // webpack 配置文件
  2. const path = require("path");
  3. module.exports = {
  4. mode: "production",
  5. entry: {
  6. main: "./src/index.js",
  7. },
  8. output: {
  9. path: path.resolve(__dirname, "dist"),
  10. filename: "bundle.js",
  11. },
  12. // *** 模块选项中匹配的文件会通过 loaders 来转换!
  13. module: {
  14. rules: [
  15. // 图片文件
  16. {
  17. test: /\.(jpe?g|png|gif|svg)$/i,
  18. type: "asset", // 一般会转换为 "asset/resource"
  19. },
  20. // 字体文件
  21. {
  22. test: /\.(otf|eot|woff2?|ttf|svg)$/i,
  23. type: "asset", // 一般会转换为 "asset/inline"
  24. },
  25. // 数据文件
  26. {
  27. test: /\.(txt|xml)$/i,
  28. type: "asset", // 一般会转换成 "asset/source"
  29. },
  30. ],
  31. },
  32. };

最小化svg

  1. yarn add mini-svg-data-uri -D

配置静态文件名

  1. const path = require('path') // node.js 的路径模块
  2. const miniSVGDataURI = require("mini-svg-data-uri"); // 最小化svg
  3. module.exports = {
  4. mode: 'development', // 'development' | 'production'
  5. // entry: './src/index.js', // 入口文件(简写形式)
  6. entry: {
  7. main: './src/index.js',
  8. },
  9. output: {
  10. path: path.resolve(__dirname, 'dist'), // 打包后的路径
  11. filename: 'bundle.js', // 打包后的文件名
  12. // 静态文件打包后的路径及文件名(默认是走全局的,如果有独立的设置就按照自己独立的设置来。)
  13. assetModuleFilename: "assets/[name]_[hash][ext]",
  14. },
  15. // *** 模块选项中匹配的文件会通过 loaders 来转换!
  16. module: {
  17. rules: [
  18. // 图片文件
  19. {
  20. test: /\.(jpe?g|png|gif|svg)$/i,
  21. type: 'asset', // 一般会转换为 "asset/resource"
  22. generator: {
  23. filename: "images/[name]_[hash][ext]", // 独立的配置
  24. },
  25. parser: {
  26. dataUrlCondition: {
  27. maxSize: 8 * 1024 // 8kb (低于8kb都会压缩成 base64)
  28. }
  29. },
  30. },
  31. // 字体文件
  32. {
  33. test: /\.(otf|eot|woff2?|ttf|svg)$/i,
  34. type: 'asset', // 一般会转换为 "asset/inline"
  35. generator: {
  36. filename: "fonts/[name]_[hash][ext]",
  37. },
  38. },
  39. // 数据文件
  40. {
  41. test: /\.(txt|xml)$/i,
  42. type: 'asset/source', // 一般会转换成 "asset/source"
  43. },
  44. // svg文件
  45. {
  46. test: /\.svg$/i,
  47. type: "asset",
  48. generator: {
  49. dataUrl(content) {
  50. content = content.toString();
  51. return miniSVGDataURI(content);
  52. },
  53. },
  54. parser: {
  55. dataUrlCondition: {
  56. maxSize: 2 * 1024 // 2kb (低于2kb都会压缩)
  57. }
  58. },
  59. },
  60. ],
  61. },
  62. }

assetModuleFilename: 'assets/[name][ext]', 用于设置全局的静态文件路径及文件名。如果文件模块没有单独进行配置,就按照这个来设置文件名。

其中,[name] 表示原来的文件名,[hash] 表示散列值,[ext] 表示文件后缀。

样式文件的处理

  1. yarn add css-loader style-loader -D
  1. // css配置
  2. {
  3. test: /\.css$/i,
  4. use: ["style-loader", "css-loader"],
  5. },
  1. yarn add sass-loader sass webpack -D
  1. // ...
  2. {
  3. test: /\.s[ac]ss$/i,
  4. use: [
  5. // Creates `style` nodes from JS strings
  6. "style-loader",
  7. // Translates CSS into CommonJS
  8. "css-loader",
  9. // Compiles Sass to CSS
  10. "sass-loader",
  11. ],
  12. },
  1. yarn add postcss-loader postcss -D
  1. {
  2. test: /\.s?css$/i,
  3. use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"],
  4. },

注意,postcss-loader 要放在最后。

  1. yarn add postcss-preset-env autoprefixer postcss-scss -D
  1. {
  2. test: /\.sc?ss$/i,
  3. use: [
  4. "style-loader",
  5. {
  6. loader: "css-loader",
  7. options: {
  8. importLoaders: 2,
  9. // 0 => no loaders (default);
  10. // 1 => postcss-loader;
  11. // 2 => postcss-loader, sass-loader
  12. modules: true, // 默认是 false ***
  13. },
  14. },
  15. "sass-loader",
  16. "postcss-loader",
  17. ],
  18. },

开发环境配置

  1. yarn add html-webpack-plugin -D
  1. const HtmlWebpackPlugin = require("html-webpack-plugin");
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. new HtmlWebpackPlugin({
  6. template: "./src/index.html", // 这里设置自己模板文件
  7. }),
  8. ],
  9. };

CleanWebpackPlugin

第二个问题的解决办法是在打包之前清除输出目录中的内容,然后让它重新生成。CleanWebpackPlugin 插件虽然不是官方的,但是在 5.20.0 之前的版本中仍然值得推荐。

  1. yarn add clean-webpack-plugin -D

output.clean

webpack 5.20.0+ 的版本中,内置了清除输出目录内容的功能,只要在 output 选项中配置一个参数即可。

  1. // webpack.config.js
  2. module.exports = {
  3. //...
  4. output: {
  5. clean: true, // Clean the output directory before emit.
  6. },
  7. };

1. 开发环境中的 source map

webpack.config.js

  1. module.exports = {
  2. mode: 'development',
  3. devtool: 'eval-cheap-module-source-map' // development
  4. }
  5. 复制代码

2. 生产环境中的 source map

  1. module.exports = {
  2. mode: 'production',
  3. devtool: 'nosources-source-map', // production
  4. }

DevServer

watch

监听打包

  1. // package.json
  2. {
  3. "scripts": {
  4. "watch": "webpack --watch", // 监听打包
  5. "bundle": "webpack" // 普通打包
  6. },
  7. }

webpack-dev-server (本地开发服务器)

  1. yarn add webpack-dev-server -D

package.json

  1. {
  2. "scripts": {
  3. "start": "webpack serve", // 开启本地服务器
  4. "watch": "webpack --watch", // 监听打包
  5. "bundle": "webpack" // 普通打包
  6. },
  7. }
  8. 复制代码

webpack.config.js

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. // ...
  5. mode: 'development',
  6. devtool: 'eval-cheap-module-source-map',
  7. devServer: {
  8. static: {
  9. directory: path.join(__dirname, 'dist'),// 指定被访问html页面所在目录的路径
  10. },
  11. client: {
  12. progress: true, // 在浏览器中以百分比显示编译进度。
  13. },
  14. open: true, // 开启服务器时,自动打开页面
  15. compress: true, // 开启 gzip 压缩
  16. port: 8888, // 自定义端口号
  17. hot: true, // 开启热更新
  18. hotOnly: true, // 强制热更新,不会刷新页面
  19. },
  20. // ...
  21. }
  22. 复制代码

注意:

  1. 在开发环境中,mode、devtool、devServer这三个配置是非常重要的!
  2. webpack-dev-server 在编译后不会在输出目录写入任何文件。相反,它会将打包的文件存在内存中,就好像它们被安装在服务器根路径上的真实文件一样。如果希望在其他路径上找到打包的文件,可以通过使用 devServer 中的 publicPath 选项更改此设置。
  • 为了以模板为支撑更有效率地输出打包文件,我们需要 HtmlWebpackPlugin
  • 为了快速定位代码错误的位置,我们需要 source map
  • 为了更好地模拟真实环境进行开发,我们需要 devServer(WDS);
  • 为了实时局部更新修改的内容而非全局更新,我们需要 Hot Module Replacement(HMR)!

Babel配置

有一些版本的浏览器对于JS新的语法(例如 ES6+)的支持不好,这时就需要将新的语法转换成 ES5 标准的语法,让浏览器正常识别它们,保证程序的稳定运行。

为了实现这种转换,我们该怎么办呢?用 Babel,它会把新语法转换为旧语法。

  1. yarn add babel-loader @babel/core @babel/preset-env -D
  1. // webpack.config.js
  2. module: {
  3. rules: [
  4. {
  5. test: /\.m?js$/,
  6. exclude: /node_modules/,
  7. use: {
  8. loader: 'babel-loader'
  9. }
  10. }
  11. ]
  12. }
  1. // babel.config.json
  2. {
  3. "presets": [
  4. ["@babel/preset-env", {
  5. "useBuiltIns": "usage", // 按需引入 corejs 中的模块
  6. "corejs": 3, // 核心 js 版本
  7. "targets": "> 0.25%, not dead" // 浏览器支持范围
  8. }]
  9. ]
  10. }

还需要下载的依赖:

  1. npm i core-js@3 --save
  1. yarn add @babel/plugin-transform-runtime -D
  2. yarn add @babel/runtime
  3. yarn add @babel/runtime-corejs3

Tree Shaking

  1. optimization: {
  2. usedExports: true,
  3. }

webpack-merge

  1. yarn add webpack-merge -D

配置typescript

  1. yarn add core-js
  2. yarn add -D babel-loader @babel/core @babel/cli @babel/preset-env
  3. yarn add @babel/polyfill
  4. yarn add -D @babel/preset-typescript
  5. yarn add typescript
  6. yarn add -D ts-loader
  1. // ts配置
  2. {
  3. test: /\.(jsx|ts|tsx)$/,
  4. use: [
  5. {
  6. loader: 'babel-loader',
  7. options: {
  8. presets: ['@babel/preset-env'],
  9. },
  10. },
  11. {
  12. loader: 'ts-loader',
  13. options: {
  14. compilerOptions: {
  15. noEmit: false,
  16. },
  17. },
  18. },
  19. ],
  20. exclude: /node_modules/
  21. },
  1. {
  2. "compilerOptions": {
  3. "target": "es6",
  4. "experimentalDecorators": false,
  5. "module": "esnext",
  6. "lib": [
  7. "dom",
  8. "dom.iterable",
  9. "esnext",
  10. "webworker"
  11. ],
  12. "noImplicitAny": true,
  13. "noImplicitThis": true,
  14. "strictNullChecks": true,
  15. "allowJs": true,
  16. "skipLibCheck": true,
  17. "esModuleInterop": true,
  18. "allowSyntheticDefaultImports": true,
  19. "strict": true,
  20. "forceConsistentCasingInFileNames": true,
  21. "moduleResolution": "node",
  22. "resolveJsonModule": true,
  23. "isolatedModules": false,
  24. "noEmit": true,
  25. "jsx": "react",
  26. "noFallthroughCasesInSwitch": true,
  27. "typeRoots": [
  28. "./src/types",
  29. "./node_modules/@types"
  30. ],
  31. "baseUrl": ".",
  32. "paths": {
  33. "@/*": ["src/*"]
  34. }
  35. },
  36. "include": [
  37. "src"
  38. ],
  39. "exclude": [
  40. "node_modules",
  41. "./mock"
  42. ]
  43. }

配置react

  1. yarn add react react-dom @types/react @types/react-dom

项目babel配置

  1. yarn add -D @babel/core @babel/preset-env babel-loader @babel/plugin-transform-runtime @babel/preset-react
  1. // babelrc,json
  2. {
  3. "presets": [
  4. "react-app",
  5. [
  6. "@babel/preset-env",
  7. {
  8. "useBuiltIns": "entry",
  9. "corejs": 3
  10. }
  11. ]
  12. ],
  13. "plugins": [
  14. "@babel/plugin-proposal-class-properties",
  15. ["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib", "style": "css" }, "antd-mobile"],
  16. ["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": true }, "antd"]
  17. ]
  18. }

Eslint & prettier

完成了核心的应用流程打包代码,接下来我们来聊一些轻松的代码检查。

一份良好的工程架构代码规范检查是必不可少的配置。

prettier

  1. yarn add --dev --exact prettier
  2. 复制代码

安装完成之后我们在项目根目录下:

  1. echo {}> .prettierrc.js
  2. 复制代码

我们来个这个js内容添加一些基础配置

  1. module.exports = {
  2. printWidth: 100, // 代码宽度建议不超过100字符
  3. tabWidth: 2, // tab缩进2个空格
  4. semi: false, // 末尾分号
  5. singleQuote: true, // 单引号
  6. jsxSingleQuote: true, // jsx中使用单引号
  7. trailingComma: 'es5', // 尾随逗号
  8. arrowParens: 'avoid', // 箭头函数仅在必要时使用()
  9. htmlWhitespaceSensitivity: 'css', // html空格敏感度
  10. }
  11. 复制代码

我们再来添加一份.prettierignoreprettier忽略检查一些文件:

  1. //.prettierignore
  2. **/*.min.js
  3. **/*.min.css
  4. .idea/
  5. node_modules/
  6. dist/
  7. build/
  8. 复制代码

同时让我们为我们的代码基于huskylint-staged添加git hook

具体配置可以参照这里[husky&list-staged](https://link.juejin.cn?target=https%3A%2F%2Fprettier.io%2Fdocs%2Fen%2Fprecommit.html%23option-1-lint-stagedhttpsgithubcomokonetlint-staged)

安装完成后,在我们每次commit时候都会触发lit-staged自动修复我们匹配的文件:

71 webpack基础配置 - 图2

因为我们项目中是ts文件,所以要稍微修改一下他支持的后缀文件:

  1. // package.json
  2. ...
  3. "lint-staged": {
  4. "*.{js,css,md,ts,tsx,jsx}": "prettier --write"
  5. }
  6. ...
  7. 复制代码

ESlint

Eslint其实就不用多说了,大名鼎鼎嘛。

  1. yarn add eslint --dev
  2. 复制代码

初始化eslint

  1. npx eslint --init
  2. 复制代码

eslint回和我们进行一些列的交互提示,按照提示进行选择我们需要的配置就可以了:

71 webpack基础配置 - 图3

prettiereslint共同工作时,他们可能会冲突。我们需要安装yarn add -D eslint-config-prettie插件并且覆盖eslint部分规则。

安装完成之后,我们稍微修改一下eslint的配置文件,让冲突时,优先使用prettier覆盖eslint规则:

  1. // .eslint.js
  2. module.exports = {
  3. "env": {
  4. "browser": true,
  5. "es2021": true
  6. },
  7. "extends": [
  8. "eslint:recommended",
  9. "plugin:react/recommended",
  10. "plugin:@typescript-eslint/recommended",
  11. // 添加`prettier`拓展 用于和`prettier`冲突时覆盖`eslint`规则
  12. "prettier"
  13. ],
  14. "parser": "@typescript-eslint/parser",
  15. "parserOptions": {
  16. "ecmaFeatures": {
  17. "jsx": true
  18. },
  19. "ecmaVersion": 12,
  20. "sourceType": "module"
  21. },
  22. "plugins": [
  23. "react",
  24. "@typescript-eslint"
  25. ],
  26. "rules": {
  27. }
  28. };
  29. 复制代码

同时我们来添加.eslintignore忽略掉一些我们的非ts目录文件,比如构建的一些脚本文件。

  1. *.d.ts
  2. scripts/**

优化打包体积

使用CompressionPlugin对文件进行压缩

  1. yarn add compression-webpack-plugin -D
  1. const CompressionPlugin = require("compression-webpack-plugin")
  2. plugins:[
  3. new CompressionPlugin({
  4. threshold: 0,
  5. minRatio:0.6,
  6. test:/\.(css|js)/i,
  7. algorithm:"gzip"
  8. })
  9. ]

优化打包速度,开启DLL

  1. // 输出文件
  2. output: {
  3. publicPath: './',
  4. path: path.resolve(__dirname, 'dist'),
  5. filename: '[name].dll.js',
  6. assetModuleFilename: 'assets/[name]_[hash][ext]',
  7. clean: true,
  8. // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
  9. // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
  10. library: '_dll_[name]',
  11. },
  1. const path = require('path')
  2. const DllPlugin = require('webpack/lib/DllPlugin')
  3. const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
  4. plugins: [
  5. new MiniCssExtractPlugin({
  6. filename: '[name].css',
  7. }),
  8. new CompressionPlugin({
  9. threshold: 0,
  10. minRatio: 0.6,
  11. test: /\.(css|js)/i,
  12. algorithm: 'gzip',
  13. }),
  14. new HTMLWebpackPlugin({
  15. template: './src/index.html',
  16. inject: 'body',
  17. cache: true,
  18. minify: {
  19. removeComments: true, // 是否删除注释
  20. removeRedundantAttributes: true, // 是否删除多余(默认)属性
  21. removeEmptyAttributes: true, // 是否删除空属性
  22. collapseWhitespace: false, // 折叠空格
  23. removeStyleLinkTypeAttributes: true, // 比如link中的type="text/css"
  24. minifyCSS: true, // 是否压缩style标签内的css
  25. minifyJS: {
  26. // 压缩JS选项,可参考Terser配置
  27. mangle: {
  28. toplevel: true,
  29. },
  30. },
  31. },
  32. }),
  33. new DllPlugin({
  34. // 动态链接库的全局变量名称,需要和 output.library 中保持一致
  35. // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
  36. // 例如 react.manifest.json 中就有 "name": "_dll_react"
  37. name: '_dll_[name]',
  38. // 描述动态链接库的 manifest.json 文件输出时的文件名称
  39. path: path.join(__dirname, '[name].manifest.json'),
  40. }),
  41. new DllReferencePlugin({
  42. // 描述 react 动态链接库的文件内容
  43. manifest: require(path.join(__dirname, 'main.manifest.json')),
  44. }),
  45. ],