天爱验证码迁入vite搭建的vue2后台项目闭坑实践

1,171 阅读7分钟

以前,公司项目一直使用腾讯云的TCaptcha.js实现图形验证,简单方便(TCaptcha.js官方文档)。但它是需要按次计费的,这回,项目组领导准备找一个免费的,最终天爱验证码TIANAI-CAPTCHA被选中。后端实现我就不提了,开发人员可自行去查阅 TIANAI-CAPTCHA文档,作为前端开发人员,我会将自己前端开发过程中遇到和实践成功的方案记录下来,方便以后查阅和使用。

TIANAI-CAPTCHA的文档中,是有记录前端使用方法的,按照它的叙述,应该是能快速使用起来的。 可查看 TIANAI-CAPTCHA文档 或者 天爱有情/tianai-captcha-web-sdk,它们介绍了如何将天爱图形验证码如何引入项目,以及使用方法,如下图:

1729565342870.png

为了方便记录和描述,TIANAI-CAPTCHA后面就简称tac了。

如果你开发的是webpack构建的vue2项目,或者传统的前端项目,或许你能很容易就使用它,不会遇到啥问题,比如webpack搭建的vue2项目,里面是有public目录的,index.html也包含其中,我们按照文档,应该一切OK(我没有实践,或许能出问题也不一定,大家可以去试一试)。不过我们当时使用的是vite构建的vue2项目,里面是没有public文件夹的。

上面的文档中曾提到,将打包好的tac目录放到public目录中、或者放到某个可以访问到地方,比如oss之类的可以被浏览器访问到的地方。

因为没有public,起初我将tac放到了src下,不管如何尝试,最终都失败了,稍后我会将问题记录在后文其他问题中,有兴趣的可以看看,下面我先介绍下成功实践后的两种方案。

如何安装

方案一:

1、将打包好的tac目录整个拷贝到vue项目根目录,找到根目录下的index.html,在头部head中引入load.min.js

<script type="module" src="./tac/js/load.min.js"></script>  

2、下载 rollup-plugin-copy,

npm install rollup-plugin-copy --save-dev

3、打开 vite.config.js ,在build中加入以下配置:

import copyPlugin from 'rollup-plugin-copy'
export default defineConfig(({ mode }) =>{
  return {
    build: {
      rollupOptions: {
        plugins: [
          copyPlugin({
            targets: [{ src: 'tac/*', dest: 'dist/tac' }],
            hook: 'writeBundle'
          }),
        ],
      },
      // outDir: 'dist',
      // assetsDir: 'assets',
    },
  }
})

上方代码实现了将整个tac目录及其里面文件拷贝到打包生成的dist目录中。

一定要注意 hook: 'writeBundle' ,这段代码很关键,如果没有这段代码,你会发现,在我们打包生成的dist目录中,并没有找到tac,可这是为什么呢?

其实在打包的时候,拷贝动作已经执行成功在dist中生成tac了,但是很快又被删除了。在执行npm run build的打包过程中,开始有一个过程,就是先清空dist中的所有文件,然后才会执行打包生成文件到dist中。如果没有hook这个命令,拷贝动作一早就执行,很快就会被清空这一动作搞没了。所以这下清楚了吧。

在构建流程中,vite继承了Rollup 的 writeBundle 钩子为部署环节提供的深度扩展能力。通过 writeBundle钩子,可在打包完成后自动触发。我把它当成了writeAfterBundleComplete or writeAfterBundleEnd 的缩写,你觉得呢?

hook: 'writeBundle' 用起来是真的香。

方案二:

1、将打包好的tac目录整个部署到服务器上,使其能正常访问tac中的文件(这一步很关键,是后面步骤的先决条件);

2、在各个环境配置文件中配置能访问tac目录的地址变量 VITE_APP_TAC_API,比如开发环境中.env.test

# 接口域名
VITE_APP_API_ORIGIN = 测试环境域名

# 接口前缀(项目命名空间)
VITE_APP_API_PREFIX = /standard-onetravel

# tianai-captcha前端包访问地址
VITE_APP_TAC_API = ${VITE_APP_API_ORIGIN}${VITE_APP_API_PREFIX}/tac

3、下载vite-plugin-html,在vite.config.js中配置能在index.html中正常访问tac目录的变量 appTacApi

npm install vite-plugin-html --save-dev
import { createHtmlPlugin } from "vite-plugin-html";

// 获取指定环境变量值
const getViteEnv = (mode, target) => {
  return loadEnv(mode, process.cwd())[target];
};

