结论:Vite和Vue版本不兼容(Vite@3.2.10和Vue@3.4.31)
解决:降低Vue的版本到@3.3.4
下面是问题排查和解决过程:
一、现象
1、修改.vue单组件文件【LoginForm.vue】(修改了其<template>),但页面无法进行局部自动更新,但修改.ts、.js文件可以自动刷新页面。
2、vscode终端控制台可以打印【10:55:18 [vite] hmr update /src/views/sys/login/LoginForm.vue 】,浏览器控制台(级别:详细)也可以打印【[vite] hot updated: /src/views/sys/login/LoginForm.vue】,但页面就是不局部自动更新。
二、猜测及进一步测试
所以猜测是vite HMR对于.vue
文件的处理可能存在问题。于是进一步分别修改<tempalte>、<script>、<style>三个部分的内容,发现只有改<style>部分的内容浏览器才会局部自动更新。合理猜测HMR(热模块替换)对于Vue组件的模板和脚本部分没有正常工作,而对于样式部分则正常工作。
三、查找资料
查看vite官网关于HRM热更新的资料
点击链接进去,是vite-plugin-vue(@vitejs/plugin-vue)【Vite 官方为 Vue3 提供的插件,@vitejs/plugin-vue为包依赖名】的github项目仓库。
所以“Vite内置了HMR到Vue单文件组件(SFC)” ==>“Vite内置了HMR到 vite-plugin-vue中”==>HMR 功能和@vitejs/plugin-vue有关。
于是了解@vitejs/plugin-vue
介绍:
@vitejs/plugin-vue 是一个针对 Vue 框架的 Vite 插件。它的主要功能是支持基于 Vite 构建的 Vue 项目,为 Vite 提供对 Vue 单文件组件(SFC)的支持。
具体来说,@vitejs/plugin-vue 可以在 Vite 构建过程中处理 Vue 单文件组件(SFC),将其转换为 JavaScript 模块以便在浏览器中使用。这个插件对SFC的解析“主要”是使用 Vue 3 的编译器(如:@vue/compiler-sfc)来处理 .vue 文件的<template><script>部分,使用postcss、css预处理器等来处理<style>部分。
因此,@vitejs/plugin-vue 使得 Vite 能够理解 Vue 的 SFC,并将其编译为浏览器可以执行的代码。而 Vite 的 HMR 功能则确保在编译过程中,当修改了 SFC 时,应用能够实时更新,从而提高开发效率。
了解完毕后,得出结论
Vite 内置的 HMR 功能与@vitejs/plugin-vue插件是密切相关的,而 @vitejs/plugin-vue对Vue3 单文件组件(SFC)的编译解析离不开Vue3模板编译器。因此,@vitejs/plugin-vue的版本和Vue3模板编译器(@vue/compiler-sfc)的版本息息相关。而Vite的版本决定了@vitejs/plugin-vue的版本,Vue的版本决定了@vue/compiler-sfc的版本,所以合理猜测考虑是Vite和Vue二者版本兼容性的问题。
之后进行代码审查:
四、问题解决
当前猜测不兼容的Vite@3.2.10和Vue@3.4.31版本如下:
vite@3.2.10(@types/node@17.0.45)(less@4.2.0)(terser@5.31.1):
dependencies:
esbuild: 0.15.18
postcss: 8.4.39
resolve: 1.22.8
rollup: 2.79.1
optionalDependencies:
'@types/node': 17.0.45
fsevents: 2.3.3
less: 4.2.0
terser: 5.31.1
vue@3.4.31(typescript@4.9.5):
dependencies:
'@vue/compiler-dom': 3.4.31
'@vue/compiler-sfc': 3.4.31
'@vue/runtime-dom': 3.4.31
'@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@4.9.5))
'@vue/shared': 3.4.31
optionalDependencies:
typescript: 4.9.5
我将vue的版本降低到3.3.4之后,问题解决了。
vue@3.3.4:
dependencies:
'@vue/compiler-dom': 3.3.4
'@vue/compiler-sfc': 3.3.4
'@vue/runtime-dom': 3.3.4
'@vue/server-renderer': 3.3.4(vue@3.3.4)
'@vue/shared': 3.3.4
之后我又尝试了其他版本组合
Vite@5.3.3和Vue@3.4.31最新版组合,测试通过,没有问题
vite@5.3.3:
dependencies:
esbuild: 0.21.5
postcss: 8.4.39
rollup: 4.18.1
optionalDependencies:
fsevents: 2.3.3
vue@3.4.31(typescript@5.5.3):
dependencies:
'@vue/compiler-dom': 3.4.31
'@vue/compiler-sfc': 3.4.31
'@vue/runtime-dom': 3.4.31
'@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
'@vue/shared': 3.4.31
optionalDependencies:
typescript: 5.5.3
Vite@3.0.2和Vue@3.2.33随机版组合,测试通过,没有问题
vite@https://registry.npmmirror.com/vite/-/vite-3.0.2.tgz(less@4.1.2)(terser@5.31.2):
dependencies:
esbuild: https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz
less: https://registry.npmmirror.com/less/-/less-4.1.2.tgz
postcss: https://registry.npmmirror.com/postcss/-/postcss-8.4.39.tgz
resolve: https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz
rollup: https://registry.npmmirror.com/rollup/-/rollup-2.72.0.tgz
terser: https://registry.npmmirror.com/terser/-/terser-5.31.2.tgz
optionalDependencies:
fsevents: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz
vue@https://registry.npmmirror.com/vue/-/vue-3.2.33.tgz:
dependencies:
'@vue/compiler-dom': https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz
'@vue/compiler-sfc': https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz
'@vue/runtime-dom': https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz
'@vue/server-renderer': https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.33.tgz(vue@3.2.33)
'@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.33.tgz
Vite@3.2.7和Vue@3.3.8随机版组合,测试通过,没有问题
vite@^3.0.2:
version "3.2.7"
resolved "https://registry.npmmirror.com/vite/-/vite-3.2.7.tgz#35a62826bd4d6b778ae5db8766d023bcd4e7bef3"
integrity sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==
dependencies:
esbuild "^0.15.9"
postcss "^8.4.18"
resolve "^1.22.1"
rollup "^2.79.1"
optionalDependencies:
fsevents "~2.3.2"
vue@^3.2.33:
version "3.3.8"
resolved "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz#532ff071af24f6a69e5ecc53a66858a9ee874ffc"
integrity sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==
dependencies:
"@vue/compiler-dom" "3.3.8"
"@vue/compiler-sfc" "3.3.8"
"@vue/runtime-dom" "3.3.8"
"@vue/server-renderer" "3.3.8"
"@vue/shared" "3.3.8"
以上告一段落。
五、额外话
1、最好将项目的包版本锁定文件进行代码管理(使用Git或SVN),通常npm对应package-lock.json、yarn对应yarn.lock、pnpm对应pnpm-lock.yaml等。这样就不会出现团队协作开发时,一个成员并没有出现另一个成员出现的BUG,就很有可能是他们下载的依赖的版本不一致导致的问题,就比如上述HMR热更新的问题就是这样的情况。更进一步约束团队成员使用一样的包管理工具,在项目根目录下.npmr文件中配置相同的远程镜像。
2、项目进行依赖升级时要注意版本兼容性
3、 Vue2的SFC 编译器为vue-template-compiler
4、注意:从vue 3.2.13+和@vitejs/plugin-vue 1.9.0+开始,@vue/compiler-sfc不再需要作为对等依赖。即随着Vue和Vite的更新,特别是Vue 3.2.13和@vitejs/plugin-vue1.9.0的发布,这些版本内部已经包含了与@vue/compiler-sfc兼容的编译器或相关逻辑,因此不再需要开发者手动安装@vue/compiler-sfc作为对等依赖。
然而,在某些高级或特定的场景下,你可能还需要直接使用@vue/compiler-sfc来处理一些特定的任务,例如:
- 自定义SFC的编译过程:如果你需要自定义SFC的编译过程,比如修改模板编译的某些行为,你可能需要直接使用@vue/compiler-sfc来进行更底层的操作。
- 分析SFC的结构:@vue/compiler-sfc提供了将SFC解析为AST(抽象语法树)的能力,这对于需要进行SFC结构分析的场景(如代码高亮、代码格式化等)是非常有用的。
- 构建自定义工具链:如果你正在构建一个自定义的前端工具链,并希望在其中集成对SFC的支持,那么你可能需要直接使用@vue/compiler-sfc来处理SFC。
5、在npm(Node Package Manager)的版本号表示法中,^
和~
是两个常用的符号,用于指定依赖包的版本范围。在实际项目中,通常推荐使用^(
Caret)
,因为它允许你获取到同一主版本下的新功能和bug修复。然而,如果你的项目需要极高的稳定性,或者你希望严格控制依赖的版本更新,那么~(
Tilde)
可能是一个更好的选择(只接受次要更新,通常是bug修复)。
^(Caret)符号:
当版本号使用^
开头时,它表示接受当前指定的主版本号(major version)下的任何更新。例如,"vite": "^3.0.2"
意味着npm将接受任何3.x.x
版本的vite
,其中x
可以是任何数字,但主版本号(在这个例子中是3
)必须保持不变。这意味着,如果当前安装的版本是
3.0.2
,那么当3.0.3
、3.1.0
、3.2.5
等版本发布时,npm会尝试更新到这些版本,但不会更新到4.0.0
或更高的版本,因为这些版本的主版本号已经改变。
~
(Tilde)符号:~
符号则更为保守,它只接受当前指定的次版本号(minor version)下的任何更新,但主版本号和次版本号都必须保持不变。例如,"vite": "~3.0.2"
将只接受3.0.x
版本的更新,其中x
可以是任何数字,但主版本号(3
)和次版本号(0
)都不能改变。因此,3.0.3
、3.0.4
等版本是允许的,但3.1.0
或更高版本则不允许。- 指定具体版本号:例如
"vite": "3.0.2"
,这将只安装该确切版本,而不会接受任何更新。- 使用范围:例如,
"vite": ">=3.0.0 <4.0.0"
将匹配所有大于或等于3.0.0但小于4.0.0的版本
6、@vitejs/plugin-vue 是主要为 Vue 3 提供的。然而,对于 Vue 2,虽然没有官方的@vitejs/plugin-vue直接支持,但社区提供了如@vitejs/plugin-vue2 这样的插件来实现在 Vite 中对 Vue 2 的支持。