升级到 VuePress 2.0.0-rc.31
目标:把本站从
vuepress@2.0.0-beta.64升级到2.0.0-rc.31。
包管理器:npm。打包工具:Vite(@vuepress/bundler-vite),与当前实际用法一致。
本文只整理升级事项,不包含实际改代码。官方 changelog:vuepress/core CHANGELOG。
当前项目现状
核对过 package.json、lockfile 和 .vuepress 源码,现状如下:
| 项 | 当前值 | 升级后要求 |
|---|---|---|
| Node | nvm 当前是 v16.20.1 | 用 nvm 切到 >= 22.18.0(推荐 22 LTS),不要单独安装 Node |
| npm | 8.19.4(随 Node 16) | 切到 Node 22 后随版本自带 npm 10.x |
vuepress | ^2.0.0-beta.64 | 钉死 2.0.0-rc.31 |
| 打包工具 | lockfile 里已是 vuepress-vite / @vuepress/bundler-vite | 显式安装并配置 @vuepress/bundler-vite@2.0.0-rc.31 |
| 主题 | 继承 @vuepress/theme-default,未单独声明依赖 | 必须单独安装 @vuepress/theme-default@next |
| 搜索 | @vuepress/plugin-docsearch@^2.0.0-beta.64 | 改为 @vuepress/plugin-docsearch@next |
vue | 未单独安装 | npm 不用显式安装;只有 pnpm 才需要作为 peer 手动装 |
| Sass | 部署笔记里写过 node-sass + sass-loader(那是 webpack 时期) | 继续用 Vite,一般不必装 sass-loader;只有编译 .scss 报错时再补 sass |
补充两点:
- 本仓库
package.json只写了vuepress,没有写 bundler。beta.29起vuepress默认 bundler 已经是 Vite,lockfile 也印证了这一点。升级后仍用 Vite,只是 RC 要求显式安装并在配置里声明@vuepress/bundler-vite。 - 配置文件已经是 ESM(
import/export),但根package.json还没有"type": "module"。升级后建议补上。
1. 环境:nvm / Node / npm
Node 用 nvm 管理,不要从官网或安装包单独装一份 Node,以免和 nvm 抢 PATH。
Node 版本(硬门槛)
官方最新文档要求:
Node.js >= 22.18.0
引擎范围大约是:^22.18.0 || ^24 || >=26
注意:
- 不要用 Node 16 / 18 / 20,rc.24 已 drop Node 18,rc.31 文档要求 22.18+。
- 不要用 Node 23 / 25(已 EOL,官方不让跑)。
- 中文站有的页面仍写着
v20.9.0+,那是旧信息,以英文文档 / changelog 为准。 - 本地当前 nvm 切在
v16.20.1,不先切到 22,后面任何安装、dev、build 都会失败。 - CI / 部署机如果也要
npm run build,同样用 nvm 切到 22+,不要另装系统级 Node。
Windows(nvm-windows)示例:
nvm list # 看已安装的版本
nvm list available # 看可安装的版本(可选)
nvm install 22.18.0 # 没有 22.18+ 时再装;已有 22 LTS 可跳过
nvm use 22.18.0 # 切换到该版本
node -v # 确认 >= v22.18.0
npm -v # 应变成 10.x
如果已经装过 22 的某个小版本(只要 >= 22.18.0),直接 nvm use 即可,不必再 install。
Unix / Git Bash 下的 nvm 若要把 22 设为默认:
nvm alias default 22
nvm-windows 没有 alias default,下次新开终端仍需 nvm use 22.18.0,或在 nvm 设置里指定默认版本。
npm
继续用 npm 即可,不必换成 pnpm。用 nvm 切到 Node 22 后,npm 会跟着变成 10.x。注意:
- 官方写得很清楚:只有 pnpm 才需要显式安装
vue作为 peer-dependencies。npm 会自己处理,本仓库保持现状即可,不要多装一个vue。 - 升级前删掉
node_modules和package-lock.json,重新npm install,避免 beta 残留。 - 确认
where node/where npm指向 nvm 目录,而不是单独安装的 Node。
建议补到 package.json
{
"type": "module",
"engines": {
"node": "^22.18.0 || ^24 || >=26"
}
}
2. 依赖怎么装(core 和 ecosystem 版本号不同)
从 RC 开始拆成两个仓库,版本号不要强行对齐成同一个 rc.xx:
| 来源 | 包 | 版本 |
|---|---|---|
| core(vuepress/core) | vuepress、@vuepress/bundler-vite | 必须同版本:2.0.0-rc.31 |
| ecosystem(vuepress/ecosystem) | @vuepress/theme-default、@vuepress/plugin-docsearch | 用 @next(目前大约 rc.100+,和 core 的 rc.31 不是一回事) |
不要把 @vuepress/plugin-docsearch 也钉成 2.0.0-rc.31,对不上会报 useRouteLocale() is called without provider(项目注释里已经踩过)。
推荐安装命令
# 1. 用 nvm 切到 Node 22(不要单独安装 Node)
nvm use 22.18.0
node -v # 需要 v22.18.0+
# 2. 清掉旧依赖
rmdir /s /q node_modules
del package-lock.json
# 3. core:vuepress 和 vite bundler 必须同版本
# npm 不需要显式安装 vue(pnpm 才需要)
npm i -D vuepress@2.0.0-rc.31 @vuepress/bundler-vite@2.0.0-rc.31
# 4. ecosystem:主题、搜索(代码高亮用默认主题自带的 PrismJS,不必另装插件)
npm i -D @vuepress/theme-default@next @vuepress/plugin-docsearch@next
tippy.js 继续留在 dependencies,clipboard 插件还在用。
装完后 package.json 的 devDependencies 大致应是:
{
"vuepress": "2.0.0-rc.31",
"@vuepress/bundler-vite": "2.0.0-rc.31",
"@vuepress/theme-default": "^2.0.0-rc.xxx",
"@vuepress/plugin-docsearch": "^2.0.0-rc.xxx"
}
3. Vite 专项
3.1 必须显式声明 bundler
RC 起不再内置默认 bundler,不写 bundler 会直接起不来。
docs/.vuepress/config.js 需要变成:
import { defineUserConfig } from 'vuepress'
import { viteBundler } from '@vuepress/bundler-vite'
import { baseConfig } from './config/base-config'
import { theme } from './config/theme'
import { plugins } from './plugins/index'
export default defineUserConfig({
...baseConfig,
bundler: viteBundler(),
theme,
plugins,
})
当前项目没有自定义 Vite 配置,先用空的 viteBundler() 即可。
可选参数(一般用不到):
viteBundler({
viteOptions: {},
vuePluginOptions: {},
})
3.2 rc.31 的 bundler 接口变化
rc.31 给 bundler 补了 type、mergeConfig,并新增 configureVite / viteMergeConfig。用官方 viteBundler() 即可,不必自己实现接口。
如果以后要改 Vite 配置,优先改 viteOptions;若用 configureVite 做合并,用官方的 viteMergeConfig,不要假设返回值会自动 merge。
3.3 Sass
Vite 内置处理 .scss / .sass,不需要 sass-loader。部署笔记里 node-sass + sass-loader@10 是 webpack 时期的写法,本次不要照搬。
node-sass已废弃,也不支持 Node 22。- Vite 编译 Sass 用的是 Dart Sass 包
sass。npm 通常会通过默认主题把sass带进来。 - 如果启动时报找不到
sass或无法编译.scss,再补:npm i -D sass。
本站有大量 .scss(styles/、NotFound.vue),升级后若样式编译失败,优先检查有没有 sass,而不是去装 webpack 的 loader。
3.4 base 与 Public 文件
Vite 会给 Public 文件自动处理 base,比 webpack 省事。当前站点 base: '/',线上若仍是根路径,无感。
如果以后又改成 /note/ 这类子路径:
- Vite 会给 public 资源补前缀。
- JS 里手写的绝对路径(如
useInit.js的/images/wx/qrcode_258_8.jpg)仍不会自动加base,需要自己处理,或改用 VuePress 的withBase()。 - 部署笔记里 nginx
location /note+base: '/note/'这条经验仍然有效。
3.5 部署时的 MIME
打包产物里会有 .mjs。nginx 需要:
application/javascript js mjs;
这条在 deploy.md 里已经记过,升级后仍然要保留。
4. 配置 API 必改项
4.1 导入路径
VuePress 内部包改为从 vuepress/* 子路径导出:
| 旧(beta.64) | 新(rc.31) | 本仓库文件 |
|---|---|---|
@vuepress/client | vuepress/client | client.js、clipboard 的 client/index.js |
@vuepress/utils | vuepress/utils | config/base-config.js、clipboard 的 index.js |
@vuepress/theme-default 这个包名不变,但必须单独安装。
defineUserConfig 仍从 vuepress 导入,不用改。
4.2 markdown.code 已删除
docs/.vuepress/config/base-config.js 里这段会失效:
markdown: {
headers: { level: [1, 2, 3, 4] }, // 保留
code: { // 已删除
vPre: { inline: true, block: true },
},
}
改为:
markdown: {
headers: { level: [1, 2, 3, 4] },
vPre: { inline: true, block: true },
}
代码高亮不再由 VuePress core 内置。使用默认主题时,主题已经集成了 @vuepress/plugin-prismjs,不必单独安装,也不要再手动注册一份高亮插件。只要不要把 themePlugins.prismjs 设成 false 即可。关掉的话代码块没有高亮,clipboard 插件依赖的 div[class*="language-"] 也可能对不上。
4.3 主题 option 大多还能用
config/theme.js 里这些 locale / 功能开关仍然有效,可先不动:
colorMode/colorModeSwitch/toggleColorModenavbar/sidebar/sidebarDepth/logo/logoDarkrepo/repoLabel/docsBranch/docsDir/editLink*lastUpdated/lastUpdatedText/contributors/contributorsTexttip/warning/danger/notFound/backToHome
lastUpdated、contributors 依赖 Git 信息。默认主题会带 @vuepress/plugin-git,仓库里要有 commit。
自定义字段 userConfig(背景图、打字机、页脚文案)是传给默认主题的额外 option,会进 theme-data。数据还能读到,但下面第 6 节的 DOM 选择器会失效。
4.4 其它站点配置
这些可以先保持:
port: 8088dest: 'docs/dist'temp: 'docs/.temp'cache: 'docs/.cache'alias(@components/@hooks)head里的 favicon、iconfont
rc.31 的 shouldPrefetch 默认从 true 改成 'as-needed'。文档站页面多,建议保持新默认,不必改回 true。
5. 自定义主题:还能用的部分
当前结构:
// theme/index.js
extends: defaultTheme(options)
// layouts/Layout.vue
import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
// client.js
layouts: { Layout, NotFound }
官方仍支持:
extends: defaultTheme(options)做子主题- Layout 插槽:
page-bottom等(navbar/sidebar/page-top也都还在) - 从
@vuepress/theme-default/layouts/Layout.vue引入父布局 - 在
client.js的layouts里覆盖Layout、NotFound
也就是说:子主题继承方式不用推倒重来。真正会坏的是样式选择器和 DOM 查询。
theme/index.js 里不要再写 layouts(beta.51 已移除,本仓库已经改到 client.js,这块是对的)。
6. 自定义主题:一定会坏的部分(重点)
默认主题在 ecosystem 做过破坏性重构:
- 组件加
VP前缀:Navbar→VPNavbar - class 加
vp-前缀:.navbar→.vp-navbar - 暗色模式:
html.dark→html[data-theme='dark'] - CSS 变量:
--c-*→--vp-c-*
容器根节点 class 从 .theme-container 变成 .vp-theme-container,但 no-sidebar 这个修饰 class 还在。
6.1 CSS class 对照
按当前源码,升级后选择器要改成类似:
| 本仓库现在写的 | rc 默认主题 |
|---|---|
header.navbar | .vp-navbar |
.navbar-items-wrapper | .vp-navbar-items-wrapper |
nav.navbar-items | nav.vp-navbar-items |
.navbar-item | .vp-navbar-item |
.toggle-color-mode-button | 对应 VPToggleColorModeButton(大概率 .vp-toggle-color-mode-button,以实际 DOM 为准) |
div.theme-container | .vp-theme-container |
main.home | .vp-home |
header.hero / .hero | .vp-hero |
p.description | .vp-hero-description |
.site-name / img.logo | 以 VPNavbarBrand 实际 DOM 为准 |
.sidebar / .sidebar-item | .vp-sidebar 等(以实际 DOM 为准) |
.custom-container.tip | 默认主题已改用 @vuepress/plugin-markdown-hint,不再是 .custom-container |
div.back-to-top | 回到顶部已抽到独立插件,class 会变 |
涉及文件:
styles/navbar.scssstyles/home.scssstyles/sider.scssstyles/normal.scssstyles/back-to-top.scssstyles/scroll.scss(html:has(.theme-container.no-sidebar)也要改)styles/footer.scss(首页 footer 结构也可能变)
6.2 CSS 变量对照
styles/variables.scss、NotFound.vue、LoadingPage.vue 里的旧变量要换:
| 旧 | 新 |
|---|---|
--c-brand | --vp-c-accent / --vp-c-accent-bg |
--c-brand-light | --vp-c-accent-hover |
--c-bg | --vp-c-bg |
--c-text | --vp-c-text |
--c-text-accent | --vp-c-accent |
--c-bg-navbar | --vp-navbar-c-bg |
html.dark { ... } | [data-theme='dark'] { ... } |
layout 类变量名大多还在,例如 --content-width、--navbar-height、--navbar-padding-v。品牌色和暗色选择器必须改,否则会回到默认主题的绿色 / 默认暗色。
默认主题现在约定:
- Sass 变量覆盖:
.vuepress/styles/palette.scss(断点等,当前项目几乎没覆盖 Sass 变量) - 额外 CSS / CSS 变量:
.vuepress/styles/index.scss(当前就是这个入口,可继续用)
6.3 JS 里的 DOM 查询(首页和导航会挂)
这些是按 beta 默认主题 DOM 写的,升级后选择器对不上,功能会静默失效:
hooks/useUserConfig.js
document.querySelector('#app .no-sidebar main.home')
document.querySelector('#app .no-sidebar main.home header.hero .description')
hooks/useBackgroundImgLoaded.js
document.querySelector('#app .no-sidebar main.home header.hero')
document.querySelector('#app .no-sidebar main.home')
hooks/useInit.js(微信二维码挂在倒数第二个导航项上)
document.querySelector('nav.navbar-items .navbar-item:nth-last-child(2)')
建议对照:
旧:#app .no-sidebar main.home
新:.vp-theme-container.no-sidebar .vp-home
旧:header.hero / p.description
新:.vp-hero / .vp-hero-description
旧:nav.navbar-items .navbar-item
新:nav.vp-navbar-items .vp-navbar-item
useThemeData 从 @vuepress/plugin-theme-data/client 导入这条路还在(默认主题内置了 theme-data),userConfig 一般还能读到。不要在 Node 环境或 SSR 阶段直接操作 document,继续放在 onMounted / watch(route) 里。
长期更稳的做法:用 Layout 插槽或 @theme 组件替换,少依赖 querySelector。升级第一轮可以先改选择器,保证功能回来。
7. 自定义 clipboard 插件
文件:docs/.vuepress/plugins/clipboard/
必改:
- Node 入口把
@vuepress/utils换成vuepress/utils。 - ESM 不要依赖
__dirname。rc.31 CLI 已从 esbuild 换成 rolldown,注入行为可能变:
import { getDirname, path } from 'vuepress/utils'
const __dirname = getDirname(import.meta.url)
- 客户端入口:
import { defineClientConfig } from 'vuepress/client'
- 代码高亮走默认主题自带的 PrismJS,代码块 DOM 仍是
div[class*="language-"]。clipboard 的selector保持这个即可,真实页面上再验一次。 tippy.js继续用,确认 Vite 能正确处理tippy.js/dist/tippy.css。
8. DocSearch
docsearchPlugin({ appId, apiKey, indexName, translations }) 用法大体兼容,但:
- 必须装 ecosystem 的
@next,不要跟 core 一样钉rc.31。 indexName还能用,文档说会弃用,新字段是indices。- 默认主题 DOM 变了,Algolia 爬虫选择器要改,否则搜索框能开、命中和跳转可能不准。官方现在类似:
recordProps: {
lvl0: {
selectors: '.vp-sidebar-heading.active',
defaultValue: 'Documentation',
},
lvl1: '[vp-content] h1',
lvl2: '[vp-content] h2',
content: '[vp-content] p, [vp-content] li',
}
自定义搜索按钮样式(normal.scss 里的 .DocSearch-Button)一般还能用,变量是否还叫 --docsearch-* 以插件实际注入为准。
9. 建议的落地顺序
按这个顺序改,避免一次改太多、问题分不清是依赖还是样式。
- 用 nvm 切到 Node 22.18+(
nvm install/nvm use,不要单独装 Node),确认node -v、npm -v。 - 删
node_modules/package-lock.json,按第 2 节重装依赖,加上"type": "module"。 - 改能编译的配置:
bundler: viteBundler()- 导入路径
vuepress/client、vuepress/utils markdown.vPre;代码高亮用默认主题自带的 PrismJS(不要关themePlugins.prismjs,也不要另装 shiki / prismjs)- clipboard 的
__dirname/ 导入路径
- 先求
npm run dev能起来(允许首页样式乱)。 - 打开首页 / 文档页,对照实际 DOM 改:
styles/*.scss的 class、CSS 变量、[data-theme='dark']useUserConfig/useInit/useBackgroundImgLoaded的选择器
- 回归清单:
- [ ]
npm run dev能启动(Vite,端口 8088) - [ ] 首页背景图随机切换
- [ ] 首页打字机文案
- [ ] 导航栏「订阅号」微信二维码
- [ ] 亮色 / 暗色切换
- [ ] 侧边栏、自定义 container(tip/warning/danger)
- [ ] 代码高亮 + 复制按钮
- [ ] DocSearch
- [ ] 404 页
- [ ] 文章页 footer(
page-bottom插槽) - [ ] 回到顶部按钮
- [ ]
npm run build,检查docs/dist;部署环境同样用 nvm 切到 22+,nginx 仍识别.mjs。
10. 本仓库需要动的文件清单
| 文件 | 原因 |
|---|---|
package.json | 依赖、"type": "module"、可选 engines |
docs/.vuepress/config.js | 增加 viteBundler() |
docs/.vuepress/config/base-config.js | vuepress/utils;markdown.code → markdown.vPre |
docs/.vuepress/client.js | vuepress/client |
docs/.vuepress/theme/index.js | 确认 defaultTheme 仍从 @vuepress/theme-default 导入 |
docs/.vuepress/config/theme.js | 关掉 copyCode(继续用自定义 clipboard);PrismJS 保持默认主题自带,不要设 prismjs: false |
docs/.vuepress/plugins/index.js | 不要加 shiki / prismjs 插件;docsearch 继续用但版本改为 @next |
docs/.vuepress/plugins/clipboard/index.js | vuepress/utils + getDirname |
docs/.vuepress/plugins/clipboard/client/index.js | vuepress/client |
docs/.vuepress/hooks/useUserConfig.js | DOM 选择器 |
docs/.vuepress/hooks/useBackgroundImgLoaded.js | DOM 选择器 |
docs/.vuepress/hooks/useInit.js | 导航 DOM 选择器 |
docs/.vuepress/styles/variables.scss | CSS 变量 + 暗色选择器 |
docs/.vuepress/styles/navbar.scss | vp- class |
docs/.vuepress/styles/home.scss | vp-home / vp-hero |
docs/.vuepress/styles/sider.scss | sidebar class |
docs/.vuepress/styles/normal.scss | container / html.dark / --c-brand |
docs/.vuepress/styles/scroll.scss | html.dark、.theme-container |
docs/.vuepress/styles/back-to-top.scss | 回到顶部 class |
docs/.vuepress/styles/footer.scss | 视首页 footer DOM 是否变化 |
docs/.vuepress/components/NotFound.vue | --c-brand |
docs/.vuepress/components/LoadingPage.vue | --c-bg |
docs/.vuepress/layouts/Layout.vue | 父布局导入路径先保持,确认仍能解析 |
config/navbar、config/sidebar 的数据结构可先不动;config/theme.js 只需处理 themePlugins(关掉 copyCode、保留默认 PrismJS),等页面起来再看导航高亮、侧边栏折叠有没有问题。