利用Vue实现一个markdown编辑器实例代码


Posted in Javascript onMay 19, 2019

前言

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

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

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

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

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

利用Vue实现一个markdown编辑器实例代码

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

话不多说,来看思路

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 安装两个库,分别执行下面两条命令

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

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

布局文件的代码如下:

<template>
 <div class="md_root_content" v-bind:style="{width:this.width,height: this.height}">

 <!--功能按钮区-->
 <div class="button_bar">
 <span v-on:click="addBold"><B>B</B></span>
 <span v-on:click="addUnderline"><B>U</B></span>
 <span v-on:click="addItalic"><B>I</B></span>
 </div>

 <!--主要内容区-->
 <div class="content_bar">

 <!--markdown编辑器区-->
 <div class="markdown_body">
 <textarea ref="ref_md_edit" class="md_textarea_content" v-model="markString">
 </textarea>
 </div>

 <!--解析成html区-->
 <div class="html_body">
 <p v-html="htmlString"></p>
 </div>

 </div>

 </div>
</template>

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

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

对应的样式代码如下:

<style scoped>

 .md_root_content {
 display: flex;
 display: -webkit-flex;
 flex-direction: column;
 }

 .button_bar {
 width: 100%;
 height: 40px;
 background-color: #d4d4d4;
 display: flex;
 display: -webkit-flex;
 align-items: center;
 }

 div.button_bar span {
 width: 30px;
 line-height: 40px;
 text-align: center;
 color: orange;
 cursor: pointer;
 }

 .content_bar {
 display: flex;
 display: -webkit-flex;
 width: 100%;
 height: 100%;
 }

 .markdown_body {
 width: 50%;
 height: 100%;
 display: flex;
 display: -webkit-flex;
 }

 .html_body {
 width: 50%;
 height: 100%;
 display: flex;
 display: -webkit-flex;
 background-color: #dfe9f1;
 }

 .md_textarea_content {
 flex: 1;
 height: 100%;
 padding: 12px;
 overflow: auto;
 box-sizing: border-box;
 resize: none;
 outline: none;
 border: none;
 background-color: #f4f4f4;
 font-size: 14px;
 color: #232323;
 line-height: 24px;
 }
</style>

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

<script>
 import marked from 'marked' //解析mardown语法的库
 import hljs from 'highlight.js' //对代码进行语法高亮的库


 import testData from '../testData' //测试数据


 export default {
 name: "HelloMarkDown",

 props: {
 width: {
 type: String,
 default: '1000px'
 },

 height: {
 type: String,
 default: '600px'
 }
 },

 data() {
 return {
 markString: '',
 htmlString: '',
 }
 },

 mounted(){
 this.markString = testData
 },

 methods: {
 //加粗
 addBold() {
 this.changeSelectedText("**","**")
 },

 //斜体
 addItalic() {
 this.changeSelectedText("***","***")
 },

 addUnderline() {
 this.changeSelectedText("<u>","</u>")
 },

 changeSelectedText(startString,endString){
 let t = this.$refs.ref_md_edit
 if (window.getSelection) {
  if (t.selectionStart != undefined && t.selectionEnd != undefined) {

  let str1 = t.value.substring(0, t.selectionStart)
  let str2 = t.value.substring(t.selectionStart, t.selectionEnd)
  let str3 = t.value.substring(t.selectionEnd)

  let result = str1 + startString + str2 + endString + str3
  t.value = result
  this.markString = t.value
  }
 }
 }
 },

 watch: {

 //监听markString变化
 markString: function (value) {
 marked.setOptions({
  renderer: new marked.Renderer(),
  gfm: true,
  tables: true,
  breaks: true,
  pedantic: false,
  sanitize: false,
  smartLists: true,
  smartypants: false
 })

 this.htmlString = marked(value)
 },

 //监听htmlString并对其高亮
 htmlString: function (value) {
 this.$nextTick(() => {
  const codes = document.querySelectorAll(".html_body pre code");

  // elem 是一个 object
  codes.forEach(elem => {
  elem.innerHTML = "<ul><li>" + elem.innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>"
  hljs.highlightBlock(elem);
  });
 });
 }
 }

 }
</script>

script中的代码解释

props: {
 width: {
 type: String,
 default: '1000px'
 },

 height: {
 type: String,
 default: '600px'
 }
 },

width: 组件的宽度

height:组件的高度

data() {
 return {
 markString: '',
 htmlString: '',
 }
 },

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

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

mounted(){
  this.markString = testData
 },

显示默认数据

//加粗
  addBold() {
  this.changeSelectedText("**","**")
  },

  //斜体
  addItalic() {
  this.changeSelectedText("***","***")
  },

  //加下划线
  addUnderline() {
  this.changeSelectedText("<u>","</u>")
  },

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

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

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

