系统化学习前端之webpack(基础)

webpack 基本介绍

Webpack是以一个文件多个文件为入口,将项目中所有文件编译成一个或多个文件输出出去,编译好的文件(bundle)可以在浏览器端运行的前端构建工具。

写在前面

之所以会有webpack的出现,主要原因是前端工程化,包括ES6模块化,Less/Scss预处理器,MVVM框架等技术。随着前端的发展,前端项目越来越重,模块化,组件化成为前端项目的必然走向,但浏览器底层只能解析js,css以及html文件,对于工程化技术使用的ES Module,Less/Scss,vue,jsx等技术无法解析,而webpack恰恰是将浏览器无法解析的文件转化为可以解析的文件的桥梁。这类桥梁,我们统称为打包工具,常见打包工具有:Grunt,Gulp,Parcel,Webpack,Rollup,Vite。
值得注意是,打包工具发展至今,除了解析文件以外,还集成了许多其他功能,如压缩代码,处理兼容性,提高代码性能等;

基本用法

创建资源目录

webpack-demo # 项目根目录
└── src # 项目源码目录
	├── js # js文件目录
	│   ├── count.js
	│   └── sum.js
	└── main.js # 项目主文件

新建文件

count.js

export default function count(x, y) {
	return x - y;
}

sum.js

export default function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

main.js

import count from "./js/count";
import sum from "./js/sum";

console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));

安装依赖

初始化 package.json(在webpack-demo根目录下打开终端输入)

npm init -y

下载依赖

npm i webpack webpack-cli -D

启动webpack

开发模式

npx webpack ./src/main.js --mode=development

生产模式

npx webpack ./src/main.js --mode=production

注意:默认 Webpack 会将文件打包输出到 dist 目录下,查看 dist 目录下文件情况:

  1. 开发模式下,仅能编译 JS 中的 ES Module 语法
  2. 生产模式下,能编译 JS 中的 ES Module 语法,还能压缩 JS 代码和 HTML 代码

核心概念

Webpack 配置是围绕 核心概念 进行不同配置的。

entry

入口,指示 Webpack 从哪个文件开始打包

output

输出,指示 Webpack 打包完的 js 文件输出到哪里去,如何命名等

loader

加载器,webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析

plugins

插件,扩展 Webpack 的功能,如压缩,分包等

mode

模式,开发模式:development;生产模式:production

基础配置

新建配置文件

在项目根目录下新建文件:webpack.config.js

module.exports = {
	// 入口
	entry: "./src/main.js", // 相对路径和绝对路径都行
	// 输出
	output: {
		path: path.resolve(__dirname, "dist"),  // path: 文件输出目录,必须是绝对路径,path.resolve()方法返回一个绝对路径,__dirname 当前文件的文件夹绝对路径
		filename: "main.js",  // filename: 输出文件名,可以配置 filename: "/static/js/main.js" 来修改输出 js 文件路径
		clean: true, // 自动清空上一次打包资源
	},
	// 加载器
	module: {
		rules: [
			// loader 配置
		],
	},
	// 插件
	plugins: [],
	// 模式
	mode: "development", // 开发模式
};

启动 webpack

npx webpack

或者

npx webpack --config webpack.config.js

注意:如果 webpack.config.js 存在,则 webpack 命令将默认选择使用 webpack.config.js 。

资源处理

处理 CSS 资源

  1. 安装依赖

    npm install -D style-loader css-loader
    
  2. 配置loader

    module.exports = {
      module: {
    	rules: [
    	  {
    		test: /\.css$/i,
    		use: ["style-loader", "css-loader"],
    	  },
    	],
      },
    };
    
  3. 创建css文件

    添加 /src/css/index.css

    div {
      font-size: 16px;
      color: skyblue;
    }
    

    入口文件引入css文件

    main.js

    import './css/index.css'
    
  4. 启动 webpack

    npx webpack
    

    注意:配置loader按照style-loader,css-loader顺序配置(否则,报错CssSyntaxError);loader执行顺序是先执行css-loader(将css资源编译成commonjs模块添加至js中),再执行style-loader(将js中css通过动态创建style标签添加至html文件中)。

处理 Less 资源

  1. 安装依赖

    npm install -D style-loader css-loader less-loader
    
  2. 配置loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.less$/i,
            use: ["style-loader", "css-loader", "less-loader"],
          },
        ],
      },
    };
    
  3. 创建less文件

    添加 /src/less/index.less

    .less {
    	&-demo {
    		font-size: 16px;
    		color: skyblue;
    	}
    }
    

    入口文件引入less文件

    main.js

    import './less/index.less'
    
  4. 启动webpack

    npx webpack
    

    注意:loader执行顺序:less-loader,css-loader,style-loader。less-loader是将less编译成css文件。

