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

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

相关推荐
Jmyd01238 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
砚凝霜8 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
u01110267510 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
jerrywus10 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
派小心.11 小时前
小程序分享链路中来源参数的传递校验
大数据·前端·数据库·人工智能·小程序·数据分析
hhb_61811 小时前
一键生成数据看板,ECharts实战演示
前端·javascript·echarts
Java后端的Ai之路11 小时前
08_React组件API
前端·react.js·前端框架
小马不起床11 小时前
零基础玩转 FastAPI_09:前端基础工程构建
前端·状态模式·fastapi
风骏时光牛马12 小时前
用户管理模块接口开发
前端
彭亚川Allen13 小时前
从零手搓 RAG 之检索全流程,给大模型接上自己的知识库
前端·后端·ai编程