Vue3+Vite实现一个Markdown编辑器组件

 更新时间:2025年04月10日 09:58:48   作者:前端极客探险家  
在现代前端开发中,Markdown 编辑器广泛应用于博客,文档,Wiki,代码注释等场景,本文将使用 Vue 3 构建一个简单的 Markdown 编辑器组件,感兴趣的小伙伴可以了解下

一、项目背景与需求分析

在现代前端开发中,Markdown 编辑器广泛应用于博客、文档、Wiki、代码注释等场景。一个优秀的 Markdown 编辑器需要具备以下功能:

  • 实时预览:用户输入时,能够看到实时的渲染效果。
  • 语法高亮:支持 Markdown 语法的实时高亮显示。
  • 导出功能:支持将编辑的内容导出为 Markdown 或 HTML 格式,方便分享和存档。
  • 自动保存:避免用户丢失未保存内容。
  • 文件上传:支持上传文件(如图片),并在 Markdown 内容中嵌入显示。

本文将使用 Vue 3 构建一个简单的 Markdown 编辑器组件,并实现上述基本功能。

二、搭建基础项目

1. 初始化 Vue 3 项目

我们使用 Vite 来初始化 Vue 3 项目,并选择 TypeScript 模板:

npm create vite@latest markdown-editor --template vue-ts
cd markdown-editor

2. 安装依赖

接下来,我们安装实现 Markdown 渲染和语法高亮所需要的依赖:

marked:用于将 Markdown 转换为 HTML。

highlight.js:用于提供 Markdown 语法的高亮显示。

npm install marked highlight.js

三、实现 Markdown 编辑器组件

1. 创建 Markdown 编辑器组件

在 src/components 目录下创建 MarkdownEditor.vue 文件,封装 Markdown 编辑器功能。

<template>
  <div class="markdown-editor">
    <textarea
      v-model="content"
      class="editor-textarea"
      placeholder="请输入 Markdown 内容..."
      @input="saveToLocalStorage"
    ></textarea>
    <div class="preview">
      <div v-html="renderedContent" class="preview-content"></div>
    </div>
    <div class="export-buttons">
      <button @click="exportMarkdown">导出 Markdown</button>
      <button @click="exportHTML">导出 HTML</button>
    </div>
    <div class="file-upload">
      <input type="file" @change="handleFileUpload" />
      <p>上传图片并在 Markdown 中显示</p>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed, onMounted } from 'vue';
import { marked } from 'marked';
import hljs from 'highlight.js';

export default defineComponent({
  name: 'MarkdownEditor',
  setup() {
    const content = ref('');
    const fileUrl = ref<string | null>(null);

    // 从本地存储加载内容
    onMounted(() => {
      const savedContent = localStorage.getItem('markdown-content');
      if (savedContent) {
        content.value = savedContent;
      }
    });

    // 将 Markdown 转换为 HTML 并加上语法高亮
    const renderedContent = computed(() => {
      marked.setOptions({
        highlight: (code: string) => {
          return hljs.highlightAuto(code).value;
        },
      });
      return marked(content.value);
    });

    // 保存内容到本地存储(自动保存)
    const saveToLocalStorage = () => {
      localStorage.setItem('markdown-content', content.value);
    };

    // 导出 Markdown 内容
    const exportMarkdown = () => {
      const blob = new Blob([content.value], { type: 'text/markdown' });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = 'document.md';
      link.click();
    };

    // 导出 HTML 内容
    const exportHTML = () => {
      const blob = new Blob([renderedContent.value], { type: 'text/html' });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = 'document.html';
      link.click();
    };

    // 文件上传功能
    const handleFileUpload = (event: Event) => {
      const fileInput = event.target as HTMLInputElement;
      const file = fileInput.files ? fileInput.files[0] : null;
      if (file) {
        const reader = new FileReader();
        reader.onload = () => {
          const imageUrl = reader.result as string;
          fileUrl.value = imageUrl; // 存储图片的 URL
          const markdownImage = `![image](${imageUrl})`; // 将图片路径转化为 Markdown 格式
          content.value += `\n\n${markdownImage}\n`; // 插入到 Markdown 内容中
        };
        reader.readAsDataURL(file); // 将图片读取为 Data URL
      }
    };

    return {
      content,
      renderedContent,
      exportMarkdown,
      exportHTML,
      handleFileUpload,
    };
  },
});
</script>

<style scoped>
.markdown-editor {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
}

.editor-textarea {
  width: 100%;
  height: 300px;
  padding: 10px;
  font-size: 16px;
  line-height: 1.5;
  border: 1px solid #ccc;
  border-radius: 8px;
  resize: none;
}

.preview {
  background-color: #f8f8f8;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 8px;
  min-height: 300px;
}

.preview-content {
  max-width: 100%;
  word-wrap: break-word;
}

.export-buttons {
  display: flex;
  gap: 10px;
}

button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}

