前言
前天(周日),写了一个 webpack-loader,auto-inject-async-catch-loader,早晨 8 点多发的 npm 包。昨天早上,上 npm 官网一看,才十几个小时下载量 282 次(着实有点意外????)~
在之前,我写过一篇文章《现场教学,优雅地解决基于 Vue CLI 我的项目中的 async await 异样》介绍了如何在 Vue CLI 中给代码主动加 try catch
,过后在文中也讲了这种做法欠妥。所以,上周末我花了一些工夫,重新整理了一下如何应用 babel 的 tool 基于形象语法树 AST 来实现对 Vue CLI 我的项目代码主动注入 try catch
。
一、 auto-inject-try-catch-loader 简介
auto-inject-async-catch-loader 是一个 webpack-loader,基于 babel 提供的 tool 来通过遍历形象语法树 AST 来实现对 AwaitExpression
AST 节点增加 TryStatement
AST 节点,从而实现对 async function
主动注入 try catch
的机制。auto-inject-async-catch-loader 的工作机制如下图所示:
而 auto-inject-async-catch-loader 这个 loader 是 fork 的 async-catch-loader。所以,这里给上作者一个大大的 Respect ????。起初,我是想着提个 PR,没必要重复造轮子,然而出于工夫问题,就 fork 了一份改了改。
相比拟 async-catch-loader ,auto-inject-async-catch-loader 做了以下两点优化:
1.优化了向上查找 parent 的过程,优化后的 traverse
如下所示:
traverse(ast, {
AwaitExpression(path) {
// 曾经蕴含 try 语句则间接退出
if (
path.findParent(path => t.isTryStatement(path.node))
) {
return;
}
// 查找最外层的 async 语句
const blockParent = path.findParent(path => t.isBlockStatement(path.node) && isAsyncFuncNode(path.parentPath.node))
const tryCatchAst = t.tryStatement(
blockParent.node,
t.catchClause(
t.identifier(options.identifier),
t.blockStatement(catchNode)
),
finallyNode && t.blockStatement(finallyNode)
)
blockParent.replaceWithMultiple([tryCatchAst])
}
});
2.反对应用 TypeScript + Vue CLI 开发的状况。此时 Vue 组件是 Class 语法,例如:
<template></template>
<script lang="ts">
import { Vue, Component } from "@vue-property-decorator"
@Component
export default class Login extends Vue {
private async created(): Promise<any> {
await Promise.resolve("user login")
}
}
</script>
能够看到 created
(async function
)是写在 Class 外部的,即其对应的形象语法树 AST 的 type 为 ClassMethod
,所以在 isAsyncFunction
判断函数中减少加了对 ClassMethod
的判断:
const isAsyncFuncNode = node =>
t.isFunctionDeclaration(node, {
async: true
}) ||
t.isArrowFunctionExpression(node, {
async: true
}) ||
t.isFunctionExpression(node, {
async: true
}) ||
t.isObjectMethod(node, {
async: true
}) ||
t.isClassMethod(node, {
async: true
});
二、auto-inject-async-catch-loader 在 Vue CLI 中应用
置信很多同学对于配置 Vue CLI 的 Webpack 一头雾水,这里我就顺带地给大家的解说一下如何在 Vue CLI 中应用 auto-inject-async-catch-loader。
首先,当然是装置 auto-inject-async-catch-loader 依赖:
npm i auto-inject-async-catch-loader -D
而后,配置 Webpack。而通常状况下,应用 Vue CLI 开发的同学会分为两类:
1.应用 JavaScript 开发
应用 JavaScirpt 开发的同学只须要通过 chainwebpack
选项在 js
rule 中增加一个 loader
就行。在 vue.config.js 的 chainWepack
中退出如下配置:
chainWepack: (config) => {
const jsRule = config.module.rule("js");
jsRule
.use("auto-inject-async-catch-loader")
.loader("auto-inject-async-catch-loader")
.end()
}
2.应用 TypeScript 开发
应用 TypeScript 开发的同学则须要重写整个 ts
rule 的 loader 配置。在 vue.config.ts 的 chainWepack
选项中退出如下配置:
chainWebpack: (config) => {
const tsRule = config.module.rule("ts");
tsRule.uses.clear();
tsRule
.use("cache-loader")
.loader("cache-loader")
.end()
.use("babel-loader")
.loader("babel-loader")
.end()
.use("auto-inject-async-catch-loader")
.loader("auto-inject-async-catch-loader")
.tap(() => {
return {
catchCode: 'console.error(e)'
}
})
.end()
.use("ts-loader")
.loader("ts-loader")
.tap(() => {
return {
transpileOnly: true,
appendTsSuffixTo: [
'\\.vue$'
],
happyPackMode: false
}
})
.end()
}
至于为什么须要重写整个 ts
对应的 rule,因为此时我的项目中代码的 loader 的加载程序是 ts-loader 到 babel-loader 再到 cache-loader,auto-inject-async-catch-loader 必须要在 babel-loader 之前、ts-loader 之后加载:
而 Vue CLI 的 Webpack 配置是应用 webpack-chain 生成的,然而 webapck-chain 并没有提供在某个 loader 后插入 loader 的办法(只对 plugin 反对 before 和 after 办法)。所以,这里咱们须要重写整个 ts rule,保障 ts-loader 到 auto-inject-async-catch-loader 到 babel-loader 再到 caceh-loader 的加载程序:
结语
最初讲讲前期打算,写一个 babel-plugin 实现同样的性能,用 babel-plugin 实现则会精简地多,因为 babeld-plugin 自身提供了一些变量供于应用,例如能够应用 visitor
遍历形象语法树 AST。当然,欢送各位同学应用 auto-inject-async-catch-loader,如果有其余需要也能够给我提 Issue,或者微信(wu1957503379)分割我。
参考
https://github.com/yeyan1996/…
❤️ 爱心三连击
写作不易,能够的话麻烦点个赞,这会成为我保持写作的能源,奥力给!!!
我是五柳,喜爱翻新、捣鼓源码,专一于 Vue3 源码、Vite 源码、前端工程化等技术畛域分享,欢送关注我的「微信公众号:Code center」。
发表回复