Open WJCHumble opened 3 years ago
前段时间,写了一个 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。
try catch
auto-inject-async-catch-loader 是一个 webpack-loader,基于 babel 提供的 tool 来通过遍历抽象语法树 AST 来实现对 AwaitExpression AST 节点添加 TryStatement AST 节点,从而完成对 async function自动注入 try catch 的机制。auto-inject-async-catch-loader 的工作机制如下图所示:
AwaitExpression
TryStatement
async function
而 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
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 的判断:
created
ClassMethod
isAsyncFunction
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 });
相信很多同学对于配置 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 中加入如下配置:
chainwebpack
js
loader
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 选项中加入如下配置:
ts
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)联系我。
visitor
https://github.com/yeyan1996/async-catch-loader
前言
前段时间,写了一个 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
如下所示:2.支持使用 TypeScript + Vue CLI 开发的情况。此时 Vue 组件是 Class 语法,例如:
可以看到
created
(async function
)是写在 Class 内部的,即其对应的抽象语法树 AST 的 type 为ClassMethod
,所以在isAsyncFunction
判断函数中增加加了对ClassMethod
的判断:二、auto-inject-async-catch-loader 在 Vue CLI 中使用
相信很多同学对于配置 Vue CLI 的 Webpack 一头雾水,这里我就顺带地给大家的讲解一下如何在 Vue CLI 中使用 auto-inject-async-catch-loader。
首先,当然是安装 auto-inject-async-catch-loader 依赖:
然后,配置 Webpack。而通常情况下,使用 Vue CLI 开发的同学会分为两类:
1.使用 JavaScript 开发
使用 JavaScirpt 开发的同学只需要通过
chainwebpack
选项在js
rule 中添加一个loader
就行。在 vue.config.js 的chainWepack
中加入如下配置:2.使用 TypeScript 开发
使用 TypeScript 开发的同学则需要重写整个
ts
rule 的 loader 配置。在 vue.config.ts 的chainWepack
选项中加入如下配置:至于为什么需要重写整个
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/async-catch-loader