核心概念
- 入口起点(entry point): 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始,默认值是 ./src/index.js
//单个入口语法 string | string[]
module.exports = {
entry: './path/to/my/entry/file.js', //简写
entry:{ //与上面等效
main: './path/to/my/entry/file.js'
}
};
//对象语法 entry: { <entryChunkName> string | [string] } | {}
module.exports = {
entry: { //可以扩展多页面
dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
filename: 指定要输出的文件名称。
import: 启动时需加载的模块。
library: 指定 library 选项,为当前 entry 构建一个 library。
runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。请查看 output.publicPath
}
}
- output: 告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件,默认值 ./dist/main.js
const path = require('path');
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
- loader:webpack 只能理解 JavaScript 和 JSON 文件,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中
//test 属性,识别出哪些文件会被转换。
//use 属性,定义出在进行转换时,应该使用哪个 loader
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],//从右到左(或从下到上)地取值(evaluate)/执行(execute)
},
};
//使用正则表达式匹配文件时,你不要为它添加引号。也就是说,/\.txt$/ 与 '/\.txt$/' 或 "/\.txt$/" 不一样。
//前者指示 webpack 匹配任何以 .txt 结尾的文件,后者指示 webpack 匹配具有绝对路径 '.txt' 的单个文件; 这可能不符合你的意图
- 插件:可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
//使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
常用配置
文件解析
es6 解析
npm install -D babel-loader @babel/core @babel/preset-env webpack
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/, //bower_components 是包管理器 Bower 依赖文件
use: {
loader: 'babel-loader',
options: { //使用 options 属性,来向 loader 传递 options
presets: ['@babel/preset-env']
}
}
}
]
}
解析 css
less-loader:less 文件预解析,css-loader 解析 css 文件成一个数组([文件地址,css 字符串]),style-loader 将 css 注入到
npm i less-loader css-loader style-loader -D
module.exports = {
module: {
rules:[
{
test: /\.less$/ ,
// 注意要按照从右到左依赖的顺序编写
use: ['style-loader','css-loader','less-loader']
}
]
}
}
兼容处理 css
- 自动补齐浏览器前缀:为了向下兼容,通过 postcss-loader 和 autoprefixer 实现前缀的自动补齐
npm i postcss-loader autoprefixer -D
module.exports = {
module: {
rules:[
{
test: /\.css$/,
loader:[
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')({
browsers:['last 2 version','>1%','iOS 7']
})
]
}
}
]
}
]
}
}
- 响应式适配:移动端适配步骤
- 根据屏幕分辨率设置根元素大小,以前使用 lib-flexible(已弃用),现在大多使用 viewport 单位实现
- 开发时根据设计稿使用 px,最后统一转化为 rem
npm i --save postcss-plugin-px2rem
module.exports = {
postcss: [px2rem({
rootValue: 100,//换算基数
unitPrecision: 5,//允许REM单位增长到的十进制数字
exclude:false, 排除文件
selectorBlackList: [],
ignoreIdentifier: false,
replace: true,//替换包含REM的规则,而不是添加回退
mediaQuery: false,//允许在媒体查询中转换px
minPixelValue: 0 //设置要替换的最小像素值
})],
}
解析图片字体
file-loader:可处理图片,可使用 [name] 占位符 处理 build 后的文件名,不配置会使用 hash
npm install url-loader file-loader -D
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[ext]', //img 自定义目录
esModule: false
},
},
],
},
],
}
url-loader:与 file-loader 效果配置一样,但是可以设置 limit。依赖 file-loader
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, //小于 8192 byte 的编码成 base64,大于的使用 file-loader 处理
//fallback: require.resolve('file-loader'), 预设,不需配置
name: 'img/[name].[ext]',
publicPath: '../', //修改公共路径
},
},
],
},
]
开发和构建
文件监听
原理是轮询文件最后一次编辑时间是否改变
module.exports = {
watch: true, //开启后自动重新构建
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 2000, //2s 内没更改在构建
poll: 1000 //每秒轮询次数
}
}
热重载
live reload,在源代码发生更新时 自动重新构建 + 自动刷新浏览器。通过 webpack-dev-server 实现
npm i webpack-dev-server -D
module.exports = {
devServer: {
open: true, // 构建完成后自动打开浏览器
port: 8888, // 监听端口
contentBase: './dist' // 服务器根路径
}
}
缺点:会全局刷新浏览器,重置所有状态。
热更新
HMR(热模块替换),解决热重载缺陷,可实现局部刷新,可保存数据状态,通过 webpack 内置 HotModuleReplacementPlugin 实现
const webpack = require('webpack')
module.exports = {
devServer: {
hot: true // 开启热更新
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]
}
sourceMap
mode:production 模式下,文件都是压缩过的,出错后不易调试,开启 sourceMap 可以定位源代码
module.exports = {
devtool: 'sourcemap'
}
跨域代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://mysite'
}
}
}
}
过程:/api/getData/ -> http://localhost/api/getData -> <http://mysite/api/getData>
自定生成 html 文件
dist 目录下需要手动创建 index.html 文件,可以通过 html-webpack-plugin 自动生成
npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
tamplate: path.resolve(__dirname,'./src/index.html') // 提供一个模板 html 文件作为基础
})
]
}
优化日志处理
npm i friendly-errors-webpack-plugin -D
const friendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
module.exports = {
stats: 'errors-only'
devServer: {
stats: 'errors-only'
}
plugins: [
new friendlyErrorsWebpackPlugin()
]
}
自定义 plugin 捕获构建错误
//每次构建结束后会触发 compiler 对象的 done 钩子函数,可以在这个 hook 中捕获构建错误并进行相关处理
module.exports = {
plugins: [
function() {
this.hooks.done.tap('done', (stats) => {
if (stats.compilation.errors &&
stats.compilation.errors.length &&
process.argv.indexOf('--watch') == -1)
{
// 进行相关处理
process.exit(1);
}
})
}
]
}
分离开发生产环境配置
- 分离文件形式 node 中有个process 对象,通过 cross-env(跨平台设置和使用环境变量的脚本)在 process.env 上挂载一个 NODE_ENV 环境变量用于区分
package.json:
"scripts": {
"build": cross-env NODE_ENV = 'production' webpack --config webpack.prod.js //按生产方式打包后上线
"dev": cross-env NODE_ENV = 'development' webpack-dev-server webpack.dev.js //开发
}
webpack.base.js:
module.exports = {
//公共配置
devtool: process.env.NODE_ENV === 'production' ? 'none' : 'sourcemap'
}
webpack.prod.js 文件:
const merge = require('webpack-merge') //合并配置文件
const base = require('./webpack.base.js')
module.exports = merge(base,{
mode: 'production'
// 生产环境专用的配置
})
webpack.dev.js 文件:
//同上
module.exports = merge(base,{
mode: 'development'
// 开发环境专用的配置
})
- 函数判断形式
module.exports = (mode) => {
// 公共配置
if(mode === 'production'){
// 生产环境配置
} else {
// 开发环境配置
}
}
webpack 优化方式
性能分析
webpack 可以配置 stats 展示打包构建信息,但是粒度比较大,可以借助插件分析
打包速度分析:
npm install --save-dev speed-measure-webpack-plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
//配置
});
打包体积分析:
npm install --save-dev webpack-bundle-analyzer
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
通过 localhost:8888 访问可视化文件
splitChunks 代码分割
形成 chunk 有三中方式:
- 多入口文件,一个 entry 对应一个 chunk
- 动态导入的代码,会被打包到一个 chunk 中
- 通过 splitChunks 分割的代码被打包到一个 chunk 中
module.exports = {
optimization: {
splitChunks: {
// 这里的默认配置项省略,它们最终都会作用到 cacheGroups 上
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/, //可以将公用的第三方库代码抽离成一个单独的 chunk
//chunks 字段指的是分离 chunk 的标准
chunks: 'async' // 默认值,表示会将异步导入(动态导入)的模块抽离成单独的 chunk
}
}
}
}
}
示例:
// page1.js
import $ from 'jquery'
import React from 'react'
import(/* webpackChunkName: "page1-lodash"*/ 'lodash')
entry 入口对应一个 chunk,jquery 和 React 都是同步,会打包在入口 chunk 中,loadsh 是异步会单独打包成一个 chunk
待补充...