写点什么

30 分钟开发 Vue 3 插入客制化脚本插件

  • 2024-03-04
    四川
  • 本文字数:3474 字

    阅读完需:约 11 分钟

30分钟开发Vue 3插入客制化脚本插件

1. 引言

嘿,大家好,我是星辰编程理财,今天要和大家聊聊一个让我颇有心得的话题:Vue 组件的客制化方式之前-“埋点”。或许你也曾为埋点这个问题烦恼过,在历史的包袱下加上技术债,我们需要实现一套客制化的脚本,埋点成了我们选择的方向。

为什么要进行 Vue 组件的埋点?

曾经,我们在没有引入 Vue 框架是,代码里面到处都是埋点,随着业务的增长和技术迭代,需要向 Vue 框架下过度。于是,在这个痛点的驱使下,开始对迁移和兼容代码。通过我们统计分析发现,会有一部分公共埋点的位置。如果每个文件都去加上通用埋点代码,工作量大并和业务代码耦合。于是采用编译时插入代码的方式实现。

为何使用 AST?

最初,我们尝试了简单的字符串匹配,想着解决问题应该不难。但是,特征字符串太少,简单匹配往往事倍功半,不好扩展,而且容易翻车。于是 AST 算是一个稳妥,虽然不太熟悉开发时间要长一些。

2. AST 技术调研

首先,我开始进行了 AST 技术的调研,由于技术上我们使用 Vite 作为开发及打包工具。于是直接问 GTP:


Q: 开发 vite 插件,如何在插件中用 ast 分析代码?请简要回答。A: 在 Vite 插件中使用 AST(抽象语法树)来分析代码可以通过以下步骤完成:


  1. 安装依赖:首先,在你的插件项目中安装 @babel/parser 或其他适用的 AST 解析器。npm install @babel/parser --save-dev

  2. 引入解析器:在你的插件代码中引入 AST 解析器。const parser = require('@babel/parser');

  3. 解析代码:使用解析器来解析你感兴趣的代码文件。


const code = fs.readFileSync('path/to/your/file.js', 'utf-8');const ast = parser.parse(code, {    sourceType: 'module',    plugins: ['jsx']  // 如果你的代码中包含JSX});
复制代码


  1. 遍历 AST:通过遍历 AST 节点,你可以获取代码中的各种信息。


traverse(ast, {    // 在这里添加处理不同类型节点的逻辑});
复制代码


你可能需要使用 @babel/traverse 这个插件来实现遍历。


通过上面回答,已了解需要 @babel/parser 与 @babel/traverse 实现。我们再向文心一言侧面印证一下常规方案是不是采用 @babel/parser 与 @babel/traverse 即可。

3. 具体需求描述

既然已经确认用 @babel/parser、@babel/traverse 作为 AST 解析和处理库。接下来就是整理具体的需求描述,需求描述一定要整理好,因为我并不打算直接开发。而是直接基于需求问 GTP。


Q:在开发 Vite 插件时,使用 @babel/traverse 遍历 AST 如何找到选项式 API 的 mounted 方法?如何找到组合式 API 的 setup 方法中直接包含的最后一个 return 语句,需要忽略 setup 方法内部的子方法的 return 语句。A:在开发 Vite 插件时,使用 @babel/traverse 遍历 AST 可以找到选项式 API 的mounted方法和组合式 API 的setup方法中直接包含的最后一个return语句。下面是具体的步骤:


  1. 找到选项式 API 的 mounted 方法:在 AST 遍历过程中,可以通过匹配对象方法节点中键名为mounted的节点来找到选项式 API 的mounted方法。具体步骤如下:

  2. 找到组合式 API 的 setup 方法中的最后一个 return 语句:对于组合式 API 的setup方法,需要在遍历过程中找到最后一个return语句,但要忽略 setup 方法内部的子方法的return语句。可以通过递归遍历ReturnStatement节点来实现。具体步骤如下:


通过这些步骤,你可以在开发 Vite 插件时使用 AST 找到选项式 API 的mounted方法和组合式 API 的setup方法中直接包含的最后一个return语句。

4. 整理开发

通过提需求给 GTP 的回答,我们大概知道了这类需求开发实现的基本套路。再结合vite插件API文档,基本可以写一个插件了。


具体我整理后的插件简单版代码如下🔍:


import * as parser from '@babel/parser';import traverse from '@babel/traverse';
const defaultConfig = { code: `;console.log("插入代码");`, // 具体插入代码 include: (id) => id.includes('/src/pages/') && id.includes('.vue'), // 处理时包含 exclude: (id) => id.includes('?vue&type') || id.includes('App.vue'), // 处理时不包含};
/** * 对Vue文件指定位置插入一段代码 * 用法: * import vueScript from './plugins/plugin.js'; * // vite plugins * plugins: [..., vueScript(), ...] * @param config * @returns {*|{transform(*, *): (*|undefined), name: string}} */export default (config = defaultConfig) => { return { name: 'vue-insert-script', transform(code, id) { if ((config?.include(id) ?? true) && !config?.exclude(id)) { try { let index = -1; let insertCodes = []; const ast = parser.parse(code, { sourceType: 'module', // 指定代码类型,例如 'module' 或 'script' }); traverse.default(ast, { // 进入对象方法节点 ObjectMethod(path) { // 选项api created、mounted if ( path.node.key.type === 'Identifier' && ['created', 'mounted'].includes(path.node.key.name) ) { let index = path.node.end - 1; // created、mounted方法代码最后面插入 if (path.node.key.name === 'created') { insertCodes.push({ code: config.code, index, }); } if (path.node.key.name === 'mounted') { insertCodes.push({ code: config.code, index, }); } } // 组合式api setup if (path.node.key.type === 'Identifier' && path.node.key.name === 'setup') { let setupIndex = -1; path.traverse({ Function(path) { path.skip(); }, ReturnStatement(returnPath) { setupIndex = returnPath.node.start - 1; // return语句前面插入 }, }); if (setupIndex >= 0) { insertCodes.push({ code: config.code, index: setupIndex, }); } } }, }); if (insertCodes.length) { return insertCodes .sort((c1, c2) => c2.index - c1.index) .reduce((code, item) => { return code.substring(0, item.index) + item.code + code.substring(item.index); }, code); } } catch (e) { console.log('报错文件ID:', id); throw e; } } }, };};
复制代码

测试

在测试阶段,我选择了一些代表性的组件进行抽样测试,并且确保了插件在各种场景下都能正常运行。效果如下:选项式 Api



组合式 Api


5. 最后

在实践中,我发现某些特殊情况下的处理可能还不够完善,后面还需要一些优化。通过这次插件开发,我体会到 AST 的优势,AST 分析能够更精准、更灵活地操作代码、提高了代码的可扩展和维护性。AST 技术应用前景应该非常广泛,它为前端开发带来了更多可能性。


用户头像

还未添加个人签名 2023-08-08 加入

开源金融数据查询工具iStock Shell

评论

发布
暂无评论
30分钟开发Vue 3插入客制化脚本插件_Vue_星辰编程理财_InfoQ写作社区