在咱們平時的開發中,會常用到webpack,但不少時候咱們都不作任何配置(webpack4)也能夠進行開發,可是默認的配置真的夠咱們使用了嗎?因此本文將帶你開啓webpack的大門。javascript
學習webpack前先讓咱們一塊兒學習下必要的輔助概念css
在此以前咱們有必要了解下webpack中會使用到的NodeJS路徑知識:NodeJS路徑知識html
Entry 用來指定 webpack 的打包⼊口html5
單⼊⼝:entry 是⼀個字符串java
module.exports = {
entry: './src/index.js'
};
複製代碼
多⼊⼝:entry 是⼀個對象node
module.exports = {
entry: {
index: './src/index.js',
manager: './src/manager.js'
}
};
複製代碼
Output ⽤來告訴 webpack 如何將編譯後的⽂件輸出到磁盤的指定路徑react
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js’, path: __dirname + '/dist' } }; 複製代碼
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
複製代碼
經過[name]佔位符確保⽂件名稱的惟⼀jquery
自己是一個函數,接受源文件做爲參數,返回轉換的結果。webpack
babel-loader, 將es6+語法轉換爲es3/es5語法
css-loader,
style-loader,
scss-loader,
less-loader,
postcss-loader,
file-loader, 進行圖片字體等資源的打包
ts-loader, 將ts轉換爲js
raw-loader 將文件以字符串形式導入
...
複製代碼
const path = require('path');
module.exports = {
output: {
filename: 'bundle.js'
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
};
複製代碼
test 指定匹配規則ios
use 指定使⽤的 loader 名稱
插件⽤於 bundle ⽂件的優化,資源管理和環境變量注⼊ 做⽤於整個構建過程
CleanWebpackPlugin 清理構建目錄
MiniCssExtractPlugin 將css從bundle文件裏提取爲一個獨立的css文件
htmlWebpackPlugin 建立html去承載輸出的bundle文件
UglifyWebpackPlgin 壓縮js 去除console等指定語句
...
複製代碼
const path = require('path');
module.exports = {
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({template:'./src/index.html'})
]
};
複製代碼
全部的插件都應放到plugins數組裏
在此以前咱們先安裝下必須依賴:
npm install webpack webpack-cli -D
複製代碼
安裝所需依賴 npm i @babel/core @babel/preset-env babel-loader -D
webpack.config.js配置以下:
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
]
};
複製代碼
再增長ES6的babel preset配置
babel的配置⽂件是:.babelrc[項目根目錄下新建]
{
"presets": [
"@babel/preset-env" //能夠根據配置的目標瀏覽器或者運行環境來自動將ES2015+的代碼轉換爲es5。
]
}
複製代碼
安裝所需依賴
npm i @babel/preset-react -D
配置以下:
babel的配置⽂件是:.babelrc[項目根目錄下新建]
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": []
}
複製代碼
npm install style-loader css-loader less-loader -D
複製代碼
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
]
},
複製代碼
css-loader ⽤於加載 .css ⽂件,而且轉換成 commonjs 對象
style-loader 將樣式經過 <style>
標籤插⼊到 head 中
style-loader配置:
options: {
insertAt: 'top', // 樣式插入到 <head>
singleton: true, //將全部的style標籤合併成一個
}
複製代碼
less-loader 將less語法轉換爲css語法
注意: loader的執行順序的從下到上,從右到左
npm install url-loader file-loader -D
複製代碼
module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: 'file-loader'
}
]
},
複製代碼
file-loader ⽤於處理字體⽂件
url-loader 能夠用來處理圖⽚和字體 能夠設置limit將較⼩資源⾃動 base64
url-loader中用到了file-loader
⽂件監聽是在發現源碼發⽣變化時,⾃動從新構建出新的輸出⽂件。
webpack 開啓監聽模式,有兩種⽅式:
·啓動 webpack 命令時,帶上 --watch 參數 [推薦]
·在配置 webpack.config.js 中設置 watch: true
在package.json中配置
{
"name": "hello-webpack",
"main": "index.js",
"scripts": {
"watch": "webpack --watch",
"build": "webpack --config webpack.prod.js",
},
}
複製代碼
npm install webpack-dev-server -D
複製代碼
package.json中的scripts對象下配置:
{
"scripts": {
"dev": "webpack-dev-server --open --mode development"
},
}
複製代碼
--open表明自動打開瀏覽器
webpack-dev-server 不刷新瀏覽器 不輸出⽂件,⽽是放在內存中
使⽤ HotModuleReplacementPlugin插件(webpack內置插件)可使瀏覽器自動刷新
webpack.config.js中配置:
const webpack = require('webpack');
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
devServer:{
hot: true,
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")], //contentBase 用於配置提供額外靜態文件內容的目錄,以前提到的 publicPath 是配置構建好的結果以什麼樣的路徑去訪問
proxy: {
'/api': {
target: "http://localhost:3000", // 將 URL 中帶有 /api 的請求代理到本地的 3000 端口的服務上
pathRewrite: { '^/api': '' }, // 把 URL 中 path 部分的 `api` 移除掉
},
},
before(app) {
app.get('/some/path', function(req, res) { // 當訪問/some/path 路徑時,返回自定義的 json 數據
res.json({ custom: 'response' })
})
}
}
複製代碼
before 在 webpack-dev-server 靜態資源中間件處理以前,能夠用於攔截部分請求返回特定內容,或者實現簡單的數據 mock。
after 在 webpack-dev-server 靜態資源中間件處理以後,比較少用到,能夠用於打印日誌或者作一些額外處理。
Webpack Compile: 將 JS 編譯成 Bundle
HMR Server: 將熱更新的⽂件輸出給 HMR Rumtime
Bundle server: 提供⽂件在瀏覽器的訪問 (好比localhost:8080/Bundle.js)
HMR Rumtime: 在開發階段的打包階段會被注⼊到瀏覽器端的bundle.js中,瀏覽器端的bundle.js會和瀏覽器創建一個鏈接,一般是一個websocket,這樣就能夠更新文件的變化,當收到文件的一些變化消息時會自動更新文件
bundle.js: 構建輸出的⽂件
⽂件哈希值就是打包後輸出的⽂件名的後綴
Hash:和整個項⽬的構建相關,只要項⽬⽂件有修改,整個項⽬構建的 hash 值就會更改
Chunkhash:和 webpack 打包的 chunk 有關,不一樣的 entry 會⽣成不一樣的 chunkhash 值
Contenthash:根據⽂件內容來定義 hash ,⽂件內容不變,則 contenthash 不變
設置 output 的 filename,使⽤ [chunkhash]
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
}
複製代碼
注意: chunkhash沒法和熱更新一塊兒使用
設置 MiniCssExtractPlugin 的 filename,
使⽤ [contenthash]
npm install mini-css-extract-plugin -D
複製代碼
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
new MiniCssExtractPlugin({
filename: `[name][contenthash:8].css`
});
]
複製代碼
若是想把css提取出來,那麼style-loader就不能用了,由於兩個是互斥的,因此咱們能夠這樣寫:
module: {
rules: [
{
test: /\.css$/,
use: [
- 'style-loader',
+ MiniCssExtractPlugin.loader
'css-loader'
]
},
{
test: /\.less$/,
use: [
- 'style-loader',
+ MiniCssExtractPlugin.loader
'css-loader',
'less-loader'
]
},
]
},
複製代碼
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader’, options: { name: 'img/[name][hash:8].[ext] ' } }] } ] 複製代碼
[ext] 資源名後綴
[name] 文件名稱
[path] 文件的相對路徑
[folder] 文件所在的文件夾
[contenthash] 文件的內容hash 默認md5生成
[hash] 文件內容的hash 默認md5生成
HTML 壓縮
CSS 壓縮
JS 壓縮
npm install uglifyjs-webpack-plugin -D
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
plugins: [
new UglifyJsPlugin()
]
複製代碼
npm install optimize-css-assets-webpack-plugin cssnano -D
複製代碼
使⽤ optimize-css-assets-webpack-plugin, 同時使⽤ cssnano[預處理器]
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano’) }) ] 複製代碼
npm install html-webpack-plugin -D
複製代碼
使用html-webpack-plugin,設置壓縮參數
webpack.config.js中
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '/dist/index.html’), filename: 'index.html’,
chunks: ['index’], inject: true, minify: { html5: true, collapseWhitespace: true, //去除空格 preserveLineBreaks: false, //去除換行 minifyCSS: true, minifyJS: true, removeComments: false //去除註釋 } }) ] 複製代碼
避免構建前每次都須要⼿動刪除 dist
使⽤ clean-webpack-plugin
默認會刪除 output 指定的輸出⽬錄
npm install clean-webpack-plugin -D
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin(),
]
複製代碼
IE: Trident(-ms) 火狐: Geko(-moz) 谷歌: Webkit(-webkit) 歐鵬:Presto(-o)
npm install postcss-loader autoprefixer -D
複製代碼
module: {
rules: [
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')({
browsers: ['last 2 version', '>1%', 'ios 7']
})
]
}
},
]
},
]
},
複製代碼
rem 是什麼?
W3C 對 rem 的定義: font-size of the root element
rem 和 px 的對⽐:
rem 是相對單位
px 是絕對單位
⻚⾯渲染時計算根元素的 font-size 值,可使⽤⼿淘的lib-flexible庫
npm install px2rem-loader -D
npm i lib-flexible raw-loader@0.5.1 -S
module: {
rules: [
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')({
browsers: ['last 2 version', '>1%', 'ios 7']
})
]
}
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75,
remPrecision: 8 //轉換好rem的小數點位數
}
}
]
}
]
}
複製代碼
index.html中使用raw-loader內聯lib-flexible
<script> ${require('raw-loader!babel-loader!../node_modules/lib-flexible')} </script>
複製代碼
<script> ${require(' raw-loader!babel-loader!. /meta.html')} </script>
複製代碼
每⼀次⻚⾯跳轉的時候,後臺服務器都會給返回⼀個新的 html ⽂檔, 這種類型的⽹站也就是多⻚⽹站,也叫作多⻚應⽤。
多⻚⾯打包通⽤⽅案: 動態獲取 entry 並設置 html-webpack-plugin
利用glob
庫
webpack.config.js中編寫以下代碼:
npm install glob -D
const glob = require('glob');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
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({
inlineSource: '.css$',
template: path.join(__dirname, `src/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: ['vendors', pageName],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
);
});
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
entry: entry,
plugins: [].concat(htmlWebpackPlugins)
}
複製代碼
module.exports = {
plugins: [
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true), // const PRODUCTION = true
VERSION: JSON.stringify('5fa3b9'), // const VERSION = '5fa3b9'
BROWSER_SUPPORTS_HTML5: true, // const BROWSER_SUPPORTS_HTML5 = 'true'
TWO: '1+1', // const TWO = 1 + 1,
CONSTANTS: {
APP_VERSION: JSON.stringify('1.1.2') // const CONSTANTS = { APP_VERSION: '1.1.2' }
}
}),
],
}
複製代碼
有了上面的配置,就能夠在應用代碼文件中,訪問配置好的變量了,如:
console.log("Running App version " + VERSION);
if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");
複製代碼
若是配置的值是字符串,那麼整個字符串會被當成代碼片斷來執行,其結果做爲最終變量的值,如上面的 "1+1",最後的結果是 2
若是配置的值不是字符串,也不是一個對象字面量,那麼該值會被轉爲一個字符串,如 true,最後的結果是 'true'
若是配置的是一個對象字面量,那麼該對象的全部 key 會以一樣的方式去定義
這樣咱們就能夠理解爲何要使用 JSON.stringify() 了,由於 JSON.stringify(true) 的結果是 'true',JSON.stringify("5fa3b9") 的結果是 "5fa3b9"。
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
// ...
plugins: [
new CopyWebpackPlugin([
{ from: 'src/file.txt', to: 'build/file.txt', }, // 顧名思義,from 配置來源,to 配置目標路徑
{ from: 'src/*.ico', to: 'build/*.ico' }, // 配置項可使用 glob
// 能夠配置不少項複製規則
]),
],
}
複製代碼
能夠理解爲更方便的引入,例如jquery,lodash
new webpack.ProvidePlugin({
_: 'lodash',//import _ from lodash
$: 'jquery'//import $ from jquery
})
複製代碼
能夠直接使用 webpack.IgnorePlugin 來獲取。
這個插件用於忽略某些特定的模塊,讓webpack不把這些指定的模塊打包進去。例如咱們使用moment.js,直接引用後,裏邊有大量的 i18n的代碼,致使最後打包出來的文件比較大,而實際場景並不須要這些 i18n 的代碼,這時咱們可使用 IgnorePlugin 來忽略掉這些代碼文件,配置以下:
module.exports = {
// ...
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
}
複製代碼
IgnorePlugin 配置的參數有兩個,第一個是匹配引入模塊路徑的正則表達式,第二個是匹配模塊的對應上下文,即所在目錄名。
devtool:
source-map:
開發: cheap-module-eval-source-map
生產: hidden-source-map
思路:將 react、react-dom,axios,element-ui 基礎包經過 cdn 引⼊,不打⼊ bundle 中
npm install html-webpack-externals-plugin -D
使⽤ html-webpackexternals-plugin
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
plugins:[
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'cdn.com/react.min.js',
global: 'React',
},
{
module: 'axios',
entry: 'cdn.com/axios.min.js',
global: 'Axios',
},
]
}),
]
複製代碼
SplitChunksPlugin是Webpack4 內置的,替代webpack3的CommonsChunkPlugin插件
async 異步引⼊的庫進⾏分離(默認)
initial 同步引⼊的庫進⾏分離
all 全部引⼊的庫進⾏分離(推薦)
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,//抽離的公共包最小的大小
maxSize: 0, //抽離的公共包最大的大小
minChunks: 1, //一段代碼多處都使用的次數 若是大於這裏的次數就抽離成公共的文件
maxAsyncRequests: 5,
maxInitialRequests: 3,//瀏覽器同時請求的異步js的數量
name: true,
cacheGroups: {
vendors: {
test: /(axios|react)/,
priority: -10,
minChunks: 1
}
}
}
}
複製代碼
概念:1 個模塊可能有多個⽅法,只要其中的某個⽅法使⽤到了,則整個⽂件都會被打到bundle ⾥⾯去,tree shaking 就是隻把⽤到的⽅法打⼊ bundle ,沒⽤到的⽅法會在uglify 階段被擦除掉。
使⽤:webpack4 默認⽀持,在 .babelrc ⾥設置 modules: false 便可 要求:必須是 ES6 的語法,CommonJS 的⽅式不⽀持.
production mode的狀況下默認開啓
treeShaking的狀況:
代碼執⾏的結果不會被⽤到
代碼不會被執⾏,不可到達
代碼只會影響死變量(只寫不讀)
複製代碼
Tree-shaking 原理
利⽤ ES6 模塊的特色:
只能做爲模塊頂層的語句出現
import 的模塊名只能是字符串常量
import binding 是 immutable的
代碼擦除: uglify 階段刪除⽆⽤代碼
若是代碼中有反作用則tree-shaking失效
能夠在package.json中配置sideEffect:[] 好比babel-polyfill
⼤量做⽤域包裹代碼,致使體積增⼤(模塊越多越明顯)
運⾏代碼時建立的函數做⽤域變多,內存開銷變⼤
結論:
被 webpack 轉換後的模塊會帶上⼀層包裹
import 會被轉換成 __webpack_require
分析: 打包出來的是⼀個 IIFE (匿名閉包)
modules 是⼀個數組,每⼀項是⼀個模塊初始化函數
__webpack_require ⽤來加載模塊,返回 module.exports
原理:將全部模塊的代碼按照引⽤順序放在⼀個函數做⽤域⾥,而後適當的重命名⼀些變量以防⽌變量名衝突
必須是 ES6 語法,CJS 不⽀持
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
複製代碼
webpack 有⼀個功能就是將你的代碼庫分割成chunks(語塊),當代碼運⾏到須要它們的時候再進⾏加載。
CommonJS:require.ensure
ES6:動態 import(⽬前尚未原⽣⽀持,須要 babel 轉換)
安裝 babel 插件 ES6:動態 import(⽬前尚未原⽣⽀持,須要 babel 轉換)
在.babelrc中配置:
npm install @babel/plugin-syntax-dynamic-import --save-dev
{
"plugins": ["@babel/plugin-syntax-dynamic-import"],
}
複製代碼
代碼中的使用:
loadComponent() {
import('./text.js').then((Text) => {
this.setState({
Text: Text.default
});
});
}
複製代碼
這樣的話text.js在打包時就會被自動拆分爲一個單獨的文件,當咱們調用這個方法時才進行加載,也算是一個優化手段
webpack 除了能夠⽤來打包應⽤,也能夠⽤來打包 js 庫
實現⼀個加法庫的打包
須要打包壓縮版和⾮壓縮版本
⽀持 AMD/CJS/ESM 模塊引⼊
//src下index.js
export default function add(a, b) {
return a + b;
}
複製代碼
只對.min 壓縮可使用TerserPlugin插件進行匹配
npm install terser-webpack-plugin -D
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNumber',
libraryTarget: 'umd',
libraryExport: 'default'
},
mode: 'none',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
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');
}
複製代碼
package.json 的 main 字段爲 index.js
而後webpack打包就能夠了
思路:
服務端 使⽤ react-dom/server 的 renderToString ⽅法將 React 組件渲染成字符串,服務端路由返回對應的模板
客戶端
打包出針對服務端的組件
<!--search.html-->
<body>
<div id="root"><!--HTML_PLACEHOLDER--></div>
</body>
複製代碼
這裏使用註釋進行佔位
const fs = require('fs');
const path = require('path');
const express = require('express');
const { renderToString } = require('react-dom/server');
const SSR = require('../dist/search-server');
const template = fs.readFileSync(path.join(__dirname, 'search.html'), 'utf-8');
const server = (port) => {
const app = express();
app.use(express.static('dist'));
app.get('/search', (req, res) => {
const html = renderMarkup(renderToString(SSR));
res.status(200).send(html);
});
app.listen(port, () => {
console.log('Server is running on port:' + port);
});
};
server(process.env.PORT || 3000);
const renderMarkup = (str) => {
return template.replace('<!--HTML_PLACEHOLDER-->', str)
}
複製代碼
SSR優化
減小首屏須要的數據量,剔除冗餘數據和請求;
控制好緩存,對數據/頁面進行合理的緩存;
頁面的請求使用流的形式進行傳遞;
使⽤ friendly-errors-webpack-plugin
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
plugins: [
new FriendlyErrorsWebpackPlugin()
],
stats: 'errors-only'
複製代碼
plugins:[
new FriendlyErrorsWebpackPlugin(),
function() {
this.hooks.done.tap('done', (stats) => {
if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)
{
console.log('錯誤上報');
process.exit(1);
}
})
}
]
複製代碼
compiler 在每次構建結束後會觸發 done 這 個 hook
webpack4中是this.hooks.done.tap webpack3中是this.plugin
0 表示成功完成,回調函數中,err 爲 null
⾮ 0 表示執⾏失敗,回調函數中,err 不爲 null,err.code 就是傳給 exit 的數字
升級補丁版本號:npm version patch //通常是修改了bug
升級小版本號:npm version minor //通常是發佈了feture
升級大版本號:npm version major //通常是重大更新
複製代碼
npm install webpack-bundle-analyzer -D
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins:[
new BundleAnalyzerPlugin(),
]
複製代碼
構建完成後會在 8888 端口展現大小
通常推薦使用高版本的node和webpack,由於他們內部本身作了優化
V8 帶來的優化(for of 替代 forEach、Map 和 Set 替代 Object、includes 替代 indexOf)
默認使用更快的 md4 hash 算法
webpack AST 能夠直接從 loader 傳遞給 AST,減小解析時間
使用字符串方法替代正則表達式
npm install cache-loader thread-loader -D
複製代碼
使用 thread-loader 解析資源
原理:每次 webpack 解析一個模塊,thread-loader 會將它及它的依賴分配給 worker 線程
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 3
}
},
'cache-loader',//使用cacheDirectory,能夠緩存編譯結果,避免屢次重複編譯;
'babel-loader',
]
},
]
}
複製代碼
方式一:使用 parallel-uglify-plugin 插件
plugins:[
new ParallelUglifyPlugin({
uglifyJS:{
output:{
beautify:false,
comments:false
},
compress:{
warning:false,
drop_console:true,
collapse_vars:true,
reduce_vars:true
}
}
})
]
複製代碼
方式二:uglifyjs-webpack-plugin 開啓 parallel 參數
plugins:[
new UglifyJsPlugin({
uglifyOptions:{
warning:false
},
parallel:true
})
]
複製代碼
方法三:terser-webpack-plugin 開啓 parallel 參數
optimization:{
minimizer:[
new TerserPlugin({
parallel:4
})
]
}
複製代碼
目的:儘量的少構建模塊
好比 babel-loader 不解析 node_modules
rules: [
{
test: /\.js$/,
exclude: 'node_modules',//忽略node_moudles,避免編譯第三方庫中已經被編譯過的代碼;
use: [
'babel-loader',
]
}
]
複製代碼
優化 resolve.extensions 配置
合理使用 alias
resolve: {
alias: {
'components': path.resolve(__dirname, './src/components'),
'util': path.resolve(__dirname, './src/util'),
},
extensions: ['.js']
}
複製代碼
在 webpack 中,咱們須要使用的 loader 是在 module.rules 下配置的,webpack 配置中的 module 用於控制如何處理項目中不一樣類型的模塊。
除了 module.rules 字段用於配置 loader 以外,還有一個 module.noParse 字段,能夠用於配置哪些模塊文件的內容不須要進行解析。對於一些不須要解析依賴(即無依賴) 的第三方大型類庫等,能夠經過這個字段來配置,以提升總體的構建速度。
使用 noParse 進行忽略的模塊文件中不能使用 import、require、define 等導入機制。
module.exports = {
// ...
module: {
noParse: /jquery|lodash/, // 正則表達式
// 或者使用 function
noParse(content) {
return /jquery|lodash/.test(content)
},
}
}
複製代碼
使用:配置 image-webpack-loader
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
}
複製代碼
PurifyCSS: 遍歷代碼,識別已經用到的 CSS class 這個須要和 mini-css-extract-plugin 配合使用
也就是說先提取爲css文件而後再使用PurifyCSS
npm install purgecss-webpack-plugin
const PurgecssPlugin = require('purgecss-webpack-plugin');
plugins:[
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
})
]
複製代碼
咱們可使用動態 Polyfill -> Polyfill Service
原理:識別 User Agent,下發不一樣的 Polyfill
如何使用動態 Polyfill service
polyfill.io 官方提供的服務:(引入到index.html中)
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
複製代碼
Scope Hoisting
Tree-shaking
公共資源分離
圖片壓縮
動態 Polyfill
//raw-loader.js
module.exports = function(source) {
const json = JSON.stringify(source)
.replace('foo', '')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
return `export default ${json}`;
}
複製代碼
咱們這個loader是把指定文件中的foo字符替換爲空的 使用loader-runner測試loader是否正常工做:
npm install loader-runner -D
loader-runner用法具體能夠去github查看
const { runLoaders } = require('loader-runner');
const fs = require('fs');
const path = require('path');
runLoaders({
resource: path.join(__dirname, './src/demo.txt'),//資源路徑
loaders: [
{
loader: path.join(__dirname, './src/raw-loader.js'),//指定loader路徑
}
],
context: {
minimize:true
},
readResource: fs.readFile.bind(fs)
}, (err, result) => {
err ? console.log(err) : console.log(result);
});
複製代碼
npm install loader-utils -D
複製代碼
經過 loader-utils 的 getOptions 方法獲取
const loaderUtils = require("loader-utils");
module.exports = function(content) {
const { name } = loaderUtils.getOptions(this);
};
複製代碼
loader 內直接經過 throw 拋出,經過 this.callback 傳遞錯誤
this.callback(
err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any
);
複製代碼
經過 this.async 來返回一個異步函數
第一個參數是 Error,第二個參數是處理的結果
module.exports = function(input) {
const callback = this.async();
this.cacheable(false)
// No callback -> return synchronous results
// if (callback) { ... }
callback(null, input + input);
};
複製代碼
this.async()第一個參數也是err對象,第二個參數是數據
webpack 中默認開啓 loader 緩存
可使用 this.cacheable(false) 關掉緩存
緩存條件: loader 的結果在相同的輸入下有肯定的輸出
有依賴的 loader 沒法使用緩存
經過 this.emitFile 進行文件寫入
const loaderUtils = require("loader-utils");
module.exports = function(content) {
const url = loaderUtils.interpolateName(this, "[hash].[ext]", {
content});
this.emitFile(url, content);
const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;
return `export default ${path}`;
};
複製代碼
interpolateName方法是用於替換佔位符,__webpack_public_path__是webpack的全局變量
插件沒有像 loader 那樣的獨立運行環境, 只能在 webpack 裏面運行
const path = require('path');
const MyPlugin = require('./plugins/myPlugin');//插件開發階段的路徑
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
mode: 'production',
plugins: [
new MyPlugin({
name: 'myname'
})
]
}
複製代碼
module.exports = class MyPlugin { //MyPlugin是插件名
constructor(options){
this.options = options
}
apply(compiler){ //必須是apply
console.log('個人插件執行了');
console.log('個人插件配置項',this.options)
}
}
複製代碼
參數校驗階段能夠直接 throw 的方式拋出
throw new Error(「 Error Message」)
複製代碼
若是已經進入hooks邏輯,那麼能夠經過 compilation 對象的 warnings 和 errors 接收
compilation.warnings.push("warning");
compilation.errors.push("error");
複製代碼
文件生成階段webpack會調用emit這個hooks,因此咱們能夠監聽emit階段進行操做
文件寫入須要使用 [webpack-sources]((www.npmjs.com/package/web…)
const { RawSource } = require("webpack-sources");
module.exports = class DemoPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const { name } = this.options;
compiler.plugin("emit", (compilation, cb) => {
compilation.assets[name] = new RawSource("demo");//demo爲文件內容,name爲文件的名稱
cb();
});
}
};
複製代碼
相信本文應該已經涵蓋了webpack最常使用的點以及如何進行優化,感興趣的小夥伴能夠在文章下方進行交流哦