preface
Before, I always want to develop a Markdown editor of my own, mainly because I can write articles more flexible operation, in addition to broaden their vision is also a very good choice ah! So I decided to have some fun on the weekend. First of all, I researched many popular MD editors online, and they are all excellent. Not to exceed them, mainly to use their own comfort point. The purpose of this post is to document how I went from 0 to 1 to create a decent Markdown editor.
List of completed projects
Research the Markdown editor
There are many Markdown editors at home and abroad.
- editor.md
Website: https://pandao.github.io/editor.md/
Markdown is an open source, embedable online editor (component) based on CodeMirror, jQuery, and Marked. This component seems to be domestic development, personal use before is ok.
- typora
Website: https://www.typora.io/
Typora is a free, lightweight Markdown editor. It is not as well-known as Mou, Haroopad and other Markdown editors. It is a relatively small product. In all conscience, I have used several Markdown editors, including Little Bookmaker, Haroopad, Atom, but Typora was the one I found to my heart’s content. It was lightweight, fast, easy to use, and just not too comfortable to use.
- tui-editor
Website: https://ui.toast.com/tui-editor
This is a Markdown component, and I decided to use it through research. Why is that? Confirmed the eyes ~
Technology stack
- Vue.js
- tui-editor
In actual combat
With the technology stack in place, we need to get down to business.
1. Build Vue scaffolding
We will use VueCLI to build a most basic project. There is no need for vue-Router and Vuex plug-ins, so we will pack as light as possible.
2. Create the editor component
We will create an editor. vue file in the Components directory. This is where we will do most of the work.
3. Configure the editor component
When configuring the editor, there are several things that confuse me so much that I spend a lot of time.
- The code is not highlighted
- Language is not Chinese
- There is a problem with editor style
These problems can be solved through the following measures:
- By reading the document:
https://nhn.github.io/tui.editor/latest/
- Visit Github:
https://github.com/nhn/tui.editor
Editor.vue
<template>
<div class="main">
<div id="editor"></div>
</div>
</template>
<script>
import Editor from "@toast-ui/editor";
import hljs from "highlight.js";
import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight";
import '@toast-ui/editor/dist/i18n/zh-cn.js';
import "highlight.js/styles/github.css";
import "codemirror/lib/codemirror.css"; // Editor's Dependency Style
import "@toast-ui/editor/dist/toastui-editor.css"; // Editor's Style
import "@/styles/index.css";
export default {
components: {},
data() {
return {
editor: null
};
},
mounted() {
this.editor = new Editor({
el: document.getElementById("editor"),
plugins: [[codeSyntaxHighlight, {hljs}]],
previewStyle: "vertical".height: "100vh".initialEditType: "markdown".minHeight: "200px".initialValue: "".placeholder: "What do you want to write about...".language:'zh-CN'.useCommandShortcut: true.useDefaultHTMLSanitizer: true.usageStatistics: false.hideModeSwitch: false.viewer: true.toolbarItems: [
"heading"."bold"."italic"."strike"."divider"."hr"."quote"."divider"."ul"."ol"."task"."indent"."outdent"."divider"."table"."image"."link"."divider"."code"."codeblock",]});this.editor.getUI().getToolbar().removeItem("21"); }};</script>
Copy the code
It looks like a few lines of code above, but it’s a lot of work.
Adding features
First of all, I developed this program to make it easier for me to write articles, so I set the following requirements:
- Can copy HTML format text, easy to copy to wechat public account
- Markdown text can be copied easily to rare earth Mining, CSDN blog sites such as publishing
- Downloadable Markdown files for easier saving and movement
For space reasons, I present the main logic code first. Here I used Clipboard, a plug-in that copies text to the clipboard. The website: https://clipboardjs.com/. In addition, the downloadBlobAsFile method creates a Blob object and downloads it using the DOWNLOAD property of the A tag.
downloadBlobAsFile.js
export default function downloadBlobAsFile(data, filename) {
const contentType = 'application/octet-stream';
if(! data) {console.error(' No data');
return;
}
if(! filename) { filename ='filetodonwload.txt';
}
if (typeof data === 'object') {
data = JSON.stringify(data, undefined.4);
}
let blob = new Blob([data], {type: contentType});
let e = document.createEvent('MouseEvents');
let a = document.createElement('a');
a.download = filename;
a.href = URL.createObjectURL(blob);
a.dataset.downloadurl = [contentType, a.download, a.href].join(':');
e.initMouseEvent('click'.true.false.window.0.0.0.0.0.false.false.false.false.0.null);
a.dispatchEvent(e);
}
Copy the code
Editor.vue
<template>
<div class="main">
<div class="tools">
<el-button
size="mini"
type="primary"
@click="drawer = true"
>tool</el-button>
<el-button
size="mini"
type="primary"
@click="aboutView = true"
>about</el-button>
<el-dialog
:title="' tools'"
:visible.sync="drawer"
:append-to-body="true"
>
<div class="tool-innter">
<el-button type="primary" @click="getHtml" class="htmlbtn"
>Copy HTML </el-button ><el-button type="primary" @click="getMd" class="mdbtn"
>Copy MarkDown </el-button ><el-button type="primary" @click="downloadMd" class="downloadbtn"
></el-button ></div>
</el-dialog>
<el-dialog
:title="' about '." "
:visible.sync="aboutView"
:append-to-body="true"
>
<h3>Simple, MarkDown editor</h3>
<ul class="functionList">
<li v-for="(item,index) in functionList" :key="index">
{{item}}
</li>
</ul>
<h3>The author</h3>
<ul class="functionList">
<li v-for="(item,index) in authorList" :key="index">{{item}}</li>
</ul>
<div class="wxcode">
<img src=".. /assets/wxcode.jpeg" alt="">
</div>
</el-dialog>
</div>
<div id="editor"></div>
</div>
</template>
<script>
import Editor from "@toast-ui/editor";
import Clipboard from "clipboard";
import hljs from "highlight.js";
import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight";
import '@toast-ui/editor/dist/i18n/zh-cn.js';
import downloadBlobAsFile from ".. /utils/download";
import "highlight.js/styles/github.css"; //https://github.com/highlightjs/highlight.js/tree/master/src/styles
import "codemirror/lib/codemirror.css"; // Editor's Dependency Style
import "@toast-ui/editor/dist/toastui-editor.css"; // Editor's Style
import "@/styles/index.css";
export default {
components: {},
data() {
return {
editor: null.drawer: false.aboutView: false.functionList: ['Page simplicity'.'Functional'.'Support rare Earth Mining, CSDN, wechat public account, Zhihu'.'Copiable HTML, MarkDown'.'Downloadable MarkDown files'].authorList: ['Author: Vam's Golden Bean Road'.'Welcome to follow my official account: Road to The Front End of history'.'I created a technical exchange, article sharing group, the group has a lot of big factory front end boss, after following the public account, click the menu below to learn more can add my wechat, look forward to your joining']}; },methods: {
/ / copy the HTML
getHtml() {
const clipboard = new Clipboard(".htmlbtn", {
target: () = > this.editor.preview.el,
});
clipboard.on("success".() = > {
this.$message({
message: "Copy done".type: "success"}); clipboard.destroy(); }); clipboard.on("error".() = > {
this.$message.error("Copy failed");
clipboard.destroy();
});
},
/ / copy Markdown
getMd() {
const clipboard = new Clipboard(".mdbtn", {
text: () = > this.editor.getMarkdown(),
});
clipboard.on("success".() = > {
this.$message({
message: "Copy done".type: "success"}); clipboard.destroy(); }); clipboard.on("error".() = > {
this.$message.error("Copy failed");
clipboard.destroy();
});
},
Download the Markdown / /
downloadMd() {
if (this.editor.getMarkdown().trim()) {
downloadBlobAsFile(this.editor.getMarkdown(), "unnamed.md");
} else {
this.$message.error("Download failed"); }}},mounted() {
this.editor = new Editor({
el: document.getElementById("editor"),
plugins: [[codeSyntaxHighlight, {hljs}]],
previewStyle: "vertical".height: "100vh".initialEditType: "markdown".minHeight: "200px".initialValue: "".placeholder: "What do you want to write about...".language:'zh-CN'.useCommandShortcut: true.useDefaultHTMLSanitizer: true.usageStatistics: false.hideModeSwitch: false.viewer: true.toolbarItems: [
"heading"."bold"."italic"."strike"."divider"."hr"."quote"."divider"."ul"."ol"."task"."indent"."outdent"."divider"."table"."image"."link"."divider"."code"."codeblock",]});this.editor.getUI().getToolbar().removeItem("21"); }};</script>
Copy the code
For the wechat public account style optimization
:: V-deep is a depth-acting selector, used primarily to override the original style.
::v-deep ul li {
list-style-type: disc ! important;
}
::v-deep ol li {
list-style-type: decimal ! important;
}
::v-deep ul li::before, ::v-deep ol li::before {
content: none;
}
::v-deep .tui-editor-contents p>code{
background-color: #fff5f5;
color: #ff502c;
}
::v-deep .tui-editor-contents pre {
width: 100%;
overflow: auto;
}
Copy the code
The online experience
https://www.maomin.club/site/mdeditor/
Copy the code
conclusion
Thanks for reading. I hope I didn’t waste your time.
Source address:
https://github.com/maomincoding/simpleMdEditor
Copy the code
If it helps you, welcome to Star~
I created a technical exchange, article sharing group, the group has a lot of big factory front end big boss, follow the public account, click the menu below to learn more can add my wechat, look forward to your joining.
Public number: the front end of the road