前言
前边有写过关于 webpack 的核心配置概念与基础实践,今天主要分享 webpack 针对各种情况的配置与实战
这里需要知道一个前提 loader 和 plugins 的作用是什么 ?
- loader: 文件加载器,由于webpack是用 node 编写,所以只能识别 js 文件,而 loader 的作用就是文件识别与转换
- plugins: 插件机制,在webpack执行过程中会广播一系列事件,plugin 会监听这些事件并通过 webpack Api 对输出文件做对应的处理
ok, 基于一个前章的基础实践demo开始延申,如需从0开始,可以查看上述链接基础实践
,
目录结构
webpack // 工程目录
dist // 构建生成目录
bundle.js
index.html
main.js
utils.js
webpack.config.js
package.json
node_modules
main.css
webpack.config.js
const path = require('path');
module.exports = {
entry: './main.js', // 入口文件
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle[hash:6].js',
// 输出文件都放到 dist 目录下
// __dirname 指向当前目录
path: path.resolve(__dirname, './dist'),
},
//mode:"production",
devServer:{ // 本地服务
port:"1234", // 启动端口
progress:true, // 进度条
open:true // 自动打开浏览器
}
};
html 插件 html-webpack-plugin
就单页SPA来讲,每次打包我们都会生成一个 dist 目录,目录里边一般含有打包的 bundle.js 和一个入口文件 index.html ,那么 bundle.js
我们知道是通过 output 出口配置生成的,index.html 是哪里来的 ?
ok, 其实它是通过 html-webpack-plugin
插件生成的,主要原理是基于一个模板文件,创建一个新的文件并且自动引入 bundle.js 和其它打包后的 css文件,具体是这样使用的
安装 html-webpack-plugin
cnpm install html-webpack-plugin -D
webpack.config.js 配置
const path = require('path');
// 导入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
devServer:{
port:"1234",
progress:true,
open:true
},
plugins: [
// 使用插件方式
new HtmlWebpackPlugin({
template: "./index.html", // 模板html文件
filename: "index.html", // 打包后新建的html文件名
minify: { // 一些优化配置
removeAttributeQuotes: true, // 去除引号
removeEmptyAttributes: true, // 去除空属性
collapseWhitespace: true // 去除空格
// 当然还有很多,可查阅文档
}
})
]
};
通过这样的一个插件呢,就可以做到,每次打包在 dist 目录生成一个新的入口文件,并且自动引入打包后的 js 和 css
样式处理
实际在工作中,我们都会写一些样式,除了用 css,也还会用一些 less,sass 等预处理器,浏览器本身是识别不了的,其实都是 webpack 的功劳,用一些 loader 把不能识别的 less 或者 sass 进行转换,变成 css,再用 style 标签插入到 html 中,就可以了,看一看它是怎么做的 ?
安装 style-loader css-loader less less-loader
cnpm install style-loader css-loader -D
cnpm install less less-loader -D
webpack.config.js 配置
const path = require('path');
// 导入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
devServer:{
port:"1234",
progress:true,
open:true
},
module: { // loader 主要使用的地方
rules: [
// 配置打包css文件的loader,use定义使用到的loader,注意顺序
// style-loader 必须放在 css-loader 前面,因为loader的执行顺序是从右至左
{
test: /\.css$/, // 匹配后缀为 .css 的文件
use: ['style-loader', 'css-loader']
}
]
}
plugins: [
// 使用插件方式
new HtmlWebpackPlugin({
template: "./index.html", // 模板html文件
filename: "index.html", // 打包后新建的html文件名
minify: { // 一些优化配置
removeAttributeQuotes: true, // 去除引号
removeEmptyAttributes: true, // 去除空属性
collapseWhitespace: true // 去除空格
// 当然还有很多,可查阅文档
}
})
]
};
rule
的另一种配置, 可以写成对象的形式,因为还支持一些其它的配置项
{
test: /\.(css|less)$/, // 匹配后缀为 .less 或者 .css 结尾的文件
use: [
{
loader: "style-loader",
options: {
// 其它相关设置
}
},
'css-loader', // 解析@import路径
'less-loader' // 把less解析为css
]
},
来解释一下如上配置,是怎么进行 css 样式处理的
- 由于 loader 的执行顺序是从右向左,所以这里是先执行 less-loader, 将 less 转换成 css
- 然后再由 css-loader 解析,并识别 @import 转递给 style-loader
- 最后 style-loader 会把解析后的 css 装进 style 标签,并放入 html 的 head 中
- ok 浏览器可以识别,并渲染了
那在实际开发中,肯定不可能都写成 style 标签嵌入 head 中,也不太雅观,所以我们可以通过插件机制来将 css分离出来,单独打包并通过 link 标签引入
Css 抽离 mini-css-extract-plugin
安装 mini-css-extract-plugin
npm install mini-css-extract-plugin -D
webpack.config.js 配置
const path = require('path');
// 导入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// css 抽离
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
devServer:{
port:"1234",
progress:true,
open:true
},
module: { // loader 主要使用的地方
rules: [
// 配置打包css文件的loader,use定义使用到的loader,注意顺序
// style-loader 必须放在 css-loader 前面,因为loader的执行顺序是从右至左
{
test: /\.css$/, // 匹配后缀为 .css 的文件
use: ['style-loader', 'css-loader']
}
]
}
plugins: [
// 使用插件方式
new HtmlWebpackPlugin({
template: "./index.html", // 模板html文件
filename: "index.html", // 打包后新建的html文件名
minify: { // 一些优化配置
removeAttributeQuotes: true, // 去除引号
removeEmptyAttributes: true, // 去除空属性
collapseWhitespace: true // 去除空格
// 当然还有很多,可查阅文档
}
}),
// 添加css抽离插件
new MiniCssExtractPlugin({
filename: "main.css", // css抽离出来的文件名,可配置路径,如“css/main.css”
// 还有很多配置项,请查看文档,这里只列举
})
]
};
当然,我们也可以指定文件去进行抽离, MiniCssExtractPlugin
插件的另一种用法,例如,我们想抽离 css 或者 less 就可以在 loader 配置规则里,使用 MiniCssExtractPlugin 插件,如下:
注意位置,一定是放在最上边,没有 style-loader , 意思是当 css 转换完成后,通过 MiniCssExtractPlugin 插件抽离出来,在页面中用 link 标签引入
// css 示例
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader', // 解析@import路径
]
},
// less 示例
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader', // 解析@import路径
'less-loader' // 把less解析为css
]
},
扩展
- 可以通过 postcss-loader 给 css 样式加上前缀,来满足浏览器兼容
- 通过插件 optimize-css-assets-webpack-plugin 压缩 css , 同时需要使用 uglify-js-plugin 来压缩 js
转换ES6置ES5
这里需要使用到 bable, 主要是用来转化 js 语法的
安装
// babel-loader babel的加载器
// @babel/core babel 的核心模块
// @babel/preset-env 将 es6 转化成 es5
npm install babel-loader @babel/core @babel/preset-env -D
webpack.config.js 配置
const path = require('path');
// 导入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// css 抽离
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
devServer:{
port:"1234",
progress:true,
open:true
},
module: { // loader 主要使用的地方
rules: [
// 配置打包css文件的loader,use定义使用到的loader,注意顺序
// style-loader 必须放在 css-loader 前面,因为loader的执行顺序是从右至左
{
test: /\.css$/, // 匹配后缀为 .css 的文件
use: ['style-loader', 'css-loader']
},
// js => es6 转化 es5
{
test:/\.js$/, // 匹配后缀为 .js 的文件
use:{ // 这里采用对象的形式编写
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], // es6 转化 es5
plugins: [ // 也可以给单个loader配置 plugins 插件,详见文档
// 转化class类的写法,识别某些高级语法
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
}
}
]
}
plugins: [
// 使用插件方式
new HtmlWebpackPlugin({
template: "./index.html", // 模板html文件
filename: "index.html", // 打包后新建的html文件名
minify: { // 一些优化配置
removeAttributeQuotes: true, // 去除引号
removeEmptyAttributes: true, // 去除空属性
collapseWhitespace: true // 去除空格
// 当然还有很多,可查阅文档
}
}),
// 添加css抽离插件
new MiniCssExtractPlugin({
filename: "main.css", // css抽离出来的文件名,可配置路径,如“css/main.css”
// 还有很多配置项,请查看文档,这里只列举
})
]
};
图片处理
一般我们在开发中写图片的方式有三种情况, 那么根据这三种情况,webpack 也有相应的方式做处理
- Js 中创建图片
file-loader 或者 url-loader
- css 中引入图片
默认css loader 会转化成 require 的形式
- html 中使用 img 标签
html-withimg-loader
安装
// file-loader 会将 img 图片生成在打包 build 目录下
// url-loader
npm install babel-loader file-loader url-loader -D
npm install babel-loader html-withimg-loader -D
webpack.config.js 配置
const path = require('path');
// 导入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// css 抽离
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
devServer:{
port:"1234",
progress:true,
open:true
},
module: { // loader 主要使用的地方
rules: [
// 配置打包css文件的loader,use定义使用到的loader,注意顺序
// style-loader 必须放在 css-loader 前面,因为loader的执行顺序是从右至左
{
test: /\.css$/, // 匹配后缀为 .css 的文件
use: ['style-loader', 'css-loader']
},
// js => es6 转化 es5
{
test:/\.js$/, // 匹配后缀为 .js 的文件
use:{ // 这里采用对象的形式编写
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], // es6 转化 es5
plugins: [ // 也可以给单个loader配置 plugins 插件,详见文档
// 转化class类的写法,识别某些高级语法
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
}
},
// 图片loader处理
{
test: /\.(jpg|png|gif)$/, // 匹配后缀 jpg,png,gif 为后缀的文件
use: {
loader: 'file-loader', // 用于 js 创建图片
options:{
esModule: false, // 压缩图片路径
outputPath: '/img/', // 打包存放在 img 文件夹下,可自定义,url-loader 同理
publicPath: 'htpp://localhost' // 路径前添加域名,也可以在 outout 出口公共配置
}
},
/* url-loader 替代 file-loader
{
test: /\.(jpg|png|gif)$/, // 匹配后缀 jpg,png,gif 为后缀的文件
use: {
loader: 'url-loader', // 图片不一定需要产出,可以给一个大小限制, 使用 base64
options: {
limit:200*1024 // 限制 200k 的使用 base64 格式
// 其它配置参考文档
}
},
*/
{
test: /\.html$/, 匹配后缀 html 的文件
use: 'html-withimg-loader' // 用于 html 里 img 标签 src 的路径识别
},
]
}
plugins: [
// 使用插件方式
new HtmlWebpackPlugin({
template: "./index.html", // 模板html文件
filename: "index.html", // 打包后新建的html文件名
minify: { // 一些优化配置
removeAttributeQuotes: true, // 去除引号
removeEmptyAttributes: true, // 去除空属性
collapseWhitespace: true // 去除空格
// 当然还有很多,可查阅文档
}
}),
// 添加css抽离插件
new MiniCssExtractPlugin({
filename: "main.css", // css抽离出来的文件名,可配置路径,如“css/main.css”
// 还有很多配置项,请查看文档,这里只列举
})
]
};
配置 source-map
source-map 是什么 ? 是源码映射,因为我们打包后,发布,一旦出现了问题,这个时候,代码都被混淆和压缩过,很难定位问题代码的具体位置,而 source-map 就是来解决它的
module.exports = {
entry: { /*省略*/},
// 对应多出口
output: { /*省略*/}
// 1。源码映射,单独生成一个sourcemap文件,出错时会直接提示报错行
devtool: 'source-map', // 大而全的设置
// 2。源码映射,不会产生单独的文件,单会直接显示错误的行列
// devtool: 'eval-source-map',
// 3。不会产生列,但是会生成一个单独的映射文件
// devtool: 'cheap-module-source-map', // 产生后可以保留起来
// 4。不会产生文件,集成再打包后的文件中,也不会产生列
// devtool: 'cheap-module-source-map', // 产生后可以保留起来
}
watch 实时打包
这个功能其实并不是太实用,简单介绍一下吧
安装 clean-webpack-plugin
,每次实时打包,保证 dist 目录是最新的打包代码
cnpm install clean-webpack-plugin -D // 打包前清除dist目录
// 扩展两个插件
cnpm install copy-webpack-plugin -D // copy 目录中的文件到指定目录
bannerPlugin // 版权声明插件,webpack 内置
module.exports = {
entry: { /*省略*/},
// 对应多出口
output: { /*省略*/}
// 1。源码映射,单独生成一个sourcemap文件,出错时会直接提示报错行
devtool: 'source-map', // 大而全的设置
// 开启监控,实时打包设置
watch: true,
watchOptions:{ // 设置监控选项
poll: 1000, // 每秒查询1000次
aggregateTimeout: 500, // 防抖,ctrl+s后多久开始打包
ignored: /node_modules/, // 忽略不需要监控的文件夹
},
plugins:[
new CleanWebpackPlugin(), // 先删除dist目录再打包,不用填参数
]
}
webpack 如何处理跨域
跨域应该是前端中比较常见的问题了,一般可能都给到了后端去处理,那么在 webpack 中也是有对应的解决方案,代理
export.modules{
entry: { /*省略*/},
// 对应多出口
output: { /*省略*/}
// webpack.config.js
devServer{
// 配置代理
proxy:{
'/api': { // 匹配请求接口url包含 /api 的请求,进行拦截
target: 'http://localhost:3000', // 转发配置代理指向域名
pathRewrite: {'^/api':''}, // 将请求路径中开头/api替换为空
changeOrigin: true, // target是域名的话,需要这个参数,
secure: false, // 设置支持https协议的代理
}
}
}
}
pathRewrite
这个配置项需要注意,因为在实际开发中,后端接口可能不是那么如意,并不可能每个接口都带有 /api
这样写的目的是,我们前端在请求时,都带上 /api
, 然后经过webpack proxy
代理时,匹配上,再替换掉。
resolve 解析第三方包
这个配置很有意思,对我们开发是一神器,直接关系到我们针对不同层级组件导入文件,图片等资源的路径配置
export.modules{
entry: { /*省略*/},
// 对应多出口
output: { /*省略*/}
// 解析第三方包
resolve:{
// 解析文件搜索的目录
modules:[path.resolve('node_modules')],
// 常常在工作中这样写 import A form "../a"
extensions:['.js','.css','.json','.vue'], // 限定扩展名,按序依次解析
// 设置别名,在 index.js 引入时可直接采用 import 'bootstrap'
// 在项目中,引入 components 目录下的文件时,可简写为 improt X from "@components/xxx"
alias:{
bootstrap:'bootstrap/dist/css/bootstrap.css',
'@Components': path.resolve(__dirname, 'src/components/'),
},
// 其它参考文档
},
}
打包多页 [ 多入口,多出口 ]
其实打包多页,比较容易理解,就是多个入口,对应多个出口,这里做一个简单的例子,便于大家理解
// 多入口配置
entry: {
home: './src/index.js',
other: './src/other.js'
},
// 对应多出口配置
output: {
// 多出口设置, [name]代表多入口变量名home,other,逐一打包
filename: '[name].js', // name 是内置变量,指向 entry 入口配置对应 key
// path: path.resolve(__dirname, 'dist'),
path: path.resolve(__dirname, 'dist'),
// publicPath: 'http://localhost'
},
// html-webpack-plugin 对应配置生成多个文件
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
filename: "index.html",
chunks: ['home'], // 多页面打包设置,对应入口js
}),
// 多页面打包,多个new HtmlWebpackPlugin
new HtmlWebpackPlugin({
template: "./other.html",
filename: "other.html",
chunks: ['other'], // 也可以同时引入多个 js 这样 ['other', 'home']
}),
]
ok 到这里,暂告一段落,列举了我们在开发中经常用到的各种配置以及loader和插件,紧接着我会再写一篇关于 webpack 内置优化的文章
小小鼓励,大大成长,欢迎点赞