HappyPick
開啟子執行緒打包,加快打包速度,使用id標識
rules: [
{
test:/\.css/,
use: 'Happypack/loader?id=css'
},
{
test: /\.js$/,
exclude:/node_modules/,
include:path.resolve(__dirname,'src'),
use: 'Happypack/loader?id=js'
}
]
// plugins
new HappyPack({
id:'css',
use:['style-loader','css-loader']
}),
new HappyPack({
id:'js',
use: [{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react' // react jsx
]
}
}]
}),
複製程式碼
libraryTarget 和 library
當用 Webpack 去構建一個可以被其他模組匯入使用的庫時需要用到它們
output.libraryTarget
配置以何種方式匯出庫output.library
配置匯出庫的名稱 output.libraryTarget 是字串的列舉型別,支援以下配置
var(預設)
編寫的庫將通過var被賦值給通過library指定名稱的變數。
// bundle.js
var calculator=(function (modules) {}({})
// index.html
<script src="bundle.js"></script>
<script>
let ret = calculator.add(1,2);
console.log(ret);
</script>
複製程式碼
commonjs(exports)
exports["calculator"] = (function (modules) {}({})
require('npm-name')['calculator'].add(1,2);
複製程式碼
commonjs2
module.exports = (function (modules) {}({})
require('npm-name').add();
複製程式碼
this
this["calculator"]= (function (modules) {}({})
this.calculator.add();
複製程式碼
window
window["calculator"]= (function (modules) {}({})
window.calculator.add();
複製程式碼
global
global["calculator"]= (function (modules) {}({})
global.calculator.add();
複製程式碼
DLLPlugin
把基礎模組獨立出來打包到單獨的動態連線庫裡.
定義DLL(DLLPlugin,注意output.library和DLLPlugin的name需要保持一致)
const path=require('path');
const DllPlugin=require('webpack/lib/DllPlugin');
module.exports={
entry: {
react:['react','react-dom']
},// 把 React 相關模組的放到一個單獨的動態連結庫
output: {
path: path.resolve(__dirname,'dist'),// 輸出的檔案都放到 dist 目錄下
filename: '[name].dll.js',//輸出的動態連結庫的檔名稱,[name] 代表當前動態連結庫的名稱
library: '_dll_[name]',//存放動態連結庫的全域性變數名稱,例如對應 react 來說就是 _dll_react
},
plugins: [
new DllPlugin({
// 動態連結庫的全域性變數名稱,需要和 output.library 中保持一致
// 該欄位的值也就是輸出的 manifest.json 檔案 中 name 欄位的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述動態連結庫的 manifest.json 檔案輸出時的檔名稱
path: path.join(__dirname, 'dist', '[name].manifest.json')
})
]
}
複製程式碼
使用動態連結庫文
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
plugins: [
new DllReferencePlugin({
manifest:require('./dist/react.manifest.json')
})
]
複製程式碼
html中使用
需要將dll檔案先提前引入
<script src="react.dll.js"></script>
<script src="bundle.js"></script>
複製程式碼
CDN
-
HTML檔案不快取,放在自己的伺服器上,關閉自己伺服器的快取,靜態資源的URL變成指向CDN伺服器的地址
-
靜態的JavaScript、CSS、圖片等檔案開啟CDN和快取,並且檔名帶上HASH值
- 帶上 Hash 值的原因是檔名會隨著檔案內容而變化,只要檔案發生變化其對應的 URL 就會變化,它就會被重新下載,無論快取時間有多長
-
為了並行載入不阻塞,把不同的靜態資源分配到不同的CDN伺服器上
- 多個域名後會增加域名解析時間
- 可以通過在 HTML HEAD 標籤中 加入去預解析域名,以降低域名解析帶來的延遲
-
接入cdn(publicPath)
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]_[hash:8].js',
publicPath: 'http://img.zhufengpeixun.cn'
},
複製程式碼
Tree Shaking
Tree Shaking 可以用來剔除JavaScript中用不上的死程式碼。它依賴靜態的ES6模組化語法,例如通過import和export匯入匯出。
不要編譯ES6模組
- 要讓 Tree Shaking 正常工作的前提是交給 Webpack 的 JavaScript 程式碼必須是採用 ES6 模組化語法的
- "modules": false 的含義是關閉 Babel 的模組轉換功能,保留原本的 ES6 模組化語法。
use:[{
loader: 'babel-loader',
options: {
presets:[['@babel/preset-env',{modules: false }],'@babel/preset-react']
}
}]
複製程式碼
顯示未使用的匯出例項
npx webpack --display-used-exports
複製程式碼
剔除用不上的程式碼(UglifyJS處理)
webpack --display-used-exports --optimize-minimize
複製程式碼
啟動壓縮
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,//啟動快取
parallel: true,//啟動並行壓縮
//如果為true的話,可以獲得sourcemap
sourceMap: true // set to true if you want JS source maps
}),
//壓縮css資源的
new OptimizeCSSAssetsPlugin({})
]
}
複製程式碼
提取公共程式碼
大網站有多個頁面,每個頁面由於採用相同技術棧和樣式程式碼,會包含很多公共程式碼,如果都包含進來會有問題
optimization: {
splitChunks: {
cacheGroups: {
commons: { // 頁面之間的公共程式碼
chunks: "initial",
minChunks: 2,//最小重複的次數
minSize: 0//最小提取位元組數
},
vendor: { // 第三方庫
test: /node_modules/,
chunks: "initial", // 先抽離公共的第三方庫
name: "vendor",
}
}
}
}
複製程式碼
Scope Hoisting
Scope Hoisting 可以讓 Webpack 打包出來的程式碼檔案更小、執行的更快, 它又譯作 "作用域提升",是在 Webpack3 中新推出的功能。
// hello.js
export default 'Hello';
// index.js
import str from './hello.js';
console.log(str);
// main.js
var n = name = "Hello";
console.log(n)
複製程式碼
動態匯入和懶載入(import匯入返回一個promise)
使用者當前需要用什麼功能就只載入這個功能對應的程式碼,也就是所謂的按需載入 在給單頁應用做按需載入優化時,一般採用以下原則:
- 對網站功能進行劃分,每一類一個chunk
- 對於首次開啟頁面需要的功能直接載入,儘快展示給使用者
- 某些依賴大量程式碼的功能點可以按需載入
- 被分割出去的程式碼需要一個按需載入的時機
// handler.js
module.exports=function () {
alert('你點我啦!');
}
// index.js
document.querySelector('#clickBtn').addEventListener('mouseover',() => {
import('./handler').then(clickMe => {
window.clickMe=clickMe.default;
});
});
// html
<div id="clickBtn" onclick="clickMe()">彈框</div>
複製程式碼
react-router4 路由懶載入
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {HashRouter as Router,Route} from 'react-router-dom';
import Bundle from './Bundle';
let LazyAbout=(props) => (<Bundle {...props} load={()=>import('./About')}/>)
let Home=() => <div>Home</div>
ReactDOM.render(
<Router>
<div>
<Route path="/" component={Home} />
<Route path="/about" component={LazyAbout}/>
</div>
</Router>,document.getElementById('root'));
// Bundle
import React from 'react';
export default class Bundle extends React.Component{
state={Mod: null}
componentWillMount() {
this.props.load().then(mod=>this.setState({Mod: mod.default? mod.default:mod}));
}
render() {
let Mod=this.state.Mod;
return Mod&&<Mod {...this.props}/>;
}
}
// about
import React from 'react';
export default props => <div>About</div>
複製程式碼
熱更新
配置hot
const webpack = require('webpack');
module.exports = {
entry:{
main:'./src/index.js',
},
plugins: [
// 該外掛的作用就是實現模組熱替換,實際上當啟動時帶上 `--hot` 引數,會注入該外掛,生成 .hot-update.json 檔案。
new webpack.NamedModulesPlugin(), // 用於啟動 HMR 時可以顯示模組的相對路徑
new webpack.HotModuleReplacementPlugin(), // Hot Module Replacement 的外掛
],
devServer:{
// 告訴 DevServer 要開啟模組熱替換模式
hot: true,
}
};
複製程式碼
程式碼實現
import React from 'react';
import { render } from 'react-dom';
import App from './App';
import './index.css';
render(<App/>, document.getElementById('root'));
// 只有當開啟了模組熱替換時 module.hot 才存在
if (module.hot) {
// accept 函式的第一個引數指出當前檔案接受哪些子模組的替換,這裡表示只接受 ./AppComponent 這個子模組
// 第2個引數用於在新的子模組載入完畢後需要執行的邏輯
module.hot.accept(['./App'], () => {
// 新的 AppComponent 載入成功後重新執行下組建渲染邏輯
let App=require('./App').default;
render(<App/>, document.getElementById('root'));
});
}
複製程式碼