loader
一、loader 是什么舶担,有什么用
是 webpack 用于在編譯過程中解析各類文件格式,并輸出彬呻;
本質(zhì)上就是一個 node 模塊衣陶,通過寫一個函數(shù)來完成自動化的過程;
由此我們就可以在開發(fā)模式下闸氮,通過解析各類前端無法解析的文件格式剪况,然后將其解析后返回為對象或字符串供前端開發(fā)時使用,在 webpack 的編譯過程中自動會將我們前端項目中引用的文件格式對應(yīng)到指定 loader 解析后輸出蒲跨。
二译断、怎么寫一個自定義 loader
接下來我將寫一個非常簡單的 loader 來解析 txt 文件,它將滿足以下功能
讀取 txt 文件內(nèi)容并輸出為一個對象或悲,對象內(nèi)包括文件內(nèi)容與文件名
讀取 webpack loader 選項孙咪,將內(nèi)容中的 [name] 替換為我們選項 name 的值
// webpack.config.js
module.exports = {
//...
module: {
rules: [
{
test: /\.txt$/,
use: {
loader: path.resolve(__dirname, './txt-loader.js'),
options: {
name: 'YOLO'
}
}
}
]
}
}
// txt-loader.js
var utils = require('loader-utils')
module.exports = function (source) {
const options = utils.getOptions(this)
source = source.replace(/\[name\]/g, options.name)
return `export default ${ JSON.stringify({
content: source,
filename: this.resourcePath
}) }`
}
// 測試在項目中 import 的 txt 文件
test loader output from [name]
// 前端引用
import test from '@/public/test.txt'
// 在瀏覽器中 log 出結(jié)果,因為 txt 中保存時自動換行了隆箩,所以大家可以看到存在一個換行符
{
content: "test loader output from YOLO?"
filename: "/Users/yanglu/Desktop/Programers/test-loader/src/public/test.txt"
}
Plugin
一该贾、Plugin 是什么,有什么用
是 webpack 用于在編譯過程中利用鉤子進(jìn)行各種自定義輸出的函數(shù)捌臊;
本質(zhì)上就是一個 node 模塊杨蛋,通過寫一個類來使用編譯暴漏出來的鉤子實現(xiàn)編譯過程的可控;
由此我們就可以在開發(fā)模式下理澎,可以通過監(jiān)聽編譯過程的各個鉤子事件來完成如釋出模版逞力,對 js、css糠爬、html 進(jìn)行壓縮寇荧、去重等各類操作,結(jié)束后釋出對應(yīng)文件等等你可以想到的任何操作
二执隧、先說點細(xì)節(jié)揩抡,再說怎么寫一個自定義 plugin
webpack 的插件簡單來說就是在函數(shù)中通過調(diào)用 webpack 執(zhí)行的鉤子來完成自動化的過程,在函數(shù)中我們通過監(jiān)聽 compiler 鉤子镀琉,并在回調(diào)中執(zhí)行我們需要做的事情峦嗤,最后調(diào)用回調(diào)中的第二個參數(shù) callback 使 webpack 繼續(xù)構(gòu)建,否則將在此處停止編譯屋摔,整個過程都是在 webpack 的整個編譯過程中利用其暴漏出的鉤子進(jìn)行的
以下是我寫的一個簡短的例子烁设,它完成了這樣的功能:
仿照 htmlWebpackPlugin 進(jìn)行模版輸出,實例化插件時傳入模版地址钓试,釋出文件名装黑,需解析參數(shù)變量即可副瀑;
解析過程中會解析 {{ key }} 中的 param,將 {{ key }} => 實例化對象中 params[key]
// webpack 配置
var EmitTemplatePlugin = require('./emit-template-plugin')
plugins: [
new EmitTemplate({
template: './src/template/template.html',
filename: path.resolve(__dirname, './index-emit-template.html'),
params: {
title: '測試啊',
name: 'YOLO'
}
})
]
// emit-template-plugin.js
const fs = require('fs')
function EmitTemplate (options) {
this.templateDir = options.template
this.targetDir = options.filename
this.params = options.params
}
EmitTemplate.prototype.apply = function (compiler) {
var self = this
compiler.plugin('emit', function (compilation, callback) {
fs.readFile(self.templateDir, (err, data) => {
if (err) throw err
// 匹配參數(shù)替換 html 模版中變量
var reg = new RegExp(`{{\\s*(${Object.keys(self.params).join('|')})\\s*}}`, 'g')
var html = data.toString().replace(reg, function (str, key, index) {
return self.params[key]
})
fs.writeFile(self.targetDir, html, function () {
console.log('模版寫入成功')
callback()
})
})
})
}
module.exports = EmitTemplate
// 模版 template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
</head>
<body>
<div class="">
{{ name }}
</div>
</body>
</html>
// 解析后的釋出文件 index-emit-template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>測試啊</title>
</head>
<body>
<div class="">
YOLO
</div>
</body>
</html>
上面的寫法是 webpack2 或 webpack3 調(diào)用鉤子的方法恋谭,在 4 中已變?yōu)?/p>
compilation.hooks.compile.tap([compiler hooks event], (compilation, callback) => {
})