处理 Sass/Scss 资源

css预处理器Sass有两种语法形式,对应拓展名为 .sass 和 .scss,但webpack处理loader只需要sass-loader即可。

  1. 安装依赖

    npm install -D style-loader css-loader sass-loader sass
    
  2. 配置loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.s[ac]ss$/i,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
        ],
      },
    };
    
  3. 创建scss或sass文件

    添加 /src/scss/index.scss

    .scss {
    	&-demo {
    		font-size: 16px;
    		color: skyblue;
    	}
    }
    

    添加 /src/sass/index.sass

    .sass 
    	&-demo 
    		font-size: 16px
    		color: skyblue
    

    入口文件引入scss文件

    main.js

    import './scss/index.scss'
    import './sass/index.sass'
    
  4. 启动webpack

    npx webpack
    

    注意:loader执行顺序:sass-loader,css-loader,style-loader。sass-loader是将scss编译成css文件。此外,按照sass-loader需要安装sass(dart sass和node sass),sass/scss 文件的编译环境。

处理 stylus 资源

  1. 安装依赖

    npm install -D style-loader css-loader stylus-loader stylus
    
  2. 配置loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.styl$/i,
            use: ["style-loader", "css-loader", "stylus-loader"],
          },
        ],
      },
    };
    
  3. 创建stylus文件

    添加 /src/stylus/index.styl

    .stylus {
    	&-demo {
    		font-size: 16px;
    		color: skyblue;
    	}
    }
    

    入口文件引入stylus文件

    main.js

    import './stylus/index.styl'
    
  4. 启动webpack

    npx webpack
    

    注意:loader执行顺序:stylus-loader,css-loader,style-loader。stylus-loader是将stylus编译成css文件。此外,按照stylus-loader需要安装stylus,stylus 文件的编译环境。

处理图片资源

webpack4 通过 file-loader 和 url-loader 处理图片资源,file-loader 将图片转换为浏览器能够解析的形式,url-loader 将小图片base64处理并编译到 js 文件中,实现优化(减少请求数量了,但增加 js 文件体积大小)。webpack5 将 file-loader 和 url-loader 内置,简单化了图片资源配置。

  1. 配置

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(png|jpe?g|gif|webp)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
              }
            },
            generator: {
              filename: "static/imgs/[hash:8][ext][query]", // 配置图片文件输出到 static/imgs 目录中,并将图片文件命名 [hash:8][ext][query],其中 [hash:8] 表示hash值取8位,[ext] 是使用之前的文件扩展名,[query] 表示添加之前的query参数
            },
          },
        ],
      },
    };
    
  2. 添加图片资源

    添加 /src/images/1.jpeg, /src/images/2.png

  3. 引用图片资源

    .box1 {
    	width: 100px;
    	height: 100px;
    	background-image: url("../images/1.jpeg");
    	background-size: cover;
    }
    
    .box2 {
    	width: 100px;
    	height: 100px;
    	background-image: url("../images/2.png");
    	background-size: cover;
    }
    
  4. 启动webpack

    npx webpack
    

    注意:配置中设置 type: "asset" 表示开启图片资源处理,maxSize 表示小于 maxSize的图片会被base64处理,并编译到js文件中。base64处理图片成为dataUri,相对于原始图片,体积会增大,原图体积越大,dataUri也会越大。

处理字体图标及其他资源

  1. 下载使用字体图片

    阿里巴巴矢量图标库,下载以后解压,在 html 文件中有详细的使用方法。

  2. 使用HTML5中的音频或者视频标签引入音视频内容。

    音视频用法

  3. 配置

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(ttf|woff2?|map4|map3|avi)$/i,
            type: "asset/resource",
            generator: {
              filename: "static/media/[hash:8][ext][query]", // 输出文件路径
            },
          },
        ],
      },
    };
    

处理 JS 资源

webpack 处理 js 仅仅是针对ES module语法,解决了模块之间的引入关系,而对于低版本浏览器无法兼容的ES6语法,如箭头函数,解构语法等依旧无法识别。因此,需要利用 babel 对 js 进行兼容性,同时也可以利用 Eslint 对 js 代码格式进行检查。

Eslint

