欢迎来到UniApp 实战 GitCode系列的大结局

今天,我们迎来了整个项目中最硬核、最炫酷的功能——代码语法高亮 (Syntax Highlighting)

想象一下,用户费劲点进了 main.js,结果看到的是黑底白字、密密麻麻的纯文本,那种心情就像吃泡面没有调料包。我们要做的,是把代码变成艺术品,支持关键词变色、行号显示、长按复制,还原 IDE 般的阅读体验!

目录

核心挑战

第一步:引入 Highlight.js (瘦身版)

创建高亮工具类 (utils/highlighter.js)

第二步:引入配色主题 (CSS)

第三步:实现行号渲染组件 (CodeViewer.vue)

第四步:性能优化 (进阶)

最终集成

全系列总结


核心挑战

在 Web 端,我们直接引入 Prism.jsHighlight.js 然后操作 DOM 就在行了。但在 UniApp(特别是小程序端):

  1. 没有 DOM: 无法直接操作 innerHTML 插入带 class 的 span 标签。

  2. 包体积限制: Highlight.js 完整包有几百 KB,必须按需引入。

  3. 行号对齐: 高亮库通常只返回一坨 HTML 字符串,不带行号,我们需要自己实现“行号+代码”的对齐布局。


第一步:引入 Highlight.js (瘦身版)

不要直接 npm install highlight.js 然后引入整个包!那太大了。我们要使用 core 版本,按需注册语言。

npm install highlight.js

创建高亮工具类 (utils/highlighter.js)

为了保持组件干净,我们将高亮逻辑抽离。

// 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 template
import css from 'highlight.js/lib/languages/css';
import json from 'highlight.js/lib/languages/json';
import markdown from 'highlight.js/lib/languages/markdown';

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

// 2. 导出核心函数
export default function highlight(code, lang) {
  // 如果没传语言,或者语言不支持,就直接返回转义后的文本
  if (!lang) return escapeHtml(code);
  
  // 容错处理:比如传入 'vue',但我们只注册了 'xml',可以做个映射
  const langMap = { 'vue': 'xml', 'js': 'javascript', 'ts': 'typescript' };
  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);
}

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

第二步:引入配色主题 (CSS)

Highlight.js 生成的 HTML 标签只有 class="hljs-keyword" 这种类名,没有样式。我们需要手动引入 CSS。

推荐去 highlight.js/styles 找一个喜欢的,比如 atom-one-dark.css

在 UniApp 中,你可以把这个 CSS 内容复制到 App.vue 的 style 中,或者作为一个单独的 code-theme.css 引入。

/* static/code-theme.css (示例:Atom One Dark) */
.hljs { color: #abb2bf; background: #282c34; display: block; overflow-x: auto; padding: 0.5em; }
.hljs-comment, .hljs-quote { color: #5c6370; font-style: italic; }
.hljs-keyword, .hljs-selector-tag { color: #c678dd; }
.hljs-string, .hljs-attribute { color: #98c379; }
.hljs-title, .hljs-section { color: #e06c75; }
/* ...更多样式... */

第三步:实现行号渲染组件 (CodeViewer.vue)

这是最显功力的地方。如果不显示行号,直接用 rich-text 渲染上面的结果就行了。但为了专业感,我们需要把代码按行切分

布局策略: 使用 Flex 布局,左边一列显示行号,右边一列显示代码。

<template>
  <view class="code-viewer">
    <scroll-view scroll-x scroll-y class="code-scroll-view">
      <view class="code-wrapper">
        
        <view class="line-numbers">
          <text 
            v-for="n in lineCount" 
            :key="n" 
            class="line-num"
          >{{ n }}</text>
        </view>

        <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: 'javascript' },
  fileName: { type: String, default: '' }
});

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

// 1. 核心计算:高亮处理
const highlightedHtml = computed(() => {
  if (!code.value) return '';
  
  // 步骤 A: 获取高亮后的 HTML 字符串
  let html = highlight(code.value, lang.value);
  
  // 步骤 B (关键优化): 
  // highlight.js 生成的 HTML 可能会跨行(比如一个多行注释)
  // 如果我们直接按 \n 切割字符串来做每行渲染,会打断 span 标签,导致样式失效。
  // 所以,这里我们采取 "整体渲染" 策略,
  // 只要保持行号的高度(line-height)和代码的高度一致即可!
  
  // 给每行代码外面包一个 div 也可以,但 rich-text 性能最好是渲染一大坨字符串
  return `<div class="hljs code-body">${html}</div>`;
});

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

<style scoped>
/* 引入刚才保存的主题样式 */
@import url('@/static/code-theme.css');

.code-viewer {
  background-color: #282c34; /* 背景色要和主题一致 */
  min-height: 100vh;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 28rpx;
}

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

.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;
  overflow-x: visible; /* 允许横向撑开 */
}

/* ⚠️ 深度选择器:控制 rich-text 内部样式 */
:deep(.code-body) {
  line-height: 40rpx !important; /* 必须和行号高度绝对一致! */
  white-space: pre; /* 保留空格和换行 */
  font-family: inherit;
}
</style>

第四步:性能优化 (进阶)

如果用户打开了一个 5000 行的 bundle.js,上面的代码可能会让页面卡顿 2-3 秒。

优化方案:

  1. Web Worker (强烈推荐): 高亮计算 (hljs.highlight) 是纯 CPU 密集型任务。在小程序中,可以开启一个 Worker 线程来处理字符串高亮,主线程只负责渲染。

  2. 分片渲染 (Time Slicing): 不要一次性渲染 5000 行。先渲染前 100 行,用户滚动时再追加渲染(类似虚拟列表)。

  3. 大文件拦截: 如果检测到文件体积 > 500KB,直接提示“文件过大,请下载查看”,避免崩溃。

// 简单的大文件保护逻辑
const isTooLarge = computed(() => props.code.length > 100000);

最终集成

回到上一期创建的 read.vue,把刚写好的组件放进去:

<template>
  <view>
    <CodeViewer 
      v-if="!loading"
      :code="fileContent" 
      :lang="fileExtension" 
      :fileName="fileName"
    />
  </view>
</template>

<script setup>
// ... 获取 fileExtension 的逻辑 ...
// const fileExtension = fileName.value.split('.').pop();
</script>

全系列总结

恭喜你!坚持看完了整个实战系列。从API封装递归目录,从Markdown渲染代码高亮,你现在手里已经拥有了一个功能完备的“口袋代码阅读器”的核心源码。

这不仅仅是一个 Demo,它是你迈向高级前端开发的敲门砖。

如果你把这套项目写进简历,记得加上这几个关键词:Vue 3 Composition APIUniApp 跨端开发递归组件优化虚拟 DOM 与原生渲染性能调优

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