前言
webpack这个打包的工具对于很多的开发者来说肯定是很熟悉了,这里介绍一下它的基本配置,虽然以后它可能会被替代,但是就算是被替代它也是经典的打包工具了(这里分享的均是打包src文件夹下面的文件并且装包的时候都需要初始化一个package.json文件来管理下载的包)。
安装与运行
安装和常见命令:
安装webpack之前先安装一下node,在官网中,node会存在两种下载的方式(
LTS版本
:长期支持的版本,推荐大多数用户去下载)(current版本
:当前版本, 当前最新的补丁),因为webpack是基于node来开发的(node下载成功会自动安装npm的工具)
// 1.该工具表示在命令行的窗口可以执行webpack的命令,-g为全局安装,使其可以在任意
// 的目录下去执行webpack
npm install webpack webpack-cli -g
// 2.表示本地安装,全局安装会使项目中的webpack锁定为某一个版本,如果使用不同的
// webpack版本的项目里面,可能会存在构建失败,在团队中如果不了解在全局安装的
// 话,也会出现构建问题
npm i webpack webpack-cli --save-dev
// 3.以绝对路径的方式显示用户当前的工作目录
pwd
// 4.查看webpack-cli中可以使用那些命令
npx webpack --help
// 5.保存文件之后,会自动的将需要打包的文件进行打包
npx webpack --watch
// ...命令还有很多,需要了解更多可以去webpack的官网学习
单文件打包的基本配置
流程: 使用webpack打包模块化以后的应用程序,webpack会生成一个可以部署的dist目录并将打包好的内容放置在这个目录里面,将这个目录里面的内容部署到服务器上,那么浏览器就能够正常访问服务器上的网站和资源了。
// 6.该文件名不可以随便写,因为该文件是webpack自动读取的,然后该文件是运行在
// nodejs里面,所以定义模块的时候需要使用nodejs里面的CommonJS的模块
// 7.path设置的时候需要设置为绝对路径
const path = require('path')
// 8.安装的插件需要引入才能使用,因为常量为大写字母开头,所以表示
// 它是一个构造函数或者是一个类
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 9.将需要打包的css文件合并,使其单独生成一个css的文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 10.将打包生成的css文件进行压缩
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
// 11.指示webpack应该使用哪个模块,来作为构建应用的入口。
// 进入入口起点后webpack会找出哪些模块和是入口起点的
// 直接或者间接的依赖,并将其打包到一起,默认的入口文
// 件为./src/index.js
entry: "./src/index.js",
// 12.告知webpack如何向硬盘写入编译文件
output: {
// 13.打包后的文件名叫什么
filename: "main.js",
// 14.表示获取到当前文件所在的真实路径,然后将其解析
// 到指定的路径下面
path: path.resolve(__dirname,'./dist'),
// 15.每次打包的时候先会清空dist里面的内容
clean: true,
// 16.定义资源模块的文件名,也可以设置路径,
// [contenthash]:系统默认生成文件名的方
// 法,它会根据文件的内容去生成一个hash的
// 字符串,[ext]表示使用原来的扩展名
assetModuleFilename: 'images/[contenthash][ext]'
},
// 17.如果不配置(指定当前的构建环境),打包的时候会发出
// 警告,development为开发模式
mode: 'development',
// 18.在开发模式下追踪代码,可以将编译后的代码映射回原始
// 代码(增强代码调试过程)
devtool: 'inline-source-map',
// 19.plugin表示插件的意思,后面加上s表示可以引入多个插件,
// 其次,插件的使用需要先引入,然后使用new关键字去实例化,
// 插件的参数就是一个配置对象(配置即可)
plugins: [
// 20.该插件可以完成html页面的自动化的生成(如果不配置,
// 外部的index页面和生成的index页面是没有关系的)
new HtmlWebpackPlugin({
// 21.以那个文件为模板来生成文件
template: "./index.html",
// 22.输出的文件的文件名
filename: "app.html",
// 23.输出的文件中生成的JS文件存放在那个body里面
inject: "body"
// 需要打包那些entry中的那些入口文件(chunk)
// 这里假设配置的入口文件有三个main.js、main2.js
// 和main3.js,经过下面的配置之后就不会打包main3.js
// 这个文件了
chunks:['main.js', 'main2.js']
}),
new MiniCssExtractPlugin({
// 24.输出的文件的文件夹和文件名
filename: 'styles/[contenthash].css',
}),
],
// 25.webpack-dev-server插件会提供一个基本的web服务器,具
// 有实时重加载的功能(但是其没有输出任何的物理文件,只是
// 将其打包后的文件放在了内存之中)
devServer: {
// 26.指明其指向的物理路径,使其作为server的根目录
static: './dist'
},
// 27.设置不同文件类型的模块
module: {
// 28.通过设置规则去加载不同类型的文件(rules配置项是一个数组,
// 这个数组的元素是一个配置对象,每一个配置对象对应一种解析
// 的规则)
rules: [
{
// 29.test后面书写正则来定义加载的文件类型,如果需要用
// 到.号需要使用\将其转义一下,因为.号在正则之中有特
// 殊含义
test: /\.png$/,
// 30.type用于匹配模块,它防止了 defaultRules 和它们的
// 默认导入行为发生
// 31.resource:发送一个单独的文件并导出 URL
type: 'asset/resource',
// 32.用来定义加载的文件的名字和路径,和上面output里面的
// assetModuleFilename作用是一样的,但是这里面定义的
// 优先级高于上面的
generator: {
filename: 'images/test1.png'
},
},
{
test: /\.svg$/,
// 33.inline:导出一个base64资源的 data URI
type: 'asset/inline'
},
{
test: /\.text$/,
// 34.source:导出资源的源代码
type: 'asset/source'
},
{
test: /\.svg$/,
// 35.asset:通用资源类型:在导出一个 data URI 和发送一个
// 单独的文件之间自动选择(默认情况下小于8kb的
// 文件会被视为inline模块类型)
type: 'asset',
// 36.解析器
parser: {
// 37.设置文件路径的状态
dataurlCondition: {
// 38.默认情况下文件大小不能超过
// 8 * 1024 (8M)
maxSize: 4 * 1024 * 1024
},
},
},
{
// 39.在css-loader里面,定义规则中的test属性会识别
// 那些文件需要被转换
test: /\.css/,
// 40.use属性会在定义转换的时候,需要使用那个loader
// 来转换,如果需要使用多个loader的情况需要用数组
// 将其包裹起来,并且执行的时候是从右往左执行的,
// 支持链式调用
// 41.style-loader用于将解析后的css样式渲染到页面的
// head里面的style标签里面的
use: ['style-loader','css-loader'],
// 42.因为将样式单独的抽离出来,那么style-loader就没
// 有用了,需要使用MiniCssExtractPlugin.loader来
// 代替
use: [MiniCssExtractPlugin.loader,'css-loader'],
},
{
// 43.如果存在不支持ES6语法的浏览器,打包之后运行会出
// 现报错,所以需要使用babel-loader来完成ES6、5的
// 适配
test: /\.js$/,
// 44.使其不包括那个里面的文件,因为node_modules里面
// 的JS是不需要babel去解析的
exclude: /node_modules/,
use: {
// 45.babel-loader:在webpack里面用来解析ES6
loader: 'babel-loader',
// 46.配置参数
options: {
// 47.添加一些预设(@babel/preset-env
// 为babel预设,是一组babel插件的集合)
presets: ['@babel/preset-env'],
// 48.如果出现报错为:regeneratorRuntime is
// not defined,表示这个函数是webpack生成
// 的辅助函数,用来兼容async和await的语法,
// 如果出现这个报错可能是未能正确配置babel,
// 需要安装@babel/runtime(包含
// regeneratorRuntime,运行时需要)和
// @babel/plugin-transform-runtime这两个
// 插件,如果没有async和await报错的话,不要
// 随意使用这个插件,存在问题
plugins:[
[
// 49.编译时需要,会在需要
// regeneratorRuntime的地方自动
// require导入包
'@babel/plugin-transform-runtime'
],
],
},
},
},
],
},
// 50.可选优化的配置
optimization: {
// 51.默认的压缩工具
minimizer: [
// 52.使用这个插件需要将模式(mode)改为生产模式(production)
new CssMinimizerWebpackPlugin()
],
},
}
代码的分离
const path = require('path')
module.exports = {
// 53.问题:如果入口文件中包含一些重复的代码,重复的代码都会引入到
// 各自的文件之中,造成代码的重复从而占用更多的空间(简单的
// 解决方法有两种)
// 注意:配置多个入口的文件(加载多个文件到打包的页面上的时候)的时
// 候需要使用对象,但是出口如果只有一个的话,会出现报错:
// Multiple chunks emit assets to the same filename(说明
// 需要设置多个出口)
entry: {
// 将共有的文件进行抽离出来(方法一):
index: {
// 这里是引入单个文件的写法,如果需要将多个文件
// 作为入口文件,需要用到数组,然后文件解析的顺
// 序是从左到右依次解析
import: "./src/index.js",
// 会将重复的文件给一个名字
dependOn: 'shared'
},
// 这里表示的是多个入口文件的打包
index02: {
import: "./src/index02.js",
dependOn: 'shared'
},
// 当上面的文件中存在loadsh的这个文件的时候,将其抽离出来
// 并定义为shared
shared: 'lodash'
// 使用webpack内置的插件split-chunks-Plugin(方法二):需要
// 在optimization的优化配置中去写一个splitChunks,内容为
// chunks:'all'
index: "./src/index.js",
index02: "./src/index02.js"
},
optimization: {
// 方法二
splitChunks: {
chunks: 'all',
}
},
}
打包与缓存
缓存: 然而获取资源比较损耗时间,所以浏览器会使用缓存使网站加载的速度更快,当dist文件夹被部署到服务器上后只会修改它的内容,并不会修改文件名,浏览器可能会认为你没有更新,就会使用它缓存的版本,以此会出现问题。
基本配置:
const path = require('path')
module.exports = {
entry: {
index: "./src/index.js",
index02: "./src/index02.js"
},
output: {
// 54.将JS文件打包到一个目录之中(配置出口文件即可)
// [name]可以拿到入口里面的文件名(chunk的key的名字)
// [contenthash]表示文件名会随着内容的变化而变化
// :6表示hash值文件名长度的限制
filename: "Script/[name].[contenthash:6].js",
},
optimization: {
splitChunks: {
// 55.缓存组:可以将第三方的插件(都存放在node_modules
// 这个文件夹里面)打包缓存在浏览器中
cacheGroups: {
vendor: {
// 只解析这个文件夹里面的内容
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
// 对那些chunks进行处理
chunks: 'all'
}
}
}
},
}
环境的切换
// 56.如果需要消除webpack.config.js在生产环境和开发环境中的差异,
// 可以使用环境变量来帮忙,
// 其中有一个参数为env,参数是一个对象,里面存在一个production
// 的属性,如果是开发环境,其值为true,否则为false,但是想要使用
// 这个参数的话需要将module.exports转换为一个函数
module.exports = (env) => {
// 在这个函数的内部返回webpack的配置对象
return {
entry: {
index: "./src/index.js",
index02: "./src/index02.js"
},
output: {
// 57.设置公共的路径,可以通过这个来指定所有资源的基础路径
publicPath: 'http://localhost:8000'
},
// 58.生产环境可以压缩,开发环境并不能压缩代码
mode: env.production ? 'production' : 'development',
}
}
source-map详解(代码调试)
// 59.注意:生产环境一般不会开启sourcemap的功能,在开发环境中通过
// devtool配置项开启sourcemap的功能
// 1.通过编译后生成的文件和sourcemap的文件,可以反编译出源码,
// 也就是说线上产物如果有sourcemap文件的话,就有可能存在暴露源码的风险
// 2.sourcemap文件的体积相对巨大,这跟我们生产环境准求有所区别
// (生产环境追求更小更轻的打包文件)
module.exports = {
// 取值1:默认情况下source-map的值是eval,会帮我们我们准确锁定
// 代码的位置,并且会自动在文件的后面自动生成一个sourceURL
// 的内部地址
devtool: 'eval',
// 取值2:会生成一个sourceMap的文件,在生成的文件之中的小面会有
// 注释说明这个文件在哪里,这个文件就是eval后面生成的那个
// 文件,所以也会准确锁定代码的位置
devtool: 'source-map',
// 取值3:跟第二个的区别在于不会生成注释,文件与生成的文件不会
// 存在关联,不会锁定代码的位置了
devtool: 'hidden-source-map',
// 取值4:跟第二个的区别在于不会生成sourceMap的文件,但是会生成
// 注释,其格式是dataUrl的base54,会锁定代码的位置
devtool: 'inline-source-map',
// 取值5:跟第四个的区别在于不会生成注释,会直接把格式是dataUrl
// 的base54的信息跟在eval的后面
devtool: 'eval-source-map',
// 取值6:会生成sourceMap的文件,但是这个文件里面不存在列的信息,
// 会准确锁定代码的位置
devtool: 'cheap-source-map',
// 取值7:会生成sourceMap的文件,但是这个文件里面不存在列的信息,
// 类似于babel解析的source-map混进来以后也会准确锁定代码
// 的位置,但是上一个会出现代码识别行数的问题(所以推荐在
// 开发环境下使用)
devtool: 'cheap-module-source-map',
}
devServer详解
说明: 开发环境中,需要启动一个web的服务来模拟用户的访问,以此读取我们打包后的产物,从而观测我们的代码在客户端的表现,devServer配置项就可以做到这样的功能
安装:
npm i webpack-dev-server -D
基础配置:
// 60
const path = require('path')
module.exports = {
devServer: {
// 指向当前服务的物理路径
static: path.resolve(__dirname,'./dist'),
// 是否在服务器端进行代码压缩,使其在数据传输的过程中,
// 可以减少传输的数据大小(只有true才压缩)
compress: true,
// 配置服务的端口号
port: 3000,
// 添加响应头,对资源的请求和响应打入一些标识和标志,
// 便于做一些安全的规范或者方便发生异常之后做请求的链路追踪
headers: {
// 这些信息是可以在浏览器中显示的
'X-Access-Token': '123456'
},
// 代理服务器(用来做跨域服务的)
proxy: {
'/api': 'http://localhost:3000'
},
// 将服务的协议改为https,默认使用自签名的证书,会让所有浏览
// 器知道这个是不安全的,会弹出警告
https: true
// 默认自带https的证书,可以访问到页面,在域名的右边会存在不
// 安全的字样
http2: true
// 在使用不同的路由模式的时候会出现的问题(在框架中)
historyApiFallback: true,
// 配置一个服务器,配置完成之后就可以通过一个域名来访问自己
// 配置的服务器了
host: '0.0.0.0',
},
}
页面的部分刷新
// 61
module.exports = {
devServer: {
// 开启模块热替换(webpack默认存在的配置),这样在服务器端中,
// 如果我修改页面的内容然后保存,服务器上页面是会自动更新的,
// 不需要再去刷新浏览器才能看到效果了
hot: true,
// 当页面被修改的时候,会自动的帮助我们编译并刷新页面
liveReload: true
}
}
webpack的代码规范约束
说明: 代码规范的约束的话使用ESlint插件即可,它可以检查你的代码错误(如何使用ESlint在之前的框架中分享过,这里不再说明),如果不使用ESlint的话使用webpack进行相关的配置也可以检查代码的错误。
配置:
// 62.实现代码约束主要是通过两个loader解析来显示问题的
module.exports = {
devServer: {
client: {
// 如果存在代码的书写错误,页面加载的时候会报错会显示在页面上,
// 这里值为false的时候就不会显示了
overlay: false,
},
},
module: {
rules: [
{
test: /\.js$/,
// 常规安装webpack的插件
//(webpack webpack-cli webpack-dev-server)之后还
// 需要安装以下插件才行
// npm i @babel/core babel-loader eslint-loader -D
use: ['babel-loader', 'eslint-loader'],
},
],
},
};
webpack模块的解析
解析条件:
// 63
// 绝对路径:以当前的文件为中心,如果取本本文件夹中的文件的话'./'表示当前文件夹,
// '../表示上一层文件夹等等操作来导入'
// 举例:引入自己写的文件math.js
const math = require('./math.js')
// 63
// 相对路径:相对于本文件所处的根目录来引入,引入方法跟上面引入没有什么区别
// 举例:引入自己在根目录文件夹下的文件math.js
const math = require('/math.js')
// 63
// 模块路径:相对于node_modules文件夹里面的文件
// 举例:引入Lodash这个JS库
// 引入的时候不需要加上node_modules,它会自动的拼接,直接写需要引入的
// 库名就可以了
import _ from 'loadsh'
路径的改写:
// 64.当文件夹嵌套多层的时候,写相对路径和绝对路径写多次肯定会很麻烦,
// 这里可以使用webpack来帮助简写路径
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/app.js',
resolve: {
// 用来给路径起其他的名字的配置项
alias: {
// 如果想访问某个资源,并且 在前面加上@的符号的话,
// 那么就会从src这个文件夹的下面开始查找
'@': Path.resolve(__dirname,'./src')
},
// 解析查找文件类型的优先级,优先级从左到右依次降低
extensions: [
'.json','.js'
],
}
}
cdn的加速(外部扩展)
// 65.一般用于首屏加载时间过长的优化方法之一(使用cdn进行加速)
module.exports = {
// 与下面的配置是并列的,表示以那种标签的形式放在页面之上
externalsType: script,
// 用来定义外部的第三方的包,避免直接引入或者下载的话导致文件过大的问题
externals: {
// key的名字必须和引入包的名字需要是一样的,
// 值为在window上暴露的哪一个对象
jquery: [
'使用的库的cdn的地址',
// 引入的库的名字
'jQuery'
]
},
}
postCSS与CSS模块
postCSS: 用JS工具和插件来转换css代码,它会自动获取浏览器的流行度和能够支持的属性,根据这些属性自动为css规则添加前缀,将css的语法转换成浏览器能够理解的语法
CSS模块: 能够解决因为文件名的问题而产生的冲突
常规配置:
// 66.webpack.config.js
module.exports = {
module: {
// 前面说到过postCSS是用来处理CSS的文件的,自然需要在解析CSS的
// 规则中进行使用,这些插件都需要安装,执行的顺序是从右往左的
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader','postcss-loader'],
}
],
},
}
// 66.postcss.config.js
// 用于配置css的各种插件(重要的在于这个文件)
module.exports = {
// 下面的这两个插件是需要安装的
Plugins: [
// 加载一些样式的前缀的插件
require('autoprefixer'),
// 帮助浏览器去识别一个嵌套的样式
require('postcss-nested')
],
}
// 66.package.json
// 假设书写的css代码与浏览器的版本相关的话,需要在package.json中配置
// browserslist来实现版本的约束
"browserslist": [
"> 1%",
"last 2 versions"
]
TypeScript的集成
// 67.在使用TypeScript的时候,需要先安装TypeScript(使用)、ts-loader(解析)
// 两个插件
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
// 将指定的文件排出在解析的范围里面
exclude: /node_modules/
}
],
},
resolve: {
// 定义识别的文件
extensions: ['.ts', '.js']
},
}
多文件打包
// 68
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 多文件打包主要依赖HtmlWebpackPlugin插件以及入口的配置
plugins: [
new HtmlWebpackPlugin({
// 打包时使用的模板
template: './index.html',
// 打包后的文件名(这里如果防止路径的话不存
// 在文件夹的时候会自动创建),其默认值是
// index.html
filename: 'age/index.html',
// 打包那些文件
chunks: 'main',
// 还可以设置打包后文件名的前缀,
// 在设置之后每个文件名前会多一串
// 前缀
publicPath:'www.baidu.com/'
}),
new HtmlWebpackPlugin({
template: './index2.html',
filename: 'age2/index2.html',
chunks: 'main2',
})
],
entry: {
main: {
import: ['./src/app.js'],
// [name]表示名字会随着文件名的变化而变化
filename: 'age/[name].js'
},
main2: {
import: ['./src/app.js'],
filename: 'age2/[name].js'
}
},
}
tree-shaking
作用: 打包后的文件会将无关紧要的代码去掉,保证每句代码都是有意义的。
// 69
// math.js
// 定义两个函数
export const add = (a, b) => a + b
export const sum = (a, b) => (a + b) * 2
// main.js
// 然后我在main.js中使用上面定义的函数
import { add } from './math.js'
// 在这里使用add函数,在常规打包配置中,不做任何处理的话,math.js
// 中的两个函数都会被打包进来,不管那些代码有没有被使用
console.log( add(1, 2) )
// webpack.config.js
module.export = {
// 在设置完这个之后,如果在开发环境下,他只会引入被使用的代码
//(sum函数是不会被打包的),如果是在生产环境下,它会直接算出
// 计算的结果
optimization: {
usedExports: true
}
}
sideEffects
说明: 并不是所有的模块的导入都会进行tree-shaking
,比如全局的样式表或者是全局的JS文件,这些文件会带来一定的影响(具有副作用),会影响整个程序的运行,webpack4默认不做tree-shaking
的操作,但是webpack5默认进行了tree-shaking
的操作,那么如何指定是否有无副作用呢。
配置:
// 70.在package.json文件中存在一个sideEffects的配置选项,它表示是否有
// 副作用的意思。
// package.json
{
// 表示所有的文件有副作用(默认不需要tree-shaking)
"sideEffects": true
// 表示所有的文件没副作用(默认需要tree-shaking)
"sideEffects": false
// 表示指定的文件有副作用(这些文件默认需要tree-shaking)
"sideEffects": ['*.css'] // 所有的css文件
"sideEffects": ['main.js'] // 仅仅只有main.JS文件有副作用....
}