不懂Webpack4的前端不是好工程師(進階篇)

NO IMAGE

包含了 Tree Shaking,Code Spliting,打包環境區分,緩存,shimming 等內容,繼續擴展 Webpack 的基礎知識面。

Tree Shaking

<!--index.js-->
import { add } from './math' 
add(1,2)

<!--math.js--> export const add = (a,b) => { console.log(a,b) return a + b } export const minus = (a,b) =>{ console.log(a,b) return a - b }

執行npx webpack打包後,打算dist/main.js可以看不到沒有用到的minus方法也一併打包了,這種就造成我們打包的文件過於冗餘,Tree Shaking就可以幫助我們解決這個問題,但是隻能解決ES模塊導入,像require其他導入方式就不行,因為ES是底層是導入靜態資源,require是動態資源。

mode為development下配置增加

<!--webpack.config.js-->
module.exports = {
mode: 'development',
optimization: {
usedExports: true //使用模塊導入
}
}

配置後可以看到只到導出了add方法
不懂Webpack4的前端不是好工程師(進階篇)
import './index.css'文件怎麼辦,不就是會被忽略了,這個時候我們需要在package.json增加忽略的配置

{
...
"sideEffects": false, 
}

如果是生成環境mode為production下,就不需要增加optimization配置,因為生成環境默認幫我們增加了,但是sideEffects還是要配置的。

Develoment 和 Production 模式的區分打包

項目上線我們就需要把mode Develoment 切換為 Production,我們可以在包文件分別配置不同的打包命令

{
"scripts": {
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
},
}

兩種模式有很多共總的打包配置,我們可以使用webpack-merge抽離公用的配置,新建文件夾build,在該目錄新建三個文件webpack.dev.js webpack.prod.js webpack.common.js

<!--webpack.common.js-->
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js', //入口文件 默認:src/index.js
},
output: { //出口文件 默認: dist/main.js
filename: '[name].js', //輸出的文件名 
path: path.resolve(__dirname, 'dist') //輸出的路徑,只能是絕對路徑
},
module: {
...
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
}
<!--webpack.dev.js-->
const commonCongif = require('./webpack.common')
const merge = require("webpack-merge");
const devCongif = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
contentBase: './dist',
port: 9000, //服務端口號
open: true, //首次打包編譯自動打開瀏覽器
hot: true,
hotOnly: true,
},
optimization: {
usedExports: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}

module.exports = merge(commonCongif,devCongif)

<!--webpack.prod.js-->
const commonCongif = require('./webpack.common')
const merge = require("webpack-merge");
const prodCongif = {
mode: 'production',
devtool: 'cheap-module-source-map',
}
module.exports = merge(commonCongif,prodCongif)

Code Splitting

Code Splitting可以實現代碼切割,按需加載。

  • 不使用Code Splitting方式:首次訪問頁面時,加載main.js(2mb),當頁面業務邏輯發生改變時,又要加載2mb的內容

//index.js
import _ from 'loadsh' //1mb
console.log(_.join(['a','b','c'],'***'))
//此省略一萬行業務代碼
console.log(_.join(['a','b','c'],'***'))
  • 使用Code Splitting方式:main.js被拆成lodash.js(1mb),main.js(1mb),當頁面業務邏輯發生改變時,只要加載main.js即可(1mb)
<!--loadsh.js-->
import _ from 'loadsh' //1mb
window._ = _
<!--index.js-->
console.log(_.join(['a','b','c'],'***'))
//此省略一萬行業務代碼
console.log(_.join(['a','b','c'],'***'))

//webpack.commom.js
module.exports = {
entry: {
loadsh: './src/loadsh.js',
main: './src/index.js', //入口文件 默認:src/index.js
},
}

webpack中實現代碼分割,有兩種方式:

  • 1.同步代碼:只需要在webpack.commom.js中坐optimization的配置
  • 2.異步代碼(類似import導入),無需做任何配置,會自動進行代碼分割
    // 同步導入
import _ from 'loadsh' //1mb
console.log(_.join(['a','b','c'],'***'))
//此省略一萬行業務代碼
console.log(_.join(['a','b','c'],'***'))
//異步導入
function getComponent() {
return import('loadsh').then(({ default: _ }) => {
var elem = document.createElement('div')
elem.innerHTML = _.join(['a','b','c'],'***')
return elem
})
}

getComponent().then(elem => { document.body.appendChild(elem) })

//webpack.common.js
module.exports = {
...
optimization: {
splitChunks: {
chunks: 'all'
}
}
}

splitChunks默認配置詳解

 optimization: {
splitChunks: {
chunks: 'all', //代碼切割的類型: async: 只切割異步代碼  initial:只切割同步代碼 all:兩種都切割
minSize: 30000, //切割的文件最小要求,單位kb
maxSize: 0,//切割的文件最大要求,單位kb,超過部分會繼續切分
minChunks: 1, //最小需要切割的次數需求:至少需要切割一次
maxAsyncRequests: 6,//按需加載時並行請求的最大數量。
maxInitialRequests: 4,//入口點的最大並行請求數。
automaticNameDelimiter: '~',//默認情況下,webpack將使用塊的來源和名稱生成名稱(例如vendors~main.js)。此選項使您可以指定用於生成名稱的定界符。
cacheGroups: {//緩存組,同步切割時候會走該模塊區分切割組
defaultVendors: { 
test: /[\\/]node_modules[\\/]/, //配置的模塊:node_modules文件夾下的模塊切割
priority: -10 //切割優先級
},
default: { //如果沒有達到上方的,就走到該默認組切割
minChunks: 2,
priority: -20, //切割優先級
reuseExistingChunk: true //如果已存在切割模塊,忽略這個組的切割
}
}
}
}

