经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » HTML/CSS » Markdown » 查看文章
利用Vue实现一个markdown编辑器实例代码
来源:jb51  时间:2019/5/20 8:35:02  对本文有异议

前言

前段时间做项目的时候,需要一个Markdown编辑器,在网上找了一些开源的实现,但是都不满足需求

说实话,这些开源项目也很难满足需求公司项目的需求,与其实现一个大而全的项目,倒不如实现一个简单的,易于在源码上修改的项目,核心功能都有的,以供修改使用

本文的源码地址如下:https://github.com/jiulu313/HelloMarkDown本地下载

喜欢的朋友可以帮忙star一下,欢迎交流学习

先看一下本项目的效果图(图片经过压缩)


本文的目的就是实现一个有核心功能的,简单,易于修改的项目

话不多说,来看思路

1 markdown内容如何转换成 html?

网上有一个开源的库叫 marked,地址如下:https://github.com/markedjs/marked.git

我们可以安装这个库,使用很简单,就一个函数,传进去markdown内容,就返回了html内容

2 markdown内容转换成了html,如何进行语法高亮?

网上也有一个开源的库,地址如下 :https://highlightjs.org/

我们可以使用这两个库

先把markdown内容解析成html内容

把html内容进行语法高亮

下面我们来一步一步实现代码

3 代码实现

默认你已经创建好了vue的项目 , 创建vue项目 vue init webpack demo
这里面不多讲。

3.1 安装两个库,分别执行下面两条命令

  1. npm install marked --save
  2. npm install highlight.js --save

3.2 首先创建一个 HelloMarkDown 的 Vue组件

布局文件的代码如下:

  1. <template>
  2. <div class="md_root_content" v-bind:style="{width:this.width,height: this.height}">
  3.  
  4. <!--功能按钮区-->
  5. <div class="button_bar">
  6. <span v-on:click="addBold"><B>B</B></span>
  7. <span v-on:click="addUnderline"><B>U</B></span>
  8. <span v-on:click="addItalic"><B>I</B></span>
  9. </div>
  10.  
  11. <!--主要内容区-->
  12. <div class="content_bar">
  13.  
  14. <!--markdown编辑器区-->
  15. <div class="markdown_body">
  16. <textarea ref="ref_md_edit" class="md_textarea_content" v-model="markString">
  17. </textarea>
  18. </div>
  19.  
  20. <!--解析成html区-->
  21. <div class="html_body">
  22. <p v-html="htmlString"></p>
  23. </div>
  24.  
  25. </div>
  26.  
  27. </div>
  28. </template>

主要分为上下两块,上面是功能区的布局

下面一块,分左右两部分,左边是markdown,右边是显示html部分

对应的样式代码如下:

  1. <style scoped>
  2.  
  3. .md_root_content {
  4. display: flex;
  5. display: -webkit-flex;
  6. flex-direction: column;
  7. }
  8.  
  9. .button_bar {
  10. width: 100%;
  11. height: 40px;
  12. background-color: #d4d4d4;
  13. display: flex;
  14. display: -webkit-flex;
  15. align-items: center;
  16. }
  17.  
  18. div.button_bar span {
  19. width: 30px;
  20. line-height: 40px;
  21. text-align: center;
  22. color: orange;
  23. cursor: pointer;
  24. }
  25.  
  26. .content_bar {
  27. display: flex;
  28. display: -webkit-flex;
  29. width: 100%;
  30. height: 100%;
  31. }
  32.  
  33. .markdown_body {
  34. width: 50%;
  35. height: 100%;
  36. display: flex;
  37. display: -webkit-flex;
  38. }
  39.  
  40. .html_body {
  41. width: 50%;
  42. height: 100%;
  43. display: flex;
  44. display: -webkit-flex;
  45. background-color: #dfe9f1;
  46. }
  47.  
  48. .md_textarea_content {
  49. flex: 1;
  50. height: 100%;
  51. padding: 12px;
  52. overflow: auto;
  53. box-sizing: border-box;
  54. resize: none;
  55. outline: none;
  56. border: none;
  57. background-color: #f4f4f4;
  58. font-size: 14px;
  59. color: #232323;
  60. line-height: 24px;
  61. }
  62. </style>

