Webpack幾個概念:javascript
chunk: chunk是webpack拆分出來的:css
問題分析:html
解決思路:前端
如何拆分,方式因人而異,因項目而異。拆分原則有:vue
經過將公共模塊拆出來,最終合成的文件在最開始的時候加載一次,便存到緩存中供後續使用。這個帶來速度上的提高,由於瀏覽器會迅速將公共的代碼從緩存中取出來,而不是每次訪問一個新頁面時,再去加載一個更大的文件。java
webpack提供了一個很是好的內置插件幫咱們實現這一需求:CommonsChunkPlugin
。不過在 webpack4 中CommonsChunkPlugin
被刪除,取而代之的是optimization.splitChunks
。
CommonsChunkPlugin
CommonsChunkPlugin
插件,是一個可選的用於創建一個獨立文件(又稱做 chunk)的功能,這個文件包括多個入口 chunk
的公共模塊。node
childrenreact
minChunks含義:
數字:模塊被多少個chunk公共引用才被抽取出來成爲commons chunk
函數:接受 (module, count) 兩個參數,返回一個布爾值,你能夠在函數內進行你規定好的邏輯來決定某個模塊是否提取成爲commons chunk
Infinity: 只有當入口文件(entry chunks) >= 3 才生效,用來在第三方庫中分離自定義的公共模塊
1. 分離出第三方庫、自定義公共模塊、webpack運行文件, 放在同一個文件中webpack
修改webpack.config.js新增一個入口文件vendor, 使用CommonsChunkPlugin插件進行公共模塊的提取:git
const path = require("path"); const webpack = require("webpack"); const packageJson = require("./package.json"); module.exports = { entry: { first: './src/first.js', second: './src/second.js', // 新增一個入口文件vendor vendor: Object.keys(packageJson.dependencies) }, output: { path: path.resolve(__dirname,'./dist'), filename: '[name].js' }, plugins: [ // 使用CommonsChunkPlugin插件進行公共模塊的提取 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].js' }), ] }
生成dist文件夾下文件有:first.js, second.js, vendor.js。
經過查看vendor.js文件,發現first.js和second.js文件中依賴的第三方庫和自定義公共模塊都被打包進vendor.js中,同時還有webpack的運行文件。
2. 單獨分離出第三方庫、自定義公共模塊、webpack運行文件
plugins: [ // 抽離第三方庫與webpack運行文件 new webpack.optimize.CommonsChunkPlugin({ name: ['vendor','runtime'], // 建立runtime.js進行webpack運行文件的抽離,其中source chunks是vendor.js filename: '[name].js', minChunks: Infinity }), // 抽離自定義公共模塊 new webpack.optimize.CommonsChunkPlugin({ name: 'common', filename: '[name].js', chunks: ['first','second'],// 從first.js和second.js中抽取commons chunk }), ]
生成dist文件夾下文件有:first.js, second.js, vendor.js, runtime.js, common.js
splitChunks
cacheGroups
是splitChunks
配置的核心,在cacheGroups
緩存組裏配置代碼的拆分規則。緩存組的每個屬性都是一個配置規則, 例如配置default屬性,屬性名能夠不叫default能夠本身定。屬性的值是一個對象。index/a.js
這樣的。all
,async
,initial
。all
表明全部模塊,async
表明異步加載的模塊, initial
表明初始化時就能獲取的模塊。若是是函數,則能夠根據chunk參數的name等屬性進行更細緻的篩選。minChunks:splitChunks
是自帶默認配置的,而緩存組默認會繼承這些配置,其中有個minChunks
屬性:
minSize設置生成文件的最小大小,單位是字節。若是一個模塊符合以前所說的拆分規則,可是若是提取出來最後生成文件大小比minSize要小,那它不會被提取出來。這個屬性能夠在每一個緩存組屬性中設置,也能夠在splitChunks屬性中設置,在每一個緩存組都會繼承這個配置。
priority設置拆分規則的優先級,屬性值爲數字,能夠爲負數。當某個模塊同時符合一個以上的規則時,經過優先級屬性priority來決定使用哪一個拆分規則。優先級高者執行。
test設置緩存組選擇的模塊,與chunks屬性的做用有一點像,可是維度不同。test的值能夠是一個正則表達式,也能夠是一個函數。它能夠匹配模塊的絕對資源路徑或chunk名稱,匹配chunk名稱時,將選擇chunk中的全部模塊。
1. 實現代碼分離:
//webpack.config.js optimization: { splitChunks: { cacheGroups: { default: { name: 'common', chunks: 'initial', minChunks: 2, //模塊被引用2次以上的才抽離 } } } }
進入dist目錄查看:common.js
: 包含引用2次以上的全部模塊
2. 分離第三方庫與自定義組件庫
//webpack.config.js optimization: { splitChunks: { minSize: 300, //提取出的chunk的最小大小 cacheGroups: { default: { name: 'common', chunks: 'initial', minChunks: 2, //模塊被引用2次以上的才抽離 priority: -20, }, // 拆分第三方庫(經過npm|yarn安裝的庫) vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'initial', priority: -10, }, // 拆分指定文件 locallib: { test: /(src\/locallib\.js)$/, name: 'locallib', chunks: 'initial', priority: -9 } } } }
進入dist目錄查看:common.js
,vendor.js
包含第三方庫代碼,locallib.js
包含locallib
模塊的代碼。
DLL(Dynamic Link Library)文件爲動態連接庫文件。在Windows中,許多應用程序並非一個完整的可執行文件,它們被分割成一些相對獨立的動態連接庫,即DLL文件,放置於系統中。當咱們執行某一個程序時,相應的DLL文件就會被調用。
一般來講,咱們的代碼均可以致少簡單區分紅業務代碼和第三方庫。若是不作處理,每次構建時都須要把全部的代碼從新構建一次,耗費大量的時間。而後大部分狀況下,不少第三方庫的代碼並不會發生變動(除非是版本升級),這時就能夠用到dll:把複用性較高的第三方模塊打包到動態連接庫中,在不升級這些庫的狀況下,動態庫不須要從新打包,每次構建只從新打包業務代碼。
DllPlugin
和 DllReferencePlugin
用某種方法實現了拆分 bundles,大幅度提高了構建的速度。使用DLL時,能夠把構建過程分紅dll構建過程和主構建過程,因此須要兩個構建配置文件,例如叫作webpack.config.js
和webpack.dll.config.js
。
1. 使用DLLPlugin
打包須要分離到動態庫的模塊
DllPlugin
是webpack
內置的插件,不須要額外安裝,直接配置webpack.dll.config.js
文件。此插件用於在單獨的 webpack 配置中建立一個 dll-only-bundle,會生成一個名爲 manifest.json
的文件,這個文件是用於讓 DllReferencePlugin
可以映射到相應的依賴上。
context
(可選): manifest 文件中請求的 context (默認值爲 webpack 的 context)format
(boolean = false):若是爲 true
,則 manifest json 文件 (輸出文件) 將被格式化。name
:暴露出的 DLL 的函數名(TemplatePaths:[fullhash]
& [name]
)path
:manifest.json 文件的 絕對路徑(輸出文件)entryOnly
(boolean = true):若是爲 true
,則僅暴露入口type
:dll bundle 的類型
咱們建議 DllPlugin 只在
entryOnly: true
時使用,不然 DLL 中的 tree shaking 將沒法工做,由於全部 exports 都可使用。
// webpack.dll.config.js module.exports = { entry: { // 第三方庫 react: ['react', 'react-dom', 'react-redux'] }, output: { // 輸出的動態連接庫的文件名稱,[name] 表明當前動態連接庫的名稱, filename: '[name].dll.js', path: resolve('dist/dll'), // library必須和後面dllplugin中的name一致 library: '[name]_dll_[hash]' }, plugins: [ new webpack.DllPlugin({ // 動態連接庫的全局變量名稱,須要和 output.library 中保持一致 // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值 name: '[name]_dll_[hash]', // 描述動態連接庫的 manifest.json 文件輸出時的文件名稱 path: path.join(__dirname, 'dist/dll', '[name].manifest.json') }), ] }
2. 在主構建配置文件使用DllReferencePlugin引用動態庫文件
在webpack.config.js
中使用dll要用到DllReferencePlugin
, 此插件會把 dll-only-bundles 引用到須要的預編譯的依賴中。
context
:(絕對路徑) manifest (或者是內容屬性)中請求的上下文extensions
:用於解析 dll bundle 中模塊的擴展名 (僅在使用 'scope' 時使用)。manifest
:包含 content
和 name
的對象,或者是一個字符串 —— 編譯時用於加載 JSON manifest 的絕對路徑content
(可選): 請求到模塊 id 的映射(默認值爲 manifest.content
)name
(可選):dll 暴露地方的名稱(默認值爲 manifest.name
)(可參考externals
)scope
(可選):dll 中內容的前綴sourceType
(可選):dll 是如何暴露的 (libraryTarget)經過引用 dll 的 manifest 文件來把依賴的名稱映射到模塊的 id 上,以後再在須要的時候經過內置的 __webpack_require__
函數來 require
對應的模塊。
new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./dist/dll/react.manifest.json') }),
第一步產出的manifest
文件就用在這裏,給主構建流程做爲查找dll的依據:DllReferencePlugin去 manifest.json 文件讀取 name 字段的值,把值的內容做爲在從全局變量中獲取動態連接庫中內容時的全局變量名,所以:在 webpack.dll.config.js 文件中,DllPlugin 中的 name 參數必須和 output.library 中保持一致。
3. 在入口文件引入dll文件
生成的dll暴露出的是全局函數,所以還須要在入口文件裏面引入對應的dll文件。
<body> <div id="app"></div> <!--引用dll文件--> <script src="../../dist/dll/react.dll.js"></script> </body>
1.分離代碼,業務代碼和第三方模塊能夠被打包到不一樣的文件裏,這個有幾個好處:
2.提高構建速度。第三方庫沒有變動時,因爲咱們只構建業務相關代碼,相比所有從新構建天然要快的多。
moment.js
日期處理庫,佔用很大的體積, 由於全部的locale
文件都被引入,而這些文件在整個庫的體積中佔了大部分,所以當webpack打包時移除這部份內容會讓打包文件的體積有所減少。
webpack自帶的兩個庫能夠實現這個功能:
IgnorePlugin
的使用方法以下:
// 插件配置 plugins: [ // 忽略moment.js中全部的locale文件 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), ], // 使用方式 const moment = require('moment'); // 引入zh-cn locale文件 require('moment/locale/zh-cn'); moment.locale('zh-cn');
ContextReplacementPlugin
的使用方法以下:
// 插件配置 plugins: [ // 只加載locale zh-cn文件 new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/), ], // 使用方式 const moment = require('moment'); moment.locale('zh-cn');
在項目中使用了lodash
這個很經常使用的工具庫,然而在使用這類工具庫的時候每每只使用到了其中的不多的一部分功能,但卻把整個庫都引入了。所以這裏也能夠進一步優化,只引用須要的部分。
import { chain, cloneDeep } from 'lodash'; // 或者 import chain from 'lodash/chain'; import cloneDeep from 'lodash/cloneDeep';
咱們日常也會對代碼進行壓縮混淆,能夠經過UglifyJS
等工具來對js代碼進行壓縮,同時能夠去掉沒必要要的空格、註釋、console信息等,也能夠有效的減少代碼體積。
hash通常是結合CDN緩存來使用,經過webpack構建以後,生成對應文件名自動帶上對應的MD5值。若是文件內容改變的話,那麼對應文件哈希值也會改變,對應的HTML引用的URL地址也會改變,觸發CDN服務器從源服務器上拉取對應數據,進而更新本地緩存。
hash是跟整個項目的構建相關,只要項目裏有文件更改,整個項目構建的hash值都會更改。同一次構建過程當中生成的哈希都是同樣的。
output:{ path:path.join(__dirname, '/dist'), filename: 'bundle.[name].[hash].js', }
根據不一樣的入口文件(Entry)進行依賴文件解析、構建對應的chunk,生成對應的哈希值。把一些公共庫和程序入口文件區分開,單獨打包構建,接着咱們採用chunkhash的方式生成哈希值,那麼只要咱們不改動公共庫的代碼,就能夠保證其哈希值不會受影響。
output:{ path:path.join(__dirname, '/dist/js'), filename: 'bundle.[name].[chunkhash].js', }
採用chunkhash,項目主入口文件Index.js及其對應的依賴文件Index.css因爲被打包在同一個模塊,共用相同的chunkhash。因爲公共庫是不一樣的模塊,有單獨的chunkhash。因此Index文件的更改不會影響公共庫。若是index.js更改了代碼,css未改變,因爲該模塊發生了改變,致使css文件會重複構建。
根據文件內容建立出惟一 hash。當文件內容發生變化時,[contenthash] 纔會發生變化。
output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js', path: path.resolve(__dirname, '../dist'), }
模塊熱替換(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它容許在運行時更新全部類型的模塊,而無需徹底刷新。
實現
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); { devServer: { contentBase: './dist', hot: true, // DevServer開啓模塊熱替換模式 }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Hot Module Replacement', }), ], }
filename
指列在 entry
中,打包後輸出的文件的名稱。
chunkFilename
指未列在 entry
中,卻又須要被打包出來的文件的名稱。默認使用 [id].js 或從 output.filename 中推斷出的值([name] 會被預先替換爲 [id] 或 [id].)。
// webpack.config.js module.exports = { entry: './src/index.js', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), chunkFileName: '[name].bundle.js' } }
由於 css 不是編程語言,因此不能聲明變量、函數,不能作判斷、循環和計算,也不能嵌套。爲了解決這個問題,衍生了兩種拓展語言 less 與 sass,它們兼容 css,而且拓展了編程的功能,主要是帶來了如下的特性:
@import
避免重複導入問題,所以能夠放心大膽的導入其餘文件。從模塊化的角度來說,less 與 sass 只是擴充了 css 的功能,但並無在語言的層面作模塊化,由於全局命名衝突的問題依然還在。
想要讓 css 具有模塊化功能,暫時還不能從語言的層面來考慮,因此只能從工具的角度來實現。目前比較好的方式是使用 js
來加載 css
文件,並將 css
的內容導出爲一個對象,使用 js
來渲染整個 dom 樹和匹配相應的樣式到對應的元素。
css文件建議遵循以下原則
.class
才能導出爲對象的屬性)composes
組合來實現複用.className
書寫,而非 .class-name
(前者能夠經過 styles.className
訪問,後者須要經過 styles['class-name']
才能訪問)。實例
/* dialog.css */ .root {} .confirm {} .disabledConfirm {}
js文件引入dialog.css
, 使用 classnames 庫來操做 class 名:
/* dialog.jsx */ import classNames from 'classnames'; import styles from './dialog.css'; export default class Dialog extends React.Component { render() { const cx = classNames({ [styles.confirm]: !this.state.disabled, [styles.disabledConfirm]: this.state.disabled }); return <div className={styles.root}> <a className={cx}>Confirm</a> ... </div> } }
若是你不想頻繁的輸入styles.**
,能夠試一下 react-css-modules,它經過高階函數的形式來避免重複輸入styles.**
。
依賴webpack: css-loader
這個功能須要構建工具的支持,若是使用 webpack ,可使用 css-loader,並設置 options.modules
爲 true
, 即可使用模塊化的功能了。css-loader
解析@import和 url() ,會 import/require()
後再解析(resolve)它們。css-loader
配置項:
名稱 | 類型 | 默認值 | 描述 | |
---|---|---|---|---|
root | String | root值將被添加到 URL 前面,而後再進行轉譯。由於對於以 / 開頭的 URL,默認行爲是不轉譯。 |
||
url | Boolean | true | 啓用/禁用解析 url() |
|
alias | Object | {} | 給url建立別名。用別名重寫你的 URL,在難以改變輸入文件的url 路徑時,這會頗有幫助。 | |
import | Boolean | true | 啓用/禁用 @import 處理 | |
minimize | Boolean\ | Object | false | 啓用/禁用 壓縮 |
sourceMap | Boolean | false | 啓用/禁用 Sourcemap | |
importLoaders | Number | 0 | 在 css-loader 前應用的 loader 的數量 | |
modules | Boolean | false | 啓用/禁用 CSS 模塊 | |
camelCase | Boolean\ | String | false | 是否以駝峯化式命名導出類名 |
localIdentName | String | [hash:base64] | 配置生成的類名標識符(ident) |
module.exports = { module: { rules: [ { test: /\.css$/i, loader: "css-loader", options: { modules: true, }, }, ], }, };
loader:
是一個轉換器,將A文件進行編譯成B文件,好比:將A.less轉換爲A.css,單純的文件轉換過程。
是一個導出爲function的node模塊。能夠將匹配到的文件進行一次轉換,同時loader能夠鏈式傳遞。
plugin:
是一個擴展器,經過鉤子能夠涉及整個構建流程,能夠作一些在構建範圍內的事情。
它並不直接操做文件,而是基於事件機制工做,會監聽webpack打包過程當中的某些節點,執行普遍的任務。
sass-loader
轉化sass爲css文件,而且包一層module.exports成爲一個js module。
css-loader
解析@import和 url() 。
style-loader
將建立一個style標籤將css文件嵌入到html中。
vue-loader、coffee-loader、babel-loader
等能夠將特定文件格式轉成js模塊、將其餘語言轉化爲js語言和編譯下一代js語言。
file-loader
能夠處理資源,file-loader能夠複製和放置資源位置,並能夠指定文件名模板,用hash命名更好利用緩存。
url-loader
能夠處理資源, 將小於配置limit大小的文件轉換成內斂Data Url的方式,減小請求。
raw-loader
能夠將文件以字符串的形式返回
imports-loader、exports-loader
能夠向模塊注入變量或者提供導出模塊功能。
UglifyJsPlugin
,壓縮和混淆代碼。CommonsChunkPlugin
,將指定的模塊或公用模塊打包出來,減小主bundle文件的體積,配合緩存策略,加快應用訪問速度。DllPlugin
和DllReferencePlugin
相互配合,前置第三方包的構建,只構建業務代碼,同時能解決Externals屢次引用問題。DllReferencePlugin引用DllPlugin配置生成的manifest.json文件,manifest.json包含了依賴模塊和module id的映射關係html-webpack-plugin
能夠根據模板自動生成html代碼,並自動引用css和js文件extract-text-webpack-plugin
將js文件中引用的樣式單獨抽離成css文件HotModuleReplacementPlugin
熱更新optimize-css-assets-webpack-plugin
不一樣組件中重複的css能夠快速去重webpack-bundle-analyzer
一個webpack的bundle文件分析工具,將bundle文件以可交互縮放的treemap的形式展現。compression-webpack-plugin
生產環境可採用gzip壓縮JS和CSShappypack
:經過多進程模型,來加速代碼構建clean-wenpack-plugin
清理每次打包後沒有使用的文件爲何要分離第三方庫?
第三方庫是比較穩定,不會輕易改變的,利用瀏覽器緩存後,用戶再次加載頁面會減小服務器請求,提升速度優化體驗。提取多個應用(入口)公共模塊的做用和他相似,公共部分會被緩存,全部應用均可以利用緩存內容從而提升性能。
分離第三方庫就能利用瀏覽器換緩存了麼?
答案是否認的。致使沒法利用緩存的因素有不少,好比每次分離的庫文件從新打包都會獲得不一樣的名稱,後臺的同事給js文件設置的緩存過時時間爲0,只要文件是徹底不變的,包括修改時間,文件內容等,依然會利用緩存。
瀏覽器緩存機制是什麼樣的?
HTTP1.1給的策略是使用Cache-control配合Etag。
Apache中,ETag的值默認是對文件的索引節(INode),大小(Size)和最後修改時間(MTime)進行Hash後獲得的。若是Etag相同,依然不會請求新資源,而會使用之前的文件。
CommonsChunkPlugin與SplitChunksPlugin
做用
將公共模塊抽離。每次打包的時候都會從新打包,仍是會去處理一些第三方依賴庫,只是它能把第三方庫文件和咱們的代碼分開掉,生成一個獨立的 js 文件。可是它仍是不能提升打包的速度。
自 webpack 4.0 上線以後,CommonsChunkPlugin 已被替換成 SplitChunksPlugin,旨在優化 chunk 的拆分。
CommonsChunkPlugin
設計思路:知足 minChunks 的引用次數時,都會將對應的模塊抽離如一個新的 chunk 文件中,這個文件爲全部的業務文件的父級。
這種設計思路帶來了會形成模塊打包冗餘。總的來講會形成這麼幾個問題:
SplitChunksPlugin
SplitChunksPlugin 優化了 webpack 的打包策略,使用自動重複算法,會自動計算出各頁面公共的包引用以及部分頁面公共的包引用,固然,對於那些部分共有可是閾值太小的文件其不會建立單獨的輸出文件,由於其大小不值得去新開一個請求。(緩存策略配置在 cacheGroup 中)
SplitChunksPlugin 默認的分包策略基於如下 4 個條件:
- SplitChunksPlugin 配合使用 RuntimeChunk 對運行時的 hash 變更作優化(至關於 CommonsChunkPlugin 的兩次使用)
- 減小
maxInitial/AsyncRequest
會加大 module 的冗餘,可是會進一步的減小請求。
DllPlugin與DllReferencePlugin
使用
DLLPlugin 這個插件是在一個額外獨立的 webpack 設置中建立一個只有 dll 的 bundle,也就是說,除了 webpack.config.js,項目中還會新建一個 webpack.dll.config.js 文件來配置 dll 的打包。webpack.dll.config.js 做用是把全部的第三方庫依賴打包到一個 bundle 的 dll 文件裏面,還會生成一個名爲 manifest.json 文件。該 manifest.json 的做用是用來讓 DllReferencePlugin 映射到相關的依賴上去的。(可類比 CommonsChunkPlugin 的兩次打包或者 RuntimeChunk 的運行包配置)
設計思路
DLLPlugin 是提早將公共的包構建出來,使得在 build 時過濾掉這些構建過的包,使得在正是構建時的速度縮短。因此其相對來講打包速度會更快。
webpack的運行過程能夠簡單概述爲以下流程:
初始化配置參數 -> 綁定事件鉤子回調 -> 肯定Entry逐一遍歷 -> 使用loader編譯文件 -> 輸出文件
什麼是webpack事件流?
Webpack 就像一條生產線,要通過一系列處理流程後才能將源文件轉換成輸出結果。 這條生產線上的每一個處理流程的職責都是單一的,多個流程之間有存在依賴關係,只有完成當前處理後才能交給下一個流程去處理。 插件就像是一個插入到生產線中的一個功能,在特定的時機對生產線上的資源作處理。Webpack 經過 Tapable 來組織這條複雜的生產線。 Webpack 在運行過程當中會廣播事件,插件只須要監聽它所關心的事件,就能加入到這條生產線中,去改變生產線的運做。 Webpack 的事件流機制保證了插件的有序性,使得整個系統擴展性很好。 -- 吳浩麟《深刻淺出webpack》
咱們將webpack事件流理解爲webpack構建過程當中的一系列事件,他們分別表示着不一樣的構建週期和狀態,咱們能夠像在瀏覽器上監聽click事件同樣監聽事件流上的事件,而且爲它們掛載事件回調。咱們也能夠自定義事件並在合適時機進行廣播,這一切都是使用了webpack自帶的模塊 Tapable
進行管理的。咱們不須要自行安裝 Tapable
,在webpack被安裝的同時它也會一併被安裝,如需使用,咱們只須要在文件裏直接 require
便可。
Tapable的原理
Tapable的原理其實就是咱們在前端進階過程當中都會經歷的EventEmit,經過發佈者-訂閱者模式實現,它的部分核心代碼能夠歸納成下面這樣:
class SyncHook{ constructor(){ this.hooks = []; } // 訂閱事件 tap(name, fn){ this.hooks.push(fn); } // 發佈 call(){ this.hooks.forEach(hook => hook(...arguments)); } }
webpack.config.js
文件,初始化本次構建的配置參數,而且執行配置文件中的插件實例化語句,生成Compiler傳入plugin的apply方法,爲webpack事件流掛上自定義鉤子。require
語法替換成__webpack_require__
來模擬模塊化操做。compilation.assets
上拿到所需數據,其中包括即將輸出的資源、代碼塊Chunk等等信息。