編寫loader和plugin

原文地址

編寫loader和plugin

github

一、loader

1.loader 介紹

loader 是什么

loader 其實是一個函數(shù),對匹配到的內(nèi)容進行轉(zhuǎn)換,將轉(zhuǎn)換后的結(jié)果返回。

loader 作用

webpackloader 就像是一位翻譯官。webpack 只認識 JavaScript 這們語言,對于其他的資源通過 loader 后可以轉(zhuǎn)化做預處理

  • loader 的執(zhí)行是有順序的,支持鏈式的調(diào)用。loader的執(zhí)行順序是從下到上,從右到左。比如處理樣式類的文件,use:['style-loader', 'css-loader']css-loader處理后的文件返回給 style-loader。
  • 一個 Loader 的職責是單一的,只需要完成一種轉(zhuǎn)換。
  • Webpack 會默認緩存所有 Loader 的處理結(jié)果,對沒有修改的 loader 不會重新加載,關(guān)閉webpack 的默認緩存結(jié)果需要添加this.cacheable(false);

常見的loader

  • 樣式類的 loader:css-loader, style-loader, less-loader, postcss-loader(添加-webkit)
  • 文件類的 loader:url-loader, file-loader, raw-loader等。
  • 編譯類的 loader:babel-loader, ts-loader
  • 校驗測試類 loader:eslint-loader, jslint-loader

4. loader 的三種使用方式

    1. webpack.config.js 中配置
module.exports = {
    module:{
        rules:[{
                test:/\.css$/,
                use:['css-loader'],
                // use:{loader:'css-loader',options:{}}
            }
        ]
    }
}
    1. 通過命令行的參數(shù)方式
webpack --module-bind 'css=css-loader'
    1. 通過內(nèi)聯(lián)使用
import txt from 'css-loader!./file.css';

2. 編寫一個 loader

思路:前面我們說過 1.loader 是一個函數(shù);2.對匹配到的內(nèi)容進行轉(zhuǎn)換;3.再將轉(zhuǎn)換內(nèi)容返回,按照這個思路我們可以編寫一個最簡單的loader

// 在 ./loader/replaceLoader.js 創(chuàng)建一個替換字符串的 loader
module.exports = function(source) {
    return source.replace('a', 'b')
}

// 在webpack.config.js 使用 自己寫的loader
module.exports = {
    module:{
        rules:[{
            test:"/\.js$/",
            use:[{
                    loader: path.resolve(__dirname, './loader/replaceLoader.js')
                    options:{
                        name: '林一一'   
                    }
                }
            ]
        }]
    }
}

// 或者使用 replaceLoader
module.exports={
    resolveLoader:['node_modules', './loader']
    module:{
        rules:[{
            test:"/\.js$/",
            use:['resolveLoader']
        }]
    }
}

上面就是一個最簡單的編寫 loader 的案例

  • loader 還可以接收 options 傳入的參數(shù),詳情查看 loader API,也可以使用官方提供的 loader-util 接收參數(shù)
const loaderUtil = require('loader-utils')
module.exports = function(source) {
    console.log(this.query.name) // 林一一
    const options = loaderUtil.getOptions(this)
    return source.replace('a', 'b')
}
  • 異步:loader 是一個函數(shù)自然有同步和異步的區(qū)分。使用異步的loader需要添加 this.async() 申明異步操作
const loaderUtils = require('loader-utils')
module.exports = function(source) {
    const options = loaderUtils.getOptions(this)
    const callback = this.async()
    setTimeout(()=>{
        console.log(options.name)
        let res = source.replace('a', options.name)
        callback(null, res, sourceMaps, ast)
    }, 4000)
}

上面的代碼會在4秒后打包成功,如果沒有 this.async() 異步操作就會失敗,callback() 回調(diào)函數(shù)將結(jié)果放回。

  • 默認情況下 webpack 給 loader 傳遞的字符串編碼是 utf-8,如果需要處理二進制的文件需要添加exports.raw = true。
  • 上面提到過 webpack 會默認將loader的加載結(jié)果緩存如果需要關(guān)閉webpack的緩存結(jié)果需要添加this.cacheable(false);。
  • Npm link 專門用于開發(fā)和調(diào)試本地 Npm 模塊,在沒有發(fā)布到 npm 上面也可以在調(diào)式本地的loader。具體需要在package.json 中配置 本地loader,在根目錄下執(zhí)行npm link loader-name 就可以在node_modules中使用本地的loader了。同時也可以采用上面的resolveLoader 實現(xiàn)導入 loader 的方式

總結(jié)編寫 loader 的思路

  1. loader 是一個導出函數(shù),有返回值,可以借助第三方模塊和Node api 實現(xiàn)。
  2. loader 可以使用 loader-utils 接收到options 中傳遞過來的參數(shù)
  3. loader 的異步編寫需要顯示的申明 const callback = this.async() 表明異步。
  4. loader 如果需要處理二進制文件也需要聲明exports.raw = true
  5. loader 的允許結(jié)果會被webpack緩存,如果需要關(guān)閉webpack的緩存結(jié)果需要聲明this.cacheable(false)
  6. 編寫后的本地loader 可以借助 Npm linkresolveLoader 導入。

二、webpack 的構(gòu)建流程

