vue3實現(xiàn)markdown預(yù)覽和編輯詳解
Markdown作為一種輕量級標記語言,已經(jīng)成為開發(fā)者編寫文檔的首選工具之一。
在Vue3項目中集成Markdown編輯和預(yù)覽功能可以極大地提升內(nèi)容管理體驗。
本文將介紹如何使用Vditor這一強大的開源Markdown編輯器在Vue3項目中實現(xiàn)這一功能。
一、Vditor簡介
Vditor是一款瀏覽器端的Markdown編輯器,支持所見即所得(WYSIWYG)、即時渲染(IR)和分屏預(yù)覽模式。
它具有以下特點:
- 支持三種編輯模式:WYSIWYG、IR和SV
- 內(nèi)置流程圖、甘特圖、時序圖等圖表支持
- 數(shù)學(xué)公式、音視頻、代碼高亮等豐富功能
- 高度可定制化的主題和工具欄
- 官網(wǎng)Vditor - 一款瀏覽器端的 Markdown 編輯器,支持所見即所得(富文本)、即時渲染(類似 Typora)和分屏預(yù)覽模式 (b3log.org)
二、安裝Vditor
在項目中安裝Vditor:
npm install vditor --save
三、實現(xiàn)預(yù)覽
Vdior中提供了專門的預(yù)覽方法來針對不需要編輯僅需要展示markdown文檔的場景

