面試官:你使用webpack時手寫過loader,分離過模組嗎?

Kev1nzh發表於2019-04-16

面試官:你使用webpack時手寫過loader,分離過模組嗎?

前言


大家好,之前出了一篇面試篇webpack入門,這篇文章繼續介紹接下來更深入東西。

概覽

  • 如何載入自己的loader
  • 使用loader-utils,schema-utils
  • 編寫自己的loader
  • 擴充套件

如何載入自己的loader

講道理大家都是直接import一個loader或者使用webpack內建的loader的。

如果除錯自己的loader,應該如下寫法:

//webpack.config.js
const path = require("path");
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: path.resolve("loader.js"),
                        options: {
                            test: 'apple'
                        }
                    }
                ]
            }
        ]
    }
};
複製程式碼

其實和正常載入loader一樣,只是這裡指向的是本地檔案的路徑。

path.resolve('loader.js') 得出路徑

/Users/kev1nzh/Desktop/my/webpack/loader.js

使用單個loader
在使用一個loader的時候,loader會接收你正則匹配的資原始檔(如上,所有js檔案)的字串。

loader通過程式碼轉化模組後,最後返回傳遞出去。
複製程式碼
使用多個loader
  • 當使用多個loader的時候,從傳入loader陣列的最後一個開始反向傳入資原始檔字串。

  • 最後一個loader接收最原始的資原始檔字串,轉化後傳入下一個lodaer。

  • 中間的loader接收上一個loader,轉化後傳入下一個。

  • 第一個loader最後接收轉化,並傳出所有loader處理完的資原始檔字串。

{
     test: /\.css$/,
     use: [
         {
             loader: 'css-loader'
         },
         {
             loader: 'style-loader'
         },
     ]
}
//style-loader接收所有css的檔案,轉化完再傳給css-loader,轉化完後再懟出來。
複製程式碼

使用loader-utils,schema-utils

loader-utils, schema-utils是webpack的loader工具庫,有很多便捷的方法可以呼叫。

const { getOptions,stringifyRequest, parseQuery  } = require("loader-utils");
const validateOptions = require("schema-utils");
const schema = {
    type: "object",
    properties: {
        test: {
            type: "string"
        }
    }
};
module.exports = function(source) {
    //getOptions 用於在loader裡獲取傳入的options,返回的是物件值。
    const options = getOptions(this);
    
    // stringifyRequest轉換路徑,避免require()或impot時使用的絕對路徑
    stringifyRequest(this, "./test.js"); //   Result =>  "\"./test.js\""
    
    //parseQuery獲取query引數的,這個很簡單就不說啦
    parseQuery('?name=kev&age=14') // Result => {name: 'kev', age: '14'}
    
    //驗證引數的型別是否正確。
    validateOptions(schema, options, "loader");
};
複製程式碼

編寫自己的loader

總算到手寫環節了!!!!

//webapck.config.js
const path = require("path");

module.exports = {
    entry: "./src",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "package.js"
    },
    mode: "production",
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: path.resolve("loader.js"),
                        options: {
                            work: '996',
                            sick: 'ICU',
                        }
                    }
                ]
            }
        ]
    }
};
複製程式碼

首先載入工具庫,為了後續使用。

第一步先驗證options是否符合型別,

第二步獲取引數,然後替換傳入的資原始檔字串。

//loader.js
const { getOptions } = require("loader-utils");
const validateOptions = require("schema-utils");

const schema = {
    type: "object",
    properties: {
        work: {
            type: 'String'
        },
        sick: {
            type: 'String'
        }
    }
};

module.exports = function(source) {

    const options = getOptions(this);
    validateOptions(schema, options, 'loader');

    const  {work, sick} = options;
    source = source.replace(/\[work\]/g, work).replace(/\[sick\]/g, sick);

    return `export default ${JSON.stringify(source)}`;
};
複製程式碼

展示下要轉換的js。

// src/index.js
console.log('工作[work] 生病[sick] 加班不規範 親人兩行淚');
複製程式碼

最後在命令列,webpack!!!!!

面試官:你使用webpack時手寫過loader,分離過模組嗎?


擴充套件

webpack是如何執行的?
const index = require('./index');
const console = require('./console');

//index.js
const axios = require('./scripts/debounce.js'');
const moment = require('moment');
// do something
複製程式碼
  1. webpack會解析所有模組,如果模組中有依賴其他檔案,那就繼續解析依賴的模組。直到檔案沒有依賴為止。
  2. 解析結束後,webpack會把所有模組封裝在一個函式裡,並放入一個名為modules的陣列裡。
  3. 將modules傳入一個自執行函式中,自執行函式包含一個installedModules物件,已經執行的程式碼模組會儲存在此物件中。
  4. 最後自執行函式中載入函式(webpack__require)載入模組。

分離程式碼

// ./src/moment.js
const moment = require('moment');
console.log(moment().format('MMMM Do YYYY, h:mm:ss a'))

// ./index.js
const momentJs = require('./src/moment');
console.log(123);
複製程式碼

如上程式碼,我們打包一下試試看。

面試官:你使用webpack時手寫過loader,分離過模組嗎?
兩個檔案有依賴關係,所以打包後,都會把moment模組打包進去。

SplitChunksPlugin

webpack4.x的分離程式碼方法,之前的CommonsChunkPlugin外掛已被移除。 此模組開箱即用,預設情況下,它僅影響按需塊,因為更改初始塊會影響HTML檔案應包含的指令碼標記以執行專案。

webpack將根據以下條件自動拆分塊:

  • 可以共享新塊或來自該node_modules資料夾的模組
  • 新塊將大於30kb(在min + gz之前)
  • 根據需要載入塊時的最大並行請求數將小於或等於5
  • 初始頁面載入時的最大並行請求數將小於或等於3
  • 當試圖滿足最後兩個條件時,首選更大的塊。

讓我們看下程式碼如何實現!

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'initial',   //選擇哪些模組需要優化, 引數為 all、async、initial
      minSize: 30000,  // 要生成的塊的最小數
      maxSize: 0,  //要生成的塊的最大數
      minChunks: 2, // 分割前共享模組的最小塊數
      maxAsyncRequests: 5, //按需載入時的最大並行請求數
      maxInitialRequests: 3, // 入口的最大並行請求數
      automaticNameDelimiter: '~',  //指定生成檔名當中的分隔符
      name: true, //拆分塊的名稱
      cacheGroups: { //快取組
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};
複製程式碼

面試官:你使用webpack時手寫過loader,分離過模組嗎?


好了這篇需要講的東西已經結束了。

面試系列第一篇: 面試官:你知道Callback Hell(回撥地獄)嗎?

面試系列第二篇: 面試官:react和vue有什麼區別嗎?

面試系列第三篇: 面試官:你瞭解es6的知識嗎?

面試系列第四篇: 面試官:你瞭解Webpack嗎?

如果您有收穫或者疑問請在下方評論,求贊!謝謝觀看到這裡。

相關文章