export default defineConfig(({ mode }) =>{
    return {
        plugins: [
            createHtmlPlugin({
              inject: {
                  data: {
                    appTacApi:getViteEnv(mode, "VITE_APP_TAC_API")
                  },
              },
            }),
        ]
    }
}

4、index.html的头部head中引入 load.min.js

<script src="<%- appTacApi %>/js/load.min.js"></script>

问题: 为什么不省略第3和4步骤,直接写死tac的访问地址呢?

我们的项目有最少开发、测试、生产3个环境,每个环境域名都不同,甚至有时候各个环境命名空间也不同,生产还可能是第三方的,直接写死,怎么知道其他环境的地址,并且保证能正常访问。如果是第三方,我们总不能让生产访问我们的服务器啥的tac文件吧,何况项目开发时可能还没第三方的地址呢?总不能写死每遇到一个环境都去更改项目中的代码吧!

以上两个方案,推荐使用方案一,我们不必去关注它部署在哪个环境,因为tac已经集成到了项目中。

如何应用

上面的准备工作都做好了,又该如何使用呢?

使用的时候,我们当然就可以像文档中提到的那样使用了,唯一需要注意的就是 tac文件目录地址

使用方案一:

为了在调试阶段和服务器上都能正常访问,tac文件目录地址 我使用了 location.href截取拼接

const tacUrl = location.href.split("#")[0]+'tac'

使用方案二:

tac必须事先部署到服务器,tac文件目录地址直接取值变量 VITE_APP_TAC_API即可

const tacUrl = import.meta.env.VITE_APP_TAC_API

具体使用如下:

  const baseUrl = `${import.meta.env.VITE_APP_BASE_API}/sot-admin-api`;
  const config = {
    requestCaptchaDataUrl: `${baseUrl}/captcha/gen`,
    validCaptchaUrl: `${baseUrl}/captcha/check`,
    bindEl: "#captcha-box",
    // 验证成功回调函数(必选项,必须配置)
    validSuccess: (res, c, tac) => {
      // 销毁验证码服务
      tac.destroyWindow();
      // console.log("验证成功,后端返回的数据为", res);
      this.$emit("adopt", {});
    },
    // 验证失败的回调函数(可忽略,如果不自定义 validFail 方法时,会使用默认的)
    validFail: (res, c, tac) => {
      console.log("验证码验证失败回调...");
      // 验证失败后重新拉取验证码
      tac.reloadCaptcha();
    },
    // 刷新按钮回调事件
    btnRefreshFun: (el, tac) => {
      console.log("刷新按钮触发事件...");
      tac.reloadCaptcha();
    },
    // 关闭按钮回调事件
    btnCloseFun: (el, tac) => {
      console.log("关闭按钮触发事件...");
      tac.destroyWindow();
    },
  };
  const style = {
    // logo地址
    logoUrl: null,
    // // 按钮样式
    // btnUrl: "https://minio.tianai.cloud/public/captcha-btn/btn3.png",
    // // 背景样式
    // bgUrl: "https://minio.tianai.cloud/public/captcha-btn/btn3-bg.jpg",
    // 滑动边框样式
    // moveTrackMaskBgColor: "#f7b645",
    // moveTrackMaskBorderColor: "#ef9c0d",
  };
  const tacUrl = location.href.split("#")[0]+'tac'
  window
    .initTAC(tacUrl, config, style)
    .then((tac) => {
      this.captcha = tac;
    })
    .catch((e) => {
      console.log("初始化tianai-captcha失败", e);
    });

其他问题

1、打包好的tac如何获取?

下载 tianai-captcha-demo ,找到目录 src/main/resources/static,拷贝 static 并改名为tac即可。

2、为什么使用tac这么麻烦,没有腾讯云TCaptcha.js方便?

其实源头还在tac的作者在文档中推荐的前端使用方法,让我们引入的load.min.js上,

1729587094707.png

load.min.js 没有提供 未压缩版本,不过从加载进来的代码我们可以看到

1729587435341.png

我们使用的函数 initTAC 就是从 load.min.js 中 来的

window.initTAC = initTAC;

而使用的initTAC 函数, 又根据我们使用时传入的 tac文件目录地址 变量值,对 tac.min.js 和 tac.css 的访问地址进行了重新赋值,

1729588361150.png

然后加载tac的js和css。就是这坑爹的一个步骤,导致加载tac的js和css失败,从而出现其他各种使用方式使用tac失败的情况。

比如直接引用文档中提供的地址:

<script src="https://minio.tianai.cloud/public/static/captcha/js/load.min.js"></script>

使用initTAC时的第一个参数 tac文件目录地址,如果你传 './tac'等值,都会导致出现下面的报错,

1729589224925.png

看到这里,或许有人想说,你在index.html的头部引入天爱的CDN链接

<script src="https://minio.tianai.cloud/public/static/captcha/js/load.min.js"></script>

然后使用initTAC方法时, tac文件目录地址

const tacUrl = 'https://minio.tianai.cloud/public/static/captcha/'
window.initTAC(tacUrl, config, style)
.then((tac) => {
    this.captcha = tac;
})
.catch((e) => {
   console.log("初始化tianai-captcha失败", e);
});

这样,拼接的tac.css和tac.min.js都能正常加载不会报错,很高兴你能想到这一点,这种方案我也尝试了(使用方案一和方案二都正常,后端接口也OK的情形下),很遗憾的是,图形验证码都出来了,可后端接口 /check (initTAC中第二个参数config.validCaptchaUrl )却又报错了

1729650035078.png

是不是有种骂人的冲动,可千万别。反正试错这一步我已经走过了,你不想麻烦,也没必要试错了不是。

这里介绍的是后台项目中天爱验证码的使用,如果您想在小程序中使用天爱验证码,又不想慢慢研究的,可查看我的这篇文章 微信小程序中封装天爱验证码 ,好了,大家能看我啰嗦到这里,不胜感激。