Eslint 由 FaceBook(Meta) 收购维护,因此,Eslint 是 Javascript 和 JSX 检查工具。

  1. 配置文件

    配置文件格式是以 .eslintrc.* 形式,可以为 .eslintrc.eslintrc.js.eslintrc.json,不同的文件后缀,配置格式不一致。

  2. 具体配置(eslintrc.js)

    module.exports = {
      // 解析选项
      parserOptions: {
    	  ecmaVersion: 6, // ES 语法版本
    	  sourceType: "module", // ES 模块化
    	  ecmaFeatures: { // ES 其他特性
    		jsx: true // 如果是 React 项目,就需要开启 jsx 语法
    	  }
      },
      // 具体检查规则,off | 0 表示关闭规则; warn | 1 表示开启规则,警告级别,不会导致退出;error | 2 表示开启规则,错误级别,会导致退出;
      rules: {
    	  semi: "error", // 禁止使用分号
    	  'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
    	  'default-case': [
    		'warn', // 要求 switch 语句中有 default 分支,否则警告
    		{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
    	  ],
    	  eqeqeq: [
    		'warn', // 强制使用 === 和 !==,否则警告
    		'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
    	  ],
      },
      // 继承其他规则
      extends: [
    	//  "eslint:recommended", // Eslint 官方的规则
    	// "plugin:vue/essential", // Vue Cli 官方的规则
    	// "react-app", // React Cli 官方的规则
      ],
      plugins: ["import"], // 解决动态导入import语法报错
    };
    

    注意:Eslint 配置可以根据实际开发环境自行配置,可以使用 规则文档,设定 rules;也可以使用 extends 继承 官方配置的规则,如 Eslint 官方的规则Vue Cli 官方的规则 , React Cli 官方的规则

  3. webpack 配置使用

    • 安装依赖

      npm i eslint-webpack-plugin eslint -D
      
    • Eslint 配置(.eslintrc.js)

      module.exports = {
        // 继承 Eslint 规则
        extends: ["eslint:recommended"],
        env: {
      	node: true, // 启用node中全局变量
      	browser: true, // 启用浏览器中全局变量
        },
        parserOptions: {
      	ecmaVersion: 6,
      	sourceType: "module",
        },
        rules: {
      	"no-var": 1, // 不能使用 var 定义变量
        },
      };
      
    • webpack 配置(webpack.config.js)

      const path = require("path");
      const ESLintWebpackPlugin = require("eslint-webpack-plugin");
      
      module.exports = {
      	entry: "",
      	output: {},
      	module: {
      		rules: []
      	},
      	plugins: [
      		new ESLintWebpackPlugin({
      		  context: path.resolve(__dirname, "src"), // 指定检查文件的根目录
      		}),
      	]
      }
      
  4. VSCode 安装 ESlint 插件

    新建 .eslintignore 忽略 dist 文件检查

Babel

JavaScript 编译器,将 ES6 语法编译成兼容的 Javascript 的语法。

  1. 配置文件

    配置文件格式有两种形式:babel.config.* 和 .babelrc.* 。

  2. 具体配置(babel.config.js)

    module.exports = {
      // 预设
      presets: [
    	// "@babel/preset-env", // 编译 javascript 的语法预设
    	// "@babel/preset-react", // 编译 jsx 的语法预设
    	// "@babel/preset-typescript", // 编译 typescript 的语法预设
      ],
    };
    

    注意: 预设可以加载一个或者多个。

  3. webpack 配置使用

    • 安装依赖

      npm i babel-loader @babel/core @babel/preset-env -D
      
    • babel 配置(babel.config.js)

      module.exports = {
        presets: ["@babel/preset-env"],
      };
      
    • webpack 配置(webpack.config.js)

      module.exports = {
        module: {
      	rules: [
      	  {
      		test: /\.js$/,
      		exclude: /node_modules/, // 排除node_modules代码不编译
      		loader: "babel-loader",
      		// options: {
      		// 	presets: ["@babel/preset-env"]
      		// }
      	  },
      	],
        },
      };
      

    注意: webpack options 选项 配置 presets 等同于 babel.config.js 配置 presetsx,可以在 babel.config.js 或者 webpack.config.js 中选取一个配置 presets 。

处理 HTML 资源

webpack 编译输出的 js 等其他文件,需要引入到 html 文件中,通过处理 html 资源,可以实现 js 自动注入 html 文件中。

  1. 安装依赖

    npm i html-webpack-plugin -D
    
  2. webpack 配置

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
    	entry: "",
    	output: {},
    	module: {
    		rules: []
    	},
    	plugins: [
    		new HtmlWebpackPlugin({
    	      template: path.resolve(__dirname, "public/index.html"), // 以 public/index.html 为模板创建文件,并注入打包生成的js等资源
    	    }),
    	]
    }
    

    注意:自动注入的 js 文件,会用在 script 标签使用 defer 属性。async 和 defer 表示 script 异步加载,async 仅是异步加载,加载完成立即执行;defer 是异步加载,在浏览器所有 dom 元素解析完成后,再执行,均有效优化页面加载阻塞的问题。

