成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

寫一個(gè) JS 調(diào)用??梢暬ぞ?hound-trace

solocoder / 3227人閱讀

摘要:背景最近在分析一些框架源碼,在寫筆記的時(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)然是先 googlestackoverflow 一波,看看能不能輕輕松松當(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

相關(guān)文章

  • 「全初探」- Mongoose的簡(jiǎn)單使用

    摘要:下載依賴包完成項(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...

    vboy1010 評(píng)論0 收藏0
  • Node.js 究竟是什么?

    摘要:在回調(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ò)這究竟...

    yeyan1996 評(píng)論0 收藏0
  • PyCon China 深圳站精彩回顧(附PPT及視頻)

    摘要:月日,第六屆大會(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...

    lykops 評(píng)論0 收藏0
  • JavaScript引擎是如何工作的?從調(diào)用到Promise你需要知道的一切

    摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說(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...

    Simon_Zhou 評(píng)論0 收藏0
  • 前端人員必須知道的三個(gè)問(wèn)題

    摘要:第一個(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)簽等等...

    zollero 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<