摘要:背景最近在分析一些框架源码,在写笔记的时候,一些函数的调用栈希望用流程图的形式记录下来,打开就是一顿操作,画了几个调用栈之后,感觉很麻烦。
背景
最近在分析一些框架源码,在写笔记的时候,一些函数的调用栈希望用流程图的形式记录下来,打开 http://draw.io 就是一顿操作,画了几个调用栈之后,感觉很麻烦。于是蹲在厕所里的我开始思考了,调用栈既然可以用 console.trace() 打印出来,那是不是也可以把数据记录下来直接画出流程图来?
当然我从不喜欢造轮子,首先熟练的打开 google 操作一波,发现地球之大,竟然没有我想要的工具?没有 JS 调用栈可视化工具?怎么办?在继续用 draw.io 手画和自己造轮子之间我陷入了深思。
当然我最后选择了造轮子,不然就没有这篇文章了。
轮子长这样 —> hound-trace <--比如说有这样一份代码:
// 轮子 import houndTrace from "hound-trace-ui"; function f() {} function e() {} function d() { f() } const b = () => { d(); e() }; const c = function () { d() }; function a() { b(); c() } houndTrace.start(); a(); houndTrace.end();
可视化输出:
造轮子的过程### 首先要取个酷炫的名字
代码写的好不好,工具好不好用,不重要,一定要有酷炫的名字,那就是 hound-trace ,看这个谷歌翻译出来的名字多么清新脱俗,光芒四射。
怎么去拿调用栈信息?首先想到的是能不能在函数调用前后做点什么?作为 21 世纪的程序员,下手前当然是先 google 和 stackoverflow 一波,看看能不能轻轻松松当个搬运工。逛了半天,靠谱的答案似乎有这些:
// 偷天换日大法 var old = UIIntentionalStream.instance.loadOlderPosts; UIIntentionalStream.instance.loadOlderPosts = function() { // hook before call old(); // hook after call }; // 原型拓展大法 Function.prototype.before = function (callback) { var that = this; return (function() { callback.apply(this, arguments); return (that.apply(this, arguments)); }); } Function.prototype.after = function (callback) { var that = this; return (function() { var result = that.apply(this, arguments); callback.apply(this, arguments); return (result); }); } var both = test.before(function(a) { console.log("Before. Parameter = ", a); }).after(function(a) { console.log("After. Parameter = ", a); }); both(17);
看起来不错,可是这,我要是看个 react 源码,想拿到调用栈信息。函数调用岂不都要重写个遍?不靠谱,还不如去手画呢。
找啊找啊找,灵光一闪?AST。运行的时候不行,直接改代码不就完了。就这么干?于是就写了个 babel 插件,在代码里下点毒。babel-plugin-hound-trace 这个插件干啥呢?
// ... module.exports = function (babel) { return { visitor: { // 在我们的代码里面遇到函数声明语句,函数表达式,箭头函数表达式的时候 // 该插件会注入一些代码 FunctionDeclaration: (path) => { // ... }, FunctionExpression: expressionHandle.bind(null, babel), ArrowFunctionExpression: expressionHandle.bind(null, babel) } }; }; // ...
比如源码里的函数如下:
function test(a, b, c) { const cj = "cj"; }
下毒之后(经过这个插件处理之后):
function test(a, b, c) { let __traceParent__ = window.__traceParent__; let __traceOldParent__ = __traceParent__; if (window.__trace__) { if (!__traceParent__.next) { __traceParent__.next = []; } const current = { name: "test", params: ["a", "b", "c"] }; __traceParent__.next.push(current); window.__traceParent__ = current; } const cj = "cj"; window.__traceParent__ = __traceOldParent__; }
注入的代码比较奇怪,因为实现的思路是借助 window 上的全局变量来做这个事情,所以看起来奇怪。(暂时还没想其它好方法)
注入了代码之后就简单了 ,可以看到代码里注入了 window.__trace__ 这个变量用于是否记录该函数,所以 hound-trace 包的代码就自然出来了:
let trace = false; // 开始记录调用栈 function __NoTraceHook__start() { if (trace) { return } window.__trace__ = trace = true; window.__traceParent__ = {}; } // 结束记录调用栈 function __NoTraceHook__end(callback) { if (!trace) { return } window.__trace__ = trace = false; callback && callback(window.__traceParent__); } export default { start: __NoTraceHook__start, end: __NoTraceHook__end };
到这里,就差可视化渲染了,考虑到 UI 层是比较个性的,所以又拆了个包出来 hound-trace-ui 底层调用 hound-trace 的 API ,这样以后就能随意加皮肤了。
hound-trace-ui 其实很简单,就是在 __NoTraceHook__end 的回调里可以拿到调用栈的数据,然后怎么用这个数据就随意了,这个包里使用的是 mermaid 做可视化(因为简单):
// 调用 hound-trace 包代码 import houndTrace from "../../hound-trace/src/index"; // 渲染数据逻辑 import renderCallStack from "./renderCallStack"; import "./index.css"; function start() { houndTrace.start(); } // 包装底层 API function end() { houndTrace.end(callStack => { setTimeout(() => { // 调用 mermaid 包渲染数据 renderCallStack(callStack); }, 14); }); } export default { start, end, endAndRenderCallStack: end };
好吧,就这样世界上又多了一个轮子。
感兴趣的可以去 star ,当然更希望大家给出奇淫技巧一起完善。—> hound-trace <--
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106801.html
摘要:下载依赖包完成项目创建,项目结构连接数据库在根目录下创建,输入以下代码,监听的几个事件,如果以上操作都没错的话,那么就会监听第一个事件事件,表示连接数据库成功,在最后,我们导出对象,以供其他模块使用。 一、准备工作 1. 启动mongo数据库 关于下载安装启动数据库我这里就不做过多解释,谷歌下会有很多教程,启动成功后的命令窗如下所示: showImg(https://segmentfa...
摘要:在回调队列中,函数等待调用栈为空,因为每个语句都执行一次。最后一个运行,并且从调用栈中弹出。它将回调以先进先出顺序移动到调用栈并执行。 翻译:疯狂的技术宅原文: https://medium.freecodecamp.o... 本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 Node.js 是一个 JavaScript 运行时环境。听起来还不错,不过这究竟...
摘要:月日,第六届大会在深圳召开。这是这次大会的第二站活动,第一站已在上海成功举办。深圳站视频及,请在公众号后台回复,获取分享链接。据介绍,目前支持多种开发库,如内置和等。该协议的推出,是为了统一标准,提高效率。 本文为 PyChina 和「编程派」联合首发,作者为 EarlGrey。「编程派」是一个专注 Python 学习交流的微信公众号。 9 月 25 日,第六届 PyCon China...
摘要:最受欢迎的引擎是,在和中使用,用于,以及所使用的。单线程的我们说是单线程的,因为有一个调用栈处理我们的函数。也就是说,如果有其他函数等待执行,函数是不能离开调用栈的。每个异步函数在被送入调用栈之前必须通过回调队列。 翻译:疯狂的技术宅原文:https://www.valentinog.com/bl... 本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 sh...
摘要:第一个问题前端都做哪些事呢,前端都需要哪些技术呢前端发展的三个阶段初级阶段入门常见标签,新增的,语义化标签等等选择器,背景,文本,链接,列表,盒模型,定位,浮动,新增的属性栅格化系统,按钮,表单,导航数据类型,对象,函数,运算符,语句,,选 第一个问题:前端都做哪些事呢,前端都需要哪些技术呢 前端发展的三个阶段: 初级阶段:(入门) html:常见标签,html5新增的,语义化标签等等...
阅读 2451·2021-11-24 09:39
阅读 3495·2019-08-30 15:53
阅读 573·2019-08-29 15:15
阅读 2876·2019-08-26 13:23
阅读 3188·2019-08-26 10:48
阅读 614·2019-08-26 10:31
阅读 728·2019-08-26 10:30
阅读 2302·2019-08-23 18:32