🤩 用Babel自动埋点,原来这么简单!

大家好呀!今天给大家分享一个超实用的前端小技巧------用Babel自动给代码添加埋点功能。

听起来很高大上?其实超级简单,跟着我一步步来,保证你能学会!

什么是埋点?

埋点就是在代码里插入一些统计代码,用来记录用户行为,比如按钮点击、页面访问等。

传统做法是手动在每个函数里加统计代码,但这样太麻烦了!

为什么要用Babel自动埋点?

  1. 省时省力:不用手动加代码
  2. 干净整洁:业务代码和埋点代码分离
  3. 一劳永逸:一次配置,到处使用

手把手教你实现

第一步:准备环境

bash 复制代码
mkdir babel-tracker
cd babel-tracker
npm init -y
npm i -D @babel/core @babel/helper-plugin-utils

第二步:创建测试代码

新建src/sourceCode.js

javascript 复制代码
import "./index.css";

// 各种函数类型
const test1 = () => {}; // 箭头函数
const test2 = function() {}; // 函数表达式
function test3() {} // 函数声明
class test4 { // 类方法
  test4_0() {}
  test4_1 = () => {};
  test4_2 = function() {};
}

第三步:创建Babel插件

新建src/babel-plugin-tracker.js

javascript 复制代码
// 引入 Babel 提供的辅助函数,用于自动添加 import 语句
const { addDefault } = require("@babel/helper-module-imports");

// 导出 Babel 插件函数,接收 api 和 options 两个参数
module.exports = (api, options) => {
  // 返回插件对象
  return {
    // visitor 对象定义了对哪些 AST 节点类型感兴趣
    visitor: {
      // 使用 | 分隔符匹配多种函数类型
      "ArrowFunctionExpression|FunctionDeclaration|ClassMethod|FunctionExpression": {
        // 进入这些节点时的处理函数
        enter: (path, state) => {
          // 获取函数体的路径
          const bodyPath = path.get("body");
          // 从 state 中获取之前创建的埋点函数 AST
          const ast = state.trackerAst;
          
          // 判断函数体是否是块语句(即是否有 { } 包裹)
          if (api.types.isBlockStatement(bodyPath.node)) {
            // 如果是块语句,直接在开头插入埋点调用
            bodyPath.node.body.unshift(ast);
          } else {
            // 如果不是块语句(如箭头函数直接返回表达式)
            // 创建一个新的块语句 AST,包含埋点调用和原返回值
            const ast2 = api.template.statement(`{
              ${state.importTrackerId}();
              return BODY;
            }`)({ BODY: bodyPath.node });
            // 用新创建的块语句替换原来的函数体
            bodyPath.replaceWith(ast2);
          }
        }
      },
      
      // 处理整个程序(Program 是文件的根节点)
      Program: {
        enter: (path, state) => {
          // 从插件配置中获取 tracker 模块的路径
          const trackerPath = options.trackerPath;
          
          // 遍历当前程序的所有 import 声明
          path.traverse({
            ImportDeclaration(path) {
              // 检查是否已经导入了 tracker 模块
              if (path.node.source.value === trackerPath) {
                // 如果已导入,获取导入的变量名
                // specifiers.0.local 表示第一个导入说明符的本地名称
                state.importTrackerId = path.get("specifiers.0.local").toString();
                // 找到后停止遍历
                path.stop();
              }
            }
          });
          
          // 如果没有找到 tracker 的导入
          if (!state.importTrackerId) {
            // 使用 addDefault 添加默认导入
            // path.scope.generateUid("tracker") 生成唯一的变量名
            state.importTrackerId = addDefault(path, trackerPath, {
              nameHint: path.scope.generateUid("tracker")
            }).name; // 返回导入的变量名
          }
          
          // 创建埋点函数调用的 AST 节点
          // 使用之前获取或生成的变量名
          state.trackerAst = api.template.statement(`${state.importTrackerId}();`)();
        }
      }
    }
  };
};

第四步:使用插件处理代码

新建src/index.js

javascript 复制代码
const { transformFileSync } = require("@babel/core");
const path = require("path");
const tracker = require("./babel-plugin-tracker");

const pathFile = path.resolve(__dirname, "./sourceCode.js");

// 转换代码
const { code } = transformFileSync(pathFile, {
  plugins: [
    [tracker, { trackerPath: "tracker" }] // 使用插件并配置
  ]
});

console.log(code);

第五步:运行看看效果

bash 复制代码
node ./src/index.js

你会看到输出结果中,所有函数都被自动加上了埋点代码,而且还自动导入了tracker模块!

原理揭秘

  1. AST转换:Babel先把代码转换成抽象语法树(AST)
  2. 遍历AST:插件会遍历AST找到各种函数
  3. 修改AST:在函数开头插入埋点函数调用
  4. 检查导入:确保埋点函数已导入,没有就自动添加
  5. 生成代码:把修改后的AST转换回代码

总结

用Babel自动埋点真的太方便了!一次配置,终身受用,再也不用在业务代码里到处写埋点了。赶紧试试吧,让你的代码更干净,开发更高效!

如果有任何问题,欢迎留言讨论哦~ 😊

相关推荐
IT_陈寒9 分钟前
Python 3.12 的这5个新特性,让我的代码性能提升了40%!
前端·人工智能·后端
方安乐26 分钟前
vite+vue+js项目使用ts报错
前端·javascript·vue.js
韩立233328 分钟前
Vue 3.5 升级指南
前端·vue.js
子兮曰36 分钟前
🚀别再乱写package.json了!这些隐藏技巧让项目管理效率提升300%
前端·javascript·npm
我叫汪枫41 分钟前
Spring Boot图片验证码功能实现详解 - 从零开始到完美运行
java·前端·javascript·css·算法·html
小桥风满袖1 小时前
极简三分钟ES6 - ES8中async,await
前端·javascript
一直在学习的小白~1 小时前
node_modules 明明写进 .gitignore,却还是被 push/commit 的情况
前端·javascript·vue.js
前端小超超2 小时前
如何配置capacitor 打包的ios app固定竖屏展示?
前端·ios·web app
nightunderblackcat2 小时前
新手向:从零理解LTP中文文本处理
前端·javascript·easyui
kyle~2 小时前
python---PyInstaller(将Python脚本打包为可执行文件)
开发语言·前端·python·qt