highlight.js。uniapp中使用详解

1. 安装与核心配置

1.1 安装依赖

bash

复制代码
npm install highlight.js
1.2 创建高亮工具类 (utils/highlighter.js)

核心要点 :按需注册语言可大幅减少包体积(完整包约 300KB+,按需后通常小于 100KB)。同时需增加 escapeHtml 防止 XSS 攻击。

javascript

复制代码
// utils/highlighter.js
import hljs from 'highlight.js/lib/core';

// 1. 按需注册常用语言
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import xml from 'highlight.js/lib/languages/xml';      // HTML/Vue
import css from 'highlight.js/lib/languages/css';
import json from 'highlight.js/lib/languages/json';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('css', css);
hljs.registerLanguage('json', json);

// 语言别名映射
const langMap = {
  'vue': 'xml',
  'js': 'javascript',
  'ts': 'typescript'
};

// 简单的 HTML 转义(防止 XSS)
function escapeHtml(value) {
  return value
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

export default function highlight(code, lang) {
  if (!lang) return escapeHtml(code);
  
  const targetLang = langMap[lang] || lang;
  
  try {
    if (hljs.getLanguage(targetLang)) {
      return hljs.highlight(code, { language: targetLang }).value;
    }
  } catch (e) {
    console.warn('Highlight parsing failed', e);
  }
  
  return escapeHtml(code);
}

2. 引入主题样式 (App.vue 或单独 css 文件)

由于小程序不支持动态注入样式,需将 CSS 内容静态引入。推荐从 node_modules/highlight.js/styles/ 选择主题文件。

方法一 :直接在 App.vue<style> 中复制主题内容

方法二:在 main.js 中导入(H5 有效,小程序需额外配置)

javascript

复制代码
// main.js
import 'highlight.js/styles/atom-one-dark.css';

3. 实现高亮组件 (CodeViewer.vue)

核心思路 :利用 rich-text 组件渲染高亮后的 HTML 字符串。注意行高必须一致才能让行号与代码对齐。

vue

复制代码
<template>
  <view class="code-viewer">
    <!-- 横向+纵向滚动 -->
    <scroll-view scroll-x scroll-y class="code-scroll">
      <view class="code-wrapper">
        
        <!-- 左侧行号列 -->
        <view class="line-numbers">
          <text v-for="n in lineCount" :key="n" class="line-num">{{ n }}</text>
        </view>
        
        <!-- 右侧代码列 (rich-text 渲染) -->
        <view class="code-content">
          <rich-text :nodes="highlightedHtml" class="code-text"></rich-text>
        </view>
        
      </view>
    </scroll-view>
  </view>
</template>

<script setup>
import { computed, toRefs } from 'vue';
import highlight from '@/utils/highlighter';

const props = defineProps({
  code: { type: String, default: '' },
  lang: { type: String, default: '' }
});

const { code, lang } = toRefs(props);

// 高亮处理:包裹一层 div.hljs.code-body 便于统一样式
const highlightedHtml = computed(() => {
  if (!code.value) return '';
  const highlighted = highlight(code.value, lang.value);
  return `<div class="hljs code-body">${highlighted}</div>`;
});

// 计算行数
const lineCount = computed(() => {
  return code.value ? code.value.split('\n').length : 0;
});
</script>

<style scoped>
/* 引入主题样式 - 确保深色/浅色主题路径正确 */
@import url('@/static/code-theme.css');

.code-viewer {
  background-color: #282c34;
  font-family: 'Consolas', monospace;
  font-size: 28rpx;
}

.code-scroll {
  width: 100%;
  height: 100%;
}

.code-wrapper {
  display: flex;
  min-width: 100%;
}

/* 行号列 */
.line-numbers {
  width: 80rpx;
  background-color: #21252b;
  color: #636d83;
  text-align: right;
  padding: 20rpx 10rpx 20rpx 0;
  border-right: 1px solid #3b4048;
  flex-shrink: 0;
  user-select: none;
}

.line-num {
  display: block;
  height: 40rpx;      /* 必须与代码行高一致 */
  line-height: 40rpx;
  font-size: 24rpx;
}

/* 代码内容区 */
.code-content {
  flex: 1;
  padding: 20rpx;
}

/* 关键:强制 rich-text 内部的行高与行号列一致 */
:deep(.code-body) {
  line-height: 40rpx !important;
  white-space: pre;
  font-family: inherit;
}
</style>

4. 在页面中使用组件

vue

复制代码
<template>
  <view>
    <CodeViewer 
      :code="exampleCode" 
      lang="javascript"
    />
  </view>
</template>

<script setup>
import CodeViewer from '@/components/CodeViewer.vue';

const exampleCode = `function greet(name) {
  console.log("Hello, " + name + "!");
}

greet("uniapp");`;
</script>

5. 进阶方案:直接使用现成插件(推荐)

如果不想手动处理行号和兼容性,可以直接使用 DCloud 插件市场的现成组件:

插件名称 核心实现 特点
uaMarkdown highlight.js 基于 markdown-it + highlight.js,支持代码块行号,兼容 H5 + 小程序 + App
mp-html 插件机制 功能强大的富文本组件,需手动启用 highlight 插件

使用示例(uaMarkdown)

vue

复制代码
<template>
  <ua-markdown :source="markdownText" :showLine="true" />
</template>

<script>
import uaMarkdown from '@/components/ua-markdown/ua-markdown.vue';
export default {
  components: { uaMarkdown },
  data() {
    return {
      markdownText: '```js\nconsole.log("hello")\n```'
    };
  }
};
</script>

6. 关键注意事项

  1. 小程序环境的限制 :无法直接操作 DOM,必须使用 rich-text 组件渲染高亮后的 HTML。

  2. 按需引入 :切勿使用 import hljs from 'highlight.js'(体积过大),应使用 lib/core + 按需注册语言。

  3. 行高对齐:deep(.code-body) 中的 line-height 必须与行号列的 line-height 数值完全一致,否则会出现错位。

  4. 大文件性能:代码超过 5000 行时,可考虑使用 Web Worker 或分片渲染(先显示前 100 行)。

  5. vue2 语法差异 :若使用 vue2,需将 script setup 改为普通 export default,并将 :deep() 替换为 >>>/deep/

如果遇到具体平台的报错(如微信开发者工具报 \p 正则不支持),请检查 highlight.js 版本并降级至 10.7.3

相关推荐
belldeep3 个月前
nodejs: 能在线编辑 Markdown 文档的 Web 服务程序,更多扩展功能
前端·node.js·markdown·mermaid·highlight·katax
在半岛铁盒里1 年前
代码填空题技术实现:突破 highlight.js 安全限制的工程实践
开发语言·javascript·highlight