深入淺出webpack -- loader和plugin原理及區別
一、loader原理
1、概念
loader就像一個翻譯員,能將原始檔翻譯後輸出新的結果,並且一個檔案可以鏈式的經過幾個翻譯員。
以.scss檔案為例子:
- 先將.scss檔案內容交給sass-loader翻譯為css
- 在將翻譯後的css交給css-loader,找出css中依賴的資源,壓縮css
- 再將css-loader輸出的內容交給style-loader,轉化為通過指令碼載入的JavaScript程式碼
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
// console.log(path.resolve('webpack.config.js'))
module.exports = {
mode:'development',
entry: './app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
...
{
test: /\.scss$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'style-loader'
}
]
}
]
},
}
webpack是執行在Node.js上面的,一個Loader其實就是一個模組,需要匯出一個函式。
2、自己來實現一個Loader:
a、簡單實現
新建一個test.wy檔案,內容如下
c(89)
根目錄下建一個資料夾
index.js的內容
module.exports = function(source) {
return source.replace('c', 'console.log');
}
webpack.common.js中增加配置
{
test: /\.wy$/,
loader: './wy-loader'
}
index.js中引入 import './test.wy'
npm run build
內容會被轉化為:
給loader設定屬性:
{
test: /\.wy$/,
loader: './wy-loader',
options: {
name: '麥樂'
}
}
wy-loader/index.js
b、使用一個外掛,獲取配置的屬性:
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
console.log(source, options)
return source;
}
上面的loader只是返回了原內容轉換後的內容,在某些情況下還需要返回其它的內容。
以babel-loader轉換es6為例子,需要輸出轉化後的es5和程式碼對應的Source Map,這種情況需要這樣寫:
module.exports = function(source) {
this.callback(null, source, sourceMaps)
return;
}
這樣就告訴webpack內容在callback中不在返回值中。
c、非同步loader
如果處理結果是非同步拿到的,可以這樣來寫loader:
module.exports = function(source) {
var callback = this.async()
someAsyncOperation(source, function(err, result, sourceMaps, ast) {
callback(err, result, sourceMaps, ast)
})
}
以二進位制的格式輸入給 loader
webpack傳遞給Loader的資料格式是utf-8的字串,有的時候需要處理二進位制檔案,例如file-loader, 這是就需要webpack為Loader傳入二進位制的資料。
const loaderUtils = require('loader-utils')
module.exports = function(source) {
console.log(source)
return source;
}
module.exports.raw = true // 將source變成buffer型別
d、快取加速
在某些情況下有寫轉換非常耗時,如果每次構建都執行重複的操作,構建會變得很緩慢,webpack會快取所有Loader處理的結果,在需要處理的檔案和其依賴的檔案 沒有發生變化時,不會重新呼叫Loader去執行轉換。如果讓webpack不快取處理的結果,可以這樣:
module.exports = function(source) {
// 關閉快取功能
this.cacheable(false)
return source;
}
e、載入本地loader
自己開發好的Loader,需要測試下能不能正常執行,配置到webpack中,才可以正確的使用Loader。例如上面的style-loader, 引入的時候是訪問的node_modules中的。這樣就需要把編寫好的loader釋出到npm才可以正常測試,這就會很麻煩。解決問題的辦法有兩種:
- npm link
-
ResolveLoader
假如本地的 Loader 在專案目錄中的 ./wy-loader
中,則需要如下配置:
module.exports = {
resolveLoader:{
// 去哪些目錄下尋找 Loader,有先後順序之分
modules: ['node_modules','./wy-loader'],
}
}
f、其它 Loader API
除了以上提到的在 Loader 中能呼叫的 Webpack API 外,還存在以下常用 API:
-
this.context
:當前處理檔案的所在目錄,假如當前 Loader 處理的檔案是/src/main.js
,則this.context
就等於/src
。 -
this.resource
:當前處理檔案的完整請求路徑,包括 querystring,例如/src/main.js?name=1
。 -
this.resourcePath
:當前處理檔案的路徑,例如/src/main.js
。 -
this.resourceQuery
:當前處理檔案的 querystring。 -
this.target
:等於 Webpack 配置中的 Target -
this.loadModule
:當 Loader 在處理一個檔案時,如果依賴其它檔案的處理結果才能得出當前檔案的結果時, 就可以通過this.loadModule(request: string, callback: function(err, source, sourceMap, module))
去獲得request
對應檔案的處理結果。 -
this.resolve
:像require
語句一樣獲得指定檔案的完整路徑,使用方法為resolve(context: string, request: string, callback: function(err, result: string))
。 -
this.addDependency
:給當前處理檔案新增其依賴的檔案,以便再其依賴的檔案發生變化時,會重新呼叫 Loader 處理該檔案。使用方法為addDependency(file: string)
。 -
this.addContextDependency
:和addDependency
類似,但addContextDependency
是把整個目錄加入到當前正在處理檔案的依賴中。使用方法為addContextDependency(directory: string)
。 -
this.clearDependencies
:清除當前正在處理檔案的所有依賴,使用方法為clearDependencies()
。 -
this.emitFile
:輸出一個檔案,使用方法為emitFile(name: string, content: Buffer|string, sourceMap: {...})
。
其它沒有提到的 API 可以去 Webpack 官網 檢視。
二、plugin原理
1、概念
2、自己實現plugin
Webpack 通過 Plugin 機制讓其更加靈活,以適應各種應用場景。 在 Webpack 執行的生命週期中會廣播出許多事件,Plugin 可以監聽這些事件,在合適的時機通過 Webpack 提供的 API 改變輸出結果。
一個簡單的plugin
class BasicPlugin{
// 在建構函式中獲取使用者給該外掛傳入的配置
constructor(options){
}
// Webpack 會呼叫 BasicPlugin 例項的 apply 方法給外掛例項傳入 compiler 物件
apply(compiler){
compiler.plugin('compilation',function(compilation) {
})
}
}
// 匯出 Plugin
module.exports = BasicPlugin;
使用
const BasicPlugin = require('./BasicPlugin.js');
module.export = {
plugins:[
new BasicPlugin(options),
]
}
Webpack 啟動後,在讀取配置的過程中會先執行 new BasicPlugin(options)
初始化一個 BasicPlugin 獲得其例項。 在初始化 compiler 物件後,再呼叫 basicPlugin.apply(compiler)
給外掛例項傳入 compiler 物件。 外掛例項在獲取到 compiler 物件後,就可以通過 compiler.plugin(事件名稱, 回撥函式)
監聽到 Webpack 廣播出來的事件。 並且可以通過 compiler 物件去操作 Webpack。
相關文章
- Webpack中Loader和Plugin的區別和編寫思路WebPlugin
- 面試題: Webpack 的 plugin 和 loader 有什麼區別面試題WebPlugin
- webpack loader和plugin編寫WebPlugin
- 說說Loader和Plugin的區別?編寫Loader,Plugin的思路?Plugin
- webpack3-loader和plugin的概念理解WebPlugin
- 深入淺出WebpackWeb
- 《深入淺出webpack》有感Web
- 回顧webpack在vue.config.js中寫loader和pluginWebVueJSPlugin
- Webpack原理-編寫PluginWebPlugin
- 深入解析webpack 外掛html-webpack-pluginWebHTMLPlugin
- 深入淺出HTTPS工作原理HTTP
- 深入淺出MyBatis:MyBatis解析和執行原理MyBatis
- Redis Sentinel-深入淺出原理和實戰Redis
- 深入淺出人臉識別原理
- 深入淺出的webpack構建工具---webpack基本配置(一)Web
- 深入淺出 webpack(vue 專案優化)WebVue優化
- 深入淺出,ARCore開發原理
- 深入淺出 Viewport 設計原理View
- 深入淺出的webpack構建工具---PostCss(五)WebCSS
- Webpack 原理淺析Web
- 深入淺出瀏覽器渲染原理瀏覽器
- 深入淺出Service外掛化原理
- webpack系列--淺析webpack的原理Web
- 深入淺出React和ReduxReactRedux
- 淺入淺出webpackWeb
- 深入淺出VACUUM核心原理(中): index by passIndex
- 深入淺出 - vue變化偵測原理Vue
- webpack-loaderWeb
- webpack系列-loaderWeb
- 淺析html webpack plugin外掛的使用教程HTMLWebPlugin
- 深入淺出FE(十四)深入淺出websocketWeb
- 深入淺出的webpack構建工具---AutoDllPlugin外掛(八)WebPlugin
- 深入淺出的webpack構建工具---DevServer配置項(二)WebdevServer
- 深入淺出的webpack構建工具--webpack4+vue搭建環境 (十三)WebVue
- 深入淺出一致性Hash原理
- 深入淺出 PLT/GOT Hook與原理實踐GoHook
- webpack同型別檔案執行不同loader操作Web型別
- webpack loader的"套路"Web