日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

js压缩代码后怎么生成source map_??markdown生成导航? #x27;[toc]#x27;足矣

發(fā)布時(shí)間:2024/9/19 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js压缩代码后怎么生成source map_??markdown生成导航? #x27;[toc]#x27;足矣 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

快速食用地址:https://github.com/ahungrynoob/showdown-toc

背景

什么是toc? table of contents 是markdown中的導(dǎo)航信息,能夠通過(guò)heading和錨點(diǎn)快速定位。

最近在開(kāi)發(fā)個(gè)人博客的時(shí)候,希望有一個(gè)生成目錄導(dǎo)航的功能,目的是做一個(gè)像語(yǔ)雀右上角一樣的導(dǎo)航欄:


使用的markdown渲染引擎是showdown,找了半天,只找到一個(gè)jquery寫(xiě)的toc插件,也不支持nodejs。(很好,又是一個(gè)造輪子,開(kāi)源,刷福報(bào)的好機(jī)會(huì)!)
于是就自己做了一個(gè)插件,支持兩大功能:

  • 會(huì)把文章中所有的heading信息,通過(guò)閉包的形式傳達(dá)到上層域
  • markdown中寫(xiě)[toc],即可生成toc到markdown的相應(yīng)位置中。
  • 效果:

    使用前:

    使用后:

    開(kāi)發(fā)過(guò)程

    汲取和熟悉

    首先閱讀showdown的wiki,了解extension機(jī)制和寫(xiě)法。了解到以下幾點(diǎn):

  • extension 分為 lang 和 output兩種
  • lang 的extension會(huì)在markdown一開(kāi)始的時(shí)候(normalize之后)就執(zhí)行,output會(huì)在最后執(zhí)行。
  • 作者還很熱心的指出了一些坑(遞歸調(diào)用,不要修改converter):https://github.com/showdownjs/showdown/wiki/extensions#filter-property
  • 思路

    老的思路:自己寫(xiě)一個(gè)lang插件,在一開(kāi)始引擎讀md的時(shí)候,就去收集信息,結(jié)果證明:?
    原因: 實(shí)踐過(guò)程中,showdown有緩存機(jī)制,只會(huì)讀取一遍md內(nèi)容,之后讀的都是html的內(nèi)容,就無(wú)法收集toc的元信息了。

    新思路:在output階段中解析html的h1到h6標(biāo)簽,收集heading信息,并替代[toc]的占位符,輸出到html中,結(jié)果證明:?

    步驟以及代碼:

    整個(gè)代碼的核心是這個(gè)正則:/(<h([1-6]).*?id="([^"]*?)".*?>(.+?)</h[1-6]>)|(<p>[toc]</p>)/g;
    這個(gè)正則,獲取了h1-h6的標(biāo)題信息以及toc的占位符的位置和次數(shù)

    第一步:

    在這里,showdown-toc會(huì)去尋找并按出現(xiàn)次序收集tocItem和[toc]

    // find and collect all headers and [toc] node;const collection: MetaInfo[] = [];source.replace(regex, (wholeMatch, _, level, anchor, text) => {if (wholeMatch === '<p>[toc]</p>') {collection.push({ type: 'toc' });} else {text = text.replace(/<[^>]+>/g, '');const tocItem = {anchor,level: Number(level),text,};// 如果傳了閉包的數(shù)組參數(shù)toc,就會(huì)把標(biāo)題信息推入if (toc) {toc.push(tocItem);}collection.push({type: 'header',...tocItem,});}return '';});

    tocItem長(zhǎng)這樣:

    type TocItem = {anchor: string; // 錨點(diǎn)level: number; // 標(biāo)題級(jí)別text: string; // 標(biāo)題內(nèi)容 };

    第二步

    這里,出現(xiàn)[toc]之后,會(huì)收集這個(gè)[toc]到下個(gè)[toc]之間的標(biāo)題信息

    // calculate toc infoconst tocCollection: TocItem[][] = [];collection.forEach(({ type }, index) => {if (type === 'toc') {if (collection[index + 1] && collection[index + 1].type === 'header') {const headers = [];const { level: levelToToc } = collection[index + 1] as TocItem;for (let i = index + 1; i < collection.length; i++) {if (collection[i].type === 'toc') break;const { level } = collection[i] as TocItem;if (level === levelToToc) {headers.push(collection[i] as TocItem);}}tocCollection.push(headers);} else {tocCollection.push([]);}}});

    第三步:

    這個(gè)階段,會(huì)把source中的showdown給我們生成的<p>[toc]</p>標(biāo)簽替換成我們生成toc標(biāo)簽,也就是ol和li標(biāo)簽啦。然后把處理后的source返回給showdown就好了。整個(gè)插件也就完成了。

    // replace [toc] node in sourcesource = source.replace(/<p>[toc]</p>[n]*/g, () => {const headers = tocCollection.shift();if (headers && headers.length) {const str = `<ol>${headers.map(({ text, anchor }) => `<li><a href="#${anchor}">${text}</a></li>`).join('')}</ol>n`;return str;}return '';});

    總結(jié)

    整個(gè)插件很簡(jiǎn)單,但是中間也遇到了不少坑,出現(xiàn)過(guò)思路的碰撞。實(shí)現(xiàn)的主要步驟邏輯如下:

  • 尋找和收集h1-h6以及[toc]的出現(xiàn)次數(shù)和次序
  • 計(jì)算兩次[toc]之間的標(biāo)題
  • 用我們自己生成toc標(biāo)簽去替換<p>[toc]</p>占位標(biāo)簽,輸出成html字符串。
  • 歡迎pr

    項(xiàng)目地址:https://github.com/ahungrynoob/showdown-toc

    總結(jié)

    以上是生活随笔為你收集整理的js压缩代码后怎么生成source map_??markdown生成导航? #x27;[toc]#x27;足矣的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。