比如

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

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

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

//监听markString变化
  markString: function (value) {
  marked.setOptions({
   renderer: new marked.Renderer(),
   gfm: true,
   tables: true,
   breaks: true,
   pedantic: false,
   sanitize: false,
   smartLists: true,
   smartypants: false
  })

  this.htmlString = marked(value)
  },

此时是监听markString的变化

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

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

//监听htmlString并对其高亮
  htmlString: function (value) {
  this.$nextTick(() => {
   const codes = document.querySelectorAll(".html_body pre code");

   // elem 是一个 object
   codes.forEach(elem => {
   elem.innerHTML = "<ul><li>" + elem.innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>"
   hljs.highlightBlock(elem);
   });
  });
  }

原本通过 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,如有发现,欢迎提出

总结

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

Javascript 相关文章推荐
JS实现可调整倒计时间代码分享
Aug 18 Javascript
基于jquery实现省市联动特效
Dec 17 Javascript
js仿微博实现统计字符和本地存储功能
Dec 22 Javascript
javascript实现随机显示星星特效
Jan 28 Javascript
基于jquery编写分页插件
Mar 07 Javascript
ECMAScript6快速入手攻略
Jul 18 Javascript
详解jQuery插件开发方式
Nov 22 Javascript
Koa 使用小技巧(小结)
Oct 22 Javascript
Angular实现svg和png图片下载实现
May 05 Javascript
在Vue中用canvas实现二维码和图片合成海报的方法
Jun 10 Javascript
ES10的13个新特性示例(小结)
Sep 23 Javascript
jQuery+ThinkPHP实现图片上传
Jul 23 jQuery
小程序实现搜索界面 小程序实现推荐搜索列表效果
May 18 #Javascript
微信小程序实现搜索指定景点周边美食、酒店
May 18 #Javascript
vue+高德地图写地图选址组件的方法
May 18 #Javascript
微信小程序实现搜索功能并跳转搜索结果页面
May 18 #Javascript
js实现图片推拉门效果代码实例
May 18 #Javascript
微信小程序代码上传、审核发布小程序
May 18 #Javascript
详解js中let与var声明变量的区别
Apr 05 #Javascript
You might like
php购物网站支付paypal使用方法
2010/11/28 PHP
可以保证单词完整性的PHP英文字符串截取代码分享
2014/07/15 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
2015/05/27 PHP
Thinkphp结合ajaxFileUpload实现异步图片传输示例
2017/03/13 PHP
thinkphp ajaxfileupload实现异步上传图片的示例
2017/08/28 PHP
PHP 应用容器化以及部署方法
2018/02/12 PHP
让FireFox支持innerText的实现代码
2009/12/01 Javascript
js验证模型自我实现的具体方法
2013/06/21 Javascript
JS沙箱模式实例分析
2017/09/04 Javascript
浅谈JavaScript find 方法不支持IE的问题
2017/09/28 Javascript
微信小程序商品详情页的底部弹出框效果
2020/11/16 Javascript
VUE 3D轮播图封装实现方法
2018/07/03 Javascript
vue中组件的过渡动画及实现代码
2018/11/21 Javascript
JavaScript常见鼠标事件与用法分析
2019/01/03 Javascript
微信实现自动跳转到用其他浏览器打开指定APP下载
2019/02/15 Javascript
Layui实现带查询条件的分页
2019/07/27 Javascript
解决vue组件中click事件失效的问题
2019/11/09 Javascript
详解js中的几种常用设计模式
2020/07/16 Javascript
微信小程序wx.getUserInfo授权获取用户信息(头像、昵称)的实现
2020/08/19 Javascript
[01:03:36]Ti4 循环赛第三日DK vs Titan
2014/07/12 DOTA
Python实现数据库编程方法详解
2015/06/09 Python
Python爬虫爬验证码实现功能详解
2016/04/14 Python
python3使用pyqt5制作一个超简单浏览器的实例
2017/10/19 Python
Python实现OpenCV的安装与使用示例
2018/03/30 Python
Python实现获取邮箱内容并解析的方法示例
2018/06/16 Python
Python中实例化class的执行顺序示例详解
2018/10/14 Python
Django后台admin的使用详解
2019/07/08 Python
详解Django定时任务模块设计与实践
2019/07/24 Python
人力资源部经理岗位职责规定
2014/02/23 职场文书
优秀经理获奖感言
2014/03/04 职场文书
2014年民主评议党员个人总结
2014/09/24 职场文书
优秀班主任申报材料
2014/12/16 职场文书
质量保证书格式
2015/02/27 职场文书
2015年推普周活动方案
2015/05/06 职场文书
MySQL注入基础练习
2021/05/30 MySQL
详解 TypeScript 枚举类型
2021/11/02 Javascript