摘要:背景最近在分析一些框架源碼,在寫筆記的時候,一些函數的調用棧希望用流程圖的形式記錄下來,打開就是一頓操作,畫了幾個調用棧之后,感覺很麻煩。
背景
最近在分析一些框架源碼,在寫筆記的時候,一些函數的調用棧希望用流程圖的形式記錄下來,打開 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 <--
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://www.hztianpu.com/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新增的,語義化標簽等等...
閱讀 2872·2021-11-24 09:39
閱讀 3725·2019-08-30 15:53
閱讀 862·2019-08-29 15:15
閱讀 3106·2019-08-26 13:23
閱讀 3500·2019-08-26 10:48
閱讀 908·2019-08-26 10:31
閱讀 1008·2019-08-26 10:30
閱讀 2573·2019-08-23 18:32