闪烁高亮当前的目标标题 v2.11.1
效果演示
可以试着点击下面的两个链接,然后观察标题部分发生的变化:
当我们点击右侧的大纲进行导览的时候会有黄色的闪烁块在标题元素周围闪烁。
为什么
当今的文档网站都会支持读取 URL 中传递的 Hash ID 然后滚动页面直至 Hash ID 选中的元素出现或出现在靠上的位置上,这个功能通常以用户点击标题左侧或右侧的 anchor 图标,或者是点击了右侧可交互和可选择的大纲列表中的元素为触发条件。
然而绝大多数的文档网站的目标标题都不会在点击后高亮和指示当前高亮的标题行在哪里。
目标标题是什么?这样的功能是如何工作的?
当点击右侧大纲内的链接或者直接点击标题左侧的链接按钮的时候,注意观察地址栏中的变化。
你会发现 URL 中会出现一个 #
符号,后面跟着一个 ID,这个 ID 就是目标标题的 ID。
这里说的目标标题的 ID 其实就是 HTML 元素的 ID,所以理论上不光是可以要求框架和浏览器配合着去滚动标题元素到视窗内,也可以是其他有效的带有匹配得上 HTML ID 的元素。
这样的元素可以通过按下 F12 打开开发者工具,然后在控制台中输入
document.querySelector('#为什么')
来查找到,这段代码将会返回一个 HTML 元素,这个元素就是我们说的「目标标题」。
为什么会需要高亮呢?
- 一个屏幕中出现了好几个标题的时候,用户点击了其中一个标题,然后页面滚动到了标题所在的位置,但是用户并不知道当前的标题在哪里,这个时候用户就需要自行寻找标题,这个过程是非常不友好的,因为用户需要重新阅读视口内的内容并自行寻找到他们希望寻找的标题。
- 对于文档末尾有多个标题的时候,点击右侧的大纲将不会发生页面的滚动动画,用户,作为读者自然也就无法通过一个固定的「看窗口顶部的标题来寻找和定位标题」的可重复行为模式来学习如何寻找标题。
这个时候能够指示和高亮察觉不到的标题高亮就是一件非常重要的事情,这个插件就是因此而诞生的。
怎么安装和配置
安装
你可以通过下面的命令将 @nolebase/vitepress-plugin-highlight-targeted-heading
安装到 VitePress 项目的依赖中:
pnpm add @nolebase/vitepress-plugin-highlight-targeted-heading -D
npm install @nolebase/vitepress-plugin-highlight-targeted-heading -D
yarn add @nolebase/vitepress-plugin-highlight-targeted-heading -D
为 VitePress 配置
配置分为两个大步骤:
添加 Vite 相关的配置
首先,请在你的 VitePress 核心配置文件 中(注意不是主题配置文件,通常为 docs/.vitepress/config.ts
,文件路径和拓展名也许会有区别)的根字段中添加 Vite 与 SSR 服务端渲染相关 的配置。
将高亮插件包 @nolebase/vitepress-plugin-highlight-targeted-heading
添加到需要 VitePress 底层的 Vite 帮忙处理的依赖:
如果你未曾见过这种红色绿色的标记
这是一种用于在用户界面(UI)上展示差异对比的标记规则。
红色的部分是你需要删除的,通常也以「减号 -」来表示,或者你可以理解为:这行文本原本的模样;
绿色的部分是你需要添加的,通常也以「加号 +」来表示,或者你可以理解为:这行文本将会被修改为的模样。
如果你想要了解更多有关「差异对比」的知识,你可以查看这篇有关 diffutils 历史的英文回答和 Git 的文档
是 TypeScript 用户吗?
至少需要配置下面的几个选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
其中的
module
选项指定了 JavaScript/TypeScript 的模块格式,Nolebase Integrations 默认使用与ESNext
相兼容的模块格式。moduleResolution
选项指定了模块解析策略,因为所有的 Nolebase Integrations 插件都遵循最新的 ECMAScript 规范与导出声明,如果你遇到了Cannot find module ... or its corresponding type declarations
的错误,你可能需要将moduleResolution
设置为Bundler
。
如果你想要更多的配置,可以参考下面的示例:
{
"compilerOptions": {
"jsx": "preserve",
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"removeComments": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
import { defineConfig } from 'vitepress'
// https://vitepress.dev/reference/site-config
export default defineConfig({
vite: {
ssr: {
noExternal: [
// 如果还有别的依赖需要添加的话,并排填写和配置到这里即可
'@nolebase/vitepress-plugin-highlight-targeted-heading',
],
},
},
lang: 'zh-CN',
title: '网站标题',
themeConfig: {
// 其他的配置...
}
// 其他的配置...
})
如果你很厉害,为 VitePress 的文档站点配置了分离和单独的 Vite 配置文件(比如 vite.config.ts
),那你也可以省略上面的配置,直接在 Vite 的配置文件中添加下面的配置:
如果你未曾见过这种红色绿色的标记
这是一种用于在用户界面(UI)上展示差异对比的标记规则。
红色的部分是你需要删除的,通常也以「减号 -」来表示,或者你可以理解为:这行文本原本的模样;
绿色的部分是你需要添加的,通常也以「加号 +」来表示,或者你可以理解为:这行文本将会被修改为的模样。
如果你想要了解更多有关「差异对比」的知识,你可以查看这篇有关 diffutils 历史的英文回答和 Git 的文档
是 TypeScript 用户吗?
至少需要配置下面的几个选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
其中的
module
选项指定了 JavaScript/TypeScript 的模块格式,Nolebase Integrations 默认使用与ESNext
相兼容的模块格式。moduleResolution
选项指定了模块解析策略,因为所有的 Nolebase Integrations 插件都遵循最新的 ECMAScript 规范与导出声明,如果你遇到了Cannot find module ... or its corresponding type declarations
的错误,你可能需要将moduleResolution
设置为Bundler
。
如果你想要更多的配置,可以参考下面的示例:
{
"compilerOptions": {
"jsx": "preserve",
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"removeComments": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
import { defineConfig } from 'vite'
export default defineConfig(() => {
return {
ssr: {
noExternal: [
// 如果还有别的依赖需要添加的话,并排填写和配置到这里即可
'@nolebase/vitepress-plugin-highlight-targeted-heading',
],
},
optimizeDeps: {
exclude: ['vitepress'],
},
plugins: [
// 其他 Vite 插件配置...
],
// 其他的配置...
}
})
如果你在没有单独配置过 Vite 配置文件(比如 vite.config.ts
)的情况下想要直接复制上面的代码的话,只需要在 VitePress 站点所在的地方新建一个 vite.config.ts
即可,不过也记得还需要通过包管理器安装一下 vite
哦。
添加 VitePress 主题相关的配置
在 VitePress 的主题配置文件中(注意不是配置文件,通常为 docs/.vitepress/theme/index.ts
,文件路径和拓展名也许会有区别),将 @nolebase/vitepress-plugin-highlight-targeted-heading
导入,并且将其添加到 Layout
的拓展中:
如果你未曾见过这种红色绿色的标记
这是一种用于在用户界面(UI)上展示差异对比的标记规则。
红色的部分是你需要删除的,通常也以「减号 -」来表示,或者你可以理解为:这行文本原本的模样;
绿色的部分是你需要添加的,通常也以「加号 +」来表示,或者你可以理解为:这行文本将会被修改为的模样。
如果你想要了解更多有关「差异对比」的知识,你可以查看这篇有关 diffutils 历史的英文回答和 Git 的文档
是 TypeScript 用户吗?
至少需要配置下面的几个选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
其中的
module
选项指定了 JavaScript/TypeScript 的模块格式,Nolebase Integrations 默认使用与ESNext
相兼容的模块格式。moduleResolution
选项指定了模块解析策略,因为所有的 Nolebase Integrations 插件都遵循最新的 ECMAScript 规范与导出声明,如果你遇到了Cannot find module ... or its corresponding type declarations
的错误,你可能需要将moduleResolution
设置为Bundler
。
如果你想要更多的配置,可以参考下面的示例:
{
"compilerOptions": {
"jsx": "preserve",
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"removeComments": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import type { Theme as ThemeConfig } from 'vitepress'
import {
NolebaseHighlightTargetedHeading,
} from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css' // [!code ++]*
export const Theme: ThemeConfig = {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// 其他的配置...
'layout-top': () => [
h(NolebaseHighlightTargetedHeading),
],
})
},
enhanceApp() {
// 其他的配置...
},
}
export default Theme
就是这么多了,接下来已经可以使用了!
如何使用
现在再次构建或者打开 VitePress 的开发服务器,你将可以观测到,只要是 URL 中的 #
有对应的 ID 的元素的时候,下面的情况都会生效:
- 直接点击标题左侧的链接按钮
- 直接点击右侧大纲部分中的链接
- 首次加载含有
#
的 URL
就是这么多了,祝撰写愉快!
错误排查
遭遇了 Cannot find module ... or its corresponding type declarations
错误?
如果有任何的 Nolebase Integrations 插件在 .ts
或 .vue
文件中出现此错误,你可能需要在 tsconfig.json
文件中配置 moduleResolution
选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}
如果错误仍然存在,请提交 GitHub issue 以获得进一步帮助并排除故障。