loader
\
\
loader本质是个函数,会接受一个参数,参数内容是需要处理的内容,并且需要返回处理好的内容
\
// 最简单的loader,拿到参数直接返回出去
module.exports = source => {
return source
}
// webpack.config.js
const path = require('path')
const MyPlugin = require('./plugin/MyPlugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
module: {
rules: [
{
test: /.js$/,
loader: './loader/loader.js'
}
]
}
plugins: [
new MyPlugin()
]
}
\
实现一个js语法转换loader,需要几个库
\
- loader-utils 获取loader的参数等 (如果有需要的话)
- loader-runner 无需webpack 调试loader (如果有需要的话)
- schema-utils 对loader的参数进行校验 (如果有需要的话)
- generate 将es6的代码转换为es5
\
// loader-runner的基本用法
const { runLoaders } = require('loader-runner')
const path = require('path')
const fs = require('fs')
// console.log(runLoaders)
runLoaders({
resource: './index.js',
// String: Absolute path to the resource (optionally including query string)
loaders: [
{
loader: path.resolve(__dirname, './test-loader'),
options: {
test: 'hello world'
}
}
],
context: { minimize: true },
// Additional loader context which is used as base context
readResource: fs.readFile.bind(fs)
}, (err, result) => {
console.log(result)
})
\
实现我们的loader
\
const loaderUtils = require('loader-utils')
const validate = require('schema-utils')
const parser = require('@babel/parser')
const { transform } = require('@babel/core')
const generate = require('@babel/generator').default
const ast = (code) => {
return parser.parse(code, {
sourceType: 'module'
})
}
const generator = (code, callback) => {
transform(code, {
presets: [
[
'@babel/preset-env', {
targets: {
node: 'current',
browsers: 'last 2 version'
},
corejs: 3,
useBuiltIns: 'usage'
}
]
]
}, (err, result) => {
callback(err, result)
})
}
module.exports = function testLoader (source) {
// loader异步执行,需要这个
const callback = this.async()
// 获取loader的参数
const options = loaderUtils.getOptions(this)
// console.log(this)
// 如果有需要参数校验
// const schema = {
// type: 'object',
// "properties": {
// "test": {
// "description": "This is description of option.",
// "type": "string"
// }
// },
// }
// validate(schema, options, {
// name: 'test-loader',
// baseDataPath: 'options',
// })
// 把源码传进@babel/generator 生成转换后的源码
generator(source, (err, result) => {
if (err) throw err
return callback(null, result.code)
})
}
\
plugin
\
\
webpack的plugin是个类,必须提供一个apply方法,apply会接受一个compiler对象,这个是webpack编译时生成的。plugin会参与到webpack的各种生命周期,可以通过compiler.hooks进行监听,在该周期内执行需要处理的内容。
\
// 该插件实现了webpack, build结束的时候会输出console.log
class MyPlugin {
constructor (options) {
this.options = options
}
apply (compiler) {
compiler.hook.done.tap('MyPlugin', () => {
console.log('编译完成')
})
}
}
// webpack.config.js
const path = require('path')
const MyPlugin = require('./plugin/MyPlugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
plugins: [
new MyPlugin()
]
}
\
以上就是loader和plugin的基本写法,通过以上例子,你已经学会了如何自己实现一个webpack的loader和plugin,现在你可以自己动手写了。