webpack 性能優(yōu)化
開發(fā)環(huán)境性能優(yōu)化
優(yōu)化打包構(gòu)建速度
HMR優(yōu)化代碼調(diào)試source-map
生產(chǎn)環(huán)境性能優(yōu)化
優(yōu)化打包構(gòu)建速度
oneOf babel 緩存 多進(jìn)程打包 externals dll 優(yōu)化代碼運行的性能 緩存(hash,chunkhash,contenthash) tree shaking code split 懶加載和預(yù)加載 pwa
優(yōu)化 開發(fā)環(huán)境 打包構(gòu)建速度
HMR hot module replacement 熱模塊替換/模塊熱替換
作用:一個模塊發(fā)生變化,只會重新打包這一個模塊 而不是重新打包所有,極大提升構(gòu)建速度
樣式文件: 可以使用HMR功能,因為style-loader內(nèi)部實現(xiàn)了 js文件: 默認(rèn)不能使用HMR功能 -->解決:需要修改js代碼,添加支持HMR功能的代碼。注意,HMR功能對js的處理,只能處理非入口js文件的其他文件。
- if(module.hot){
- //一旦module.hot是true,說明開啟HMR功能,讓HMR功能代碼生效
- module.hot.accept('./xxx.js',function(){
- //此方法會監(jiān)聽print.js文件的變化,一旦發(fā)生變化,其他默認(rèn)不會重新打包構(gòu)建
- //會執(zhí)行后面的回調(diào)函數(shù)
- xxx();
- })
- }
html文件: 默認(rèn)不能使用HMR功能,同時會導(dǎo)致問題:html文件不能熱更新了 解決:改 entry:['入口js','html'] ,但html文件只有一個,所以不用做HMR功能
- devServer:{
- //項目構(gòu)建后的目錄
- contentBase: resolve(__dirname,'build'),
- //啟用gzip壓縮
- compress:true,
- //端口號
- port:3000,
- //自動打開瀏覽器
- open:true
- }
優(yōu)化 開發(fā)環(huán)境 代碼調(diào)試
source-map 一種提供源代碼到構(gòu)建后代碼映射的技術(shù)
如果構(gòu)建后代碼出錯了,通過映射可以追蹤到錯誤的代碼
- webpack.config.js
- devtools:'source-map'
- //其他 參數(shù) [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map : 外部
- 錯誤代碼的準(zhǔn)確信息和錯誤位置
- inline-source-map : 內(nèi)聯(lián)
- 錯誤代碼的準(zhǔn)確信息和錯誤位置
- hidden-source-map : 外部
- 錯誤代碼的錯誤原因 但沒有錯誤位置,不能追蹤到源代碼的錯誤,只能提示到構(gòu)建后代碼的位置
- eval-source-map : 內(nèi)聯(lián)
- 每一個文件都生成對應(yīng)的source-map,都在eval
- 錯誤代碼的準(zhǔn)確信息和錯誤位置
- nosources-source-map : 外部
- 能找到錯誤代碼的準(zhǔn)確信息 但沒有任何源代碼信息
- cheap-source-map : 外部
- 錯誤代碼的準(zhǔn)確信息和錯誤位置 只精確到行,不精確到列
- cheap-module-source-map : 外部
- 錯誤代碼的準(zhǔn)確信息和錯誤位置
- module 會將 loader 的 source-map加入
- 內(nèi)聯(lián) 和 外部的區(qū)別 :
- 1.外部生成了文件但內(nèi)聯(lián)沒有生成
- 2.內(nèi)聯(lián)構(gòu)建速度更快
開發(fā)環(huán)境:速度快,調(diào)試更友好
- 速度快 eval>inline>cheap>...
- 調(diào)試更友好 souce-map>cheap-module-souce-map>cheap-souce-map
- 所以 一般用eval-source-map
生產(chǎn)環(huán)境:源代碼要不要隱藏,調(diào)試要不要友好?內(nèi)聯(lián)會讓體積編碼,所以一般不用內(nèi)聯(lián)
- nosources-source-map 隱藏源代碼
- hidden-source-map 只隱藏源代碼,會提示構(gòu)建購代碼錯誤信息
- --> source-map /cheap-module-souce-map
優(yōu)化生產(chǎn)環(huán)境
oneOf
rules里中有許多個loader,這樣會導(dǎo)致每個文件都會被所有的loader過一遍,有些能處理,有些處理不了。所以可以利用oneOf達(dá)到以下loader只會匹配到第一個。但需要注意,不能有兩個loader同時處理同一個文件
- webpack.config
- module.exports={
- //....
- module:{
- rule:[
- //正常來講,一個文件只能被一個loader處理
- //當(dāng)一個文件要被多個loader處理,那么一定要指定loader的執(zhí)行順序
- // 先執(zhí)行eslint 再執(zhí)行babel
- {
- test:/\.js$/,
- exclude:/node_modules/,
- //優(yōu)先執(zhí)行
- enforce:'pre',
- loader:'eslint-loader',
- options:{
- fix:true
- }
- },
- {
- oneOf:[
- {
- test: /\.css$/,
- use:[
- ...commonCssLoader
- ]
- },
- {
- test:/\.less$/,
- use:[
- ...commonCssLoader,'less-loader'
- ]
- },
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ]
- }
- },
- {
- test:/\.(png|jpg|gif)/,
- loader:'url-loader',
- enModule:true,
- options:{
- limit:8*1024,
- name: '[hash:10].[ext]',
- outputpath:''
- }
- },
- {
- test:/\.html$/,
- loader:'html-loader'
- },
- {
- exclude:/\.(js|less|css|png|jpg|gif)/,
- loader:'file-loader',
- options:{
- name:'[hash:10].[ext]'
- }
- }
- ]
- }
- ]
- },
- }
緩存
1.babel緩存-->第二次打包更快
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構(gòu)建時,會讀取之前的緩存
- cacheDirectory: true
- }
- },
2.文件資源緩存-->上線緩存優(yōu)化
- hash:每次webpack構(gòu)建會生成一個唯一hash值
- 問題: 因為js和css同時使用一個hash值,如果重新打包,會導(dǎo)致所有緩存失效,可能我卻只改了一個文件,
- chunkhash:根據(jù)chunk生成hash值,如果打包來源于同一個chunk,那么hash值也一樣
- 問題:js和css的hash值還是一樣的。
- 原因:css是由js引入的,所以屬于同一個chunk
- contenthash: 根據(jù)文件內(nèi)容生成hash值,
- webpack.config.js
tree shaking
去除應(yīng)用程序中沒有使用的代碼
- 前提:
- 1.必須使用es6模塊化
- 2.開啟production模式
- 在package.json中配置
- "sideEffects":false 所有代碼都沒有副作用,都可以鏡像tree sharking
- 問題 可能會把css/@babel/polyfille 干掉
- "sideEffects": ["*.css","*.less"] 哪些文件不 tree sharking
code split
1.入口配置
- 單入口 //單頁面應(yīng)用
- entry:'./src/js/index.js'
- 多入口 //多頁面應(yīng)用
- entry:{
- index:'./src/js/index.js',
- test:'./src/js/test.js'
- }
2.optimization
- module.exports={
- //...
- // 可以將nodemudules中的代碼單獨打包成一個chunk最終輸出
- // 還可以自動分析多入口chunk中,有沒有公共的文件,如果有會打包成一個單獨的chunk
- optimization:{
- splitChunks:{
- chunks:'all'
- }
- }
- }
3.import 動態(tài)導(dǎo)入語法,能將某個文件單獨打包
通過js代碼,讓某個文件被單獨打包成一個chunk,通過注釋可以固定此文件的名稱
- import(/*webpackChunkName: 'xxxName' */'./xx/xxx.js')
- .then(res =>{
- //加載成功
- })
- .catch(()=>{
- //加載失敗
- })
懶加載和預(yù)加載
1.懶加載 當(dāng)文件需要用時才加載
import 動態(tài)導(dǎo)入語法
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName' */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
2.預(yù)加載 webpackPrefetch:true
./xx/ss.js 已經(jīng)被加載了,點擊的時候再從緩存中加載,
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName',webpackPrefetch:true */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
正常加載可以認(rèn)為是并行加載(同一時間加載多個文件) 預(yù)加載prefectch 等其他資源加載完畢,瀏覽器空閑了,再偷偷加載資源 兼容性比較差 慎用
PWA 漸進(jìn)式網(wǎng)絡(luò)開發(fā)應(yīng)用程序
網(wǎng)絡(luò)離線可訪問
webbox-->webbox-webpack-plugin
- const WebboxWebpackPlugin = require('webbox-webpack-plugin')
- module.exports={
- plugins:[
- new WebboxWebpackPlugin.GenerateSW({
- /*
- 1.幫助 serviceWorker 快速啟動
- 2.刪除舊的 serviceWorker
- 生成一個 serviceWorker 配置文件
- */
- clientsClaim:true,
- skipWaiting:true
- })
- ]
- }
- index.js 中注冊serviceworker
- //處理兼容性
- if('serviceWorker' in navigator){
- window.addEventListener('load',()=>{
- navigator.serviceWorker
- .register('./service-work.js')
- .then(()=>{
- //成功
- })
- .catch(()=>{
- //失敗
- })
- })
- }
- 1.可能會出現(xiàn)問題 eslint不認(rèn)識window和navigator
- 解決 package.json中eslintConfig中配置
- "env":{
- "browser":true //支持瀏覽器端的變量
- }
- 2. sw代碼必須運行在服務(wù)器上
- -->node.js
- --> npm i serve -g
- serve -s build 啟動一個服務(wù)器將build下的資源作為靜態(tài)資源暴露出去
多進(jìn)程打包
thread-loader 一般給babel-loader用
但需要注意
進(jìn)程啟動大約需500ms,進(jìn)程間通信也有開銷。只有工作消耗時間比較長,才需要多進(jìn)程打包
- {
- test:/\.js$/,
- exclude:/node_modules/,
- use:[
- //'thread-loader',
- {
- loader:'thread-loader',
- options:{
- workers: 2 //進(jìn)程2個
- }
- }
- {
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構(gòu)建時,會讀取之前的緩存
- cacheDirectory: true
- }
- }
- ]
- },
externals
- module.exports={
- externals:{
- //忽略/拒絕 庫名 -- npm 包名
- //可以在index.html中引入cdn
- }
- }
dll 動態(tài)連接
使用dll技術(shù) 對某些庫(第三方庫) 進(jìn)行單獨打包
- 指令 webpack --config webpack.dll.js
- webpack.dll.js
- const {resolve} = require('path')
- const webpack = require('webpack')
- module.exports = {
- entry:{
- //最終打包生成的[name]-->jquery
- //['jquery']-->要打包的庫是jquery
- jquery:['jquery']
- },
- ouput:{
- filename:'[name].js',
- path:resolve(__dirname,'dll'),
- library:'[name]_[hash]' //打包的庫里面向外暴露出去的內(nèi)容叫什么名字
- },
- plugin:[
- new webpacl.DllPlugin({
- //打包生成一個manifest.json --> 提供和jquery映射
- name: '[name]_[hash]',//映射庫的暴露內(nèi)容名稱
- path:resolve(__dirname,'dll/manifest.json')
- })
- ],
- mode:'production'
- }
- webpack.config.js
- const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
- module.exports={
- plugins:[
- //告訴webpack哪些庫不參與打包,同時名稱也得變
- new webpack.DllReferencePlugin({
- manifest: resolve(__dirname,'dll/manifest.json')
- }),
- //將某個文件打包輸出出去,并在html中引入該資源
- new AddAssetHtmlWebpackPlugin({
- filepath:resolve(__dirname,'dll/jquery.js')
- })
- ]
- }
編輯推薦