前言
作为解决扩展性的普遍方案,很多工具都支持引入插件来应对个性化场景的需求。比如现在已经离不开的webpack, babel. 冉冉升起的vite. 还有我们伴随我们青春的老大哥jquery. 下面我将尝试用两条叙事线来说明,一条是CLI,另一条是使用CLI的项目。
1. 初始化一个极简CLI
初始化一个项目
npm init -y
简单装两个依赖,意思一下
npm i webpack minimist -D
// minimist这个库是解析命令行参数用的
在根目录创建这个CLI打包时要用的webpack的配置文件
// ./webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bandle.js'
}
}
在package.json里加入bin字段
// ./package.json
"bin":{
"snowpack":"./bin/index.js"
},
创建bin文件夹,里面创建index.js, 我们开始写内容
#!/usr/bin/env node
// ./bin/.index.js
const { join } = require('path')
const webpack = require('webpack')
// 假设这个`CLI`用`snowpack.config.js`作为配置文件。
const snowpackConfig = 'snowpack.config.js'
// 读取webpack配置
const webpackConfig = require('../webpack.config.js')
// 命令和函数的映射
const __commands = {}
// 这个对象会传给用户传入的函数里
const api = {
// 在插件作者传入的函数里会调用registeCommand注册命令
registeCommand: (command, func) => {
if(!__commands[command]){
__commands[command] = func
}
}
}
...
先写到这里,我们切换故事线,切换之前回顾一下snowpack的目录结构。
.
|-snowpack
|-bin
| |-index.js
|-node_modules
|...
|-package-lock.json
|-package.json
2. 创建一个要使用 "snowpack" 的项目
同样先npm init一下
npm init -y
在根目录创建一个index.js, 并随便写一写代码
// ./index.js
console.log('im test project')
在根目录创建一个snowpack的配置文件
// ./snowpack.config.js
const { clean } = require('./plugins.js')
module.exports = {
plugins:{
commands: [clean('cleaned')]
}
}
再把plugins.js写一写
// ./plugins.js
module.exports = {
clean: (str) => (api) => {
// 这里接收api对象
api.registeCommand('clean', () => {
console.log('exec plugin', str)
})
}
}
最后看一下目录结构
.
|-testProject
|-index.js
|-package.json
|-plugins.js
|-snowpack.config.js
3. 补上 ./bin/index.js 剩余部分
第一节我们写到这里,通过第二节我们知道了snowpack.config.js的内容,和api对象会被传到哪里。
// ./bin/.index.js
const { join } = require('path')
const webpack = require('webpack')
// 假设这个`CLI`用`snowpack.config.js`作为配置文件。
const snowpackConfig = 'snowpack.config.js'
// 读取webpack配置
const webpackConfig = require('../webpack.config.js')
// 命令和函数的映射
const __commands = {}
// 这个对象会传给用户传入的函数里
const api = {
// 在插件作者传入的函数里会调用registeCommand注册命令
registeCommand: (command, func) => {
if(!__commands[command]){
__commands[command] = func
}
}
}
...
好,接着往下写
// ./bin/.index.js
...
// 封装一个执行webpack打包的函数
const runWebpackBuild = () => {
webpack(webpackConfig, (err, status) => {
if(err || status.hasErrors()){
console.log('build failed')
}
console.log('build success')
})
}
// 读取用户项目的配置文件
const readLocalOptions = () => {
return new Promise((resolve, reject) => {
const options = require(join(process.cwd(), snowpackConfig)) || {}
// commands 就是用户传入的函数数组
const { plugins: {commands = []} = {}} = options
if(commands.length > 0){
for(command of commands){
// 调用用户传入的函数时把api暴露出去
command(api)
}
}
resolve(__commands)
})
}
// 使用minimist把cli命令的参数提取出来
const minimist = require('minimist')
const args = minimist(process.argv.slice(2))
// 调用readLocalOptions,执行传入的命令参数
readLocalOptions().then((__commands)=>{
const arg = args._[0]
const func = __commands[arg]
// 传入的参数如果存在就执行
if(func){
func()
}else{ // 没有就走默认打包逻辑
runWebpackBuild()
}
})
3. 让我们康康效果
敲到这里就差不多了,先到snowpack目录下执行npm link
npm link
再到testProject目录npm link snowpack, 建立软连接
npm link snowpack
此时的目录
.
|-testProject
|-node_modules
| |-.bin
| |-snowpack
|-index.js
|-package-lock.json
|-package.json
|-plugins.js
|-snowpack.config.js
在命令行输入snowpack
xuxuefeng@xuxuefengdeMacBook-Pro-2 testProject % snowpack
build success
在命令行输入snowpack clean
xuxuefeng@xuxuefengdeMacBook-Pro-2 testProject % snowpack clean
exec plugin cleaned
嗯,运行正常
结语
从这个小例子,能大体知道插件是如何工作的。但真正的实现肯定要更加复杂,有时间可以读一读具体源码。