每天讀一點webpack
day-01-webpack入口
- npm包我們可以通過
package.json
中的包資訊檔案快速的定位到這個包的入口檔案,通過webpack
的包資訊main
欄位可以得到整個webpack
專案的入口位置為lib/index.js
入口檔案
- 入口檔案中通過動態匯入的方式將專案的各個模組進行整合,如各種內部外掛,內部依賴,內部配置資訊等。
包的快取
在入口檔案中通過一個快取方法
lazyFunction
將webpack
這個模組進行了快取操作,其中原始碼如下// 通過 lazyFunction 對指定包進行快取操作 const fn = lazyFunction(() => require("./webpack"));
如何實現包的快取?
lazyFunction
其實是使用了閉包的方式對包進行了快取。先看關鍵原始碼,memoize
方法通過閉包的方式將入參中函式fn
的執行結果快取起來,這樣在下次想要獲取fn
函式的返回結果時就無需在執行fn
方法了。const memoize = fn => { let cache = false let result = undefined // 返回一個函式 構造一個 閉包,用於快取 fn 函式的返回值 return () => { if(cache) { return result }else { result = fn() cache = true return result } } }
而
lazyFunction
只是對以上memoize
方法的簡單封裝,使得其返回的方法支援引數傳遞,實際上相當於一次柯里化處理。但是memoize
中並沒有接受其傳入的引數,因此此次封裝並無效果。其實可以直接將memoize
方法進行一步改造,讓其作為返回值的函式接受入參,並傳遞給fn
函式即可達到預期效果,const lazyFunction = factory => { const fac = memoize(factory); const f = (...args) => { return fac()(...args); } return f; };
包的合併
入口檔案中並沒有簡單對依賴進行組合,而是通過
mergeExports
方法對各個模組進行了組裝。該方法主要通過Object.defineProperty
來達到物件組裝的目的,這樣做的目的是,可以更好的對物件的屬性進行控制,如屬性的讀、寫、刪除、列舉。相關原始碼如下getOwnPropertyDescriptors
用於獲取物件上 所有屬性的描述符該方法中使用了遞迴的方式將 物件型別的屬性平鋪開來,這樣使得 原始碼模組特間關係可以保持的同時,降低了模組功能應用的複雜性。
const mergeExports = (obj, exports) => { const descriptors = Object.getOwnPropertyDescriptors(exports); for (const name of Object.keys(descriptors)) { const descriptor = descriptors[name]; if (descriptor.get) { const fn = descriptor.get; Object.defineProperty(obj, name, { configurable: false, enumerable: true, get: memoize(fn) }); } else if (typeof descriptor.value === "object") { Object.defineProperty(obj, name, { configurable: false, enumerable: true, writable: false, // 通過遞迴的方式將 物件型別的 屬性 平鋪開來 value: mergeExports({}, descriptor.value) }); } else { throw new Error( "Exposed values must be either a getter or an nested object" ); } } return /** @type {A & B} */ (Object.freeze(obj)); };