配置开发服务器及自动化编译

之前的 webpack 配置都没有开启 webpack 内置的服务器,每次修改代码都需要执行 npx webpack 来重新编译。而配置开发服务器以后,可以实现代码自动编译,无需多次执行 webpack 命令,当然为了提高编译效率,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。

  1. 安装依赖

    npm i webpack-dev-server -D
    
  2. webpack 配置

    module.exports = {
      // 开发服务器
      devServer: {
        host: "localhost", // 启动服务器域名
        port: "3000", // 启动服务器端口号
        open: true, // 是否自动打开浏览器
      },
    }
    
  3. 启动 webpack

    npx webpack serve
    

配置开发环境和生产环境下的 webpack

开发环境和生产环境需求是不同的,开发环境配置讲究效率问题,而生产环境配置讲究代码运行性能和打包速度问题,因此,针对不同环境可以配置不同的文件。

  1. 开发环境 webpack.dev.js

    const path = require("path");
    const ESLintWebpackPlugin = require("eslint-webpack-plugin");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
      entry: "./src/main.js",
      output: {
        path: "",
        filename: "static/js/main.js", 
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ["style-loader", "css-loader"],
          },
          {
            test: /\.less$/,
            use: ["style-loader", "css-loader", "less-loader"],
          },
          {
            test: /\.s[ac]ss$/,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
          {
            test: /\.styl$/,
            use: ["style-loader", "css-loader", "stylus-loader"],
          },
          {
            test: /\.(png|jpe?g|gif|webp)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
              },
            },
            generator: {
              filename: "static/imgs/[hash:8][ext][query]",
            },
          },
          {
            test: /\.(ttf|woff2?)$/,
            type: "asset/resource",
            generator: {
              filename: "static/media/[hash:8][ext][query]",
            },
          },
          {
            test: /\.js$/,
            exclude: /node_modules/, 
            loader: "babel-loader",
          },
        ],
      },
      plugins: [
        new ESLintWebpackPlugin({
          context: path.resolve(__dirname, "../src"),
        }),
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, "../public/index.html"),
        }),
      ],
      devServer: {
        host: "localhost",
        port: "3000", 
        open: true, 
      },
      mode: "development",
    };
    
  2. 生产环境 webpack.prod.js

    const path = require("path");
    const ESLintWebpackPlugin = require("eslint-webpack-plugin");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
      entry: "./src/main.js",
      output: {
        path: path.resolve(__dirname, "../dist"), 
        filename: "static/js/main.js", 
        clean: true,
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ["style-loader", "css-loader"],
          },
          {
            test: /\.less$/,
            use: ["style-loader", "css-loader", "less-loader"],
          },
          {
            test: /\.s[ac]ss$/,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
          {
            test: /\.styl$/,
            use: ["style-loader", "css-loader", "stylus-loader"],
          },
          {
            test: /\.(png|jpe?g|gif|webp)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024,
              },
            },
            generator: {
              filename: "static/imgs/[hash:8][ext][query]",
            },
          },
          {
            test: /\.(ttf|woff2?)$/,
            type: "asset/resource",
            generator: {
              filename: "static/media/[hash:8][ext][query]",
            },
          },
          {
            test: /\.js$/,
            exclude: /node_modules/, 
            loader: "babel-loader",
          },
        ],
      },
      plugins: [
        new ESLintWebpackPlugin({
          context: path.resolve(__dirname, "../src"),
        }),
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, "../public/index.html"),
        }),
      ],
      mode: "production",
    };
    

    配置运行指令 package.json

    {
      "scripts": {
        "dev": "npx webpack serve --config ./config/webpack.dev.js",
        "build": "npx webpack --config ./config/webpack.prod.js"
      }
    }
    

    开发模式命令 npm run dev,生产模式命令:npm run build

css资源兼容、抽离、压缩处理

css 样式会编译注入 js 文件中,会使得 js 文件体积较大,同时 js 动态生成 style 标签嵌入样式会导致闪屏现象,用户体验不好,因此需要对 css 进行抽离,并进行优化处理,如兼容,压缩。通常配置在生产模式下,如 webpack.prod.js

