摘要:背景最近在分析一些框架源碼,在寫筆記的時(shí)候,一些函數(shù)的調(diào)用棧希望用流程圖的形式記錄下來(lái),打開(kāi)就是一頓操作,畫了幾個(gè)調(diào)用棧之后,感覺(jué)很麻煩。
背景
最近在分析一些框架源碼,在寫筆記的時(shí)候,一些函數(shù)的調(diào)用棧希望用流程圖的形式記錄下來(lái),打開(kāi) http://draw.io 就是一頓操作,畫了幾個(gè)調(diào)用棧之后,感覺(jué)很麻煩。于是蹲在廁所里的我開(kāi)始思考了,調(diào)用棧既然可以用 console.trace() 打印出來(lái),那是不是也可以把數(shù)據(jù)記錄下來(lái)直接畫出流程圖來(lái)?
當(dāng)然我從不喜歡造輪子,首先熟練的打開(kāi) google 操作一波,發(fā)現(xiàn)地球之大,竟然沒(méi)有我想要的工具?沒(méi)有 JS 調(diào)用??梢暬ぞ撸吭趺崔k?在繼續(xù)用 draw.io 手畫和自己造輪子之間我陷入了深思。
當(dāng)然我最后選擇了造輪子,不然就沒(méi)有這篇文章了。
輪子長(zhǎng)這樣 —> hound-trace <--比如說(shuō)有這樣一份代碼:
// 輪子 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();
可視化輸出:
造輪子的過(guò)程### 首先要取個(gè)酷炫的名字
代碼寫的好不好,工具好不好用,不重要,一定要有酷炫的名字,那就是 hound-trace ,看這個(gè)谷歌翻譯出來(lái)的名字多么清新脫俗,光芒四射。
怎么去拿調(diào)用棧信息?首先想到的是能不能在函數(shù)調(diào)用前后做點(diǎn)什么?作為 21 世紀(jì)的程序員,下手前當(dāng)然是先 google 和 stackoverflow 一波,看看能不能輕輕松松當(dāng)個(gè)搬運(yùn)工。逛了半天,靠譜的答案似乎有這些:
// 偷天換日大法 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);
看起來(lái)不錯(cuò),可是這,我要是看個(gè) react 源碼,想拿到調(diào)用棧信息。函數(shù)調(diào)用豈不都要重寫個(gè)遍?不靠譜,還不如去手畫呢。
找啊找啊找,靈光一閃?AST。運(yùn)行的時(shí)候不行,直接改代碼不就完了。就這么干?于是就寫了個(gè) babel 插件,在代碼里下點(diǎn)毒。babel-plugin-hound-trace 這個(gè)插件干啥呢?
// ... module.exports = function (babel) { return { visitor: { // 在我們的代碼里面遇到函數(shù)聲明語(yǔ)句,函數(shù)表達(dá)式,箭頭函數(shù)表達(dá)式的時(shí)候 // 該插件會(huì)注入一些代碼 FunctionDeclaration: (path) => { // ... }, FunctionExpression: expressionHandle.bind(null, babel), ArrowFunctionExpression: expressionHandle.bind(null, babel) } }; }; // ...
比如源碼里的函數(shù)如下:
function test(a, b, c) { const cj = "cj"; }
下毒之后(經(jīng)過(guò)這個(gè)插件處理之后):
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__; }
注入的代碼比較奇怪,因?yàn)閷?shí)現(xiàn)的思路是借助 window 上的全局變量來(lái)做這個(gè)事情,所以看起來(lái)奇怪。(暫時(shí)還沒(méi)想其它好方法)
注入了代碼之后就簡(jiǎn)單了 ,可以看到代碼里注入了 window.__trace__ 這個(gè)變量用于是否記錄該函數(shù),所以 hound-trace 包的代碼就自然出來(lái)了:
let trace = false; // 開(kāi)始記錄調(diào)用棧 function __NoTraceHook__start() { if (trace) { return } window.__trace__ = trace = true; window.__traceParent__ = {}; } // 結(jié)束記錄調(diào)用棧 function __NoTraceHook__end(callback) { if (!trace) { return } window.__trace__ = trace = false; callback && callback(window.__traceParent__); } export default { start: __NoTraceHook__start, end: __NoTraceHook__end };
到這里,就差可視化渲染了,考慮到 UI 層是比較個(gè)性的,所以又拆了個(gè)包出來(lái) hound-trace-ui 底層調(diào)用 hound-trace 的 API ,這樣以后就能隨意加皮膚了。
hound-trace-ui 其實(shí)很簡(jiǎn)單,就是在 __NoTraceHook__end 的回調(diào)里可以拿到調(diào)用棧的數(shù)據(jù),然后怎么用這個(gè)數(shù)據(jù)就隨意了,這個(gè)包里使用的是 mermaid 做可視化(因?yàn)楹?jiǎn)單):
// 調(diào)用 hound-trace 包代碼 import houndTrace from "../../hound-trace/src/index"; // 渲染數(shù)據(jù)邏輯 import renderCallStack from "./renderCallStack"; import "./index.css"; function start() { houndTrace.start(); } // 包裝底層 API function end() { houndTrace.end(callStack => { setTimeout(() => { // 調(diào)用 mermaid 包渲染數(shù)據(jù) renderCallStack(callStack); }, 14); }); } export default { start, end, endAndRenderCallStack: end };
好吧,就這樣世界上又多了一個(gè)輪子。
感興趣的可以去 star ,當(dāng)然更希望大家給出奇淫技巧一起完善?!? hound-trace <--
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106801.html
摘要:下載依賴包完成項(xiàng)目創(chuàng)建,項(xiàng)目結(jié)構(gòu)連接數(shù)據(jù)庫(kù)在根目錄下創(chuàng)建,輸入以下代碼,監(jiān)聽(tīng)的幾個(gè)事件,如果以上操作都沒(méi)錯(cuò)的話,那么就會(huì)監(jiān)聽(tīng)第一個(gè)事件事件,表示連接數(shù)據(jù)庫(kù)成功,在最后,我們導(dǎo)出對(duì)象,以供其他模塊使用。 一、準(zhǔn)備工作 1. 啟動(dòng)mongo數(shù)據(jù)庫(kù) 關(guān)于下載安裝啟動(dòng)數(shù)據(jù)庫(kù)我這里就不做過(guò)多解釋,谷歌下會(huì)有很多教程,啟動(dòng)成功后的命令窗如下所示: showImg(https://segmentfa...
摘要:在回調(diào)隊(duì)列中,函數(shù)等待調(diào)用棧為空,因?yàn)槊總€(gè)語(yǔ)句都執(zhí)行一次。最后一個(gè)運(yùn)行,并且從調(diào)用棧中彈出。它將回調(diào)以先進(jìn)先出順序移動(dòng)到調(diào)用棧并執(zhí)行。 翻譯:瘋狂的技術(shù)宅原文: https://medium.freecodecamp.o... 本文首發(fā)微信公眾號(hào):前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 Node.js 是一個(gè) JavaScript 運(yùn)行時(shí)環(huán)境。聽(tīng)起來(lái)還不錯(cuò),不過(guò)這究竟...
摘要:月日,第六屆大會(huì)在深圳召開(kāi)。這是這次大會(huì)的第二站活動(dòng),第一站已在上海成功舉辦。深圳站視頻及,請(qǐng)?jiān)诠娞?hào)后臺(tái)回復(fù),獲取分享鏈接。據(jù)介紹,目前支持多種開(kāi)發(fā)庫(kù),如內(nèi)置和等。該協(xié)議的推出,是為了統(tǒng)一標(biāo)準(zhǔn),提高效率。 本文為 PyChina 和「編程派」聯(lián)合首發(fā),作者為 EarlGrey。「編程派」是一個(gè)專注 Python 學(xué)習(xí)交流的微信公眾號(hào)。 9 月 25 日,第六屆 PyCon China...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說(shuō)是單線程的,因?yàn)橛幸粋€(gè)調(diào)用棧處理我們的函數(shù)。也就是說(shuō),如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開(kāi)調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過(guò)回調(diào)隊(duì)列。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號(hào):前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 sh...
摘要:第一個(gè)問(wèn)題前端都做哪些事呢,前端都需要哪些技術(shù)呢前端發(fā)展的三個(gè)階段初級(jí)階段入門常見(jiàn)標(biāo)簽,新增的,語(yǔ)義化標(biāo)簽等等選擇器,背景,文本,鏈接,列表,盒模型,定位,浮動(dòng),新增的屬性柵格化系統(tǒng),按鈕,表單,導(dǎo)航數(shù)據(jù)類型,對(duì)象,函數(shù),運(yùn)算符,語(yǔ)句,,選 第一個(gè)問(wèn)題:前端都做哪些事呢,前端都需要哪些技術(shù)呢 前端發(fā)展的三個(gè)階段: 初級(jí)階段:(入門) html:常見(jiàn)標(biāo)簽,html5新增的,語(yǔ)義化標(biāo)簽等等...
閱讀 2490·2021-11-24 09:39
閱讀 3531·2019-08-30 15:53
閱讀 607·2019-08-29 15:15
閱讀 2912·2019-08-26 13:23
閱讀 3228·2019-08-26 10:48
閱讀 653·2019-08-26 10:31
閱讀 779·2019-08-26 10:30
閱讀 2373·2019-08-23 18:32