业务逻辑部分的代码如下:

  1. <script>
  2. import marked from 'marked' //解析mardown语法的库
  3. import hljs from 'highlight.js' //对代码进行语法高亮的库
  4.  
  5.  
  6. import testData from '../testData' //测试数据
  7.  
  8.  
  9. export default {
  10. name: "HelloMarkDown",
  11.  
  12. props: {
  13. width: {
  14. type: String,
  15. default: '1000px'
  16. },
  17.  
  18. height: {
  19. type: String,
  20. default: '600px'
  21. }
  22. },
  23.  
  24. data() {
  25. return {
  26. markString: '',
  27. htmlString: '',
  28. }
  29. },
  30.  
  31. mounted(){
  32. this.markString = testData
  33. },
  34.  
  35. methods: {
  36. //加粗
  37. addBold() {
  38. this.changeSelectedText("**","**")
  39. },
  40.  
  41. //斜体
  42. addItalic() {
  43. this.changeSelectedText("***","***")
  44. },
  45.  
  46. addUnderline() {
  47. this.changeSelectedText("<u>","</u>")
  48. },
  49.  
  50. changeSelectedText(startString,endString){
  51. let t = this.$refs.ref_md_edit
  52. if (window.getSelection) {
  53. if (t.selectionStart != undefined && t.selectionEnd != undefined) {
  54.  
  55. let str1 = t.value.substring(0, t.selectionStart)
  56. let str2 = t.value.substring(t.selectionStart, t.selectionEnd)
  57. let str3 = t.value.substring(t.selectionEnd)
  58.  
  59. let result = str1 + startString + str2 + endString + str3
  60. t.value = result
  61. this.markString = t.value
  62. }
  63. }
  64. }
  65. },
  66.  
  67. watch: {
  68.  
  69. //监听markString变化
  70. markString: function (value) {
  71. marked.setOptions({
  72. renderer: new marked.Renderer(),
  73. gfm: true,
  74. tables: true,
  75. breaks: true,
  76. pedantic: false,
  77. sanitize: false,
  78. smartLists: true,
  79. smartypants: false
  80. })
  81.  
  82. this.htmlString = marked(value)
  83. },
  84.  
  85. //监听htmlString并对其高亮
  86. htmlString: function (value) {
  87. this.$nextTick(() => {
  88. const codes = document.querySelectorAll(".html_body pre code");
  89.  
  90. // elem 是一个 object
  91. codes.forEach(elem => {
  92. elem.innerHTML = "<ul><li>" + elem.innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>"
  93. hljs.highlightBlock(elem);
  94. });
  95. });
  96. }
  97. }
  98.  
  99. }
  100. </script>

script中的代码解释

  1. props: {
  2. width: {
  3. type: String,
  4. default: '1000px'
  5. },
  6.  
  7. height: {
  8. type: String,
  9. default: '600px'
  10. }
  11. },

width: 组件的宽度

height:组件的高度

  1. data() {
  2. return {
  3. markString: '',
  4. htmlString: '',
  5. }
  6. },

markString:保存我们输入的markdown内容

htmlString:保存markdown内容转换成的html内容,也就是通过marked函数转换过来的

  1. mounted(){
  2. this.markString = testData
  3. },

显示默认数据

  1. //加粗
  2. addBold() {
  3. this.changeSelectedText("**","**")
  4. },
  5.  
  6. //斜体
  7. addItalic() {
  8. this.changeSelectedText("***","***")
  9. },
  10.  
  11. //加下划线
  12. addUnderline() {
  13. this.changeSelectedText("<u>","</u>")
  14. },

这三个函数都是调用了 changeSelectedText 函数

主要是对鼠标选中的内容进行改变,比如加粗效果,是在选中文本的两边分别添加 **

所以changeSelectedText函数的作用就是在选中的文本两边添加不同的md的符号

比如

this.changeSelectedText("","") ,就是在选中的文本左边和右边都添加**

然后再把最新的内容赋值给 this.$refs.ref_md_edit.value,同时也两会给markString

这样就可以做到选中文本加粗效果了

  1. //监听markString变化
  2. markString: function (value) {
  3. marked.setOptions({
  4. renderer: new marked.Renderer(),
  5. gfm: true,
  6. tables: true,
  7. breaks: true,
  8. pedantic: false,
  9. sanitize: false,
  10. smartLists: true,
  11. smartypants: false
  12. })
  13.  
  14. this.htmlString = marked(value)
  15. },

此时是监听markString的变化

然后调用marked函数进行转换成html内容,并赋值给htmlString

marked.setOptions 是设置一些配置,有兴趣的可以查一下这些配置的作用

  1. //监听htmlString并对其高亮
  2. htmlString: function (value) {
  3. this.$nextTick(() => {
  4. const codes = document.querySelectorAll(".html_body pre code");
  5.  
  6. // elem 是一个 object
  7. codes.forEach(elem => {
  8. elem.innerHTML = "<ul><li>" + elem.innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>"
  9. hljs.highlightBlock(elem);
  10. });
  11. });
  12. }

原本通过 highlight.js这个库在显示语法高亮的时候,是没有行号的。这里我进行了扩展

通过 document.querySelectorAll(".html_body pre code") 找到nodeList

然后对其循环,动态添加 ul , li, 这样就可以显示行号了

不过这需要对 highlight的css文件添加几个样式

源码里面我把highlight中的css文件全部copy到项目中了,使用的是github.css

具体位置是在项目中的 assets/markdown/styles/github.css

如果想使用其它的主题,可以自己修改其它的对应的css文件,这里使用了github的主题,所以只修改了github.css这一个文件

有兴趣的可以查看一下

github.css文件的提交记录

具体的思路就是这些,水平有限,难免有bug,如有发现,欢迎提出

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对w3xue的支持。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号