每天讀一點webpack-001

語言: CN / TW / HK

每天讀一點webpack

day-01-webpack入口

  • npm包我們可以通過 package.json 中的包資訊檔案快速的定位到這個包的入口檔案,通過 webpack 的包資訊 main 欄位可以得到整個 webpack 專案的入口位置為 lib/index.js

入口檔案

  • 入口檔案中通過動態匯入的方式將專案的各個模組進行整合,如各種內部外掛,內部依賴,內部配置資訊等。

包的快取

  • 在入口檔案中通過一個快取方法 lazyFunctionwebpack 這個模組進行了快取操作,其中原始碼如下

    // 通過 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));
      };