关于前端:推荐收藏带给你幸福感的-autoinjectasynccatchloader-????

前言

前天(周日),写了一个 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>

能够看到 createdasync 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」。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理