webpack实战之手写一个loader和plugin
webpack實(shí)戰(zhàn)之編寫一個簡易的loader和plugin
- 🔔序言
- 🎵一、如何編寫一個Loader
- 1. 碎碎念
- 2. 項(xiàng)目結(jié)構(gòu)
- 3. 業(yè)務(wù)代碼編寫
- (1)入口文件代碼
- (2)編寫loader
- (3)引用loader
- (4)在loader里面做一些異步的操作
- (5)loader路徑自定義
- 🎶二、如何編寫一個Plugin
- 1. 碎碎念
- 2. 項(xiàng)目結(jié)構(gòu)
- 3. 業(yè)務(wù)代碼編寫
- (1)入口文件代碼
- (2)編寫plugin
- (3)引用plugin
- 💹三、結(jié)束語
- 🐣彩蛋 One More Thing
- (:往期推薦
- (:番外篇
🔔序言
對于 webpack 來說, loader 和 plugin 可以算是需求程度最為廣泛的配置項(xiàng)了。但是呢,單單止步于配置可能還不夠。如果我們自己有時候想要 diy 一個需求,但是 webpack 又沒有相關(guān)的 loader 和 plugin 。那這個時候我們可能就得開始造點(diǎn)輪子來供給自己使用了。
因此,在今天的文章當(dāng)中,將帶領(lǐng)大家手寫一個簡易的 loader 和 plugin ,并學(xué)會如何在項(xiàng)目中運(yùn)用自己所編寫的 loader 和 plugin 。
一起來學(xué)習(xí)吧~📢
🎵一、如何編寫一個Loader
1. 碎碎念
之前的文章中我們講到了關(guān)于 loader 的一些配置。那如果把那些引用的 loader 改為我們寫的 loader ,該怎么處理呢?
現(xiàn)在,我們來了解一下,如何手寫一個簡易的 loader ,并運(yùn)用到我們的項(xiàng)目當(dāng)中。
2. 項(xiàng)目結(jié)構(gòu)
首先用一張圖,來看我們的項(xiàng)目結(jié)構(gòu)。如下圖所示:
其中 loaders 文件夾下放置我們想要寫的 loader ,同時里面的 replaceLoader.js 文件放置我們即將要寫的 loader 的代碼邏輯。之后,index.js 文件是我們的入口文件,放置我們的業(yè)務(wù)邏輯。 webpack.config.js 文件放置關(guān)于 webpack 的相關(guān)配置,而 dist 文件夾內(nèi)的內(nèi)容,放置的是我們通過 webpack 打包后,生成的打包文件。
3. 業(yè)務(wù)代碼編寫
(1)入口文件代碼
現(xiàn)在,我們先來編寫入口文件 index.js 的代碼。具體代碼如下:
console.log('hello monday');(2)編寫loader
入口文件的內(nèi)容很簡單,我們想要達(dá)到的目的就是輸出 hello monday 這個語句。現(xiàn)在,我們來編寫 loader 的內(nèi)容,已達(dá)到對入口文件 index.js 的內(nèi)容進(jìn)行修改。 replaceLoader.js 文件的代碼具體如下:
module.exports = function(source) {const result = source.replace('monday', 'mondaylab');this.callback(null, result); }以上的代碼意思為,將入口文件 index.js 文件中的 monday 替換為 mondaylab 。這樣寫似乎沒啥問題,但是大家有沒有想過,我們有時候傳的屬性可能會很詭異,不一定每次都能像這樣以字符串的形式來替換。
所以,我們引用 webpack 官方推薦的 loadertils 這個工具,來解決這個問題。
第一步: 安裝 loader-utils 插件。具體命令如下:
npm install loader-utils --save-dev第二步: 改造 loader 文件。接下來,我們對 replaceLoader.js 文件進(jìn)行改造升級,具體代碼如下:
const loaderUtils = require('loader-utils');//用function的原因在于為了業(yè)務(wù)層可以調(diào)用this //source為引入文件的源代碼 module.exports = function(source) {//getOptions會自動地幫我們分析this.query,然后把參數(shù)的所有內(nèi)容放在options里面去const options = loaderUtils.getOptions(this);const result = source.replace('monday', options.name);this.callback(null, result); }大家可以看到,通過使用 loaderUtils 插件,間接地,調(diào)用 getOptions 方法,來自動的幫我們分析 this.query ,從而取到我們想要的內(nèi)容。
值得注意的是,我們還需要再了解一下 this.callback 的內(nèi)容。
一般情況下,如果我們接收到了源代碼 source ,那么現(xiàn)在我們只能對源代碼做處理。但是呢,有的時候,我們想要使用一些 sourceMap ,或者對源代碼分析好了之后,我們不僅想要返回源代碼,還要把 sourceMap 也帶回去。
因?yàn)槲覀?return 的時候只能 return 一個參數(shù),其余的一些額外的內(nèi)容就帶不出去了。這個時候呢,我們就需要 this.callback 來幫我們把 sourceMap 給帶出去。因此,一般用 this.callback 來返回內(nèi)容。
(3)引用loader
現(xiàn)在,我們在 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中的nameoptions: {name: 'mondaylab'}} ]}]},output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js' } }通過以上方式,我們寫了一個簡易的 loader ,這個 loader 實(shí)現(xiàn)了將 monday 替換為 mondaylab 的功能。并且供我們在 webpack 中使用自己書寫的 loader 。
(4)在loader里面做一些異步的操作
好了現(xiàn)在,如果我們想要給 loader 做一些異步操作,該怎么實(shí)現(xiàn)呢?
在我們所寫的 loader 當(dāng)中,加入異步操作,那么我們需要調(diào)用官方提供給我們的 this.async() 這個 API 來實(shí)現(xiàn)。現(xiàn)在,我們來改造一下 replaceLoader.js 文件的代碼。具體代碼如下:
const loaderUtils = require('loader-utils');module.exports = function(source) {const options = loaderUtils.getOptions(this);//調(diào)用this.async()這個API,來給異步代碼使用const callback = this.async();setTimeout(() => {const result = source.replace('monday', options.name);callback(null, result);}, 1000); }通過這種方式,我們就可以在 loader 中編寫異步代碼,來達(dá)到我們想要的效果。
(5)loader路徑自定義
有一個很小的注意點(diǎn)就是,當(dāng)我們在配置 webpack.config.js 文件中, loader 的路徑時,每回都要 path.resolve 去尋找路徑文件。文件少的時候還好,但如果遇到多文件的時候呢?豈不是會很麻煩。
所以,我們引用 resolveLoader 來簡化它。現(xiàn)在我們在 webpack.config.js 文件中進(jìn)行改造。具體配置如下:
const path = require('path');module.exports = {// 先到node_modules中去找,找不到則去./loaders目錄下去找resolveLoader: {modules: ['node_modules', './loaders']},module: {rules: [{test: /\.js/,use: [{loader: 'replaceLoader'}]}]} }通過配置 resolveLoader ,來對文件文件目錄進(jìn)行查找,從而簡化了路徑內(nèi)容。
🎶二、如何編寫一個Plugin
1. 碎碎念
在講解 plugin 之前,我們先來了解 loader 和 plugin 的區(qū)別。
當(dāng)我們在源代碼里面,去引入一個新的 js 文件,或者是一個其他格式的文件時,這個時候我們可以借用 loader ,來幫我們處理我們引用的 loader 文件。 loader 的作用就在于,幫助我們處理引用的模塊。
而 plugin 呢,是當(dāng)我們在做打包的時候,在某些具體時刻上,比如說,當(dāng)我們打包結(jié)束之后,我們要生成一個 html 文件,這個時候,我們就可以使用一個 htmlWebpackPlugin 的插件。使用它之后,他就會在打包結(jié)束之后,幫我們生成對應(yīng)的 html 文件。
再比如,我們要在打包之前,把 dist 目錄進(jìn)行清空,這個時候我們就可以使用 cleanWebpackPlugin 來幫助我們做這件事情。
所以, plugin 插件,在什么時候生效呢?
它在我們打包過程中的某些時刻里,就是插件生效的場景。
plugin 的編寫相對于 loader 來說,會難一點(diǎn)點(diǎn)。但是呢,如果有看過 webpack 源碼的小伙伴們可能會知道, webpack 的一些底層原理都是依據(jù) plugin 來進(jìn)行編寫的。所以,我們還是有必要來學(xué)習(xí)一下 plugin 的編寫。
下面就帶領(lǐng)大家來編寫一個簡易的 plugin ~
2. 項(xiàng)目結(jié)構(gòu)
對于 webpack 的 plugin 來說,它是是基于發(fā)布者訂閱的設(shè)計模式,也可以說是基于事件驅(qū)動來實(shí)現(xiàn)的。在這個事件驅(qū)動里,代碼之間的執(zhí)行,是通過事件來進(jìn)行驅(qū)動的。
接下來,我們就來寫一個簡易的 plugin 。
首先用一張圖,來看我們的項(xiàng)目結(jié)構(gòu)。如下圖所示:
其中 plugins 文件夾下放置我們想要寫的 plugin ,同時里面的 copyright-webpack-plugin.js 文件放置我們即將要寫的 plugin 的代碼邏輯。之后,index.js 文件是我們的入口文件,放置我們的業(yè)務(wù)邏輯。 webpack.config.js 文件放置關(guān)于 webpack 的相關(guān)配置,而 dist 文件夾內(nèi)的內(nèi)容,放置的是我們通過 webpack 打包后,生成的打包文件。
3. 業(yè)務(wù)代碼編寫
(1)入口文件代碼
現(xiàn)在,我們先來編寫入口文件 index.js 的代碼。具體代碼如下:
console.log('hello monday');(2)編寫plugin
現(xiàn)在,我們來編寫 plugin 的內(nèi)容, copyright-webpack-plugin.js 文件的代碼具體如下:
class CopyrightWebpackPlugin {//編寫一個構(gòu)造器constructor(options) {console.log(options)}apply(compiler) {//遇到同步時刻compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {console.log('compiler');});//遇到異步時刻//當(dāng)要把代碼放到dist目錄之前,要走下面這個函數(shù)//Compilation存放打包的所有內(nèi)容,Compilation.assets放置生成的內(nèi)容compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, cb) => {debugger;// 往代碼中增加一個文件,copyright.txtCompilation.assets['copyright.txt'] = {source: function() {return 'copyright by monday';},size: function() {return 19;}};cb();})} }module.exports = CopyrightWebpackPlugin;上面的這個插件中想要實(shí)現(xiàn)的功能就是,獲取版權(quán)信息。
(3)引用plugin
現(xiàn)在,我們在 webpack.config.js 中,來引入我們上面的 plugin 。具體配置如下:
const path = require('path'); const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin');module.exports = {mode: 'development',entry: { main: './src/index.js'},plugins: [new CopyrightWebpackPlugin({name: 'monday'})],output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js'} }通過上述代碼,我們可以了解到,在(2)中,我們首先需要定義一個類,之后呢,在類中寫一個構(gòu)造器和一個 apply() 方法來調(diào)用。然后呢,大家看到(3),通過 require 的方式,來進(jìn)行 new 實(shí)例 ,實(shí)例化一個插件,從而在項(xiàng)目中使用這個插件。
最終,我們項(xiàng)目進(jìn)行打包時,就會生成一個 dist 目錄,并且在目錄下增加一個 copyright.txt 文件,并且文件中的內(nèi)容就是 copyright by monday 。
💹三、結(jié)束語
在上面的文章中,講解了關(guān)于loader和plugin的基本編寫思路,以及如何在項(xiàng)目中對他們進(jìn)行運(yùn)用,相信大家對這一塊內(nèi)容有了基礎(chǔ)的認(rèn)識。
到這里,loader和plugin的編寫講解就結(jié)束啦!希望對大家有幫助~
如文章有誤或有不理解的地方,歡迎小伙伴們評論區(qū)留言哦💬
本文代碼已上傳至公眾號,后臺回復(fù)關(guān)鍵詞 webpack 即可獲取~
🐣彩蛋 One More Thing
(:往期推薦
webpack入門核心知識👉不會webpack的前端可能是撿來的,萬字總結(jié)webpack的超入門核心知識
webpack入門進(jìn)階知識👉webpack入門核心知識還看不過癮?速來圍觀萬字進(jìn)階知識
webpack實(shí)戰(zhàn)案例配置👉萬字總結(jié)webpack實(shí)戰(zhàn)案例配置
(:番外篇
-
關(guān)注公眾號星期一研究室,第一時間關(guān)注優(yōu)質(zhì)文章,更多精選專欄待你解鎖~
-
如果這篇文章對你有用,記得留個腳印jio再走哦~
-
以上就是本文的全部內(nèi)容!我們下期見!👋👋👋
總結(jié)
以上是生活随笔為你收集整理的webpack实战之手写一个loader和plugin的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 豆油的功效与作用、禁忌和食用方法
- 下一篇: 玉米渣的功效与作用、禁忌和食用方法