這是我花了幾個星期學習webpack4的學習筆記。內容不夠細,由於一些相對比較簡單的,就隨意帶過了。但願文章能給你們帶來幫助。若有錯誤,但願及時指出。例子都在learn-webpack倉庫上。若是你從中有所收穫的話,但願你能給個人github
點個star
。javascript
一個模塊裏會導出不少東西。把一個模塊裏沒有被用到的東西都給去掉。不會把他打包到入口文件裏。tree shaking只支持es6的方式引入(import
),使用require
沒法使用tree shaking
。css
webpack
的development
沒法使用tree shaking
功能。除非在打包的配置里加上html
// 開發環境須要加以下代碼 optimization: { usedExports: true }
當你須要import某個模塊,又不想tree shaking
把他給幹掉,就須要在package.json裏修改sideEffects
參數。好比當你import './console.js'
, import './index.css'
等沒有export
(導出)模塊的文件。又不想tree shaking
把它幹掉。vue
// package.json sideEffects: ['./console.js', './index.css'] // 反之 sideEffects: false
在development
環境即便你使用tree shaking
,它也不會把其餘多餘的代碼給幹掉。他只會在打包的文件裏註明某段代碼是不被使用的。java
development
和 production
區別development
代碼不壓縮,production
代碼會壓縮node
省略…☺react
webpack-merge
jquery
react
和vue
都會區分環境進行不一樣的webpack
配置,可是它們必定會有相同的部分。這個時候須要經過使用webpack-merge
進行抽離。webpack
// webpack.base.config.js const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'production', // mode: 'development', entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, optimization: { usedExports: true }, plugins: [ new HtmlWebpackPlugin({ template: './index.html' }) ] } // webpack.dev.config.js const merge = require('webpack-merge') const baseConfig = require('./webpack.base.config') const devConfig = { mode: 'development', } module.exports = merge(baseConfig, devConfig)
這裏就不重複把production
環境在配置出來了,主要介紹下webpack-merge
用法。git
npm i webpack-merge -D
webpack.base.config.js
development
和production
兩個webpack
配置相同的抽離到webpack.base.config.js
文件中在環境配置文件中(具體代碼如上)
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config.js')
module.exports = merge(baseConfig, devConfig)
code splitting
和splitChunks
當你把全部的代碼都打包到一個文件的時候,每次改一個代碼都須要從新打包。且用戶都要從新加載下這個js文件。可是若是你把一些公共的代碼或第三方庫抽離並單獨打包。經過緩存加載,會加快頁面的加載速度。
原始代碼
import _ from 'lodash' console.log(666)
打包後的文件:
main.js 551 KiB main [emitted] main
能夠看到,webpack將業務代碼跟lodash庫打包到一個main.js文件了
方法一:
建立一個新文件
import _ from 'lodash' window._ = _
將文件掛載到window
對象上,這樣其餘地方就能夠直接使用了。
而後在webpack配置文件中的entry增長一個入口爲該文件。讓該文件單獨打包。
Asset Size Chunks Chunk Names lodash.js 551 KiB lodash [emitted] lodash main.js 3.79 KiB main [emitted] main
方法二:
經過添加optimization
配置參數
optimization
: 會將諸如lodash
等庫抽離成單獨的chunk
,還會將多個模塊公用的模塊抽離成單獨的chunk
optimization: { splitChunks: { chunks: 'all' } },
打包後文件:
Asset Size Chunks Chunk Names main.js 6.78 KiB main [emitted] main vendors~main.js 547 KiB vendors~main [emitted] vendors~main
能夠看到,webpack將lodash抽成公共的chunk打包出來了。
splitChunks
裏面還能夠在添加個參數cacheGroups
optimization: { splitChunks: { chunks: 'all', cacheGroups: { // 下面的意思是:將從node_modules中引入的模塊統一打包到一個vendors.js文件中 vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, filename: 'vendors.js' }, default: false } } }
cacheGroups
中vendors
配置表示將從node_modules
中引入的模塊統一打包到一個vendors.js文件中
splitChunks
的vendors
的default
參數:
根據上下文來解釋,如上配置了vendors
,打包node_modules
文件夾中的模塊,
那麼default
將會打包本身編寫的公共方法。
當不使用default
配置時。
Asset Size Chunks Chunk Names main.js 315 KiB main [emitted] main test.js 315 KiB test [emitted] test
添加以下配置以後:
optimization: { splitChunks: { chunks: 'all', cacheGroups: { // 下面的意思是:將從node_modules中引入的模塊統一打包到一個vendors.js文件中 vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, filename: 'vendors.js' }, // 打包除上面vendors之外的公共模塊 default: { priority: -20, reuseExistingChunks: true, // 若是該chunk已經被打包進其餘模塊了,這裏就複用了,不打包進common.js了 filename: 'common.js' } } } }
打包後的文件體積爲
Asset Size Chunks Chunk Names common.js 308 KiB default~main~test [emitted] default~main~test main.js 7.03 KiB main [emitted] main test.js 7.02 KiB test [emitted] test
配置說明
splitChunks: { chunk: 'all', // all(所有), async(異步的模塊),initial(同步的模塊) minSize: 3000, // 表示文件大於3000k的時候纔對他進行打包 maxSize: 0, minChunks: 1, // 當某個模塊知足minChunks引用次數時,纔會被打包。例如,lodash只被一個文件import,那麼lodash就不會被code splitting,lodash將會被打包進 被引入的那個文件中。若是知足minChunks引用次數,lodash會被單獨抽離出來,打出一個chunk。 maxAsyncRequests: 5, // 在打包某個模塊的時候,最多分紅5個chunk,多餘的會合到最後一個chunk中。這裏分析下這個屬性過大太小帶來的問題。當設置的過大時,模塊被拆的太細,形成併發請求太多。影響性能。當設置太小時,好比1,公共模塊沒法被抽離成公共的chunk。每一個打包出來的模塊都會有公共chunk automaticNameDelimiter: '~', // 當vendors或者default中的filename不填時,打包出來的文件名就會帶~ name: true, cashGroups: {} // 如上 }
異步import
的包會被單獨打成一個chunk
async function getComponent() { const { default: _ } = await import(/* webpackChunkNanem:'lodash */ 'lodash') const element = document.createElement('div') element.innerHTML = _.join(['Dell', 'Lee'], '-') return element } document.addEventListener('click', () => { getComponent().then(element => { document.body.appendChild(element) }) })
每個js
文件都是一個chunk
chunk
是使用Webpack
過程當中最重要的幾個概念之一。在Webpack打包機制中,編譯的文件包括entry(入口,能夠是一個或者多個資源合併而成,由html經過script標籤引入)和chunk(被entry所依賴的額外的代碼塊,一樣能夠包含一個或者多個文件)。從頁面加速的角度來說,咱們應該儘量將全部的js打包到一個bundle.js之中,可是總會有一些功能是使用過程當中纔會用到的。出於性能優化的須要,對於這部分資源咱們能夠作成按需加載。
打包分析:
安裝:npm install --save-dev webpack-bundle-analyzer
// package.json => scripts "analyz": "NODE_ENV=production npm_config_report=true npm run build"
// webpack.config.js const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; plugins: [ new BundleAnalyzerPlugin() ]
執行命令npm run analyz
瀏覽器就會自動打開localhost:8888
,分析圖就會展示在你眼前
很是清晰直觀的看出
咱們以前寫的css文件都會被打包進js文件中,要想把css單獨打包成一個css文件該怎麼作呢?
這個時候就須要用到MiniCssExtractPlugin
開發環境用不到這個功能,通常都是用在生產環境中。
安裝:npm install --save-dev mini-css-extract-plugin
// webpack.config.js const MiniCssExtractPlugin = require('mini-css-extract-plugin') module: { rules: [ { test: /\.css$/, use: [{ loader: MiniCssExtractPlugin.loader, options: { // 能夠在此處指定publicPath // 默認狀況下,它在webpackoptions.output中使用publicPath publicPath: '../', // hmr: process.env.NODE_ENV === 'development', }, }, 'css-loader'] } ] }, plugins: [ new MiniCssExtractPlugin({ // 與webpackoptions.output中相同選項相似的選項 // 兩個選項都是可選的 filename: '[name].css', chunkFilename: '[id].css', }), ] // index.js import './index.css'; console.log('haha') // index.css body { background: green; }
這樣打包以後,css會被單獨打包成一個css文件。
目前爲止,咱們每次修改內容,打包出去後的文件名都不變。線上環境的文件是有緩存的。因此當你文件名不變的話,更新內容打包上線。有緩存的電腦就沒法獲取到最新的代碼。
這個時候咱們就會用到contenthash
咱們先記錄配置contenthash
以前打包的文件名。
Asset Size Chunks Chunk Names index.html 180 bytes [emitted] main.js 3.81 KiB main [emitted] main
接下來咱們來配置下contenthash
(就是根據你文件內容生成的hash值)
// webpack.config.js output: { path: path.resolve(__dirname, '../dist'), filename: '[name][contenthash].js' },
打包完以後會在main
後面接上hash
值。
Asset Size Chunks Chunk Names index.html 200 bytes [emitted] mainf5faa2d3d1e119256290.js 3.81 KiB main [emitted] main
當你不更新內容從新打包後,contenthash
還會維持不變。因此線上用戶訪問的時候就不會去服務器從新拿取代碼,而是從緩存中取文件。
shimming
(預置依賴)以jquery
爲例,代碼以下
// index.js import $ from 'jquery' $('body').html('HHAHAH') import func from './test.js' func() // test.js export default function func() { $('body').append('<h1>2</h1>') }
當你不在test.js中引入import $ from 'jquery'
那麼瀏覽器訪問的時候,會報
test.js:5 Uncaught ReferenceError: $ is not defined
這個時候就須要使用墊片功能
const webpack = require('webpack') plugins: [ new webpack.ProvidePlugin({ $: 'jquery' }) ]
當你加上這段代碼後,模塊在打包的時候,發現你使用了$
就會在你模塊頂部自動加入import $ from 'jquery'
其餘關於shimming
的內容參考webpack
官網 shimming