Webpack 通过 Plugin 机制让其更加灵活,以适应各种应用场景。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
一个最基础的 Plugin 的代码是这样的:
class WebpackMockServicePlugin{
// 在构造函数中获取用户给该插件传入的配置
constructor(options){
}
// Webpack 会调用 WebpackMockServicePlugin 实例的 apply 方法给插件实例传入 compiler 对象
apply(compiler){
}
}
// 导出 Plugin
module.exports = WebpackMockServicePlugin;
在使用这个 Plugin 时,相关配置代码如下:
const WebpackMockServicePlugin= require('./WebpackMockServicePlugin.js');
module.export = {
plugins:[
new WebpackMockServicePlugin(options),
]
}
接下来开始我们的WebpackMockServicePlugin插件之旅
一、启动服务
我们知道mockjs可直接引入到项目,mockjs实质是通过ajax拦截,并不是真实的接口访问,要做到真实的接口访问,我们必须先启动一个服务。
安装 express
npm install express --save
or
yarn add express
创建一个serveice:
const express = require('express')
const app = express()
app.route()
.get('/', (req, res) => {
res.send('hello WebpackMockServicePlugin')
})
class WebpackMockServicePlugin {
constructor(options = {}) {}
apply(compile) {
app.listen(3000, () => {})
}
}
module.exports = WebpackMockServicePlugin
设置默认端口及默认接口配置目录
class WebpackMockServicePlugin{
constructor(options = {}) {
this.port = options.port || 3000
this.mockDir = path.join(process.cwd(), options.mockDir || 'mock')
}
async apply(compile) {
this.listen()
}
listen() {
app.listen(this.port, () => {
console.log(`mock服务启动成功: http://localhost:${this.port}`)
})
}
}
二、根据目录结构生成对应的路由
目录结构及要生成的路由如下:
├─ mock
│ ├─ user
│ │ ├─ index.json // [get] /user
│ │ ├─ del.delete.json // [delete] /user/del
│ │ ├─ update.put.json // [put] /user/update
│ │ └─ add.post.json // [post] /user/add
│ ├─ news
│ │ └─ index.json // [get] /news
生成路由代码并读取json文件生成返回数据:
class WebpackMockServicePlugin{
...
async apply(compile) {
this.createRoute()
this.listen()
}
...
createRoute() {
// 如果文件目录不存在,就创建该目录
!fs.existsSync(this.mockDir) && fs.mkdirSync(this.mockDir)
// 读取目录生成路由
fs.readdir(this.mockDir, (err, dirs) => {
if (err) throw err
dirs.forEach(dir => {
const files = fs.readdirSync(`${this.mockDir}/${dir}`)
files.forEach(file => {
const textArr = file.match(/(\w+).?(\w+)?.json/)
if (textArr) {
// type -> 请求类型
const type = textArr[2] || 'get'
/**
* 根据文件格式生成路由
* userinfo/index.json -> /userinfo [get]
* userinfo/update.post.json -> /userinfo/update [post]
*/
app.route(textArr[1] === 'index' ? `/${dir}` : `/${dir}/${textArr[1]}`)
[type]((req, res) => {
let mkdir = `${this.mockDir}/${dir}/${file}`
const content = fs.readFileSync(mkdir)
const data = JSON.parse(content.toString().replace(/\n/g, ''))
res.json(Mock.mock(data))
})
}
})
})
})
}
}
三、判断端口是否被占用
写来写一个检测端口是否可用的方法:
注:监听带上 0.0.0.0
/**
* 检测端口是否可以使用
* @param {Number} port
* @return {Promise<Boolean>}
*/
function portInUse(port) {
return new Promise((resolve) => {
const server = net.createServer().listen(port, '0.0.0.0');
server.on('listening', function () {
server.close()
resolve(true)
})
server.on('error', function (err) {
if (err.code === 'EADDRINUSE') {
resolve(false)
}
})
})
}
设置端口:
class WebpackMockServicePlugin {
...
async apply(compile) {
while (!(await portInUse(this.port))) {
this.port++
}
this.createRoute()
this.listen()
}
...
}
四、监听文件变化更新路由
创建一个监听类,实现文件变化的监听:
const event = require('events')
const fs = require('fs')
class Watcher extends event.EventEmitter {
constructor(watchDir) {
super();
this.watchDir = watchDir
this.watchList = new Set()
}
watch() {
this.watchList.forEach(value => {
fs.watchFile(value, () => {
this.emit('process', value)
})
})
}
start() {
this.watchList.add(this.watchDir)
fs.readdir(this.watchDir, ((err, files) => {
if (files) {
files.forEach(value => {
this.watchList.add(`${this.watchDir}/${value}`)
})
}
this.watch()
}))
}
}
module.exports = Watcher
因为watchFile只能监听到当前目录文件的变化,所有我们要创建一个watchList 存放所有需要监听的目录集合,
start():循环遍历所有的目录存放到watchFile集合
watch(): 监听所有目录,目录有变化时会触发process事件
使用Watcher类监听目录:
class WebpackMockServicePlugin {
...
listen() {
app.listen(this.port, () => {
console.log(`mock服务启动成功: http://localhost:${this.port}`)
/**
* 开启监听目录,当目录或文件有更新时重新生成路由
* @type {Watcher}
*/
this.watcher = new Watcher(this.mockDir)
this.watcher.on('process', dir => {
// 之前没有创建目录没有绑定监听
// 所以创建了新的目录时要重新执行监听
this.watcher.start()
// 文件发生改变时重新生成路由
this.createRoute()
})
this.watcher.start()
})
}
...
}
这样我们就实现了一个真实的接口,并通过mockjs生成模拟数据。
完整代码及实例:https://github.com/yinMrsir/w...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。