下面是我封裝的一個預(yù)覽組件
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import VditorPreview from "vditor/dist/method.min";
/**
* Markdown預(yù)覽組件
* @component
*/
const props = defineProps({
/**
* Markdown內(nèi)容
*/
content: {
type: String,
required: true
},
/**
* 預(yù)覽選項
*/
options: {
type: Object,
default: () => ({})
}
});
/**
* 預(yù)覽容器引用
*/
const previewRef = ref<HTMLDivElement | null>(null);
/**
* 默認預(yù)覽配置
*/
const defaultOptions = {
cdn: "https://ld246.com/js/lib/vditor",
mode: "dark",
anchor: 1,
hljs: {
lineNumber: true,
style: "github"
},
math: {
inlineDigit: true,
macros: {}
},
theme: {
current: "dark"
},
lazyLoadImage: "http://unpkg.com/vditor/dist/images/img-loading.svg"
};
/**
* 合并配置項
*/
const mergedOptions = {
...defaultOptions,
...props.options
};
/**
* 渲染Markdown內(nèi)容
*/
const renderMarkdown = () => {
if (previewRef.value) {
VditorPreview.preview(previewRef.value, props.content, mergedOptions);
}
};
/**
* 組件掛載完成后渲染Markdown
*/
onMounted(() => {
renderMarkdown();
// 渲染其他特殊語法
VditorPreview.mermaidRender(document);
VditorPreview.codeRender(document);
VditorPreview.mathRender(document);
VditorPreview.abcRender(document);
VditorPreview.chartRender(document);
VditorPreview.mindmapRender(document);
VditorPreview.graphvizRender(document);
});
/**
* 監(jiān)聽內(nèi)容變化重新渲染
*/
watch(
() => props.content,
() => {
renderMarkdown();
}
);
</script>
<template>
<div class="vditor-preview-container">
<div ref="previewRef" class="vditor-preview" />
</div>
</template>
<style scoped>
.vditor-preview-container {
width: 100%;
}
.vditor-preview {
box-sizing: border-box;
padding: 16px;
color: #ccc;
background-color: #1a1a1a;
border-radius: 8px;
}
:deep(.vditor-reset) {
background: #1a1a1a;
}
:deep(.vditor-reset h1),
:deep(.vditor-reset h2),
:deep(.vditor-reset h3),
:deep(.vditor-reset h4),
:deep(.vditor-reset h5),
:deep(.vditor-reset h6) {
/* color: #06ad7e; */
margin-top: 1.5em;
margin-bottom: 0.5em;
font-weight: 600;
border-bottom: none;
}
:deep(.vditor-reset h3) {
padding-bottom: 0.3em;
font-size: 1.3em;
}
:deep(.vditor-reset strong) {
font-weight: 600;
color: #fff;
}
</style>
使用:
<script setup lang="ts">
import { ref } from "vue";
import VditorPreview from "@/components/markdown/VditorPreview.vue";
const chiefComplaint = ref("");
</script>
<template>
<VditorPreview
:content="chiefComplaint"
:options="{
mode: 'dark',
theme: {
current: 'dark'
}
}"
/>
</template>四、實現(xiàn)編輯
編輯組件按照Vditor文檔來使用Vditor編輯器即可
<script setup lang="ts">
import "vditor/dist/index.css";
import Vditor from "vditor";
import { useIntervalFn } from "@vueuse/core";
import { onMounted, ref, watch, toRaw, onUnmounted } from "vue";
const emit = defineEmits([
"update:modelValue",
"after",
"focus",
"blur",
"esc",
"ctrlEnter",
"select"
]);
const props = defineProps({
options: {
type: Object,
default() {
return {};
}
},
modelValue: {
type: String,
default: ""
},
isDark:{
type: Boolean,
default: true
},
});
const editor = ref<Vditor | null>(null);
const markdownRef = ref<HTMLElement | null>(null);
onMounted(() => {
editor.value = new Vditor(markdownRef.value as HTMLElement, {
...props.options,
value: props.modelValue,
cache: {
enable: false
},
fullscreen: {
index: 10000
},
toolbar: [
"headings",
"bold",
"italic",
"strike",
"|",
"line",
"quote",
"list",
"ordered-list",
"|",
"check",
"insert-after",
"|",
"insert-before",
"undo",
"redo",
"link",
"|",
"table",
"br",
"fullscreen"
],
cdn: "https://ld246.com/js/lib/vditor",
after() {
emit("after", toRaw(editor.value));
},
input(value: string) {
emit("update:modelValue", value);
},
focus(value: string) {
emit("focus", value);
},
blur(value: string) {
emit("blur", value);
},
esc(value: string) {
emit("esc", value);
},
ctrlEnter(value: string) {
emit("ctrlEnter", value);
},
select(value: string) {
emit("select", value);
}
});
});
watch(
() => props.modelValue,
newVal => {
if (newVal !== editor.value?.getValue()) {
editor.value?.setValue(newVal);
}
}
);
watch(
() => props.isDark,
newVal => {
const { pause } = useIntervalFn(() => {
if (editor.value.vditor) {
newVal
? editor.value.setTheme("dark", "dark", "rose-pine")
: editor.value.setTheme("classic", "light", "github");
pause();
}
}, 20);
}
);
onUnmounted(() => {
const editorInstance = editor.value;
if (!editorInstance) return;
try {
editorInstance?.destroy?.();
} catch (error) {
console.log(error);
}
});
</script>
<template>
<div ref="markdownRef" />
</template>
使用
<Vditor
v-model="chiefComplaintContent"
:options="{
mode: 'ir',
outline: { enable: false, position: 'right' },
toolbarConfig: {
pin: true
}
}"
/>五、高級功能實現(xiàn)
這些都是options中的配置項,如果需要使用,直接將其加入options對象中即可
1. 自定義工具欄
Vditor允許完全自定義工具欄配置。以下是一個精簡版的工具欄配置:
toolbar: [ 'headings', 'bold', 'italic', 'strike', '|', 'list', 'ordered-list', 'check', '|', 'quote', 'code', 'inline-code', '|', 'upload', '|', 'undo', 'redo', '|', 'fullscreen', ],
2. 圖片上傳處理
upload: {
accept: 'image/*',
handler(files) {
// 這里實現(xiàn)上傳邏輯
const file = files[0];
const formData = new FormData();
formData.append('file', file);
// 示例:使用axios上傳
axios.post('/api/upload', formData)
.then(response => {
const url = response.data.url;
editor.value.insertValue(``);
})
.catch(error => {
console.error('上傳失敗:', error);
});
return false; // 阻止默認上傳行為
},
},六、常見問題解決
1. 自定義樣式
我采用的方式是給父容器加一個樣式,然后不使用scope寫css解決
<style lang="scss">
/* 自定義抽屜樣式 */
.report-drawer .el-drawer__header {
padding: 8px 20px !important;
margin-bottom: 0 !important;
font-size: 16px !important;
color: #fff !important;
border-bottom: 1px solid #383838 !important;
}
.report-drawer .vditor-ir {
overflow-x: hidden !important;
}
.report-drawer .vditorv .ditor-toolbar {
overflow-x: auto;
}
.report-drawer .vditor--dark .vditor--fullscreen .ditor-toolbar {
overflow-y: auto;
}
.report-drawer .vditor--dark .vditor--fullscreen .ditor {
height: 100% !important;
}
/* Vditor的預(yù)覽區(qū)域樣式 */
.report-drawer .vditor-preview {
overflow-x: hidden !important;
}
.report-drawer .vditor-ir pre.vditor-reset {
max-height: 100vh !important;
overflow-y: auto;
padding: 0 60px !important;
}
/* 按鈕樣式 */
.el-button {
border-radius: 4px !important;
}
</style>需要注意的是預(yù)覽區(qū)域的樣式和選擇的模式有關(guān),如果我設(shè)置的是ir,那么我預(yù)覽區(qū)域的樣式是.vditor-ir pre.vditor-reset
2.中文提示
Vditor的默認提示是英文的,可以設(shè)置中文:
new Vditor(editorContainer.value, {
lang: 'zh_CN',
// ...其他配置
});3.cdn配置
在使用時可能會碰到css文件和js文件無法加載的情況,這是因為Vditor默認的cdn地址失效,需要在options中傳入最新的可用cdn地址,目前可用的是https://ld246.com/js/lib/vditor
cdn: "https://ld246.com/js/lib/vditor",
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue鼠標hover(懸停)改變background-color移入變色問題
這篇文章主要介紹了vue鼠標hover(懸停)改變background-color移入變色問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
vue?watch中如何獲取this.$refs.xxx節(jié)點
這篇文章主要介紹了vue?watch中獲取this.$refs.xxx節(jié)點的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08

