webpack的打包理念:
一切皆为模块,在webpack中不仅仅js是模块,其他的html,css,图片和字体等都可以成为模块。
为什么使用webpack构建工具
webpack的配置非常灵活,并且具备强大的插件功能。
为什么需要构建工具?
作用:
- 转换ES6语法
- 转换JSX
- CSS前缀补全 / 预处理器(less,sass)
- 压缩混淆
- 图片压缩
webapck配置文件
默认的配置文件: webpack.config.js
可以指定配置文件:webpack --config + 配置文件名
配置文件组成
module.exports = {
entry : ' ', // 打包的文件入口
output: ' ', // 打包的输出
mode:‘ ’, // 环境
module: {
rules: [ ] // loader配置
},
plugins: [ ]
} // 插件配置
安装webpack
- 安装webpack之前先安装node.js,因为webpack依赖node.js环境
- webpack4将webpack的内核和cli进行了分离,因此需要分别安装webpack 和 webpack-cli
webpack核心概念
1. entry
(打包入口)
用法:
- 单入口(适用于单页应用): entry是一个字符串,如下:
entry: './src/index.js'
- 多入口(适用于多页应用): entry是一个对象。如下:
entry: {
app: './src/app.js',
adminApp: './src/admin-app.js'
}
2. output
(打包输出:用来告诉webpack如何将编译后的文件输出到磁盘)
用法
output
是一个对象,属性有:
- filename 指定输出文件名
- path 指定输出文件路径
例如:
output : {
filename: 'bundle.js',
path: __dirname + '/dist'
}
当有多个entry入口文件时候,为了保证输出文件名唯一,可以使用占位符,如下:
output : {
filename: '[name].js',
path: __dirname + '/dist'
}
3. loaders
loaders在webpack里是一个非常重要的概念。
- webpack原生只支持JS和JSON这两种文件 文件类型,需要通过Loaders去支持其他的文件类型,并且把它们转化为有效的模块,并且可以添加到依赖图中。
- 在webpack里面比较常见的Loaders有哪些呢?
名称 | 描述 |
---|---|
babel-loader | 转换ES6、ES7等JS新特性语言 |
css-loader | 支持.css文件的加载和解析 |
less-loader | 将less文件转换为css |
ts-loader | 将ts转换为js |
file-loader | 进行图片,字体等打包 |
raw-loader | 将文件以字符串的形式导入 |
thread-loader | 多进程打包JS和CSS(正常情况下webpack打包是开一个进程的, 使用thread-loader可以让webbpack以多进程的形式打包js和css,让打包更快。) |
- 用法 : 在
module
中使用 module
是个对象,该对象下面有一个rules
属性是一个数组,loaders就是在这个数组中使用
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
其中test指定匹配规则,use指定要使用的loader
4. plugins
(增强webpack打包的功能)
-
插件主要用于输出文件的优化、资源管理和环境变量注入。用于整个构建过程。
-
常用的plugins:
名称 | 描述 |
---|---|
CommonsChunkPlugin | 通常使用在多个页面打包的情况下 将chunks相同的模块代码提取成公共的js |
ClearWebpackPlugin | 清理构建目录 |
ExtractTextWebpackPlugin | 将CSS从输出文件中提取成一个独立的CSS文件(分离CSS) |
CopyWebpackPlugin | 将文件或目录拷贝到构建的输出目录下 |
HtmlWebpackPlugin | 创建html文件去承载输出文件 |
UglifyjsWebpackPlugin | 压缩JS |
ZipWebpackPlugin | 将打包的资源生成zip包 |
⚠️
- 在webpack4中,CommonsChunkPlugin替换成了splitchunksplugin
- extract-text-webpack-plugin替换成了mini-css-extract-plugin
Plugins用法:
import HtmlWebpackPlugin from 'html-webpack-plugin';
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
5. mode
(用来指定当前的构建环境,一共有3种:production,development,none)
mode这个概念是webpack4提出来的
设置mode可以触发webpack内置函数(自动生成当前生产环境的一些功能等),默认值为production
Mode的内置函数功能有哪些呢?
环境 | 功能 |
---|---|
development | 设置process.env.NODE_ENV的值为development 开启NamedChunksPlugin和NamedModulesPlugin(这两个插件在模块热更新阶段很实用,会在控制台打印出是哪个模块发生了热更新,模块路径) |
production | 设置process.env.NODE_ENV的值为production。 开启FlagDependencyUsagePlugin,FlagIncludeChunksPlugin,ModuleConcatnationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin和TerserPlugin(这些插件会在生产阶段会默认做一些代码的压缩) |
none | 不开启任何优化选项 |
使用示例
1. webpack解析ES6和React JSX
使用babel-loader
module.exports = {
// .....
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
}
babel-loader
是依赖Babel转换器的,因此需要在项目中配置Babel,它的配置文件是.babelrc
增加ES6的babel preset配置
需要npm i @babel/core @babel/preset-env babel-loader -D
.babelrc文件如下:
{
"presets": [
"@babel/preset-env", // ES6的babel preset
"@babel/preset-react" // React的babel preset
],
"plugins": []
}
解析React JSX文件,在.babelrc文件内添加React的babel preset配置(如上)
npm i @babel/preset-react -D
2. webpack解析css、less、sass
解析css需要使用:
css-loader
用于加载.css文件,并且转换成commonjs对象style-loader
将样式通过<style>
标签插入到head中
需安装npm i style-loader css-loader -D
代码如下:
module.exports = {
// .....
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
}
⚠️loader的调用执行链式调用,解析顺序是从右到左,所以写loader时候要注意顺序,先执行的写在后面。
解析less如下:
npm i less-loader -D
module.exports = {
// .....
module: {
rules: [
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }
]
}
}
⚠️解析less,需要使用less-loader
将less先解析成css,所以需要上面3个loader
同理解析sass如下:
npm i sass-loader -D
module.exports = {
// .....
module: {
rules: [
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
]
}
}
3. webpack解析图片和字体资源
使用file-loader
如下:
npm i file-loader -D
module.exports = {
// .....
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|woff|woff2|eot|ttf|otf)$/, // 匹配图片,字体文件
use: 'file-loader'
}
]
}
}
- 图片,字体解析还可以使用
url-loader
,与file-loader
功能差不多 url-loader
可以设置较小资源自动base64
如下:
npm i url-loader -D
module.exports = {
// .....
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|woff|woff2|eot|ttf|otf)$/, // 匹配图片,字体文件
use: [{
loader: 'url-loader',
options: {
limit: 10240 // 限制文件大小。如果图片或字体文件小于limit,url-loader就会对它进行base64转换
}
}]
}
]
}
}
4. webpack中文件监听
文件监听是在发现资源代码发生改变时,自动重新构建出新的文件
开启文件监听模式的2种方式:(浏览器不会自动刷新,需手动刷新页面)
webpack --watch
- 在配置webpack.config.js 文件中添加
watch: true
module.exports = {
// .....
watch: true,
// 只有当开启监听模式时,watchOptions才会有效
watchOptions: {
ignored: /node_modules/, //(默认为空) 不监听的文件或者文件夹,支持正则匹配
aggregateTimeout: 300, //(默认值300) 监听到文件变化后等300毫秒再去执行
poll: 1000 //,默认每秒询问文件有没有变化1000次
}
}
== 文件监听原理:==
- 轮询判断文件的最后编辑时间是否有变化(判断文件是否发生改变是通过不停询问系统指定文件有没有变化实现)
- 某个文件发生了改变,并不会立即告诉监听者,而是将修改缓存起来,等待时间(aggregateTimeout),如果在这段时间内其他文件也发生了改变,会把这些变化了的文件列表一起构建,等待时间结束后把构建结果生成到输出文件里去
5. webpack中热更新及原理
热更新方法:
第一种:
webpack-dev-server
命令,简称“WDS”。(开发过程使用mode: development)
- 使用WDS不用手动刷新浏览器
- WDS不会输出文件,而是存放在内存中(输出的放在内存里面,而不是像watch输出放在本地磁盘文件里,所以WDS的构建速度较快)
- WDS结合
HotModeuleReplacementPlugin
插件使用
webpack-dev-server --open
每次构建完成后自动开启浏览器窗口
module.exports = {
// .....
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist', // 服务的基本目录
hot: true, // 是否热更新
open: true // 是否在每次构建完成后自动开启浏览器窗口(如果命令里不带--open)
}
}
第二种:
使用webpack-dev-middleware
(简称“WDM”)
- WDM将webpack输出文件传输给服务器,适用于灵活的定制场景。
热更新原理过程
- webpack Compile(编译器):将js原代码编译成打包好输出的文件(Bundle)
- HMR Server:将热更新文件传输给HMR Runtime
- Bundle Server: 提供文件中在浏览器的访问(正常在浏览器上访问是通过文件路径访问,通过webpack热更新可以通过服务器访问,比如localhost:8080/bundle.js)
- HMR Runtime : 会注入到浏览器与服务器建立连接,更新为文件变化
6. 文件指纹策略
文件指纹指的是打包输出的文件名后缀
作用:
- 版本的管理(比如项目发布的时候,只需要把改变后的文件发布上去)
- 设置文件指纹后,对于没有修改的文件可以用浏览器本地缓存,加速页面的访问
3种常见的文件指纹:
- Hash
和整个项目的构建相关,只要项目文件有修改,这个项目构建的hash值就会改变(比如,有a,b两个页面,我修改了a页面的js,那么b页面的js文件后缀(即hash)也会发生变化。其实没有必要的) - Chunkhash
和webpack打包的chunk有关,不同的entry会生成不同的chunkhash 值 - Contenhash
根据文件内容来定义hash,文件内容不变,则contenthash不变(一般css文件使用contenthash)
js的文件指纹设置:
- 设置output的filename,使用[chunkhash]
如下:
module.exports = {
entry: {
app : './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].[chunkhash:8].js',
path: __dirname + '/dist'
}
}
css的文件指纹:
npm i mini-css-extract-plugin -D
- 设置MiniCssExtract Plugin的filename
- 使用[contenthash]
⚠️ 提取出css为独立文件,MiniExtractPlugin插件不能与style-loader一起使用,去掉style-loader,使用MiniExtractPlugin.loader
代码如下:
const MiniExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
MiniExtractPlugin.loader, //取代style-loader
'css-loader'
]
}
]
},
plugins: [
new MiniExtractPlugin({
filename: '[name][contenthash:8].css' // 指定hash值有几位
})
]
}
图片字体文件指纹设置:
- 设置file-loader的name使用[hash]
module.exports = {
// .....
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|woff|woff2|eot|ttf|otf)$/, // 匹配图片,字体文件
use: [
loader: 'file-loader',
options: {
name: 'img/[name][hash:8].[ext]'
}
]
}
]
}
}
有关占位符介绍:
占位符名称 | 含义 |
---|---|
[ext] | 资源后缀名 |
[name] | 文件名 |
[path] | 文件相对路径 |
[folder] | 文件所在文件夹 |
[contenthash] | 文件内容hash,默认是md5生成 |
[hash] | 文件内容hash,默认是md5生成 |
[emoji] | 一个随机的指代文件内容的emoj |
注意:chunkhash无法与热更新HotModuleReplacementPlugin一起使用
7. HTML,CSS,JavaScript代码压缩
- HTML压缩
npm i html-webpack-plugin -D
设置压缩参数
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// .....
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true, // 空格处理
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
]
}
- CSS压缩
npm i optimize-css-assets-webpack-plugin cssnano -D
使用optimize-css-assets-webpack-plugin
同时使用cssnano
css处理器
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
// .....
plugins: [
new OptimizeCssAssetsWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
]
}
同时还要使用cssnano
- JS压缩
在webpack4里面,mode: 'production'
模式下其实webpack内置了uglifyjs-webpack-plugin
,默认打包出来的文件是进行压缩过的。所以不需要配置