抽离

  1. 安装依赖

    npm i mini-css-extract-plugin -D
    
  2. webpack 配置

    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    module.exports = {
    	module: {
    		rules: [
    		  {
    			test: /\.css$/,
    			use: [MiniCssExtractPlugin.loader, "css-loader"],
    		  },
    		  {
    			test: /\.less$/,
    			use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
    		  },
    		  {
    			test: /\.s[ac]ss$/,
    			use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
    		  },
    		  {
    			test: /\.styl$/,
    			use: [MiniCssExtractPlugin.loader, "css-loader", "stylus-loader"],
    		  },
    		]
    	},
    	plugins: [
    		new MiniCssExtractPlugin({
    		  filename: "static/css/main.css",
    		}),
    	],
    	mode: "production"
    }
    

兼容

  1. 安装依赖

    npm i postcss-loader postcss postcss-preset-env -D
    
  2. webpack 配置

    module.exports = {
    	module: {
    		rules: [
    			{
    				test: /\.css$/,
    				use: [
    				  MiniCssExtractPlugin.loader,
    				  "css-loader",
    				  {
    					loader: "postcss-loader",
    					options: {
    					  postcssOptions: {
    						plugins: [
    						  "postcss-preset-env", // 能解决大多数样式兼容性问题
    						],
    					  },
    					},
    				  },
    				],
    			  },
    			  {
    				test: /\.less$/,
    				use: [
    				  MiniCssExtractPlugin.loader,
    				  "css-loader",
    				  {
    					loader: "postcss-loader",
    					options: {
    					  postcssOptions: {
    						plugins: [
    						  "postcss-preset-env", 
    						],
    					  },
    					},
    				  },
    				  "less-loader",
    				],
    			  },
    			  {
    				test: /\.s[ac]ss$/,
    				use: [
    				  MiniCssExtractPlugin.loader,
    				  "css-loader",
    				  {
    					loader: "postcss-loader",
    					options: {
    					  postcssOptions: {
    						plugins: [
    						  "postcss-preset-env", 
    						],
    					  },
    					},
    				  },
    				  "sass-loader",
    				],
    			  },
    			  {
    				test: /\.styl$/,
    				use: [
    				  MiniCssExtractPlugin.loader,
    				  "css-loader",
    				  {
    					loader: "postcss-loader",
    					options: {
    					  postcssOptions: {
    						plugins: [
    						  "postcss-preset-env", 
    						],
    					  },
    					},
    				  },
    				  "stylus-loader",
    				],
    			  },
    		]
    }
    
  3. 代码优化

    点击查看代码
    // 获取处理样式的Loaders
    const getStyleLoaders = (preProcessor) => {
      return [
    	MiniCssExtractPlugin.loader,
    	"css-loader",
    	{
    	  loader: "postcss-loader",
    	  options: {
    		postcssOptions: {
    		  plugins: [
    			"postcss-preset-env", // 能解决大多数样式兼容性问题
    		  ],
    		},
    	  },
    	},
    	preProcessor,
      ].filter(Boolean);
    };
    
    module.exports = {
    	module: {
    		rules: [
    		  {
    			test: /\.css$/,
    			use: getStyleLoaders(),
    		  },
    		  {
    			test: /\.less$/,
    			use: getStyleLoaders("less-loader"),
    		  },
    		  {
    			test: /\.s[ac]ss$/,
    			use: getStyleLoaders("sass-loader"),
    		  },
    		  {
    			test: /\.styl$/,
    			use: getStyleLoaders("stylus-loader"),
    		  },
    		 ]
    	}
    }
    
  4. package.json 配置兼容范围

    {
      "browserslist": ["last 2 version", "> 1%", "not dead"]
    }
    

    注意:关于兼容性处理根据实际情况处理:Browserslist

压缩

  1. 安装依赖

    npm i css-minimizer-webpack-plugin -D
    
  2. webpack 配置

    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    
    module.exports = {
    	plugins: [
    		new CssMinimizerPlugin(),
    	]
    }
    

后记

前端构建工具有很多,目前来说,webpack 还是主流构建工具,掌握 webpack 配置有助于定制适合自己团队的脚手架,一定程度帮助了团队制定了协同规范。此外,一个优秀的项目需要从多个方面优化升级,下篇从 webpack 角度来谈谈项目优化问题。

posted @ 2023-02-21 14:03  深巷酒  阅读(12)  评论(0编辑  收藏  举报