从被测试大佬鄙视出发,封装一个wepback插件
前言
前段时间上线活动,测试大佬经常找我,我说发生肾么事了?
原来是出现了好几次前端 mock
数据带到测试环境的问题,使得测试大佬仇恨值++,差点要来给我一套化挥发
加闪电五连鞭
.
每次build
的时候,心里都很慌,生怕手写的mock
数据带到测试环境.
我寻思,作为一个程序猿,这种事我应该耗子尾汁
,不应该每次都手动的去检查这个问题,所以也一直在思考怎么去解决这个问题.
经过多方面咨询和自己的思考,最终得到两个比较好的思路:
-
通过严谨,高度的封装,将所有的请求封装在一起,然后统一在请求层面去配置
mock=true
,使用mock
接口,build
的时候,强制所有的接口都自动使用正式数据. -
在
mock
数据处,手动增加约定的特殊注释,然后写一个webpack
插件在build
的时候去检测是否存在这种注释,如果存在,则直接抛出异常禁止打包.
两者对比下来,其实方法一更好些,因为不需要额外去搞webpack
插件,而且代码写起来很好看.
但是我还是采用的方法二.因为我这边业务不太合适集中把接口封装起来.而且我还有点小私心,想熟悉下wepback
的插件开发.
理解webpack工作流程
翻了很多资料,搞懂了webpack的大致的工作流程,分享下比较精髓的一段话:
下面这段引用自Webpack 从零入门到工程化实战 26章
初步尝试
通过上边的理解,我大概有了思路,我要做的事是在build前拿到由代码组成的字符串,然后对这个字符串通过正则进行遍历,如果查出了约定好的特殊mock注释,则抛出警告打断build,于是写出以下代码:
//webpack.config.js
// 使用插件
const WebpackMocWarnkPlugin = require('webpack-mock-warn')
module.exports = {
// ...
plugins: [
new WebpackMocWarnkPlugin(),
],
}
// webpackMocWarnkPlugin/index.js 插件代码
module.exports = class WebpackMockWarnPlugin {
apply(compiler) {
compiler.plugin('emit', (compilation, callback) => {
// compilation.chunks存放着所有的代码块
compilation.chunks.forEach((chucnk) => {
const chunkCode = chunk.entryModule._source._value
//.... 对chunkCode进行遍历匹配
})
})
}
}
但是这样写发现webpack警告说compiler.plugin
这个api
已经不建议使用,需要更换api
了.
再次尝试
上边webpack
工作流程中已经说了,现在(webpack4
)应该先去使用compiler
合适的钩子,拿到compilation
,然后再去用compilation
中合适的钩子去拿到代码块,钩子这块还挺绕的,看了好久资料,最后还是求助大佬才解决了疑惑.于是又有写出了以下代码:
// webpackMocWarnkPlugin/index.js
module.exports = class WebpackMockWarnPlugin {
apply(compiler) {
// compilation 这个钩子表示compilation创建成功之后的回调,参数就是热乎乎的compilation
compiler.hooks.compilation.tap('webpackMockWarnPlugin', (compilation) => {
compilation.hooks.afterChunks.tap('af', (chunks) => {
this.testChunk(chunks) // 遍历处理chunks的代码
})
})
// 完成编译和封存编译产出之后的回调
compiler.hooks.afterCompile.tap('afcompile', this.throwWarn) //如果发现存在特殊标记则抛出错误
}
}
解释下上边使用的compiler.hooks.afterCompile
,因为可能会存在多个特殊标记,所以这里需要等所有的代码块都处理完,才能拿到所有的结果,然后在这个钩子里去判断,一起抛出
进行优化
主要问题大致解决了,剩余一些边边角角,尽量的去优化一下,贴出最后实现的代码:
// webpackMocWarnkPlugin/index.js
const { red, cyan, yellow } = require('colorette') //这个插件是用来在控制台上输出多种颜色代码的,这个也是看wepbakc-cli的源码发现的 还是挺好玩的
const wanrns = []
module.exports = class WebpackMockWarnPlugin {
// 这里允许使用者去自定义特殊标记 或者直接去自己写正则
constructor(options = {}) {
this.mockReg = options.mockReg
this.mockFlag = options.mockFlag || 'mock'
}
apply(compiler) {
compiler.hooks.compilation.tap('webpackMockWarnPlugin', (compilation) => {
compilation.hooks.afterChunks.tap('af', (chunks) => {
this.testChunk(chunks)
})
})
compiler.hooks.afterCompile.tap('afcompile', this.throwWarn)
}
testChunk(chunks){
chunks.forEach((chunk) => {
const reg =
this.mockReg ||
new RegExp(`(\/\*)\s*${this.mockFlag}|\/\/ *${this.mockFlag}`, 'g')
const chunkCode = chunk.entryModule._source._value
// 先生成行数数组.每个元素的index表示当前行数,每个元素的index表示所在行,内容是所在的index
const rows = [0]
for (let i = 0; i < chunkCode.length; i++) {
if (chunkCode[i] === '\n') rows.push(i)
}
var regExec = null
while ((regExec = reg.exec(chunkCode)) !== null) {
// 若匹配到mock,则取出行数 / 上一行,下三行之内的代码方便检阅
if (regExec[0]) {
// 循环遍历所在行数
for (let index in rows) {
if (rows[index] >= regExec.index) {
let content = ''
let contentWrap = 5
let endIndex = chunkCode.length
let startIndex = 0
for (let i = regExec.index; i >= 0; i--) {
if (chunkCode[i] === '\n') {
startIndex = i
break
}
}
for (let i = regExec.index; i <= chunkCode.length - 1; i++) {
if (contentWrap === 0) {
endIndex = i
break
}
if (chunkCode[i] === '\n') {
contentWrap--
}
}
content = chunkCode.slice(startIndex, endIndex)
wanrns.push({
row: index || 1,
path: chunk.entryModule._source._name,
content,
})
break
}
}
}
}
})
}
throwWarn(){
if (wanrns.length !== 0) {
let errorStr = `${red(
`检测到存在${wanrns.length}处mock数据,请删除后再次尝试操作:
如果你想使用自定义捕获mock标记,请配置 mockFlag , 也可以配置 mockReg 自定义捕获mock正则
`
)}\n`
for (let el of wanrns) {
errorStr += yellow(
`path: ${el.path}\nrow: ${el.row}\ncontent: ${cyan(el.content)}\n\n`
)
}
console.error(red(errorStr))
process.exit(1);//结束进程抛出错误
}
}
}
发布至npm
修改下pagejson:
{
"name": "xxx",
"version": "1.0.0",
"description": "When build, it detects the mock data and throws a warning",
"main": "./src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"publishConfig": {
"registry": "http://registry.npmjs.org"
},
"keywords": [
"webpack",
"mock"
],
"author": "xxx <xxxx.com>",
"license": "ISC",
"dependencies": {
"colorette": "^1.2.1",
"webpack-cli": "^4.2.0"
}
}
再编辑下readme文件
npm publish
大功告成!
结语
其实到最后,还遗留了2个问题:
- 现在最后是调用
node
的process.exit(1)
直接在命令行抛出错误,这很不优雅,很难被称为warn
,我其实理想的是直接终止掉webpack
的打包而不抛出错误,但是没找到好的解决方案. - 对
html/css
注释进行适配,这个暂时没有需求,所以没有费力去搞,而且中间会涉及到loader
转义后的html/css
,而不是编译前的,应该也要费一番力气.
这个小东西也花费了我好几天的摸鱼时间,开发过程中遇到的问题,比上边描述的要多得多.
我个人研究东西总是要有一个源头,就是说我要先知道我想干嘛,然后再去由这个进一步去研究.如果直接去学习某种技术,总是会迷失.从业务问题出发,到开发webpack
插件去解决问题.这种体验还是挺好的.
当然,以小见大,理解了webpack
的工作流程,也算有所收获.
深刻感觉到了,提出一个好问题,或许比解决这个问题更重要.
我是菜菜驴,江湖人称驴渣.感谢你阅读我的分享!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!