如何編寫一個Loader

直接上例子,簡單易懂!

創建一個demo(make_loader)

  • 新建一個文件,名字爲make_loader
  • 在make_loader目錄下初始化項目:npm init -y
  • 安裝webpack、webpack-li:npm i webpack webpack-cli -D
  • 在make_loader目錄下新建src目錄,寫業務代碼
  • 在make_loader目錄下新建一個loaders文件夾,用來書寫loader代碼
  • 在make_loader目錄下新建webpack.config.js配置文件

目錄結構如下:

|--make_loader
	|--node_modules
	|--src
		|--index.js
	|--loaders
		|--replaceLoader.js
	|--package-lock.json
	|--package.json
	|--webpack.config.js

src/index.js中的業務代碼:

 console.log('hello xiaochengzi');

在loaders/replaceLoader.js文件裏書寫我們的loader代碼:

module.exports = function(source) { // 這裏不能用箭頭函數
  return source.replace('xiaochengzi', this.query.name);
}
  • loader對外暴露的函數,切記不能使用箭頭函數。因爲在這個函數裏,我們會用到this指向,webpack在調用loader的時候,會把this做一些變更。變更之後才能用this裏的一些方法,如果寫成箭頭函數,這裏的this指向就會有問題,所以這裏的function一定是聲明式的function
  • 參數source爲我們引入文件的源代碼
  • 在這裏拿到代碼之後,就可以把代碼做一個變更,然後再返回出去
  • 可以通過this.query取到傳遞的options內的一些參數

在webpack.config.js中做loader的使用配置:

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js/,
      use: [
        {
          loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
          options: {
            name: 'world' // 傳遞給loader的參數
          }
        }
      ] // 使用自己寫的loader模塊 
    }] 
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}
  • 在module中配置loader使用規則
  • 在options中傳遞loader參數

執行打包:npm run build

打包後的文件爲:hellow world

由此可見,我們在業務代碼中打印的 ‘hello xiaochengzi’ 通過我們編寫的loader替換成了 ‘hello world’。
到此,一個最最簡單的loader就編寫好了~
————————— ?———————————

loader中常用的API

使用loader-utils分析參數:

// loader-utils模塊需要單獨使用npm下載安裝
const loaderUtils = require('loader-utils');
module.exports = function(source) { 
  // 使用loader-utils中的getOptions接收loader參數
  const options = loaderUtils.getOptions(this);
  return source.replace('xiaochengzi', options.name);
}

callback的使用:

const loaderUtils = require('loader-utils');
module.exports = function(source) { 
  const options = loaderUtils.getOptions(this);
  const result = source.replace('xiaochengzi', options.name);
  this.callback(null, result);
}

對於callback回調函數,官方解釋是這樣的:

如果loader裏要寫一些異步的代碼的時候,需要先聲明:

const loaderUtils = require('loader-utils');
module.exports = function(source) { 
  const options = loaderUtils.getOptions(this);
  const callback = this.async(); // 聲明一下異步操作
  setTimeout(() => {
    const result = source.replace('xiaochengzi', options.name);
    callback(null, result); // 在回調裏返回結果
  }, 1000)
}
  • 使用 this.async() 進行異步聲明操作。

更多loader-API請參考官方文檔:loader-API

編寫打包多個loader時webpack.config.js配置:

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js/,
      use: [
        {
          loader: path.resolve(__dirname, './loaders/replaceLoader.js')
        },
        {
          loader: path.resolve(__dirname, './loaders/replaceLoaderAsync.js'),
          options: {
            name: 'world'
          }
        }
      ] // 使用自己寫的loader模塊 
    }] 
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}  
  • 由於loader從下向上執行,所以先執行replaceLoaderAsync.js,再執行replaceLoader.js。

在loaders文件夾下新增replaceLoaderAsync.js文件:(第一個loader)

const loaderUtils = require('loader-utils');
module.exports = function(source) { 
  const options = loaderUtils.getOptions(this);

  const callback = this.async(); // 聲明一下異步操作
  setTimeout(() => {
    const result = source.replace('xiaochengzi', options.name);
    callback(null, result); 
  }, 1000)
}
  • 使用異步代碼把我們打印的信息從 ‘hello xiaochengzi’ 替換爲 ‘world’.

./loaders/replaceLoader.js: (第二個loader)

module.exports = function(source) { 
  return source.replace('world', 'ranran')
}

再次執行打包:npm run build

  • 根據配置好的loader,我們打印的信息將通過replaceLoaderAsync.js從 ‘hello xiaochengzi’ 替換爲 ‘hello world’ ,最終再通過replaceLoader.js替換爲 ‘hello ranran’。

知識補充

當配置多個自己編寫的loader時,每次都需要使用path.resolve來指定路徑讀取loader,如:

其實,我們可以通過其他配置,去除這部分冗餘代碼:(如)

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  resolveLoader: {
    modules: ['node_modules', './loaders']
  }, // 當你去使用loader的時候,它會幫你去做一些事情
  module: {
    rules: [{
      test: /\.js/,
      use: [
        {
          loader: 'replaceLoader'
        },
        {
          loader: 'replaceLoaderAsync',
          options: {
            name: 'world'
          }
        }
      ] // 使用自己寫的loader模塊 
    }] 
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}  
  • webpack中的 resolveLoader 當你去使用loader的時候,它會幫你去做一些事情
    • modules: [‘node_modules’, ‘./loaders’] 指當你使用loader的時候,webpack首先會去node_modules文件夾下找對應的loader模塊,如果找不到就會去loaders文件夾下去找。

執行打包:npm run build

最終打印爲 ‘hello ranran’ ,成功執行。

總結:loader在我們項目開發中的用處還是比較大的,比如:代碼的異常捕獲、中英文網站的切換等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章