博客園在Markdown中使用JS


如果能在博客園的 Markdown 中使用 JS,那將是一件十分方便的事情(類似於jupyter notebook)。但是,經過測試發現,我們無法在 Markdown 中直接寫 JS,比如我寫了如下隨筆

## 標題

- foo
- bar
<script>
console.log('hello?')
</script>

進入博客園后發現控制台並沒有輸出 hello?,CTRL+U 觀察源代碼發現<script>被博客園去掉了,即

<div id="cnblogs_post_body" class="blogpost-body cnblogs-markdown">
  <h2 id="標題">標題</h2>
  <ul>
    <li>foo</li>
    <li>bar</li>
  </ul>
</div>
<!-- script不見了 -->
<div id="MySignature"></div>
... 其他代碼

針對上面的問題,下面介紹在博客園Mardown中使JS生效的兩種方式。

借助eval函數

考慮到博客園的頁腳 HTML 處是可以運行任何 JS 的。瀏覽器在解析到我們在頁腳 HTML 處的 JS 之前,已經擁有了 Dom 樹。一個思路是某個 Dom 節點的文本寫上 JS,然后再在頁腳 HTML 處的 JS 通過 eval 調用該文本。

那么如何選擇 Dom 節點呢?

我們可以將其寫在一個非標准的標簽中,如

vscode的顯示結果

但是這樣在 Markdown 中沒有代碼高亮且不能自動縮進。為了不容易寫錯,這里選擇了

vscode的顯示結果

這段代碼會被博客園在后台處理成

<pre id="copy_target_2">
    <code class="language-es6">
        console.log('run')
        function foo(){
          console.log('foo')
        }
        foo()
    </code>
</pre>

因此我們只要在頁腳 HTML 代碼處增加這樣的腳本

/**
 * 在Markdown中運行腳本,為了有高亮和提示效果,這里用了es6
 */
$('code.language-es6').each(function () {
  window.eval($(this).text())
  $(this).parent().remove()
})

奏效。

借助body標簽的onload屬性

另一個思路將代碼寫在body的onload屬性中。向onload傳遞一個函數調用。該函數需要在自定義頁腳HTML代碼處定義。

<body
  onload="invokeMyFunction(function bar(){
   console.log('load')
   function foo(){
     console.log('foo')
   }
   foo()
})"
></body>

自定義頁腳HTML代碼 處配置如下JS

window.invokeMyFunction = function(cb) {
  cb()
}

奏效。你也可以參考這篇文章的Markdown博客園調用網易新聞接口實現動態網頁

進一步測試

經過簡單測試,兩種方式均能奏效,那么選哪一種呢?我們需要進一步的測試

首先故意寫錯JS,如下

 console.log('run')
 function foo() {
   console.log('foo')
+  不存在的函數()
 }
 foo()

同時使用兩種方式,並觀察控制台的輸出結果

瀏覽器報錯結果

點進棧頂的定位

eval的報錯定位

onload的報錯定位

發現eval並不能定位到HTML的哪一行,只是定位到了我們的全局JS,並不能定位到隨筆頁。但是onload的方式就很明細了(注意要webpack要配置成調試模式inline-source-map

module.exports = {
  devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'inline-source-map',
}

接下來測試運行效率,在自定義頁腳HTML代碼處增加記時邏輯

$('code.language-es6').each(function() {
  const start = new Date().getTime()
  window.eval($(this).text())
  const end = new Date().getTime()
  console.log('debug: eval耗時' + (end - start) + '毫秒')
  $(this).parent().remove()
})
window.invokeMyFunction = function(cb) {
  const start = new Date().getTime()
  cb()
  const end = new Date().getTime()
  console.log('debug: onload耗時' + (end - start) + '毫秒')
}

在Markdonw中加入測試程序

運算

發現對於這種運算,eval慢的並不明顯

控制台輸出結果

總結

對於onload方法

優點:

  • 效率高那么一點點
  • 方便調試

缺點:

  • 只能用一次
  • 不夠優雅,可讀性差

對於eval方法

優點:

  • 看起來更為優雅,可讀性更好
  • 可以使用多次

缺點:

  • 效率低(低頻使用時影響不大)
  • 不安全(有方法防范就安全了)
  • 不方便調試

綜合比較還是使用eval方法更好些,對於無法定位這個問題,可以先用eval方法,報錯后如果不能一眼找到原因,再改用onload方法,最后切換為eval方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM