Skip to content

v-safe-html

一个安全的 HTML 渲染指令,支持 Markdown 转换和 XSS 防护。

功能特性

  • 支持 Markdown 语法转换为 HTML
  • 自动清理潜在的 XSS 攻击内容
  • 支持常见的 Markdown 格式(标题、列表、加粗等)
  • 异步处理,确保性能
  • 支持自定义 DOMPurify 配置
  • 支持动态内容更新

安全配置

默认的安全配置包括:

  • 允许 data-* 属性
  • 允许未知协议(如 tel:mailto:
  • 禁止 script 标签
  • 禁止危险的事件属性(如 onclickonerroronload

你可以通过修改 getDOMPurifyConfig 函数来自定义安全配置:

typescript
function getDOMPurifyConfig() {
  return {
    ALLOW_DATA_ATTR: true,
    ALLOW_UNKNOWN_PROTOCOLS: true,
    FORBID_TAGS: ['script'],
    FORBID_ATTR: ['onclick', 'onerror', 'onload'],
    // 添加更多配置...
  }
}

使用示例

基础用法

vue
<template>
  <div v-safe-html="markdownContent"></div>
</template>

<script setup>
const markdownContent = `
# 标题
这是一段**加粗**的文本

## 子标题
- 列表项 1
- 列表项 2

[链接文本](https://example.com)

\`\`\`javascript
console.log('代码块');
\`\`\`
`
</script>

动态内容

vue
<template>
  <div v-safe-html="content"></div>
  <button @click="updateContent">更新内容</button>
</template>

<script setup>
import { ref } from 'vue'

const content = ref('# 初始内容')

function updateContent() {
  content.value = `
# 更新后的内容
- 新的列表项
- 另一个列表项
  `
}
</script>

条件渲染

vue
<template>
  <div v-if="hasContent" v-safe-html="content"></div>
  <div v-else>暂无内容</div>
</template>

<script setup>
import { computed } from 'vue'

const content = ref('')
const hasContent = computed(() => content.value.trim().length > 0)
</script>

支持的 Markdown 语法

  • 标题(# 到 ######)
  • 加粗(文本
  • 斜体(文本
  • 列表(有序和无序)
  • 链接
  • 图片
  • 代码块
  • 引用
  • 表格
  • 等等

性能优化建议

  1. 对于静态内容,建议在组件挂载时只转换一次
  2. 对于动态内容,可以使用 v-once 指令避免重复转换
  3. 大量内容建议使用虚拟滚动或分页加载

注意事项

  1. 指令是异步的,内容会在转换完成后显示
  2. 如果输入为 nullundefined,将清空元素内容
  3. 如果转换过程中发生错误,将显示空内容并在控制台输出错误信息
  4. 确保输入的内容是字符串类型
  5. 注意内存使用,避免过大的 Markdown 内容

错误处理

如果发生错误,指令会:

  1. 在控制台输出错误信息
  2. 清空元素内容
  3. 确保不会渲染不安全的 HTML

常见错误及解决方案:

  • 类型错误:确保传入的内容是字符串类型
  • 转换错误:检查 Markdown 语法是否正确
  • 内存错误:考虑分块处理大内容

最佳实践

1. 内容验证

typescript
function validateContent(content: string): boolean {
  return typeof content === 'string' && content.trim().length > 0
}

2. 错误边界处理

vue
<template>
  <div v-safe-html="content" @error="handleError"></div>
</template>

<script setup>
function handleError(error: Error) {
  console.error('Content processing error:', error)
  // 实现自定义错误处理逻辑
}
</script>

3. 加载状态处理

vue
<template>
  <div v-if="isLoading">加载中...</div>
  <div v-else v-safe-html="content"></div>
</template>

<script setup>
const isLoading = ref(true)
const content = ref('')

onMounted(async () => {
  try {
    // 加载内容
    content.value = await fetchContent()
  } finally {
    isLoading.value = false
  }
})
</script>

Released under the MIT License.