利用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 相关文章推荐
JavaScript Event学习第十章 一些可替换的事件对
Feb 10 Javascript
jquery实现pager控件示例
Apr 09 Javascript
Jquery 点击按钮自动高亮实现原理及代码
Apr 25 Javascript
window.onload与$(document).ready()的区别分析
May 30 Javascript
JS实现方向键切换输入框焦点的方法
Aug 19 Javascript
jQuery页面元素动态添加后绑定事件丢失方法,非 live
Jun 16 Javascript
AngularJS equal比较对象实例详解
Sep 14 Javascript
使用JS实现图片轮播的实例(前后首尾相接)
Sep 21 Javascript
详解webpack打包时排除其中一个css、js文件或单独打包一个css、js文件(两种方法)
Oct 26 Javascript
如何在Express4.x中愉快地使用async的方法
Nov 18 Javascript
vue+element实现动态加载表单
Dec 13 Vue.js
js实现Element中input组件的部分功能并封装成组件(实例代码)
Mar 02 Javascript
小程序实现搜索界面 小程序实现推荐搜索列表效果
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 函数学习简单小结
2010/07/08 PHP
php不写闭合标签的好处
2014/03/04 PHP
php多功能图片处理类分享(php图片缩放类)
2014/03/14 PHP
PDO防注入原理分析以及注意事项
2015/02/25 PHP
PHP+Ajax异步带进度条上传文件实例
2016/11/01 PHP
Yii2框架操作数据库的方法分析【以mysql为例】
2019/05/27 PHP
js 获取(接收)地址栏参数值的方法
2013/04/01 Javascript
JavaScript实现存储HTML字符串示例
2014/04/21 Javascript
提交按钮的name='submit'引起的js失效问题及原因
2015/02/25 Javascript
Underscore.js 1.3.3 中文注释翻译说明
2015/06/25 Javascript
JS动态添加iframe的代码
2015/09/14 Javascript
Jquery AJAX POST与GET之间的区别详细介绍
2016/10/17 Javascript
Nodejs中Express 常用中间件 body-parser 实现解析
2017/05/22 NodeJs
vue引入jq插件的实例讲解
2017/09/12 Javascript
jQuery访问浏览器本地存储cookie、localStorage和sessionStorage的基本用法
2017/10/20 jQuery
Vue实现自定义下拉菜单功能
2018/07/16 Javascript
解决vuecli3.0热更新失效的问题
2018/09/19 Javascript
解决Layui数据表格显示无数据提示的问题
2019/11/14 Javascript
微信小程序向Java后台传输参数的方法实现
2020/12/10 Javascript
vue 使用rules对表单字段进行校验的步骤
2020/12/25 Vue.js
JavaScript如何实现防止重复的网络请求的示例
2021/01/28 Javascript
Python文件和目录操作详解
2015/02/08 Python
django rest framework之请求与响应(详解)
2017/11/06 Python
python字典快速保存于读取的方法
2018/03/23 Python
python实现批量修改图片格式和尺寸
2018/06/07 Python
详解django.contirb.auth-认证
2018/07/16 Python
pygame游戏之旅 载入小车图片、更新窗口
2018/11/20 Python
基于Python中isfile函数和isdir函数使用详解
2019/11/29 Python
Ubuntu16安装Python3.9的实现步骤
2020/12/15 Python
苹果中国官方网站:Apple中国
2016/07/22 全球购物
房屋租赁意向书
2014/04/01 职场文书
日语专业毕业生自荐书
2014/06/18 职场文书
2015年班组长工作总结
2015/04/10 职场文书
2015暑假假期总结
2015/07/13 职场文书
fastdfs+nginx集群搭建的实现
2021/03/31 Servers
apache虚拟主机配置的三种方式(小结)
2022/07/23 Servers