.file-upload {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

input[type="file"] {
  padding: 5px;
}
</style>

2. 组件说明

Markdown 渲染:通过 marked 将用户输入的 Markdown 内容转换为 HTML。

语法高亮:使用 highlight.js 对代码块进行高亮显示,增强代码阅读性。

导出功能:支持将 Markdown 或 HTML 内容导出为文件,方便用户保存或分享。

自动保存功能:使用 localStorage 自动保存 Markdown 内容,避免用户丢失未保存的输入。

文件上传支持:允许用户上传图片文件,并将图片插入到 Markdown 内容中,生成合适的 Markdown 格式。

四、优化与拓展

1. 自动保存功能

通过 localStorage 实现了自动保存功能。每次用户输入内容时,Markdown 编辑器的内容会自动存储到浏览器的本地存储中。当用户刷新页面或重新加载时,保存的内容会自动恢复。这样能有效避免用户因刷新或浏览器崩溃导致的数据丢失。

// 保存内容到本地存储(自动保存)
const saveToLocalStorage = () => {
  localStorage.setItem('markdown-content', content.value);
};

2. 文件上传功能

我们通过 HTML5 的 FileReader API 实现了文件上传支持。当用户上传图片文件时,图片会转换为 Data URL,并自动插入到 Markdown 内容中。Markdown 内容将以 ![image](image-url) 格式显示上传的图片。

// 文件上传功能
const handleFileUpload = (event: Event) => {
  const fileInput = event.target as HTMLInputElement;
  const file = fileInput.files ? fileInput.files[0] : null;
  if (file) {
    const reader = new FileReader();
    reader.onload = () => {
      const imageUrl = reader.result as string;
      fileUrl.value = imageUrl; // 存储图片的 URL
      const markdownImage = `![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=%24%7BimageUrl%7D&pos_id=img-QEVKHwoM-1744165078772)`; // 将图片路径转化为 Markdown 格式
      content.value += `\n\n${markdownImage}\n`; // 插入到 Markdown 内容中
    };
    reader.readAsDataURL(file); // 将图片读取为 Data URL
  }
};

五、总结

通过 Vue 3 和 marked、highlight.js,我们实现了一个功能完善的 Markdown 编辑器组件。这个组件不仅支持实时预览,还具备了语法高亮与导出功能,极大地方便了用户的文档编辑与分享需求。同时,自动保存功能和文件上传功能进一步提升了编辑器的可用性和用户体验。

该组件可以轻松地集成到任何 Vue 3 项目中,为你的应用增添强大的编辑能力。

到此这篇关于Vue3+Vite实现一个Markdown编辑器组件的文章就介绍到这了,更多相关Vue3 Markdown编辑器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue前端获取不同客户端mac地址最详细步骤(避免踩坑)

    vue前端获取不同客户端mac地址最详细步骤(避免踩坑)

    在开发过程中,绑定账号和电脑的功能可以通过获取电脑的MAC地址实现,下面这篇文章主要介绍了vue前端获取不同客户端mac地址的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • 在nuxt中使用路由重定向的实例

    在nuxt中使用路由重定向的实例

    这篇文章主要介绍了在nuxt中使用路由重定向的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Flutter部件内部状态管理小结之实现Vue的v-model功能

    Flutter部件内部状态管理小结之实现Vue的v-model功能

    本文是 Flutter 部件内部状态管理的小结,从部件的基础开始,到部件的状态管理,并且在过程中实现一个类似 Vue 的 v-model 的功能,感兴趣的朋友跟随小编一起看看吧
    2019-06-06
  • vue 1.x 交互实现仿百度下拉列表示例

    vue 1.x 交互实现仿百度下拉列表示例

    本篇文章主要介绍了vue 1.x 交互实现仿百度下拉列表示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Vue关于组件化开发知识点详解

    Vue关于组件化开发知识点详解

    在本篇文章里,小编给大家分享的是关于Vue关于组件化开发知识点详解内容,有兴趣的朋友们可以学习下。
    2020-05-05
  • vue实现文章点赞和差评功能

    vue实现文章点赞和差评功能

    这篇文章主要为大家详细介绍了vue实现文章点赞和差评功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 记VUE3+TS获取组件类型的方法踩坑及解决

    记VUE3+TS获取组件类型的方法踩坑及解决

    这篇文章主要介绍了VUE3+TS获取组件类型的方法踩坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 浅谈Vue下使用百度地图的简易方法

    浅谈Vue下使用百度地图的简易方法

    本篇文章主要介绍了浅谈Vue下使用百度地图的简易方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • vue组件系列之TagsInput详解

    vue组件系列之TagsInput详解

    TagsInput 是一种可编辑的输入框,通过回车或者分号来分割每个标签,用回退键删除上一个标签。这篇文章主要介绍了vue组件TagsInput的相关知识,需要的朋友可以参考下
    2020-05-05
  • 解决vue3中内存泄漏的问题

    解决vue3中内存泄漏的问题

    在项目中会发现一个奇怪的现象,当我们在使用element-plus中的图标组件时会出现内存泄漏,所以本文讲给大家讲讲如何解决vue3中的内存泄漏的问题,需要的朋友可以参考下
    2023-07-07

最新评论