1、webpack入门
- Entry: 入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入
- Module: 模块, 在Webpack里一切皆模块,一个模块对应着一个文件,Webpack会从配置的Entry开始递归找出所有依赖的模块
- Chunk: 代码块, 一个Chunk由从个模块组合而成,用于代码合并与分割
- Loader: 模块转换器, 用于把模块原内容按照需求转换成新内容
- Plugin: 扩展插件, 在Webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想做的事情
- Output: 输出结果, 在Webpack经过一系列处理并得出最终想要代码后输出结果
Webpack启动后会从Entry里配置的Module开始递归解析Entry依赖的所有Module, 每找到一个Module,就会根据配楷模置的Loader去找出对应的转换规则, 对Module进行转换后,再解析出当前Module依赖的Module。这些模块会以Entry为单位进行分组, 一个Entry和其所有依赖的Module被分到一个组也就是一个Chunk。最后Webpack会把所有的Chunk转换成文件输出,在整个流程中Webpack会在恰当的时机执行Plugin里定义的逻辑
工作准备
npm i webpack webpack-cli webpack-dev-server -D //注意这里的webpack@3,因为版本四的与dev-server不兼容
入门案例
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'), // 输出的目录,只能是绝对路径
filename: 'bundle.js'
},
devServer: { // 注意:如果使用了devServer, 那么所有产出的文件都会写到内存里,而不是写在硬盘上, 所以经常所到网页上有东西,而硬盘上没东西就是这个道理
static: path.join(__dirname, 'dist'), // 产出的文件根目录
port: 9527,
host: 'localhost',
compress: true, // 是否开启服务器压缩
proxy: {
'/api': 'http://localhost:3000' //没有重写
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api', ''} //重写请求地址 /api/user => /user
}
}
}
}
注意:如果本地没有服务器,那么可以使用devServe中的before进行添加‘
devServer: { static: path.join(__dirname, 'dist'), // 产出的文件根目录 port: 9527, host: 'localhost', compress: true, // 是否开启服务器压缩 before: (app) => { // 因为devServe是用express进行编写的,这相当于一个中间件 app.get('/api/user', (req, res) => { res.json([{ id: 1, name: 'this is test' }]) }) }, },
注意:这样就可以通过 http://localhost:9527/api/user 进行访问
然后在dist里新建html文件如下代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="./bundle.js"></script> </body> </html>
配置package.json里的命令
"scripts": { "build": "webpack", "dev": "webpack-dev-server --open" //这里的open是表示运行命令后,自动打开浏览器 }
2、loader的使用
通过使用不同的Loader, webpack可以把不同的文件都转成js文件, 比如CSS, ES6/7, JSX等
- test:匹配处理文件的扩展名的正则表达式
- use:loader名称, 就是你要使用的模块的名称
- include/exclude:手动指定必须处理的文件夹或屏弊不需要处理的文件夹
- query:为loader提供额外的配置选项
举例css-loader与style-loader的使用
module: {
rules: [
{
test: /\.css$/, // 表示如果要require或者import的文件是css文件的话,那么就会加载该Loader进行编译,
// 从右向左处理css文件, loader是一个函数,相当于每个函数的返回值就是下一个函数的参数这里use可以是一个字符串也可以是一个数组,
use: ['style-loader', 'css-loader'],
}
]
}
3、配置多文件入口
通常在做项目的时候,如果是单页应用,那么只有一个入口,但是如果项目需要配置多个入口,那么这个时候就需要调整webpack的配置
entry: { main: './src/index.js', login: './src/login.js' }, output: { path: path.join(__dirname, 'dist'), //如果entry是一个字符串类型,那么生成的的默认名字就是main filename: '[name][hash:8].js' // 可以是[name][hash].js, 而hash:8表示生成8位的hash值,这样可以防止缓存, 如果是css采用contenthash更合适, 还有一个chunkhash }
注意:生成的文件名就是entry里对应的key值,如果entry是一个字符串,那么默认的就是main
4、自动生成html模板
需要依赖html-webpack-plugin这个插件
new htmlWebpackPlugin({ title: 'index文件的标题', //输出的html的title设置 filename: 'index.html', //输出的html的文件名 template: './src/index.html', //引入template,这个可以指定一个html模板 showErrors: true, //当webpack报错的时候,会把错误信息包裹再一个pre中,默认是true。 // inject有四个值: true body head false // true =》 默认值,script标签位于html文件的 body 底部 // body =》 script标签位于html文件的 body 底部 // head =》 script标签位于html文件的 head中 // false =》 不插入生成的js文件,这个几乎不会用到的 inject: 'body', chunks: ['index'], //chunks主要用于多入口文件,当你有多个入口文件,那么数组里的值便是你需要选择打包进行的js文件包,如果不设置,则是全部显示 // excludeChunks: ['devor.js'], //排除掉一些js后全部打包 favicon: './assets/img/baidu.png', //给你生成的html文件生成一个 favicon ,值是一个路径 hash: true, //是否生成hash添加在引入文件地址的末尾,类似于我们常用的时间戳,比如最终引入是:<script type="text/javascript" src="bundle.049424f7d7ea5fa50656.js?049424f7d7ea5fa50656"></script>。这个可以避免缓存带来的麻烦 cache: true, //默认是true的,表示内容变化的时候生成一个新的文件 minify:{ caseSensitive: false, //是否大小写敏感 removeComments:true, // 去除注释 removeEmptyAttributes:true, // 去除空属性 collapseWhitespace: true //是否去除空格 } })
所对应的html的文件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> </body> </html>
配置多入口多模板的方式
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', devtool: 'eval-source-map', entry: { main: './src/index.js', login: './src/login.js' }, output: { path: path.join(__dirname, 'dist'), //如果entry是一个字符串类型,那么生成的的默认名字就是main filename: '[name][hash:8].js' // 可以是[name][hash].js, 而hash:8表示生成8位的hash值,这样可以防止缓存 }, ... plugins: [ new HtmlWebpackPlugin({ template: './index.html', filename: './html/main.html', // 会在dist下生成html文件目录然后把指定的main.html放入到对应的文件中 chunks: ['main'] }), new HtmlWebpackPlugin({ template: './login.html', filename: './html/login.html', chunks: ['login'] }) ] }
注意:在进行多入口配置的时候可以使用如下方法进行统一封装
const glob = require('glob')
const arr = glob.sync('./src/*.js').reduce((total, per) => {
const baseName = path.basename(per, '.js')
total[baseName] = per
return total
}, {})
console.log(arr)
5、自动清除打包目录下的内容
需要引入插件上cleanWebpackPlugin npm i clear-webpack-plugin -D
即每次打包的时候,都会自动清除原来目录下已有的所有内容
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); plugins: [ ... new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: [path.join(__dirname, 'dist')] }) ]
6、文件加载
如果在项目中需要使用到图片,这时就需要使用file-loader对文件进行加载 安装 npm i file-loader -D
module: { rules: [ ... { test: /\.(jpg|png|svg|jpeg|gif)$/, use: 'file-loader' } ] },
注意:在配置根目录时,在output里配置配置publicPath,那么在地址引用的时候使用/assets则可以定位到根目录下
import img from '/assets/images/timg.jpg' let elem = new Image(); elem.src = img; elem.style.width = '200px'; document.body.appendChild(elem);
实现图片内联
可以使用url-loader
{ test: /\.(jpg|png|svg|jpeg|gif)$/, use: { loader: 'url-loader', options: { // 表示当文件大小超过 30k的时候,那么就会把图片编译成base64格式的,url-loader内置file-loader limit: 10 * 1024,
esModule: false, //这个表示在输出的时候,不要{default: 'xxx'}这种格式 name: '[name].[hash].[ext]', // 表示输入的文件名 outputPath: 'images', //输出的文件所在的文件夹的名称 publicPath: '/images' //会对publicPath进行覆盖,在url引用的时候,把原有的前缀进行替换,如果不配置,系统会默认替换 } } }
注意:url-loader内置了file-loader
在进行上线打包的时候,如果图片超过一定的大小,会有警示,可以关闭提示
performance: { hints: false },
在html中使用图片的方法
需要使用依赖html-withimg-loader
npm i html-withimg-loader -D
使用
{ test: /\.(htm|html)$/i, use: 'html-withimg-loader', }
注意:在使用的过程中,会遇到图片的地址被转义成一个{default: 'xxxx'}的格式,这个时候需要在url-loader中配置esModule: false
7、分离css
因为css的下载和js可以并行,当一个html文件很大的时候, 我们可以把css单独提取出来加载
- mini-css-extract-plugin
- filename打包入口文件
- chunkFilename用来打包,import('module')方法中引入模块
安装依赖
npm i mini-css-extract-plugin -D
配置使用
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { ... module: { rules: [{ test: /\.css$/, // 表示如果要require或者import的文件是css文件的话,那么就会加载该Loader进行编译, // 从右向左处理css文件, loader是一个函数,相当于每个函数的返回值就是下一个函数的参数这里use可以是一个字符串也可以是一个数组, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, ... ] }, plugins: [ ... new MiniCssExtractPlugin({ filename: 'css/[name].css', //name是表示入口处chunk的名字,默认是Main, 表示打包到css文件夹下的指定文件名 chunkFilename: '[id].css' //在异步加载的时候用 }) ] }
8、压缩js和css
用 terser-webpack-plugin 替换掉uglifyjs-webpack-plugin解决uglifyjs不支持es6语法的问题
如果你使用的是 webpack v5 或以上版本,你不需要安装这个插件。webpack v5 自带最新的 terser-webpack-plugin
。如果使用 webpack v4,则必须安装 terser-webpack-plugin
v4 的版本。
但是需要注意:一旦配置了optimization里的minimizer,webpack就会默认不进行压缩,所以需要配置一下terser-webpack-plugin
安装:
npm i terser-webpack-plugin -D
npm i css-minimizer-webpack-plugin -D
使用
...
module.exports = { mode: 'production', ... optimization: {
}
...
}
9、编译less和sass
安装与使用
npm i less less-loader -D
npm i node-sass sass-loader -D
module: { rules: [ { test: /\.css$/, // 表示如果要require或者import的文件是css文件的话,那么就会加载该Loader进行编译, // 从右向左处理css文件, loader是一个函数,相当于每个函数的返回值就是下一个函数的参数这里use可以是一个字符串也可以是一个数组, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1, }, }, , 'postcss-loader', ], }, { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2, }, }, 'less-loader', 'postcss-loader', ], }, { test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2, }, }, 'sass-loader', 'postcss-loader', ], }, ... ], }
注意: css-loader中参数importLoaders的配置
10、处理css3属性前缀
为了处理浏览器的兼容性, 有时候我们必须加入-webkit, -ms, -o, -moz这些前缀
- Trident内核:主要代表IE浏览器, 前缀为-ms
- Gecko内核: 主要代表Firefox, 前缀为-moz
- Presto内核: 主要代表Opera, 前缀为-o
- Webkit内核: 主要代表Chrome和Safari,前缀为-webkit
安装
npm i postcss-loader autoprefixer -D
module: { rules: [ { test: /\.css$/, // 表示如果要require或者import的文件是css文件的话,那么就会加载该Loader进行编译, // 从右向左处理css文件, loader是一个函数,相当于每个函数的返回值就是下一个函数的参数这里use可以是一个字符串也可以是一个数组, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], }, { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader', 'postcss-loader'], }, { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', 'postcss-loader'], }, ... ], }
同时需要创建一个配置文件 postcss.config.js
module.exports = { plugins: [require('autoprefixer')], }