基本配置
1、拆分配置和merge ?
安装 webpack-merge
把公共的文件放在webpack.common.js中;webpack.dev.js是生产环境的配置;webpack.prod.js是开发环境的配置;
在开发环境和生产环境中分别引入公共配置,使用 smart 进行合并:
webpack.prod.js
const webpackCommonConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
module.exports = smart(webpackCommonConf, {
mode: 'production',
...
}
webpack.dev.js
const webpackCommonConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
module.exports = smart(webpackCommonConf, {
mode: 'development',
...
}
2、启动本地服务
安装 webpack-dev-server;
在packget.json的scripts中配置命令:
在webpack.dev.js中配置 devServer:
devServer: {
port: 8080,
progress: true, // 显示打包的进度条
contentBase: distPath, // 根目录
open: true, // 自动打开浏览器
compress: true, // 启动 gzip 压缩
// 设置代理
proxy: {
// 将本地 /api/xxx 代理到 localhost:3000/api/xxx
'/api': 'http://localhost:3000',
// 将本地 /api2/xxx 代理到 localhost:3000/xxx
'/api2': {
target: 'http://localhost:3000',
pathRewrite: {
'/api2': ''
}
}
}
}
3、处理 ES6
在 webpack.common.js中配置:
module: {
rules: [
{
test: /.js$/,
loader: ['babel-loader'],
include: srcPath, // 包含的文件路径
exclude: /node_modules/ // 不包含的路径
},
.....
]
}
4、处理样式
安装postcss-loader 添加浏览器兼容性;
安装autoprefixer 适用于普通的CSS,可以实现css3代码自动补全。也可以轻松跟Sass,LESS及Stylus集成,在CSS编译前或编译后运行,会清理过期的前缀,因此下面的代码:
css 代码:
a {
-webkit-border-radius : 5px;
border-radius : 5px
}
编译成: css 代码:
a {
border-radius : 5px
}
因为经过autoprefixerr处理,CSS将仅包含实际的浏览器前缀。
配置:
1、在根目录上新建一个文件,postcss.config.js,配置autoprefixer,才会生效:
module.exports = {
plugins: [require('autoprefixer')]
}
2、在 webpack.common.js中配置,
// {
// test: /.css$/,
// // loader 的执行顺序是:从后往前(知识点)
// loader: ['style-loader', 'css-loader']
// },
{
test: /.css$/,
// loader 的执行顺序是:从后往前
loader: ['style-loader', 'css-loader', 'postcss-loader'] // 加了 postcss
{
test: /.less$/,
// 增加 'less-loader' ,注意顺序
loader: ['style-loader', 'css-loader', 'less-loader']
}
},
5、处理图片
在webpack.prod.js文件中配置:
···
rules: [
// 图片 - 考虑 base64 编码的情况
{
test: /.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片用 base64 格式产出
// 否则,依然延用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,
// 打包到 img 目录下
outputPath: '/img1/',
// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
// publicPath: 'http://cdn.abc.com'
}
}
},
...
]
...
6、模块化
webpack 默认支持,用的时候再配置
7、基本配置代码
webpack.common.js 代码:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: path.join(srcPath, 'index'),
module: {
rules: [
{
test: /.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
},
// {
// test: /.vue$/,
// loader: ['vue-loader'],
// include: srcPath
// },
// {
// test: /.css$/,
// // loader 的执行顺序是:从后往前(知识点)
// loader: ['style-loader', 'css-loader']
// },
{
test: /.css$/,
// loader 的执行顺序是:从后往前
loader: ['style-loader', 'css-loader', 'postcss-loader'] // 加了 postcss
},
{
test: /.less$/,
// 增加 'less-loader' ,注意顺序
loader: ['style-loader', 'css-loader', 'less-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html'
})
]
}
webpack.dev.js 代码:
const path = require('path')
const webpack = require('webpack')
const webpackCommonConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
const { srcPath, distPath } = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'development',
module: {
rules: [
// 直接引入图片 url
{
test: /.(png|jpg|jpeg|gif)$/,
use: 'file-loader'
}
]
},
plugins: [
new webpack.DefinePlugin({
// window.ENV = 'development'
ENV: JSON.stringify('development')
})
],
devServer: {
port: 8080,
progress: true, // 显示打包的进度条
contentBase: distPath, // 根目录
open: true, // 自动打开浏览器
compress: true, // 启动 gzip 压缩
// 设置代理
proxy: {
// 将本地 /api/xxx 代理到 localhost:3000/api/xxx
'/api': 'http://localhost:3000',
// 将本地 /api2/xxx 代理到 localhost:3000/xxx
'/api2': {
target: 'http://localhost:3000',
pathRewrite: {
'/api2': ''
}
}
}
}
})
webpack.prod.js 代码:
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const webpackCommonConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
const { srcPath, distPath } = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'production',
output: {
filename: 'bundle.[contentHash:8].js', // 打包代码时,加上 hash 戳
path: distPath,
// publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
},
module: {
rules: [
// 图片 - 考虑 base64 编码的情况
{
test: /.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片用 base64 格式产出
// 否则,依然延用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,
// 打包到 img 目录下
outputPath: '/img1/',
// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
// publicPath: 'http://cdn.abc.com'
}
}
},
]
},
plugins: [
new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('production')
})
]
})
高级配置
1、多入口
index.html 和 other.html 两个入口文件如何配置:
首先安装html-webpack-plugin插件,然后配置webpack.common.js文件:
const HtmlWebpackPlugin = require('html-webpack-plugin')
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
....
plugins: [
...
// new HtmlWebpackPlugin({
// template: path.join(srcPath, 'index.html'),
// filename: 'index.html'
// })
// 多入口 - 生成 index.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html',
// chunks 表示该页面要引用哪些 chunk (即上面的 index 和 other),默认全部引用
chunks: ['index'] // 只引用 index.js
}),
// 多入口 - 生成 other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other'] // 只引用 other.js
})
...
]
接着在 webpack.prod.js 文件中配置:
output: {
// filename: 'bundle.[contentHash:8].js', // 打包代码时,加上 hash 戳
filename: '[name].[contentHash:8].js', // name 即多入口时 entry 的 key
path: distPath,
// publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
},
...
plugins: [
new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
...
]
2、抽离 css 文件
安装 mini-css-extract-plugin 插件
webpack.prod.js 文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
module: {
rules: [
...
// 抽离 css
{
test: /.css$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
'css-loader',
'postcss-loader'
]
},
// 抽离 less --> css
{
test: /.less$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
},
plugins: [
...
// 抽离 css 文件
new MiniCssExtractPlugin({
filename: 'css/main.[contentHash:8].css'
})
...
],
optimization: {
// 压缩 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
}
3、抽离公共代码
在webpack.prod.js文件中:
optimization: {
....
// 分割代码块
splitChunks: {
chunks: 'all',
/**
* initial 入口 chunk,对于异步导入的文件不处理
async 异步 chunk,只对异步导入的文件处理
all 全部 chunk
*/
// 缓存分组
cacheGroups: {
// 第三方模块
vendor: {
name: 'vendor', // chunk 名称
priority: 1, // 权限更高,优先抽离,重要!!!
test: /node_modules/,
minSize: 0, // 大小限制
minChunks: 1 // 最少复用过几次
},
// 公共的模块
common: {
name: 'common', // chunk 名称
priority: 0, // 优先级
minSize: 0, // 公共模块的大小限制
minChunks: 2 // 公共模块最少复用过几次
}
}
}
}
4、懒加载
5、处理 JSX
首先安装 @babel/preset-react ,
然后在 .babelrc 文件中进行处理:
6、处理Vue
安装 vue-loader
7、module、chunk、bundle 的区别?
性能优化-优化构建速度
1、优化babel-loader
2、IgnorePlugin
避免引用无用模块
3、noParse
避免重复打包
4、happyPack
安装 happypack
修改 webpack.prod.js 配置
const HappyPack = require('happypack')
module: {
rules: [
// js
{
test: /.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: ['happypack/loader?id=babel'],
include: srcPath,
// exclude: /node_modules/
},
...
]
}
...
plugins: [
...
// happyPack 开启多进程打包
new HappyPack({
// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
id: 'babel',
// 如何处理 .js 文件,用法和 Loader 配置中一样
loaders: ['babel-loader?cacheDirectory']
}),
...
]
5、ParalleIUglifyPlugin
多进程压缩 JS
安装插件 webpack-parallel-uglify-plugin
在 webpack.prod.js 文件中配置:
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
plugins: [
...
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParallelUglifyPlugin({
// 传递给 UglifyJS 的参数
// (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
uglifyJS: {
output: {
beautify: false, // 最紧凑的输出
comments: false, // 删除所有的注释
},
compress: {
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
}
}
})
...
]
关于多进程打包的选择:
6、自动刷新
7、热更新
安装插件:
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
...
entry: {
// index: path.join(srcPath, 'index.js'),
index: [
'webpack-dev-server/client?http://localhost:8080/',
'webpack/hot/dev-server',
path.join(srcPath, 'index.js')
],
other: path.join(srcPath, 'other.js')
},
plugins: [
...
new HotModuleReplacementPlugin()
],
devServer: {
...
hot: true,
}
8、DllPlugin
动态链接库插件
创建webpack.dll.js 文件
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
mode: 'development',
// JS 执行入口文件
entry: {
// 把 React 相关模块的放到一个单独的动态链接库
react: ['react', 'react-dom']
},
output: {
// 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
// 也就是 entry 中配置的 react 和 polyfill
filename: '[name].dll.js',
// 输出的文件都放到 dist 目录下
path: distPath,
// 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
// 之所以在前面加上 _dll_ 是为了防止全局变量冲突
library: '_dll_[name]',
},
plugins: [
// 接入 DllPlugin
new DllPlugin({
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述动态链接库的 manifest.json 文件输出时的文件名称
path: path.join(distPath, '[name].manifest.json'),
}),
],
}
在 package.json文件中进行配置:
"scripts": {
...
"dll": "webpack --config build/webpack.dll.js"
},
执行 npm run dll 生成dist目录
在 index.html 中引入文件react.dll.js:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./react.dll.js"></script>
</body>
</html>
在 webpack.dev.js 中进行配置:
// 第一,引入 DllReferencePlugin
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = smart(webpackCommonConf, {
mode: 'development',
module: {
rules: [
{
test: /.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/ // 第二,不要再转换 node_modules 的代码
},
]
},
plugins: [
...
// 第三,告诉 Webpack 使用了哪些动态链接库
new DllReferencePlugin({
// 描述 react 动态链接库的文件内容
manifest: require(path.join(distPath, 'react.manifest.json')),
}),
],
devServer: {
...
}
})
9、总结
性能优化-优化产出代码
1、优化方案
2、production
ES6 Module 和 CommonJS 的区别?
3、Scope Hosting
配置:
构建流程概述
babel
1、环境搭建 && 基本配置
2、babel-polyfill
.babelrc文件
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
3、babel-runtime
.babelrc文件
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
面试题
1、前端为何需要打包和构建 ?
2、module、chunk和bundle的区别 ?
3、loader 和 plugin 的区别 ?
4、常见的 loader 和 plugin 有哪些 ?
www.webpackjs.com/loaders/ www.webpackjs.com/plugins/