webpack概念及基本使用
配置示例:
module.exports={
entry:'./src/index.js',//打包入口,零配置webpack默认值
output:'./dist/main.js',//打包输出,零配置webpack默认值
mode:'production',//环境
module:{
rules:[//loader配置
{test:/.txt$/,use:'raw-loader'}
]
}
plugins:[//插件配置
new HtmlwebpackPlugin({
template:'./src/index.html'
})
]
}
一、概念
1.入口Entry
单入口:Entry是字符串
多入口:Entry是对象
2.输出Output
打包后文件输出到磁盘的目录
单入口
示例:
module.exports={
entry:'./src/index.js',
output:{
filename:'xxx.js',
path:__dirname+'/dist'
}
}
只需要指定filename和path即可
多入口
示例:
module.exports={
entry:{
app1:'./src/app1/index.js',
app2:'./src/app2/index.js',
},
output:{
filename:'[name].js',
path:__dirname+'/dist'
}
}
通过占位符确保名称唯一。
3.支持多种文件类型Loaders
作用
webpack只支持js和json,通过loader支持其他文件格式并转换为有效模块,使之可以添加到依赖图中。
本身是一个函数,接收源文件参数,返回转换结果。
常见loaders
- babel-loader:转换ES6、ES7等新特性语法
- css-loader:支持css文件
- less-loader:将less转换为css
- ts-loader:将TS转换为JS
- file-loader:将文件(一般是图片、字体等)打包
- raw-loader:将文件以字符串形式导入
- thread-loader:多进程打包js与css
用法
module.exports={
...
module:{
rules:[//loader配置
{test:/.txt$/,use:'raw-loader'}//use可以是字符串也可以是数组
]
}
...
}
test指定匹配规则;use指定使用的loader,use可以是字符串也可以是数组;
use调用顺序是类似于栈,因此要把先执行的loader放到后面。比如:css-loader要放在style-loader后,实际上是先解析css为commonjs对象,然后再通过style标签插入head。
4.插件plugin
用于bundle文件的优化,资源管理和环境变量注入。
作用于整个构建过程。
常见plugin
- CommonsChunkPlugin:将chunks相同的模块代码提取成公共js
- CleanWebpackPlugin:清理构建目录
- ExtractTextWebpackPlugin:将css从bundle文件中提取成独立的css文件
- CopyWebpackPlugin:将文件或者文件夹拷贝至构建的输出目录
- HtmlWebpackPlugin:创建HTML文件去承载输出的bundle
- UglifyjsWebpackPlugin:压缩js,Uglify:混淆
- ZipWebpackPlugin:将打包的资源生成一个zip包
用法:添加至plugin数组即可。
5.环境mode
webpack'4提出,用于指定当前构建环境
选项:production、development、none
可以方便地触发一些内置函数
mode的内置函数功能
| 选项 | 描述 |
|---|---|
| development | 设置process.env.NODE_ENV的值为development,开启NamedChunksPlugin和NamedModulesPlugin |
| production | 设置process.env.NODE_ENV的值为production,开启FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurenceOrderPlugin,SideEffectsFlagPlugin以及TerserPlugin |
| none | 不开启任何优化选项 |
二、使用
1.语法解析
babel
配置文件:.babelrc
preset:可以被看作是一组 Babel 插件和/或 options 配置的可共享模块
plugin:某一项功能的插件,只做一件事
安装
@babel/core、babel-loader;
解析ES6+:@babel/preset-env
解析JSX:@babel/preset-react、react、react-dom
应用
- 解析ES6+:安装
@babel/preset-env,并在.babelrc的preset中添加。 - 解析JSX:安装
@babel/preset-react、react、react-dom,并在.babelrc的preset中添加@babel/preset-react。
2.CSS解析
包括解析CSS、Less、Sass
css-loader用于加载.css文件,并转换为commonjs对象
style-loader将样式通过<style>标签插入到head中
Less和Sass额外使用less-loader或sass-loader即可
3.资源文件(字体、图片)解析
使用file-loader和url-loader都可以解析,url-loader可以设置较小资源自动base64
示例:
{
test: /.(png|jpg|gif|jpeg|svg)$/,
use: [
// 'file-loader',
{
loader: "url-loader",
options: {
limit:10240//文件小于10*1024则会base64加密
}
}
]
},
4.文件监听
在源码更新时自动构建输出,缺点:需要开发者手动刷新浏览器
配置
开启监听模式的方法:
- 执行webpack命令加上
--watch - 配置文件
webpack.config.js中设置watch=true
配置原理
轮训判断文件的最后编辑时间是否发生改变
某个文件发生了变化,并不会立即告诉监听者,而是先缓存起来,等aggregateTimeout,减少构建次数。
webpack.config.js配置:
module.export={
watch:true,//默认false,不开启
watchOptions:{
//取消监听的目录或文件,默认为空,支持正则
ignored:/node_modules/,
//监听到变化后等待的时间,默认300ms
aggregateTimeout:300,
//判断文件变化是通过不断询问文件是否改变实现的,默认每秒1000次
poll:1000
}
}
5.热更新
HMR:Hot-Module-Replacement
webpack-dev-server
不刷新浏览器
不输出文件,而是放在内存中
一般配合HotModuleReplacementPlugin插件
使用命令webpack-dev-server --open启动,webpack4.x后需要单独安装webpack-cli,并且需要注意webpack和webpack-dev-server的版本兼容
webpack-dev-middleware
将webpack输出的文件输出给服务器,用于灵活的定制场景。
示例:
const express=require('express');
const webpack=require('webpack');
const webpackDevMiddleware=require('webpack-dev-middleware');
const app=new express();
const config=require('./webpack.config.js');
const compiler=webpack(config);
app.use(webpackDevMiddleware(compiler,{
publicPath:config.output.publicPath
}));
app.listen(3000,function(){
console.log('create webpack server success!!!\n')
})
原理
(图片来源:极客时间webpack课件)
- Webpack Compiler:webpack编译器,最后输出为bundle.js
- HMR Server:
- Bundle server:可以使用URL访问
- HMR Rumtime:注入到浏览器后,
bundle.js就能够和服务器建立连接(通常是websocket),用于更新文件
步骤
- 启动阶段:编译器通过文件系统把文件给webpack complier打包,把编译好的文件,传输给
bundle server,实际上就是启动一个服务器运行工程,允许bundle.js访问。「1,2,A,B」 - 更新阶段:fs将更新推送至webpack complier,改动后的代码发送给HMR Server,HMR Server会推送至HMR Runtime,进行浏览器端的自动更新。「1,2,3,4,5」
6.文件指纹
打包输出后的文件名的后缀。
可以确定构建的修改版本,由此可以可以使用未修改的缓存来减少不必要的消耗。
三类文件指纹
- Hash:整个项目的hash,只要项目代码改动就变化;
- ChunkHash:与打包的chunk有关,每个entry产生不同的chunkhash;
- ContentHash:由文件内容定义hash,文件内容改变才改变。
指纹使用
- JS文件:设置output的filename,使用[chunkHash]
- CSS文件:配合
MiniCssExtractPlugin使用,设置filename,使用[contentHash] - 图片等资源文件:设置
file-loader对应的options-name,使用[hash] - 后面加
:表示取的hash的长度 - chunkhash不能与热更新一起使用。
占位符
| 占位符 | 含义 |
|---|---|
| [ext] | 资源后缀名 |
| [name] | 文件名称 |
| [path] | 文件相对路径 |
| [folder] | 所在文件夹 |
| [contenthash] | 文件内容hash,默认md5生成 |
| [hash] | 文件内容hash,默认md5生成,同上,不同于外面指纹的项目的hash |
| [emoji] | 一个随机指代文件内容的emoj |
示例
图片
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader’,
+ options: {
+ name: 'img/[name][hash:8].[ext] '
+ }
}]
}
]
}
};
JS
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
+ filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
}
};
CSS
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
},
plugins: [
+ new MiniCssExtractPlugin({
+ filename: `[name][contenthash:8].css
+ });
]
};
7.文件压缩
HTML、JS、CSS压缩,减少web服务器传输字节
JS压缩
webpack4.x默认内置uglifyjs-webpack-plugin,代码本身已经被压缩。
若想设置参数,可以单独安装uglifyjs和uglifyjs-webpack-plugin然后进行设置。
CSS压缩
Webpack4.x后css-loader不再提供压缩
使用optimize-css-assets-webpack-plugin,同时使用cssnano实现css压缩。
配置:
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
},
plugins: [
+ new OptimizeCSSAssetsPlugin({
+ assetNameRegExp: /.css$/g,
+ cssProcessor: require('cssnano’)
+ })
]
};
HTML压缩
html-webpack-plugin
使用minify可以进行压缩参数的设置。
一个页面对应一个HtmlWebpackPlugin
inject参数决定了打包出来的文件是否注入html,注意output的publicPath,生产环境要注意去掉。
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
},
plugins: [
+ new HtmlWebpackPlugin({
//html模板所在的位置,使用ejs语法
+ template: path.join(__dirname, 'src/search.html’),
//指定打包出来的文件的名字
+ filename: 'search.html’,
//
+ chunks: ['search’],
//打包出来的文件是否注入html
+ inject: true,
+ minify: {
+ html5: true,
+ collapseWhitespace: true,
+ preserveLineBreaks: false,
+ minifyCSS: true,
+ minifyJS: true,
+ removeComments: false
+ }
+ })
]
};
\