首先貼上自己關鍵插件版本
"webpack": "^5.52.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.1.1"
本文參考依賴較多,請注意配置是否已失效,
一、初識webpack
1、組態檔名稱
webpack默認組態檔:webpack.config.js
可以通過webpack --config指定組態檔
module.exports = {
entry: './src/index.js', // 4.0會默認制定入口位置為‘src/index.js’
output: './dist/main.js', // 4.0會默認制定入口位置為‘dist/main.js’
mode: 'production', // 環境
module: {
rules: [ // loader配置
{
test:/\.txt$/, use: 'raw-loader'
}
]
},
plugins:[
new HtmlwebpackPlugin({ // 插件配置
template: './src/index.html'
})
]
}
2、安裝nvm
安裝 nvm(node.js version management,顧名思義是一個nodejs的版本管理工具,通過它可以安裝和切換不同版本的nodejs,下面列出下載、安裝及使用方法,)
安裝命令:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
or Wget:
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
安裝完之后添加到環境變數,
source ~/.bash_profile
// 推出并重啟終端,查看是否安裝成功:
nvm --version
// 安裝node.js:
nvm i v10.15.3
// 創建專案檔案夾,并初始化
mkdir 01project
cd 01project
// 所有詢問都是yes
npm init -y
//安裝webpack
npm i webpack webpack-cli --save-dev
//查看專案是否安裝成功
./node_modules/.bin/webpack -v
webpack 5.52.0
webpack-cli 4.8.0
3、一個簡單例子
新建webpack.config.js檔案
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
mode: 'production'
}
新建src/index.js、src/helloworld.js檔案
// index.js檔案
import { helloworld } from "./helloworld";
document.write(helloworld())
// helloworld.js檔案
export function helloworld(){
return 'hello webpack'
}
運行./node_modules/.bin/webpack命令,打包檔案
新建dist/index.html檔案,并引入打包檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./bundle.js"></script>
</body>
</html>
4、通過npm script運行webpack
為什么package.json可以直接運行node_module/.bin的命令
原理:模塊區域安裝會在node_module/.bin目錄創建軟鏈接
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build":"webpack"
},
新建src/serach.js檔案
document.write('search info')
修改webpack.config.js檔案 //通過占位符確保檔案名稱唯一
const path = require('path')
module.exports = {
entry: {
'index': './src/index.js',
'search': './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
mode: 'production'
}
修改dist/index.html檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./index.js"></script>
<script src="./search.js"></script>
</body>
</html>
5、核心概念之Loaders
webpack開箱即用只支持JS和JSON兩種檔案型別,通過Loaders去支持其他檔案型別并且把她們轉化成有效的模塊,并且可以添加到依賴圖中,
本身是一個函式,接受源檔案作為引數,回傳轉換的結果,
常用的loaders有哪些

6、核心概念之Loaders
插件用于bundle檔案的優化,資源管理和環境變數注入,作用域整個構建程序,

二、常用Loaders
2.1、決議es6、React JSX
2.1.1 使用babel-loader
安裝相關依賴
npm install -D babel-loader @babel/core @babel/preset-env webpack
webpack配置
module:{
rules: [
{
test: /\.js$/,
use: 'babel-loader'
}
]
}
為了支持es6需要增加配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
option: {
presets: ["@babel/preset-env"],
plugins: ['@babel/plugin-proposal-object-rest-spread']
}
}
}
]
}
又或者給babel一個組態檔.babelrc,新建.babelrc檔案
{
"presets": ["@babel/preset-env"]
}
2.1.2 決議React JSX
安裝相關依賴
npm install -D react-dom @babel/preset-react
修改.babelrc檔案
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
修改src/search.js檔案,撰寫react組件
import React from 'react'
import ReactDOM from 'react-dom'
class Search extends React.Component{
render() {
return <div>search components</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.2 css決議相關
2.2.1css-loader、style-loader(css-loader決議css、style-loader將樣式通過<style>標簽插入到head中)
安裝依賴
npm i -D css-loader style-loader
修改webpack.config.js檔案
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
'style-loader',
'css-loader',
]
}
]
}
新建src/css/index.css檔案
.search-txt {
font-size: 20px;
color: blue;
}
在src/search.js檔案中參考css檔案
import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.css'
class Search extends React.Component{
render() {
return <div className="search-txt ">search components</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.2.2 決議less和sass
安裝依賴
npm i -D less less-loader
修改src/css/index.css檔案為src/css/index.less
修改src/css/search.js內關于index.css的參考名
修改webpack檔案
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
}
]
}
2.3 資源決議
2.3.1 file-loader用于處理圖片
安裝依賴npm i -D file-loader
在src/img檔案夾下放置一張圖片
在src/search.js檔案中引入圖片
import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.less'
import fileImg from './img/fileImg.png'
class Search extends React.Component{
render() {
return <div className="search-txt ">search components
<img src={fileImg}></img>
</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
添加相關配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
'file-loader']
}
]
}
2.3.1 file-loader用于處理字體
新建src/font檔案夾,放入字體檔案
css引入,修改src/css/index.css檔案
@font-face{
font-family: 'liuKai';
src: url('../font/liuKai.ttf')
}
.search-txt {
font-size: 20px;
color: blue;
font-family: 'liuKai';
}
修改webpack配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
'file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader']
}
]
}
2.3.2 url-loader也可以處理圖片和字體,可以設定較小資源自動base64
安裝依賴
npm i -D url-loader
webpack.config.js中修改關于圖片處理的部分
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240 // 限制大小為10k
}
}]
},
2.4 檔案監聽(watch)
檔案監聽是在發現原始碼發生變化時,自動重新構建出新的輸出檔案
webpack開啟監聽模式,有兩種方式
- 啟動webpack命令時,帶上--watch引數
- 在配置webpack.config.js中設定watch:true
缺陷:每次需要手動重繪瀏覽器
watch: true, //默認為false 不開啟
watchOptions: { // 只有watch開啟時,watchOptions才會有意義
ignored: /node_modules/, // 不堅挺的檔案或檔案夾,支持正則匹配
aggregateTimeout: 300, //監聽到變化后300ms再去執行,默認300ms
poll: 1000 //判斷檔案是否變化的輪詢間隔時間(有變化先混存,到期再更新)
}
2.5 熱更新:webpack-dev-server(使用HotModuleReplacementPlugin插件)
wds不重繪瀏覽器
wds不輸出檔案,而是放在記憶體中
安裝依賴
npm install webpack-dev-server -D
"scripts": {
"dev": "webpack-dev-server --open"//有更新自動打開瀏覽器
},
由于熱更新主要是用在開發環境中,修改mode: development
由于HotModuleReplacementPlugin是內部插件
const webpack = require('webpack')
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: { // 專門為webpack-dev-server指定相關配置選項
hot: true,
static: {
directory: path.resolve(__dirname, "dist"),
}
},
運行之后會有一點報錯,點開報錯檔案修改資料就好了,
熱更新的原理
- webpack compile:將JS變異成bundle
- HRM Server:將熱更新的檔案輸出給HMR Runtime
- Bundle Server:提供檔案在瀏覽器的訪問
- HMR Runtime:會注入到瀏覽器,更新檔案的變化
- bundle.js: 構建輸出的檔案
2.5 檔案指紋(ContentHash,ChunkHash,Hash)
檔案指紋如何生成
在webpack中有三種hash可以配置
Hash: 和整個專案構建相關,只要專案檔案有修改,整個專案構建的hash值就會更改
ChunkHash: 和webpack打包的chunk有關,不同的entry會生成不同的chunkhash的值
ContentHash: 根據檔案內容來定義hash,檔案內容不變,則contenthash不變
2.5.1 js檔案的指紋設定(chunkhash在生產環境使用,無法和熱更新HotModuleReplacementPlugin一起使用)
設定output的filename,使用[chunkhash]// filename: '[name].[chunkhash].js'
新建一份生產環境專用的webpack組態檔webpack.prod.js,
設定mode: 'production',去掉熱更新相關模塊,
設定檔案指紋長度filename: '[name]_[chunkhash:8].js'
設定圖片、字體的檔案指紋
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8][ext]'
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8][ext]'
}
}]
}
const path = require('path')
module.exports = {
entry: {
'index': './src/index.js',
'search': './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[chunkhash:8].js'
},
mode: 'production',
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
}]
}
]
}
}
添加prod執行命令
"prod":"webpack --config webpack.prod.js"
2.5.2 css檔案指紋設定(plugin:MiniCssExtractPlugin)
設定MiniCssExtractPlugin的filename,使用[contenthash]
// filename: '[name].[contenthash].js'
安裝依賴
npm i -D mini-css-extract-plugin
引入插件,加入plugin陣列中
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
});
]
}
同時還需要添加到依賴中,MiniCssExtractPlugin的功能是將css樣式抽離到css檔案,而style-loader是將樣式以<style>標簽的形式引入,二者有所沖突,可以去除style-loader,
{
test: /\.css$/,
use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
MiniCssExtractPlugin.loader,
'css-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
]
},
2.5.2 圖片、字體檔案指紋設定
設定file-loader的filename,使用[hash]
// filename: '[name].[hash].js'
2.6 檔案壓縮 (prod環境)
2.6.1 js 檔案的壓縮(內置的uglifyjs-webpack-plugin)
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
plugins: [
new OptimizeCssAssetsWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
]
}
2.6.2 css 檔案的壓縮 optimize-css-assets-webpack-plugin
使用optimize-css-assets-webpack-plugin
同時使用cssnano
安裝依賴
npm i optimize-css-assets-webpack-plugin -D
npm i -D cssnano
引入依賴
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
plugins: [
new OptimizeCssAssetsWebpackPlugin()
]
}
2.6.3 html 檔案的壓縮(html-webpack-plugin prod環境)
修改html-webpack-plugin,設定壓縮引數(模板檔案不能有注釋)
安裝依賴
npm i -D html-webpack-plugin
引入html
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/search.html'),// 需要打包的檔案路徑
filename: 'search.html',
chunks: ['search'], //
inject: true, // 設定為true,js css會自動注入html
minify: {
}
}),
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/index.html'),// 需要打包的檔案路徑
filename: 'index.html',
chunks: ['index'], //
inject: true, // 設定為true,js css會自動注入html
minify: {//壓縮選項
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCss: true,
minifyJs: true,
removeComments: false
}
})
]
}
2.7 自動清理構建目錄產物(clean-webpack-plugin)
會自動清除output目錄
安裝依賴, 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}
2.8 自動補齊css前綴(postcss-loader、autoprefixer prod環境)
安裝依賴
npm i -D postcss-loader postcss autoprefixer
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
}
]
},
新建postcss.config.js檔案進行配置
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: [
'last 2 version',
'>1%',
'ios 7']
})
]
}
2.8 移動端CSS px自動轉換成rem
px2rem-loader設定尺寸稿,
lib-flexible保證
安裝依賴
npm i -D px2rem-loader
//動態計算根元素單位
npm i lib-flexible -S
lib-flexible會自動在html的head中添加一個meta name="viewport"的標簽,同時會自動設定html的font-size為螢屏寬度除以10,也就是1rem等于html根節點的font-size,
配合2.91的raw-loader使用
引入使用
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem = 75px
remPrecision: 8 // 轉換時的位數
}
}
]
},
]
}
}
在html內引入lib-flexible代碼,后期使用2.8.1介紹raw-loader引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script>
$( require('raw-loader!babel-loader./meta.html') )
; (function (win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('將根據已有的meta標簽來設定縮放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他設備下,仍舊使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function () {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function (e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function (e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function (d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function (d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));
</script>
</body>
</html>
2.8.1 靜態資源行內 raw-loader
資源行內的意義
代碼層面:
- 頁面框架的初始化腳本
- 上報相關打點
- css行內避免頁面閃動
請求層面:減少HTTP網路請求數
- 小圖片或者字體行內(url-loader)
HTML和JS行內 raw-loader(讀取檔案回傳一個string,插入合適的位置)
css行內
- 借助style-loader
- html-inline-css-webpack-plugin
安裝依賴
npm i raw-loader@0.5.1 -D
新建src/meta.html檔案,存盤相關配置,作為靜態資源等待引入
<meta name="keywords" content="CSDN博客,CSDN學院,CSDN論壇,CSDN直播">
<meta name="description"
content="CSDN是全球知名中文IT技術交流平臺,創建于1999年,包含原創博客、精品問答、職業培訓、技術論壇、資源下載等產品服務,提供原創、優質、完整內容的專業IT技術開發社區.">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="referrer" content="always">
修改src/index.html檔案(模板檔案不可有注釋,下列代碼注釋僅作解釋說明,不可正式使用)
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 引入元標簽 -->
<%=require('raw-loader!./meta.html')%>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script>
// 引入js靜態資源
<%=require("raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js")%>
</script>
</body>
</html>
2.9 多頁面應用
每一次頁面跳轉的時候,后臺服務器都會回傳一個新的html,這種型別的網站也就是多頁網站,也叫做多頁應用,
每個頁面對應一個entry、一個html-webpack-plugin
缺點:每次新增或洗掉頁面需要改wenpack配置
利用glob.sync
- entry: glob.sync(path: path.join(__dirname, './src/*/index.js'))
安裝依賴
npm i -D glob
檔案夾變動,并改動相關參考檔案路徑,
- src/index.html=>src/index/index.html
- src/index.js=>src/index/index.js
- src/helloworld.js=>src/index/helloworld.js
- src/search.html=>src/search/index.html
- src/search.js=>src/search/index.js
修改webpack入口檔案、htmlwebpackplugins配置
const glob = require('glob')
const setMPA = ()=>{
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'))
Object.keys(entryFiles).map((index)=>{
const entryFile = entryFiles[index]
const match = entryFile.match(/src\/(.*)\/index\.js/)
const pageName = match && match[1]
entry[pageName] = entryFile
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `src/${pageName}/index.html`), // 需要打包的檔案路徑
filename: `${pageName}.html`,
chunks: [pageName],
inject: true, // 設定為true,js css會自動注入html
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCss: true,
minifyJs: true,
removeComments: false
}
})
)
})
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins} = setMPA()
module.exports = {
entry: entry,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[chunkhash:8].js'
},
mode: 'production',
plugins: [
].concat(htmlWebpackPlugins)
}
2.10 使用source map
作用:運行代碼與源代碼之間完全不同,不方便除錯應用、定位錯誤資訊,source map就是用來映射轉換之后的代碼與源代碼之間的關系,
開發環境開啟,線上環境關閉(線上排查問題可以將sourcemap上傳到錯誤監控系統)
webpack支持12中source-map實作方式,效率和效果也不相同,