Lazy Loading 懶加載

懶加載可以實現按需加載,主要是通過import()來實現,如下點擊事件才按需加載某個包
//異步導入

function getComponent() {
return import(/* webpackChunkName: "lodash" */'loadsh').then(({ default: _ }) => {
var elem = document.createElement('div')
elem.innerHTML = _.join(['a','b','c'],'***')
return elem
})
}

document.addEventListener('click', () => { getComponent().then(elem => { document.body.appendChild(elem) }) })

Chunk

每個包生成的js文件就是一個chunk,minChunks配置就和這個有關minChunks: 1 最小需要切割的次數需求:至少需要切割一次,比如lodash文件切割至少一次。
不懂Webpack4的前端不是好工程師(進階篇)

打包分析

我們可以打包生成相關的json文件,利用第三方平臺來查看分析我們打包的文件的各個方面

  • 步驟一:配置生成打包分析文件stats.json
<!--package.json-->
"scripts": {
"dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
}
  • 步驟二:利用第三方分析打包:http://webpack.github.io/analyse/

預取/預加載模塊Preloading和Prefetching

在聲明 import 時,使用下面這些內置指令,可以讓 webpack 輸出 “resource hint(資源提示)”,來告知瀏覽器:

  • prefetch(預取):將來某些導航下可能需要的資源

  • preload(預加載):當前導航下可能需要資源
    下面這個 prefetch 的簡單示例中:

<!--loadsh.js-->
function loadsh () {
var elem = document.createElement('div')
elem.innerHTML = 'test'  
}
export default loadsh
<!--index.js-->
//推薦寫法:異步導入的方式可以提高代碼的使用率,可以在瀏覽器控制面板Coverage看到使用率提高
document.addEventListener('click', () => {
import(/*webpackPrefetch: true */ './loadsh').then(({default: _}) => {
_()
})
})

不懂Webpack4的前端不是好工程師(進階篇)
查看瀏覽器加載的資源,資源會在主核心加載完畢後加載了該模塊,在點擊的時候直接從緩存直接提取出來

不懂Webpack4的前端不是好工程師(進階篇)

css文件的代碼切割

MiniCssExtractPlugin切割css

webpack在做打包的時候會把css文件打包在js裡,我們就需要藉助插件來幫我們對css文件進行切割

  • 文檔

  • 可對css的引入文件進行代碼分割

  • 會把 css 打包成單獨的一個文件

  • 這個插件適合在production模式下做打包

注意tree shaking的代碼分割,如果不設置會忽略css代碼切割導致切割失敗

不懂Webpack4的前端不是好工程師(進階篇)

optimize-css-assets-webpack-plugin對css進行代碼壓縮,具體詳情配置查看文檔,配置文件代碼如下

const commonCongif = require('./webpack.common')
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const prodCongif = {
mode: 'production',
devtool: 'cheap-module-source-map',
module: {
rules: [
{
test: /\.sass$/,
use: [MiniCssExtractPlugin.loader, {
loader: 'css-loader',
options: {
importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
// modules: true //按模塊化引入
}
}, 'sass-loader', 'postcss-loader'
]
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css', //css文件切割插件
})
]
}

module.exports = merge(commonCongif,prodCongif)

壓縮css文件

1.安裝

 npm install optimize-css-assets-webpack-plugin --save-dev

2.使用優化配置

<!--webpack.prod.js-->
const optimizeCss = require('optimize-css-assets-webpack-plugin');
optimization: {
minimizer: [
new optimizeCss({}) //進行css代碼代碼
]
},

Webpack 與瀏覽器緩存( Caching )

當我們訪問瀏覽器的時候,第一次請求資源會從服務器拿去,當再次訪問的時候,相同的文件名我們會直接從緩存中提取。但是我們會想,如果當我們修改了代碼推上去之後,當前用戶已經訪問了,當用戶再次刷新文件名字還是一樣,它就不會更新最新的代碼,而是從瀏覽器緩存中直接讀取舊的。webpackoutput相關配置就可以幫助我們解決這個問題,讓用戶可以重新加載已經修改最新的代碼模塊。

<!--webpack.prod.js-->
output: {
filename: '[name].[contenthash].js', //輸出的文件名 
chunkFilename: '[name].[contenthash].js' //chunk文件生成的名字
},

contenthash可以在打包的時候監聽代碼是否改變,改變則生成新的hash,不變則用上次的

本文使用 mdnice 排版

相關文章

不懂Webpack4的前端不是好工程師(基礎篇)

vscode配置使vue項目支持斷點調試

小程序自定義導航欄,兼容適配所有機型(附完整案例)

微信小程序初始化項目架構