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