webpack学习
基本使用
初始化项目
npm init -y
安装webpack
// 安装webpack核心包和webpack命令行工具包(webpack-cli),webpack-cli是为了执行核心包的命令
npm install webpack webpack-cli --save-dev
新增webpack配置文件,命名固定为webpack.config.js
基本概念
-
entry(入口)
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点。例如:
module.exports = { entry: './src/index.js' } -
output(输出)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。
你可以通过在配置中指定一个 output 字段,来配置这些处理过程:
module.exports = { entry: './src/index.js', putput: { path: resolve(__dirname, 'dist'), // 输出文件夹的绝对路径,__dirname指当前文件所在的目录 filename: 'main.js' // 输出的文件名 } }} -
loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
module: { rules: [ // 测试自己的loader use: resolve(__dirname, 'loaders', 'raw-loader.js') { test: /\.txt$/, use: 'raw-loader' } ] }, -
plugin(插件,本质是一个class)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ] -
mode(模式)
日常的开发工作中,一般会有两套构建环境
一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印debug信息,包含sourcemap文件
一套构建后的结果直接应用于线上,即代码经过压缩,运行时不打印debug信息,静态文件不包含sourcemap
webpack4.x版本引入了mode的概念
当你指定使用production mode时,默认启用各种性能优化的功能,包括构建结果优化以及webpack运行性能优化
如果使用的development mode,则开启debug工具,运行时打印详细错误信息,以及更加快速的增量编译构建
环境差异
- 开发环境
- 需要生成sourcemap文件
- 需要打印debug信息
- 需要live reload或者hot reload的功能(热更新)
- 生成环境
- 可能需要分离css成单独的文件,以便多个页面共享一个css文件
- 需要压缩html/css/js代码
- 需要压缩图片等资源文件
- 开发环境
-
开发服务器
可以使用watch和live server模拟webpack-dev-server的开发模式,但是有不足之处,主要是每次修改保存都是将所有源代码重新编译,编译成功都要进行文件读写操作,不能实现局部刷新
安装webpack-dev-server
npm i webpack-dev-server -D配置devServer
// devServer会启动一个http开发服务器,把一个文件夹作为静态根目录 // 为了提高性能,使用的内存文件系统(webpack使用的memory-fs,类似fs) devServer: { // 设置静态资源路径,查找资源的优先级是先从打包好的目录查找,找不到在去设置的directory里面找 static: { // 告诉服务器从哪里提供内容 directory: join(__dirname, 'static'), // 用来决定应该从哪里提供 bundle // publicPath: '/serve' }, devMiddleware: { writeToDisk: true, // 如果指定此选项为true,也会把打包后的文件写入硬盘一份 }, compress: true, // 是否启用压缩 port: 8080, // http的端口号 open: true, // 自动打开浏览器 }, -
HMR(热替换)
替换替换局部内容,其余内容不变
在webpack.config.js中设置如下字段,此时热替换并未生效,修改title.js文件,整个页面都刷新,需要在入口文件写这段代码
devServer: { hot: true },// 入口文件 if (module.hot) { // 如果开启了热更新,就把需要开启热更新的模块添加进来 module.hot.accept(['./js/title.js'], () => { console.log('title.js模块更新'); }) } -
支持react热替换
安装@pmmmwh/react-refresh-webpack-plugin和react-refresh
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh在webpack.config.js里面配置插件
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin') new ReactRefreshWebpackPlugin()在babel.config.js配置插件,具体实现热替换的是react-refresh
plugins: [ ['react-refresh/babel'] ] -
支持vue热更新
本文以vue3进行测试,先安装依赖vue、vue-loader和@vue/compiler-sfc
npm i vue npm i vue-loader @vue/compiler-sfc -D编写vue文件
<template> <div class="example">{{ msg }}</div> </template> <script setup> import { ref } from "vue"; const msg = ref("测试"); </script> <style> .example { color: skyblue; } </style>入口文件引入vue文件
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount(document.getElementById('app'))wbepack.config.js配置对vue的支持
// 配置loader { test: /\.vue$/, use: ['vue-loader'] } // 配置插件 new VueLoaderPlugin()启动devserver会发现会有警告
index.js:7550 Feature flags __VUE_OPTIONS_API__, __VUE_PROD_DEVTOOLS__ are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle. For more details, see https://link.vuejs.org/feature-flags.解决方案使用webpack的DefinePlugin插件配置全局变量
new DefinePlugin({ __VUE_OPTIONS_API__: JSON.stringify(false), __VUE_PROD_DEVTOOLS__: JSON.stringify(true) }), -
支持css\less\sass
安装css-loader和style-loader,css-loader用来翻译处理@import和url(),style-loader可以将css插入到DOM中
npm install css-loader style-loader -D安装sass(dart-sass)和sass-loader,less less-loader,建议使用dart-sass替换node-sass,sass-loader和less-loader的作用是把scss、less转换成css
npm install sass sass-loader less less-loader -D// webpack.config.js对css/scss的配置 { test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, esModule: false } }, 'postcss-loader' ] }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader' ] }, -
拆分css
安装依赖mini-css-extract-plugin(mini-css-extract-plugin记得不能和style-loader一起使用,style-loader是将样式用style标签包裹引入到html中,mini-css-extract-plugin是将样式抽离到单独的文件导出link url,html中使用link标签引入)
npm install mini-css-extract-plugin -Dconst MiniCssExtractPlugin = require("mini-css-extract-plugin") module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 1 } }, "postcss-loader"] }, { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 2 } }, "postcss-loader", "sass-loader"] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].[hash:6].css", chunkFilename: "[id].css" }) ] -
压缩css
安装依赖css-minimizer-webpack-plugin
npm i css-minimizer-webpack-plugin -Dconst CssMinimizerPlugin = require("css-minimizer-webpack-plugin") optimization: { minimizer: [ // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释 `...`, new CssMinimizerPlugin(), ], }, -
支持图片
安装file-loader、url-loader和html-loader
file-loader可以读取文件,拷贝到打包目录(可以通过options修改文件名字)
url-loader作用与file-loader类型,只是options多了一个limit参数,如果文件的大小小于limit时,就转成base64字符串嵌入到HTML中,简单来说url-loader是file-loader的增强,文件大于limit时,交给file-loader处理,否则自己处理
注意:webpack5新增了资源模块,资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader,当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto' 来解决
npm install file-loader url-loader html-loader -D{ test: /\.(jpg|jepg|png|gif|bmp)$/, use: [{ loader: 'file-loader', options: { name: '[hash:10].[ext]',//拷贝后的名字 esModule: false, // 不使用esmodule,如果使用esmodule,使用引入的文件需要.default来获取 } }] }{ test: /\.(jpg|jepg|png|gif|bmp)$/, use: [{ loader: 'url-loader', options: { name: '[hash:10].[ext]',//拷贝后的名字 esModule: false, // 不使用esmodule,如果使用esmodule,使用引入的文件需要.default来获取, limit: 8 * 1024 // 如果文件的大小小于limit(小于8kb)时,就转成base64字符串嵌入到HTML中,否则行为与file-loader一样 } }] }在webpack使用图片的几种方式
- 直接通过import或者require引入,file-loader或url-loader处理
- 放在静态文件根目录下,通过html的img标签直接引用,需要配置devServer的static
- 在css中通过url引入图片 css-loader来进行解析处理
-
asset(资源类型模块)
webpack5已经内置资源类型模块
asset/resource --- file-loader
asset/inline --- url-loader
asset/source --- raw-loader
asset可以设置体积大小阈值去使用asset/resource或者asset/inline进行处理
{ test: /\.(png|svg|gif|jpe?g)$/, type: 'asset/resource', generator: { filename: "img/[name].[hash:4][ext]" } }, { test: /\.(png|svg|gif|jpe?g)$/, type: 'asset/inline' }, { test: /\.(png|svg|gif|jpe?g)$/, type: 'asset', generator: { filename: "img/[name].[hash:4][ext]" }, parser: { dataUrlCondition: { maxSize: 10 * 1024 } } } -
转义ES6/ES7/JXS
babel是一个编译javascript的平台,可以把ES6/ES7,React的JSX转义成ES5
安装依赖,babel-loader使用babel和webpack转义javascript文件,@babel/core是babel编译的核心包,@babel/preset-env是每一个环境的预设
npm install babel-loader @babel/core @babel/preset-env -D1.把ES6转成ES6语法树(@babel/core实现)
2.调用预设perset-env把ES6语法树转成ES5语法树(@babel/preset-env实现)
3.把ES5语法树转成ES5代码(@babel/core实现)
{ test: /\.jsx?$/, use: [ { loader: 'babel-loader', options: { // persets是预设,预设是一系列插件的集合 presets: [ "@babel/preset-env", "@babel/preset-react" ], // plugins是插件,因为不常用,所以不集合到预设里 plugins: [ ["@babel/plugin-proposal-decorators", { legacy: true }], ["@babel/plugin-proposal-class-properties", { loose: true }] ] }, } ], exclude: /node_modules/ // 不处理node_modules下的js,否则打包会报corejs的错误 },可以把babel-loader的配置抽离到根目录的babel-config.js文件下(需要手动新建这个文件),
// babel-config.js的配置 module.exports = { presets: ['@babel/preset-env'] } -
polyfill
webpack4之前是直接会处理,webpack5为了提高打包效率,需要我们手动配置了,babel官方推荐不要直接安装使用@babel/polyfill,改用core-js和regenerator-runtime来配置
安装core-js和regenerator-runtime,core-js安装3的版本
npm i core-js regenerator-runtime -D在抽离出来的babel.config.js里配置
module.exports = { presets: [ [ '@babel/preset-env', { // useBuiltIns的值:false-不对当前的js做处理,usage-根据源代码当中使用到的新语法进行处理,entry-根据配置的要兼容的浏览器来处理(不管源代码是否使用,只要这个浏览器需要就都处理) useBuiltIns: "entry", // 默认使用的corejs版本是2,使用usage要指定版本为3,否则会报错 corejs: 3 } ] ] } -
resolve模块解析规则
这些选项能设置模块如何被解析
resolve: { // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀 extensions: ['.js', '.json', '.wasm', '.ts', '.jsx', '.vue'], // 创建 import 或 require 的别名,来确保模块引入变得更简单 alias: { '@': resolve(__dirname, 'src') } }, -
source-map(映射,在调试时定位到源代码的位置)
不同阶段使用的配置
// 开发阶段 devtool: 'source-map'或者'cheap-module-source-map' -
ts-loader编译ts
安装依赖ts-loader,默认ts-loader会把ts安装下来,如果打包报错提示没有安装ts,就需要手动安装一下
npm i ts-loader -D npm i typescript -Dwebpack.config.js配置对ts的支持
{ test: /\.ts$/, use: ['ts-loader'] }, -
babel-loader编译ts
安装@babel/preset-typescript
npm install --save-dev @babel/preset-typescript直接使用babel-loader处理ts,有语法错误,依然能够打包成功,如果想要打包前对语法做校验可以在命令上增加tsc --noEmit
"build": "tsc --noEmit && webpack", // 需要noEmit,否则会生成一个.js的文件 -
压缩js
安装依赖terser-webpack-plugin
npm i terser-webpack-plugin -Doptimization: { minimizer: [ // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释 // `...`, // 压缩css new CssMinimizerPlugin(), // 压缩js new TerserPlugin() ], },
配置文件解释
-
.browserslistrc
配置兼容的浏览器的信息,用于postcss和babel转换代码时指定要兼容哪些浏览器,根据编写的条件来获取符合的浏览器版本,这部分是由caniuse提供的caniuse
> 1% // 市场占有率>1% last 2 version // 该浏览器最近的两个版本 not dead // 浏览器还在更新使用(根据24个月内,是否有修改来判定是否死亡) -
babel.config.js
会根据配置的预设/插件来转换js/jsx/ts/tsx等代码,转换过程会拿.browserslistrc配置的条件来转换
module.exports = { presets: ['@babel/preset-env'] } -
postcss.config.js
会根据配置的预设/插件来转换css代码,转换过程会拿.browserslistrc配置的条件来转换