這里是在簡書仿簡書的第十三篇杉适,早睡早起身體好
Vue3 版本在線預覽 https://shuhe.cemcoe.com/
前段時間在搞在簡書仿簡書霹娄,這個問題的核心點在哪呢初斑?或者說核心是什么屯蹦?
僅就個人主觀感受,大部分人夸簡書的點一般是簡潔的編輯器笛园,黑的點大概就是首頁推薦機制了懦胞,那,核心是編輯器咯脊串。
在使用 Vue2 寫的時候在 markdown 編輯器這塊直接選用了一個組件辫呻,在使用 Vue3 重寫時清钥,不打算用外部組件了,來看一波核心放闺,搞一個 markdown 編輯器祟昭。于是就有了下面的冷知識。
先來想一下 markdown 編輯器的功能點怖侦,最重要的就是將 markdown 格式渲染成 html 了篡悟,簡言之,要完成下面的轉化匾寝。
# => h1
![]() => img
[]() => a
搬葬。。艳悔。
好辦呀急凰,思路是使用正則找特殊標志位比如 #
號,再使用字符串的一些方法轉換成 html 格式的字符串猜年。以語句 # 我要轉化h1
為例抡锈,找到 #
號和后面的文字,使用 h1
標簽包裹就得到了 html 格式的字符串了乔外。
看起來好像很容易的樣子呢床三,但事情遠沒有那么簡單,markdown 語法對于 #
的使用是有規(guī)定的袁稽,在非開頭使用是不會渲染成標題標簽的勿璃。還有 ##
,###
等格式推汽,單單一個 #
就夠頭疼的了补疑,更別說各種符號的排列組合了。這個從 markdown 到 html 的轉化的工作量還是很大的歹撒,而且也不是簡單的使用正則找到值再替換的過程莲组。這里面涉及到一些編譯原理的知識。老難搞了暖夭。
好在這個略顯“無聊”的工作已經有人幫我們做了锹杈,就像有人搞出來 babel 來幫我們完成 es6 到 es5 的轉化,已經有人搞出了 marked 來幫我們完成 markdown 到 html 的轉化迈着,當然還有其他的比如 markdownit竭望。
這里就使用 marked 了。其實還是沒有觸及到核心科技裕菠。翻看 marked 的源碼可以發(fā)現(xiàn)咬清,找字符或者術語一點叫做詞法分析階段確實用到的正則,具體可參考https://github.com/markedjs/marked/blob/master/src/rules.js
好的,第一項完成旧烧,現(xiàn)在在 textarea 寫 markdown影钉,點擊預覽調用 marked 方法。
<textarea
v-model="content"
name="post"
id="post"
placeholder="請輸入正文"
></textarea>
<div class="preview" v-show="isPreview">
<div v-html="previewContent"></div>
</div>
<script>
import marked from "marked";
state.previewContent = marked(state.content);
</script>
如果要簡潔的話掘剪,其實這就搞好了平委。
如果要在移動端使用的話最好加點按鈕用于插入符號,畢竟在手機上一些 markdown 符號打起來不是很方便夺谁。
這里就涉及到一些冷知識了廉赔,插入符號換言之就是字符串拼接,字符串拼接是很常規(guī)的操作了予权,這里的核心是如何找到拼接點昂勉。
這里就需要用到一些光標的冷知識了,上圖扫腺。
[post.selectionStart, post.selectionEnd]
通過上面的圖大概就可以明白這兩個屬性的意思了岗照。那么插入的邏輯就好搞了。
找到光標的位置接下來就好辦了笆环,甭管你用什么法子攒至,把字符串從光標位置劈開往里面插入符號。
let start = dom.selectionStart
let end = dom.selectionEnd
dom.value = dom.value.substring(0, start) + string + dom.value.substring(end, dom.value.length)
看起來完成了需求躁劣,誒迫吐,別急,當你點擊按鈕插入符號后账忘,你會發(fā)現(xiàn) textarea 中光標沒有了志膀,此時如果你再次點擊插入操作會有什么現(xiàn)象呢?它會插到最前面鳖擒。
光標消失的原因吧溉浙,其實很簡單,就是本來 textarea 是處于激活狀態(tài)蒋荚,而當你點擊插入按鈕時焦點移交給了按鈕戳稽,自然 textarea 就沒有光標了。
既然如此期升,當插入完畢時我們將焦點再次移交給 textarea 就好了惊奇。
dom.focus()
此時你會發(fā)現(xiàn)另一個問題,那就是光標的位置跑到了最后播赁。
好家伙颂郎,從頭跑到尾了,要解決也很簡單容为。在找光標位置時已經用到了祖秒,再來诞吱,設置一下舟奠。
dom.selectionStart = start + string.length;
dom.selectionEnd = start + string.length;
再試一試應該就好了竭缝。
這個 markdown 編輯器和 Vue 的關系不是很大,核心是 markdown 到 html 的轉化沼瘫。
代碼匯總后:
function useInsertText(dom, string) {
let start = dom.selectionStart
let end = dom.selectionEnd
dom.value = dom.value.substring(0, start) + string + dom.value.substring(end, dom.value.length)
dom.selectionStart = start + string.length;
dom.selectionEnd = start + string.length;
dom.focus()
}
在找資料時發(fā)現(xiàn)另一個方案抬纸,雖然已經廢棄,不過經測試還是好用的耿戚。
// 已廢棄湿故,不推薦,但無須解決焦點丟失和光標位置
document.execCommand('insertText', false, string)
這里的冷知識主要是光標相關的東西膜蛔,這玩意一般場景下用到的幾率確實也不是很多坛猪。
其實這里還是有一些待出來的東西在的,比如移動端的鍵盤皂股,當你點擊插入按鈕后墅茉,因為 textarea 失去焦點,軟鍵盤將會收起呜呐,只有 textarea 重新獲取焦點后鍵盤才會彈出就斤。此時就會頻繁出現(xiàn)鍵盤的收起和彈出。