Vue项目实战5分钟搞定highlight.js代码高亮附样式切换技巧如果你正在用Vue搭建技术博客、产品文档或者内部知识库展示代码片段几乎是刚需。但直接贴上一段灰蒙蒙的纯文本代码不仅阅读体验差也显得不够专业。这时候一个优雅的代码高亮方案就成了提升项目质感的利器。我见过不少开发者为了这个功能去引入庞大的Markdown编辑器或者自己写正则去匹配其实在Vue生态里用highlight.js配合几个轻量级的库五分钟内就能搭建出既美观又实用的代码高亮组件还能轻松实现暗黑/明亮主题切换让用户自己选择顺眼的风格。这篇文章就是为你准备的快速上手指南。我们不只讲怎么把代码“涂上颜色”更会深入几种常见的实战场景从简单的静态代码块展示到动态渲染Markdown内容中的代码再到如何封装一个可复用的、支持主题切换的智能组件。无论你是Vue 2还是Vue 3的用户都能在这里找到适合你技术栈的解决方案。让我们跳过那些繁琐的理论直接进入能复制粘贴的实战环节。1. 基础集成五分钟快速上手要在Vue项目中使用highlight.js第一步自然是安装。但这里有个小细节需要注意highlight.js本身是一个独立的、语言无关的高亮引擎而我们要在Vue里用通常需要一个“桥梁”库来帮我们创建指令或组件。对于Vue 2项目vue-highlightjs是个不错的选择对于Vue 3则可以考虑更现代的highlightjs/vue-plugin。1.1 安装与基础配置打开你的项目终端根据你的Vue版本执行对应的安装命令。如果你用的是Vue 2npm install highlight.js vue-highlightjs或者使用yarnyarn add highlight.js vue-highlightjs对于Vue 3项目安装命令略有不同npm install highlight.js highlightjs/vue-plugin安装完成后我们需要在项目的入口文件通常是main.js或main.ts中进行引入和注册。以Vue 2项目为例配置过程非常直观// main.js import Vue from vue import App from ./App.vue // 引入vue-highlightjs import VueHighlightJS from vue-highlightjs // 引入一个你喜欢的样式主题这里以atom-one-dark为例 import highlight.js/styles/atom-one-dark.css // 使用Vue插件 Vue.use(VueHighlightJS) new Vue({ render: h h(App), }).$mount(#app)注意vue-highlightjs插件会自动为我们注册一个名为v-highlightjs的全局指令。我们之后在模板中直接使用这个指令即可。对于Vue 3配置方式采用了Composition API的风格// main.js import { createApp } from vue import App from ./App.vue // 引入Vue 3专用的插件 import hljs from highlight.js/lib/core import javascript from highlight.js/lib/languages/javascript import { HLJSPlugin } from highlightjs/vue-plugin // 引入样式 import highlight.js/styles/atom-one-dark.css // 注册你想要高亮的语言这里以javascript为例 hljs.registerLanguage(javascript, javascript) const app createApp(App) // 使用插件并将hljs实例作为选项传入 app.use(HLJSPlugin, { hljs }) app.mount(#app)1.2 第一个高亮代码块配置好之后在组件中使用就非常简单了。假设我们有一个Demo.vue组件需要展示一段JavaScript代码template div classdemo h3JavaScript 代码示例/h3 !-- 使用 v-highlightjs 指令绑定代码字符串 -- pre v-highlightjscode classjavascriptconst greeting Hello, World!; function sayHello(name) { console.log(${greeting} ${name}); } sayHello(Vue Developer);/code/pre /div /template script export default { name: Demo } /script style scoped /* 可以在这里调整代码块的样式比如内边距和圆角 */ pre { background-color: #282c34; border-radius: 8px; padding: 1em; overflow-x: auto; } /style关键点在于code标签的class属性。这里设置为javascripthighlight.js就会自动调用对应的语言解析器来高亮这段代码。如果你不指定语言highlight.js会尝试自动检测但为了准确性和性能显式声明语言是更好的实践。常用的语言标识符有javascript或jshtmlcsspythonjavabash或shelljsonxml渲染后你的代码就会拥有丰富的语法着色关键字、字符串、注释等都会以不同的颜色区分开来可读性大大提升。2. 应对真实场景动态内容与Markdown渲染上面的例子展示了静态代码的高亮但在实际项目中代码内容往往是动态的比如从API接口获取或者需要渲染一整篇Markdown文章其中包含代码块。这就需要我们稍微升级一下玩法。2.1 高亮动态绑定的代码字符串很多时候我们的代码数据来源于组件的data或props是动态的。v-highlightjs指令同样可以处理这种情况。你需要将代码字符串绑定到指令上template div pre v-highlightjsdynamicCodecode classpython/code/pre /div /template script export default { data() { return { dynamicCode: def fibonacci(n): if n 1: return n else: return fibonacci(n-1) fibonacci(n-2) # 打印前10个斐波那契数 for i in range(10): print(fibonacci(i)) } } } /script注意这里的v-highlightjsdynamicCode将指令绑定到了dynamicCode这个响应式数据上。当dynamicCode的内容发生变化时高亮效果会自动更新。code标签内部是空的因为高亮内容由指令通过绑定的数据生成。2.2 集成Markdown渲染与代码高亮对于技术博客或文档站更常见的需求是渲染Markdown格式的内容并确保其中的代码块被正确高亮。我们可以借助marked或markdown-it这类Markdown解析库结合highlight.js来实现。首先安装必要的依赖npm install marked highlight.js然后我们可以创建一个名为MarkdownRenderer.vue的组件template div classmarkdown-content v-htmlcompiledMarkdown/div /template script import { marked } from marked import hljs from highlight.js // 配置marked使用highlight.js进行代码高亮 marked.setOptions({ highlight: function(code, lang) { // 如果指定了语言则使用该语言的高亮器 if (lang hljs.getLanguage(lang)) { try { return hljs.highlight(code, { language: lang }).value } catch (err) { console.warn(高亮语言 ${lang} 失败:, err) } } // 否则尝试自动检测语言 try { return hljs.highlightAuto(code).value } catch (err) { console.warn(代码自动高亮失败:, err) return code // 失败则返回原始代码 } }, // 其他marked配置例如启用GFMGitHub Flavored Markdown gfm: true, breaks: true }) export default { name: MarkdownRenderer, props: { content: { type: String, required: true, default: } }, computed: { compiledMarkdown() { // 将Markdown字符串转换为HTML return marked(this.content) } } } /script style scoped /* 引入一个highlight.js主题 */ import ~highlight.js/styles/atom-one-dark.css; .markdown-content { line-height: 1.6; } /* 为代码块添加一些基础样式 */ .markdown-content pre { background: #282c34; border-radius: 8px; padding: 1em; overflow-x: auto; } .markdown-content code { font-family: Courier New, Consolas, monospace; } /style在这个组件中我们通过marked.setOptions配置了highlight函数。当marked解析到javascript这样的代码块时会调用这个函数并传入代码字符串和语言标识。我们在函数内部使用hljs.highlight进行处理返回高亮后的HTML字符串。这样整个Markdown内容的解析和高亮就一气呵成了。使用这个组件非常简单template div MarkdownRenderer :contentmarkdownText / /div /template script import MarkdownRenderer from ./components/MarkdownRenderer.vue export default { components: { MarkdownRenderer }, data() { return { markdownText: # 我的文章 这是一段包含代码的Markdown。 \\\javascript // 这是一个JavaScript示例 const data fetch(/api/data) .then(response response.json()) .then(json console.log(json)); \\\ \\\python def hello(): print(Hello from Python!) \\\ } } } /script3. 深度优化性能、按需加载与自定义基础功能实现后我们可能会考虑更多如何减少打包体积如何高亮行内代码如何支持更多小众语言这一节我们来解决这些问题。3.1 按需加载语言与样式以优化性能highlight.js默认支持超过180种语言如果全部打包进项目体积会非常可观超过1MB。对于大多数项目我们可能只需要高亮JavaScript、HTML、CSS、JSON等少数几种语言。按需加载是优化性能的关键。在Vue 3的配置中我们已经看到了按需注册语言的例子。在Vue 2中我们可以通过修改main.js的引入方式来实现// main.js - Vue 2 优化版 import Vue from vue import VueHighlightJS from vue-highlightjs // 1. 核心库 import hljs from highlight.js/lib/core // 2. 按需引入语言定义 import javascript from highlight.js/lib/languages/javascript import xml from highlight.js/lib/languages/xml import css from highlight.js/lib/languages/css import json from highlight.js/lib/languages/json // 3. 引入样式 import highlight.js/styles/atom-one-dark.css // 注册语言 hljs.registerLanguage(javascript, javascript) hljs.registerLanguage(html, xml) // html使用xml解析器 hljs.registerLanguage(css, css) hljs.registerLanguage(json, json) // 将配置好的hljs实例传递给Vue插件 Vue.use(VueHighlightJS, { hljs })通过这种方式我们只打包了实际用到的四种语言能显著减小最终构建产物的体积。你可以根据项目需要增删import和registerLanguage的语句。3.2 高亮行内代码与Vue模板片段有时我们不仅想高亮代码块还想高亮段落中的行内代码例如请使用console.log()进行调试。highlight.js本身提供了highlightElement函数来处理单个DOM元素。我们可以创建一个自定义指令来实现这个功能template div p在JavaScript中你可以使用code v-inline-highlight classjavascripttypeof/code操作符来检查类型。/p div v-htmlrawHtml v-highlight-html/div /div /template script import hljs from highlight.js/lib/core import javascript from highlight.js/lib/languages/javascript hljs.registerLanguage(javascript, javascript) export default { name: InlineDemo, directives: { // 自定义指令高亮行内代码 inline-highlight: { inserted(el) { // 只处理code标签并且有语言类 if (el.tagName.toLowerCase() code el.className) { hljs.highlightElement(el) } }, componentUpdated(el) { // 如果内容更新重新高亮 if (el.tagName.toLowerCase() code el.className) { hljs.highlightElement(el) } } }, // 自定义指令高亮包含代码的HTML片段谨慎使用避免XSS highlight-html: { inserted(el) { // 找到el元素内所有的code标签进行高亮 el.querySelectorAll(code).forEach((block) { if (block.className) { hljs.highlightElement(block) } }) } } }, data() { return { rawHtml: p这段HTML包含code classjavascriptlet x 10;/code代码。/p } } } /script警告使用v-html渲染用户输入的HTML存在XSS跨站脚本攻击风险。在实际项目中必须确保HTML内容来源完全可信或者经过严格的净化处理。3.3 扩展支持自定义语言或语法如果你的项目需要高亮某种highlight.js官方未支持的语言或者需要对现有语言的高亮规则进行微调你可以定义自己的语言语法。创建一个简单的自定义语言定义文件my-custom-language.js// my-custom-language.js export default function(hljs) { const KEYWORDS { keyword: function return let const if else for while, literal: true false null } return { name: MyCustomLang, keywords: KEYWORDS, contains: [ hljs.QUOTE_STRING_MODE, // 字符串 hljs.C_LINE_COMMENT_MODE, // 单行注释 hljs.C_BLOCK_COMMENT_MODE, // 多行注释 { className: number, begin: hljs.NUMBER_RE // 数字 } ] } }然后在你的主文件中引入并注册它import myCustomLanguage from ./my-custom-language hljs.registerLanguage(mycustom, myCustomLanguage)现在你就可以在代码块中使用classmycustom来高亮这种自定义语言的代码了。4. 样式切换与主题管理打造个性化体验代码高亮的视觉风格直接影响用户体验。highlight.js提供了数十种精美的主题样式从深色系的atom-one-dark、monokai到浅色系的github、atom-one-light。更进一步我们可以让用户动态切换主题或者跟随系统的深色/浅色模式。4.1 实现动态主题切换思路很简单我们准备多套highlight.js的CSS主题文件通过动态改变link标签的href属性或者动态加载CSS字符串来切换样式。这里我们采用一个更模块化的方法——将主题作为可切换的CSS模块。首先在项目中安装我们需要的主题样式包或者直接从CDN引入对应的CSS文件npm install highlight.js # 或者直接引入CSS文件这里假设我们将CSS文件放在 src/assets/hljs-themes/ 目录下然后创建一个主题管理组件ThemeSwitcher.vuetemplate div classtheme-switcher label fortheme-select代码主题/label select idtheme-select v-modelselectedTheme changechangeTheme option v-fortheme in availableThemes :keytheme.id :valuetheme.id {{ theme.name }} /option /select div classtheme-preview precode classjavascript// 预览 function example() { const message Hello, Theme!; console.log(message); return message.length; }/code/pre /div /div /template script // 动态导入主题CSS文件 const themes { atom-one-dark: () import(highlight.js/styles/atom-one-dark.css), atom-one-light: () import(highlight.js/styles/atom-one-light.css), github: () import(highlight.js/styles/github.css), monokai: () import(highlight.js/styles/monokai.css), vs: () import(highlight.js/styles/vs.css), } export default { name: ThemeSwitcher, data() { return { selectedTheme: atom-one-dark, availableThemes: [ { id: atom-one-dark, name: Atom One Dark }, { id: atom-one-light, name: Atom One Light }, { id: github, name: GitHub }, { id: monokai, name: Monokai }, { id: vs, name: Visual Studio }, ] } }, methods: { async changeTheme() { // 移除旧的样式标签 const oldLink document.getElementById(hljs-theme) if (oldLink) { oldLink.remove() } // 动态加载新的CSS // 注意这里使用了动态import实际构建时主题CSS会被分割成独立的chunk const themeModule await themes[this.selectedTheme]() // 由于CSS文件通过import()加载其内容已经通过项目的CSS处理器如mini-css-extract-plugin处理 // 我们不需要手动创建link标签但为了演示动态切换这里采用另一种方式 this.loadCSSFromCDN(this.selectedTheme) }, loadCSSFromCDN(themeName) { // 方法一使用CDN链接简单但依赖网络 const linkId hljs-theme let link document.getElementById(linkId) if (!link) { link document.createElement(link) link.id linkId link.rel stylesheet link.type text/css document.head.appendChild(link) } // 这里使用jsdelivr CDN你也可以将主题CSS放在项目的public目录或assets中 link.href https://cdn.jsdelivr.net/gh/highlightjs/cdn-release11/build/styles/${themeName}.min.css } }, mounted() { // 初始化主题 this.changeTheme() } } /script style scoped .theme-switcher { margin: 20px 0; padding: 15px; border: 1px solid #eee; border-radius: 8px; } select { margin-left: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ccc; } .theme-preview { margin-top: 15px; } .theme-preview pre { border-radius: 6px; padding: 12px; overflow-x: auto; } /style这个组件提供了一个下拉选择框让用户切换主题。切换时它会动态加载对应主题的CSS文件。为了更好的性能和体验我们可以将用户的选择保存到localStorage中下次访问时自动应用。4.2 跟随系统深色/浅色模式现在很多操作系统和应用都支持深色模式。我们可以利用window.matchMediaAPI来检测用户系统的主题偏好并自动切换代码高亮主题。在ThemeSwitcher.vue组件中增加以下逻辑// 在data中增加一个字段 data() { return { // ... 其他数据 prefersDark: window.matchMedia((prefers-color-scheme: dark)).matches, } }, // 在mounted生命周期中监听系统主题变化 mounted() { this.initTheme() const darkModeMediaQuery window.matchMedia((prefers-color-scheme: dark)) // 监听变化 darkModeMediaQuery.addEventListener(change, (e) { this.prefersDark e.matches this.applyThemeBasedOnPreference() }) }, methods: { initTheme() { // 先从localStorage读取用户手动选择的主题 const savedTheme localStorage.getItem(hljs-theme) if (savedTheme) { this.selectedTheme savedTheme this.changeTheme() } else { // 如果没有保存过则根据系统偏好自动选择 this.applyThemeBasedOnPreference() } }, applyThemeBasedOnPreference() { this.selectedTheme this.prefersDark ? atom-one-dark : atom-one-light this.changeTheme() }, async changeTheme() { // ... 原有的切换逻辑 // 保存用户选择到localStorage localStorage.setItem(hljs-theme, this.selectedTheme) } }4.3 封装可复用的智能代码高亮组件最后我们将所有功能封装成一个强大、易用的SmartCodeBlock组件。这个组件支持动态语言检测、主题切换、显示行号、一键复制等功能。由于篇幅限制这里给出一个简化版的组件框架展示核心思路template div classsmart-code-block :classthemeClass div classcode-header span classlanguage-badge{{ detectedLanguage }}/span button classcopy-btn clickcopyToClipboard复制/button select v-modellocalTheme changeonThemeChange classtheme-select option valuedark深色/option option valuelight浅色/option /select /div div classcode-wrapper refcodeWrapper precode :classlanguage refcodeElement{{ code }}/code/pre /div div v-ifcopySuccess classcopy-success已复制/div /div /template script import hljs from highlight.js/lib/core // 按需引入常用语言 import javascript from highlight.js/lib/languages/javascript import python from highlight.js/lib/languages/python // ... 引入其他语言 hljs.registerLanguage(javascript, javascript) hljs.registerLanguage(python, python) // ... 注册其他语言 export default { name: SmartCodeBlock, props: { code: { type: String, required: true }, language: { type: String, default: // 为空则自动检测 }, theme: { type: String, default: auto // auto, dark, light } }, data() { return { detectedLanguage: , localTheme: this.theme, copySuccess: false } }, computed: { themeClass() { if (this.localTheme auto) { return window.matchMedia((prefers-color-scheme: dark)).matches ? theme-dark : theme-light } return theme-${this.localTheme} } }, mounted() { this.highlightCode() }, watch: { code() { this.$nextTick(() { this.highlightCode() }) }, language() { this.highlightCode() } }, methods: { highlightCode() { const codeEl this.$refs.codeElement if (!codeEl) return let result if (this.language hljs.getLanguage(this.language)) { // 使用指定的语言高亮 result hljs.highlight(this.code, { language: this.language }) this.detectedLanguage this.language } else { // 自动检测语言 result hljs.highlightAuto(this.code) this.detectedLanguage result.language || text } codeEl.innerHTML result.value }, async copyToClipboard() { try { await navigator.clipboard.writeText(this.code) this.copySuccess true setTimeout(() { this.copySuccess false }, 2000) } catch (err) { // 降级方案 const textArea document.createElement(textarea) textArea.value this.code document.body.appendChild(textArea) textArea.select() document.execCommand(copy) document.body.removeChild(textArea) this.copySuccess true setTimeout(() { this.copySuccess false }, 2000) } }, onThemeChange() { this.$emit(theme-change, this.localTheme) } } } /script style scoped .smart-code-block { border-radius: 8px; overflow: hidden; margin: 1em 0; } .code-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; font-size: 0.9em; } .language-badge { background: #666; color: white; padding: 2px 8px; border-radius: 4px; font-family: monospace; } .copy-btn, .theme-select { padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer; } .code-wrapper { overflow-x: auto; } .copy-success { text-align: center; padding: 4px; font-size: 0.85em; color: green; } /* 深色/浅色主题样式 */ .smart-code-block.theme-dark .code-header { background-color: #2d2d2d; color: #ccc; } .smart-code-block.theme-light .code-header { background-color: #f6f8fa; color: #333; } /style这个组件集成了语言检测、主题切换和复制功能你可以根据项目需求继续扩展比如添加折叠/展开、缩放字体大小、显示文件名等特性。在实际项目中这样的组件能极大提升开发效率和用户体验。