再講 plugins 之前需要先清楚 webpack 的構(gòu)建流程是怎樣的

  1. 初始化參數(shù)。從配置文件和 shell 語句中合并的參數(shù)
  2. 開始編譯。將上一步得到的參數(shù)初始化成 complier對象,加載所有的導入插件,執(zhí)行對象的 run 方法開始執(zhí)行編譯;
  3. 確定入口。從配置的 entry 入口找出所有的入口文件。
  4. 編譯模塊。根據(jù)入口文件的依賴,調(diào)用所有配置的loader進行轉(zhuǎn)換。
  5. 完成模塊編譯并輸出。根據(jù)入口文件之間的依賴關(guān)系,形成一個個代碼塊 chunk
  6. 輸出完成。將形成的代碼塊 chunk 輸出到文件系統(tǒng)。

上面初始化形成的 complier對象 會被注入到插件的 apply(complier)內(nèi)。complier對象對象包含了 Webpack 環(huán)境所有的的配置信息比如 options, loaders, plugins等等屬性,可以簡單的認為complier是 webpack 的實例,通過compler.plugin()可以監(jiān)聽到 webpack 廣播出來的事件。

三、plugin

1 plugin 介紹

plugin 是什么

plugin 是一個插件,這個插件也就是一個類,基于事件流框架 Tapable 實現(xiàn)。在 webpack 的構(gòu)建流程中在初始化參數(shù)后,就會加載所有的 plugin 插件,創(chuàng)建插件的實例。

plugin 作用

plugin 通過鉤子可以涉及到 webpack 的整一個事件流程。也就是說 plugin 可以通過監(jiān)聽這些生命周期的鉤子在合適的時機使用 webpack 提供的API 做一些事情。

常見的 plugin

  • html-webpack-plugin 會在打包后自動生成一個 html 文件,并且會將打包后的 js 文件引入到html 文件內(nèi)。
  • optimize-css-assets-webpack-plugin 對CSS 代碼進行壓縮。
  • mini-css-extract-plugin。將寫入 style 標簽內(nèi)的 css 抽離成一個 用 link 導入 生成的 CSS 文件
  • webpack-parallel-uglify-plugin。開啟多進程執(zhí)行代碼壓縮,提高打包的速度。
  • clean-webpack-plugin。每次打包前都將舊生成的文件刪除。
  • serviceworker-webpack-plugin。為網(wǎng)頁應(yīng)用增加離線緩存功能。

plugin 的使用方式

plugins中使用

const ServiceworkerWebpackPlugin = require('serviceworker-webpack-plugin')
module.exports = {
    plugins:[
        new ServiceworkerWebpackPlugin(),
    ]
}

2 編寫一個 plugin

思路:plugins 是一個類,webpack 為 plugin 提供了很多內(nèi)置的 api,需要在原型上定義 apply(compliers) 函數(shù)。同時指定要掛載的 webpack 鉤子。

class MyPlugin {
    constructor(params){
        console.log(params)
    }
    // webpack 初始化參數(shù)后會調(diào)用這個引用函數(shù),闖入初始化的 complier對象。
    apply(complier){
         // 綁定鉤子事件
        // complier.hooks.emit.tapAsync()
        compiler.plugin('emit', compilation => {
            console.log('MyPlugin')
        ))
    }
}
module.export = MyPlugin

compilation對象 包含當前的模塊資源、編譯生成資源、和能監(jiān)聽變化的文件。每一個文件發(fā)生變化后,都會生成一個 compilation 對象,通過 compilation 也能讀取到 compiler 對象。
調(diào)式plugin 可以使用 node 的調(diào)式工具在 package.json 中添加 "debug":"node --inspect --inspect brk node_modules/webpack/bin/webpack.js"

總結(jié)編寫 plugin 的思路

  1. 編寫一個 class 類 。
  2. 在類中定義一個 apply 方法。
  3. 在應(yīng)用方法apply()中指定掛載的 webpack 事件鉤子complier.hooks.。
  4. 處理 webpack 內(nèi)部實例的特定數(shù)據(jù)。
  5. 功能完成后調(diào)用 webpack 提供的回調(diào)。

四、面試題

1. loader 和 plugin 的區(qū)別

  1. loader 是一個函數(shù),用來匹配處理某一個特定的模塊,將接收到的內(nèi)容進行轉(zhuǎn)換后返回。在webpack 中操作文件,充當文件轉(zhuǎn)換器的角色。在 modules.rules 中配置。
  2. plugin 是一個插件,不直接操作文件,基于事件流框架 Tapable 實現(xiàn),plugin 通過鉤子可以涉及到 webpack 的整一個事件流程。也就是說 plugin 可以通過監(jiān)聽這些生命周期的鉤子在合適的時機使用webpack 提供的API 做一些事情。在plugins中配置插件

2. loader 的編寫思路

參上

3. plugin 的編寫思路

參上

4. complier 和 compilation 區(qū)別

  1. complier 對象暴露了 webpack 整一個生命周期相關(guān)的鉤子,是 webpack 初始化的參數(shù)的產(chǎn)物,包含options, entry, plugins等屬性可以簡單的理解為webpack的一個實例。
  2. compilation 對象是 complier 的實例,是每一次 webpack 構(gòu)建過程中的生命周期對象。每一個文件發(fā)生變化后都能生成一個complition對象。
    總結(jié):兩個對象都有自己的生命周期鉤子,compilation 對象 負責的是粒度更小的生命周期鉤子。compiler對象是webpack整一個整個生命周期鉤子的對象。

參考

webpack之loader和plugin簡介

webpack 構(gòu)建流程

webpack loader和plugin編寫

深入Webpack-編寫Loader

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容