一、webpack 和 babel
- 對於
webpack
,分爲基本配置、高級配置、優化打包效率、優化產出代碼、構建流程概述和babel
。 - 對於
webpack
的基本配置,拆分配置和merge
,啓動本地服務,處理ES6
,處理樣式,處理圖片和模塊化,代碼如下所示:
- 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')
})
]
})
- path.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 對於
webpack
的高級配置中的多入口,代碼如下所示:
- webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
},
// {
// 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'
// })
// 多入口 - 生成 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.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 = 'production'
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 戳
filename: '[name].[contentHash:8].js', // name 即多入口時 entry 的 key
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')
})
]
})
- paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 對於
webpack
的高級配置中的抽離CSS
文件,代碼如下所示:
- webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
}
// css 處理
]
},
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.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'
},
// {
// 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 webpack.DefinePlugin({
// window.ENV = 'production'
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 { smart } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'production',
output: {
// filename: 'bundle.[contentHash:8].js', // 打包代碼時,加上 hash 戳
filename: '[name].[contentHash:8].js', // name 即多入口時 entry 的 key
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'
}
}
},
// 抽離 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: [
new CleanWebpackPlugin(), // 會默認清空 output.path 文件夾
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('production')
}),
// 抽離 css 文件
new MiniCssExtractPlugin({
filename: 'css/main.[contentHash:8].css'
})
],
optimization: {
// 壓縮 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
}
})
- paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 對於
webpack
的高級配置中的抽離公共代碼和第三方代碼,代碼如下所示:
- webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
}
]
},
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', 'vendor', 'common'] // 要考慮代碼分割
}),
// 多入口 - 生成 other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other', 'common'] // 考慮代碼分割
})
]
}
- 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'
},
// {
// 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 webpack.DefinePlugin({
// window.ENV = 'production'
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 { smart } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'production',
output: {
// filename: 'bundle.[contentHash:8].js', // 打包代碼時,加上 hash 戳
filename: '[name].[contentHash:8].js', // name 即多入口時 entry 的 key
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'
}
}
},
// 抽離 css
{
test: /\.css$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,這裏不再用 style-loader
'css-loader',
'postcss-loader'
]
},
// 抽離 less
{
test: /\.less$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,這裏不再用 style-loader
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(), // 會默認清空 output.path 文件夾
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('production')
}),
// 抽離 css 文件
new MiniCssExtractPlugin({
filename: 'css/main.[contentHash:8].css'
})
],
optimization: {
// 壓縮 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
// 分割代碼塊
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 // 公共模塊最少複用過幾次
}
}
}
}
})
- paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 對於
webpack
的高級配置中,除了上面所提到的多入口、抽離CSS
文件和 抽離公共代碼,還有異步加載JS
、懶加載、處理JSX
和處理Vue
,核心配置也是匹配對應的後綴文件,以及loader
,代碼如下所示:
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'
})
]
}
module、chunk、bundle
的區別是什麼,答案如下所示:
module
是各個源碼文件,webpack
中一切皆模塊chunk
是多模塊合併成的,如entery、import()、splitChunk
等等bundle
是最終輸出的文件
- 對於
webpack
性能優化,優化構建速度,如下所示:
- 優化
babel-loader
。開啓緩存,明確範圍,排除範圍,include
和exclude
兩者選一個即可 IgnorePlugin
noParse
happyPack
多進程打包。JS
單線程,開啓多進程打包,提高構建速度,特別是多核CPU
ParallelUglifyPlugin
多進程壓縮JS
。webpack
內置Uglify
工具壓縮JS
,JS
單線程,開啓多進程壓縮更快,和happyPack
原理相同- 自動刷新。開啓監聽,監聽配置,
webpack-dev-server
會自動開啓刷新瀏覽器 - 熱更新。自動刷新會使得整個網頁全部刷新,速度較慢,狀態會丟失。但是熱更新,新代碼生效,網頁不刷新,狀態不丟失
DllPlugin
動態鏈接庫插件。前端框架如React、Vue
,體積大,構建慢,較穩定,不常升級版本,同一版本只構建一次即可,不用每次都重新構建。webpack
已內置DllPlugin
支持,DllPlugin
打包出dll
文件,DllReferencePlugin
使用dll
文件
-
對於開啓多進程,項目較大,打包較慢,開啓多進程能提高速度,項目較小,打包很快,開啓多進程會減低速度,進行開銷,按需使用。
-
對於
webpack
優化構建速度,可用於生產環境的,優化babel-loader、 IgnorePlugin、noParse、happyPack、 ParallelUglifyPlugin
。對於webpack
優化構建速度,不用於生產環境的,自動刷新、熱更新、DllPlugin
。 -
對於
webpack
性能優化,產出代碼,如下所示:
- 體積更小
- 合理分包,不重複加載
- 速度更快,內存使用更少
- 小圖片
base64
編碼 bundle
加hash
- 懶加載
- 提取公共代碼
IgnorePlugin
- 使用
CDN
加速 - 使用
production
Scope Hosting
- 對於使用
production
,如下所示:
- 自動開啓代碼壓縮
Vue、React
等會自動刪掉調試代碼,如開發環境的warning
- 啓動
Tree-Shaking
- 對於
ES6 Module
和Commonjs
區別,如下所示:
ES6 Module
靜態引入,編譯時引入Commonjs
動態引入,執行時引入- 只有
ES6 Module
才能靜態分析,實現Tree-Shaking
- 對於
Scope Hosting
,如下所示:
- 代碼體積更小
- 創建函數作用域更少
- 代碼可讀性更好
- 針對
Npm
中的第三方模塊優先採用jsnext:main
中指向的ES6
模塊化語法的文件,開啓Scope Hosting
- 對於
webpack
性能優化,優化構建速度,代碼如下所示:
- webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
module: {
rules: [
// babel-loader
]
},
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', 'vendor', 'common'] // 要考慮代碼分割
}),
// 多入口 - 生成 other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other', 'vendor', 'common'] // 考慮代碼分割
})
]
}
- 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')
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
module.exports = smart(webpackCommonConf, {
mode: 'development',
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')
},
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory'],
include: srcPath,
// exclude: /node_modules/
},
// 直接引入圖片 url
{
test: /\.(png|jpg|jpeg|gif)$/,
use: 'file-loader'
},
// {
// 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 webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('development')
}),
new HotModuleReplacementPlugin()
],
devServer: {
port: 8080,
progress: true, // 顯示打包的進度條
contentBase: distPath, // 根目錄
open: true, // 自動打開瀏覽器
compress: true, // 啓動 gzip 壓縮
hot: true,
// 設置代理
proxy: {
// 將本地 /api/xxx 代理到 localhost:3000/api/xxx
'/api': 'http://localhost:3000',
// 將本地 /api2/xxx 代理到 localhost:3000/xxx
'/api2': {
target: 'http://localhost:3000',
pathRewrite: {
'/api2': ''
}
}
}
},
// watch: true, // 開啓監聽,默認爲 false
// watchOptions: {
// ignored: /node_modules/, // 忽略哪些
// // 監聽到變化發生後會等300ms再去執行動作,防止文件更新太快導致重新編譯頻率太高
// // 默認爲 300ms
// aggregateTimeout: 300,
// // 判斷文件是否發生變化是通過不停的去詢問系統指定文件有沒有變化實現的
// // 默認每隔1000毫秒詢問一次
// poll: 1000
// }
})
- webpack.prod.js
const path = require('path')
const webpack = require('webpack')
const { smart } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const HappyPack = require('happypack')
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'production',
output: {
// filename: 'bundle.[contentHash:8].js', // 打包代碼時,加上 hash 戳
filename: '[name].[contentHash:8].js', // name 即多入口時 entry 的 key
path: distPath,
// publicPath: 'http://cdn.abc.com' // 修改所有靜態文件 url 的前綴(如 cdn 域名),這裏暫時用不到
},
module: {
rules: [
// js
{
test: /\.js$/,
// 把對 .js 文件的處理轉交給 id 爲 babel 的 HappyPack 實例
use: ['happypack/loader?id=babel'],
include: srcPath,
// exclude: /node_modules/
},
// 圖片 - 考慮 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'
}
}
},
// 抽離 css
{
test: /\.css$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,這裏不再用 style-loader
'css-loader',
'postcss-loader'
]
},
// 抽離 less
{
test: /\.less$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,這裏不再用 style-loader
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(), // 會默認清空 output.path 文件夾
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('production')
}),
// 抽離 css 文件
new MiniCssExtractPlugin({
filename: 'css/main.[contentHash:8].css'
}),
// 忽略 moment 下的 /locale 目錄
new webpack.IgnorePlugin(/\.\/locale/, /moment/),
// happyPack 開啓多進程打包
new HappyPack({
// 用唯一的標識符 id 來代表當前的 HappyPack 是用來處理一類特定的文件
id: 'babel',
// 如何處理 .js 文件,用法和 Loader 配置中一樣
loaders: ['babel-loader?cacheDirectory']
}),
// 使用 ParallelUglifyPlugin 並行壓縮輸出的 JS 代碼
new ParallelUglifyPlugin({
// 傳遞給 UglifyJS 的參數
// (還是使用 UglifyJS 壓縮,只不過幫助開啓了多進程)
uglifyJS: {
output: {
beautify: false, // 最緊湊的輸出
comments: false, // 刪除所有的註釋
},
compress: {
// 刪除所有的 `console` 語句,可以兼容ie瀏覽器
drop_console: true,
// 內嵌定義了但是隻用到一次的變量
collapse_vars: true,
// 提取出出現多次但是沒有定義成變量去引用的靜態值
reduce_vars: true,
}
}
})
],
optimization: {
// 壓縮 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
// 分割代碼塊
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 // 公共模塊最少複用過幾次
}
}
}
}
})
- paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 對於
webpack
性能優化,優化構建速度,DllPlugin
動態鏈接庫插件,代碼如下所示:
- 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/
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html'
})
]
}
- webpack.dev.js
const path = require('path')
const webpack = require('webpack')
const { smart } = require('webpack-merge')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
// 第一,引入 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: [
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('development')
}),
// 第三,告訴 Webpack 使用了哪些動態鏈接庫
new DllReferencePlugin({
// 描述 react 動態鏈接庫的文件內容
manifest: require(path.join(distPath, 'react.manifest.json')),
}),
],
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 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 域名),這裏暫時用不到
},
plugins: [
new webpack.DefinePlugin({
// window.ENV = 'production'
ENV: JSON.stringify('production')
})
]
})
- paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
- 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'),
}),
],
}
- 對於
babel
,分爲環境配置、基本配置babel-polyfill、babel-runtime
,如下所示:
- 對於基本配置,環境搭建,
.babelrc
配置,presets
和plugins
Polyfill
,core-js
和regenerator,babel-polyfill
即兩者的結合。但是在Babel 7.4
之後就棄用babel-polyfill
,推薦直接使用core-js
和regenerator
,但是面試仍然會考察babel-polyfill
babel-polyfill
按需引入,文件較大,只有一部分功能,無需全部引入,配置按需引入babel-polyfill
存在的問題,會污染全局環境。如果做一個獨立的web
系統,則無礙。如果做一個第三方lib
,則會有問題,可以使用babel-runtime
- 對於
babel
的相關代碼,如下所示:
- package.json
{
"name": "09-babel-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-transform-runtime": "^7.7.5",
"@babel/preset-env": "^7.7.5"
},
"dependencies": {
"@babel/polyfill": "^7.7.0",
"@babel/runtime": "^7.7.5"
}
}
- .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
- index.js
const sum = (a, b) => a + b
// 新的 API
Promise.resolve(100).then(data => data);
// 新的 API
[10, 20, 30].includes(20)
// 語法,符合 ES5 語法規範
// 不處理模塊化(webpack)
// 污染全局環境
// window.Promise1 = function() {}
// Array.prototype.includes1 = function () {}
// 使用方
// window.Promise = 'abc'
// Array.prototype.includes = 100
二、webpack 的面試題
- 前端爲何要進行打包和構建,答案如下所示:
- 體積更小,
Tree-shaking
、壓縮和合並,加載更快 - 編譯高級語言或語法,
TS
、ES6+
、模塊化、scss
- 兼容性和錯誤檢查,
Polyfill、postcss、eslint
- 統一、高效的開發環境
- 統一的構建流程和產出標準
- 集成公司構建規範,提測、上線等
module、chunk、bundle
的區別,答案如下所示:
module
是各個源碼文件,webpack
中一切皆模塊chunk
是多模塊合併成的,如entery、import()、splitChunk
等等bundle
是最終輸出的文件
loader
和plugin
的區別,答案如下所示:
loader
模塊轉換器,如less -> css
plugin
擴展插件,如HtmlWebpackPlugin
babel
和webpack
的區別,答案如下所示:
babel
是JS
新語法編譯工具,不關心模塊化webpack
是打包構建工具,是多個loader、plugin
的集合
- 如何產出一個
lib
,答案如下所示:
- 參考
webpack.dll.js
output.library
lib
的文件名,輸出lib
到dist
目錄下,lib
的全局變量名
babel-polyfill
和babel-runtime
的區別,答案如下所示:
babel-polyfill
會污染全局babel-runtime
不會污染全局- 產出第三方
lib
要用babel-runtime
webpack
如何實現懶加載,答案如下所示:
import()
- 結合
Vue、React
異步組件 - 結合
Vue-router、React-router
異步加載路由
- 爲何
Proxy
不能被Polyfill
,答案如下所示:
- 如
Class
可以用function
模擬 - 如
Promise
可以用callback
來模擬 - 但
Proxy
的功能用Object.defineProperty
無法模擬
- 對於
webpack
優化構建速度,可用於生產環境的,答案如下所示:
- 優化
babel-loader
IgnorePlugin
noParse
happyPack
ParallelUglifyPlugin
- 對於
webpack
優化構建速度,不用於生產環境的,答案如下所示:
- 自動刷新
- 熱更新
DllPlugin
- 對於
webpack
優化產出代碼,答案如下所示:
- 小圖片
base64
編碼 undle
加hash
- 懶加載
- 提取公共代碼
- 使用
CDN
加速 IgnorePlugin
- 使用
production
Scope Hosting