- eval:僅能定位檔案 不生成source-map
- eval-source-map:可以定位到檔案、行列資訊 生成source-map
- cheap-eval-source-map:只能定位到行 生成source-map
- cheap-module-eval-source-map:定位代碼與源代碼一模一樣
- 特征:eval是否使用eval執行代碼
- cheap-source-map:是否包含行資訊
- module是否能夠得到loader處理之前的源代碼
- inline-source-map以data-url形式嵌入代碼,會增大代碼體積,
- hidden-source-map開發第三方包使用,并沒有通過注釋的方式引入,所以瀏覽器看不到效果,
- nosources-source-map沒有源代碼,但同樣提供了行列資訊
webpack選擇合適的source-map選擇
- 開發模式:cheap-module-eval-source-map
- 發布打包:不使用sourcemap,避免暴露源代碼或者使用nosources-source-map
devtool: 'source-map'
2.11 提取公共頁面資源
思路:比如將react、react-dom基礎包通關cdn引入,不參與打包
方法:
- 使用html-webpack-externals-plugin
- 利用splitchunksplugin進行公共腳本分離
2.11.1 通過splitchunksplugin提取公共頁面資源
chunks引數說明
- async同步引入的庫進行分離(默認)
- initial同步引入的庫進行分離
- all 所有引入的庫進行分離(推薦)
test:匹配出需要分離的包
- minChunks:設定最小參考次數
- minuSize:分離包的體積大小
module.exports = {
optimization: {
splitChunks: {
minSize: 0,
cacheGroups: {
commons: {
name: "commons",
chunks: "all",
minChunks: 2
},
vendors: {
test: /(react|react-dom)/,
name: 'vendors',
chunks: 'all'
}
}
}
},
}
打包公共資源
新建common/index.js檔案
2.11.2 通過html-webpack-externals-plugin提取公共頁面資源
安裝依賴
npm i -D html-webpack-externals-plugin
const htmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
module.exports = {
new htmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js',
global: 'React'
},
{
module: 'react-dom',
entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',
global: 'ReactDOM'
}
]
})
]
}
2.12 tree shaking 搖樹優化(webpack內置)
使用 webpack默認支持,在.babelre里設定modules: false即可
production mode的情況下默認開啟,測驗時設定為none
要求:必須是es6的語法,CJS不支持
DCE( dead code elimination)
代碼不會被執行,不可到達
代碼執行的結果不會被用到
代碼只會影響死變數(只寫不讀)
Tree-shaking原理
利用es6模塊的特點:
- 只能作為模塊頂層陳述句出現
- import的模塊名只能死字串常量
- import binding是immutable的
代碼擦除:uglify階段洗掉無用代碼
2.13 scope hositing(webpack內置)
原理:將所有模塊的代碼按照參考順序放在一個函式作用域里,然后適當的重命名一些變數以防止變數名沖突
對比:通過scope hoisting可以減少函式宣告代碼和記憶體開銷
production mode的情況下默認開啟
在development模式下,需要手動打開該模塊
new webpack.optimize.ModuleConcatenationPlugin()
要求:必須是es6的語法,CJS不支持
2.14 代碼分割和動態加載
webpack可以將代碼庫分割成chunks(愉快),當代碼運行到需要它們的時候再進行加載,
適用的場景:
- 抽離相同代碼到一個共享塊
- 腳本懶加載,使得初始下載的代碼更小
懶加載JS腳本的方式
- CommonJS: require.ensure
- ES6: 動態import (需要babel轉換)
安裝依賴
npm i -D babel-plugin-syntax-dynamic-import
修改.babelra配置,使其支持動態import
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
]
}
修改src/search/search.js檔案
import React from 'react'
import ReactDOM from 'react-dom'
import '../css/index.less'
import fileImg from '../img/fileImg.png'
import '../../common/index'
class Search extends React.Component{
constructor() {
super(...arguments)
this.state = {
Text: null
}
}
loadComponent() {
import('./text.js').then((Text)=>{
console.log('Text', Text)
this.setState({
Text: Text.default
})
})
}
render() {
const { Text } = this.state
return <div className="search-txt ">search component
{Text? <Text></Text>: null}
測驗字體
<img src={fileImg} onClick={()=>this.loadComponent()}></img>
</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.15 webpack和ESLint結合
方案一:webpack與CI/CD集成
方案二:webpack與ESLint集成
2.15.1 webpack與CI/CD集成
本地開發階段增加precommit鉤子
2.15.2 webpack與ESLint集成 (打包)
安裝依賴
npm i -D eslint-config-airbnb eslint@^7.2.0 eslint-plugin-import@^2.22.1 eslint-plugin-jsx-a11y@^6.4.1 eslint-plugin-react@^7.21.5
npm i -D eslint-loader
npm i -D babel-eslint
修改webpack組態檔
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
'babel-loader',
'eslint-loader'
]
},
{
]
},
}
新建.eslintrc.js檔案,設定eslint規則
module.exports = {
"parser": "babel-eslint", //制定決議器
"extends": "airbnb"
"env": { //JavaScript 可在檔案中使用注釋來指定環境
"browser": true,
"node": true
}
"rules": {
"semi": "error" //結尾應當有分號
}
}
打包,然后demo識訓一堆報錯……
2.16 webpack打包庫和組件 terser-webpack-plugin
webpack除了可以用來打包應用,也可以用來打包js庫
實作一個大整數加法庫的打包,要求:
- 需要打包壓縮版和非壓縮版本
- 支持AMD/CJS/ESM模塊引入
建立一個新的專案檔案夾large-number
初始化專案,并安裝依賴
npm init -y
npm i webpack webpack-cli -D
新建src/index.js檔案,存放大整數加法
export default function add(a, b){
let i = a.length - 1
let j = b.length - 1
let carry = 0 // 進位
let res = ''
while ( i >= 0 || j>=0 ){
let x = 0; // a某一位的值
let y = 0; // b某一位的值
let sum
if( i >= 0 ){
x = a[i] - '0'
i --
}
if( j >= 0 ){
y = b[j] - '0'
j --
}
sum = x + y + carry
if( sum >= 10 ){
carry = 1
sum -= 10
}else {
carry = 0
}
res = sum + res
}
if( carry ){
res = carry + res
}
return res
}
修改打包檔案
module.exports = {
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNUmber', // 打包后庫的的名字
libraryTarget: 'umd', // 打包類別庫的發布格式,這里使用UMD
libraryExport: 'default' // 對外暴露default屬性,就可以直接呼叫default里的屬性
}
}
添加打包命令
"build": "webpack"
如果就此打包的話,'large-number'、'large-number.min'均會被壓縮,需要進一步改動
npm i -D terser-webpack-plugin
修改webpack配置,設定使用環境、使用 terser-webpack-plugin插件
const terserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'none', // 避免全部自動壓縮,打包出未壓縮檔案
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNUmber', // 打包后庫的的名字
libraryTarget: 'umd', // 打包類別庫的發布格式,這里使用UMD
libraryExport: 'default' // 對外暴露default屬性,就可以直接呼叫default里的屬性
},
optimization: {
minimize: true,
minimizer: [new terserWebpackPlugin({
include: /\.min\.js$/
})],
},
}
新建index.js檔案
if(process.env.NODE_ENV === 'production'){
module.exports = require('./dist/large-number.min.js')
}else {
module.exports = require('./dist/large-number.js')
}
發布npm包
npm login
npm publish
在01project專案中使用打包
npm i -D gu-large-number
直接使用
import '../../common/index'
import largeNumber from 'gu-large-number'
export function helloworld(){
return 'hello awebpack'+largeNumber(10,20)
}
2.17 功能模塊設計和目錄結構:
2.17.1 基礎配置
- 資源決議
- 樣式增強
- 目錄清理
- 多頁面打包
- 命令列打包和優化
- css提取成一個單獨的檔案
2.17.2 開發階段配置
- 代碼熱更新
- sourcemap
通過webpack-merge
安裝依賴
npm i -D webpack-merge
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const webpack = require('webpack')
const devConfig = {
mode: 'development',
plugins:[
new webpack.HotModuleReplacementPlugin(),
],
devServer: { // 專門為webpack-dev-server指定相關配置選項
contentBase: './dist',
hot: true,
stats: 'error-only'
},
devtool: 'cheap-module-eval-source-map'
}
module.exports = merge(baseConfig, devCOnfig)
2.17.2 生產階段配置
- 代碼壓縮
- 檔案修改
- tree-shaking
- scope hositing
- 速度優化
- 提及優化
安裝依賴
npm i -D eslint babel-eslint eslint-config-airbnb-base
新建.eslintrc.js組態檔,配置eslint規則
module.exports = {
"parser": "babel-eslint", //制定決議器
"extends": "airbnb-base"
"env": { //JavaScript 可在檔案中使用注釋來指定環境
"browser": true,
"node": true
}
"rules": {
// "semi": "error" //結尾應當有分號
}
}
增加運行命令
"eslint": "eslint --fix"
三、分析webpack
3.1、使用webpack內置的stats
在package.json中設定新命令
"build:stats": "webpack --config webpack.prod.js --json > stats.json",
啟動,得到一個stats.json檔案,著實不直觀……
3.2、速度分析:使用speed-measure-webpack-plugin
可以看到每個loader和插件執行耗時
安裝依賴
npm i -D speed-measure-webpack-plugin
webpack引入
const speedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
const smp = new speedMeasureWebpackPlugin()
module.exports = smp.wrap({
})

報錯:
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem = 75px
remPrecision: 8 // 轉換時的位數
}
}
]
},
ERROR in ./src/css/index.less
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started
at Object.pitch (/Users/gujianxiang/Demo/study/webpack/01project/node_modules/mini-css-extract-plugin/dist/loader.js:43:14)
@ ./src/search/index.js 25:0-27
暫未解決……
3.3 體積分析:使用webpack-bundle-analyzer
可以分析哪些問題?
- 依賴的第三方模塊檔案大小
- 業務組件大小
安裝依賴
npm i -D webpack-bundle-analyzer
修改組態檔
const webpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [
new webpackBundleAnalyzer(),
],
}
3.3 Babel-polyfill 的作用
Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域物件,以及一些定義在全域物件上的方法(比如Object.assign)都不會轉碼,舉個栗子,ES6在Array物件上新增了Array.from方法,Babel就不會轉碼這個方法,如果想讓這個方法運行,必須使用babel-polyfill,為當前環境提供一個墊片,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/303066.html
標籤:其他
上一篇:Vue結合element ui 實作圖片上傳可預覽,可洗掉,以base64字串上傳到服務器
下一篇:ES6總結下
