美文网首页
13 | 编写markdown编辑器时遇到的冷知识

13 | 编写markdown编辑器时遇到的冷知识

作者: cemcoe | 来源:发表于2021-02-04 06:42 被阅读0次

    这里是在简书仿简书的第十三篇,早睡早起身体好

    Vue3 版本在线预览 https://shuhe.cemcoe.com/

    前段时间在搞在简书仿简书,这个问题的核心点在哪呢?或者说核心是什么?

    仅就个人主观感受,大部分人夸简书的点一般是简洁的编辑器,黑的点大概就是首页推荐机制了,那,核心是编辑器咯。

    在使用 Vue2 写的时候在 markdown 编辑器这块直接选用了一个组件,在使用 Vue3 重写时,不打算用外部组件了,来看一波核心,搞一个 markdown 编辑器。于是就有了下面的冷知识。


    先来想一下 markdown 编辑器的功能点,最重要的就是将 markdown 格式渲染成 html 了,简言之,要完成下面的转化。

    # => h1
    ![]() => img
    []() => a
    。。。
    

    好办呀,思路是使用正则找特殊标志位比如 # 号,再使用字符串的一些方法转换成 html 格式的字符串。以语句 # 我要转化h1 为例,找到 # 号和后面的文字,使用 h1 标签包裹就得到了 html 格式的字符串了。

    看起来好像很容易的样子呢,但事情远没有那么简单,markdown 语法对于 # 的使用是有规定的,在非开头使用是不会渲染成标题标签的。还有 ##### 等格式,单单一个 # 就够头疼的了,更别说各种符号的排列组合了。这个从 markdown 到 html 的转化的工作量还是很大的,而且也不是简单的使用正则找到值再替换的过程。这里面涉及到一些编译原理的知识。老难搞了。

    好在这个略显“无聊”的工作已经有人帮我们做了,就像有人搞出来 babel 来帮我们完成 es6 到 es5 的转化,已经有人搞出了 marked 来帮我们完成 markdown 到 html 的转化,当然还有其他的比如 markdownit。

    这里就使用 marked 了。其实还是没有触及到核心科技。翻看 marked 的源码可以发现,找字符或者术语一点叫做词法分析阶段确实用到的正则,具体可参考https://github.com/markedjs/marked/blob/master/src/rules.js

    好的,第一项完成,现在在 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 符号打起来不是很方便。

    这里就涉及到一些冷知识了,插入符号换言之就是字符串拼接,字符串拼接是很常规的操作了,这里的核心是如何找到拼接点。

    这里就需要用到一些光标的冷知识了,上图。


    Snipaste_2021-02-03_11-05-56.png
    [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)
    

    看起来完成了需求,诶,别急,当你点击按钮插入符号后,你会发现 textarea 中光标没有了,此时如果你再次点击插入操作会有什么现象呢?它会插到最前面。

    demo.gif

    光标消失的原因吧,其实很简单,就是本来 textarea 是处于激活状态,而当你点击插入按钮时焦点移交给了按钮,自然 textarea 就没有光标了。

    既然如此,当插入完毕时我们将焦点再次移交给 textarea 就好了。

    dom.focus()
    

    此时你会发现另一个问题,那就是光标的位置跑到了最后。

    demo1.gif

    好家伙,从头跑到尾了,要解决也很简单。在找光标位置时已经用到了,再来,设置一下。

    dom.selectionStart = start + string.length;
    dom.selectionEnd = start + string.length;
    

    再试一试应该就好了。

    demo2.gif

    这个 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()
    }
    

    在找资料时发现另一个方案,虽然已经废弃,不过经测试还是好用的。

    // 已废弃,不推荐,但无须解决焦点丢失和光标位置
    document.execCommand('insertText', false, string)
    

    这里的冷知识主要是光标相关的东西,这玩意一般场景下用到的几率确实也不是很多。

    其实这里还是有一些待出来的东西在的,比如移动端的键盘,当你点击插入按钮后,因为 textarea 失去焦点,软键盘将会收起,只有 textarea 重新获取焦点后键盘才会弹出。此时就会频繁出现键盘的收起和弹出。

    相关文章

      网友评论

          本文标题:13 | 编写markdown编辑器时遇到的冷知识

          本文链接:https://www.haomeiwen.com/subject/gvlptltx.html