Neovim nvim-lspconfig Vue语言服务器完整指南:4步跑通 vue_ls 与 vtsls 协同
【免费下载链接】nvim-lspconfigQuickstart configs for Nvim LSP项目地址: https://gitcode.com/GitHub_Trending/nv/nvim-lspconfig
如果你的 .vue 文件里 TypeScript 一直没有智能提示,这篇教程基于 nvim-lspconfig 项目,讲清 vue_ls 与 vtsls 两个语言服务器的分工与整合配置:按 4 个步骤操作后,你能在 Neovim 中拿到 Vue 与 TypeScript 的完整提示,Vue 2 项目也不再误报语法错误。
先搞懂协同模型:为什么 .vue 文件需要两个语言服务器
一个 .vue 单文件组件实际上是"三区文件":template 区是 HTML,style 区是 CSS 或预处理器,script 区是 TypeScript 或 JavaScript。三个区的语法体系完全不同,单一语言服务器很难全部吃下。vue_ls 从 v3.0.0 起取消了 takeover mode,不再接管整个文件,而是采用"混合模式":它只负责 HTML 与 CSS 部分,script 区的 TypeScript 能力则交给 TypeScript 生态来处理。
这就是 vtsls 成为必备组件的原因。vtsls 是一个 TypeScript 专用 LSP,加载@vue/typescript-plugin插件后,tsserver 能够理解 .vue 文件<script>块里的内容。两者的衔接写死在 lsp/vue_ls.lua 的on_init里:vue_ls 收到tsserver/request请求后,会在当前 buffer 中依次查找 ts_ls、vtsls、typescript-tools 客户端,通过typescript.tsserverRequest命令把请求转发过去,拿到结果再用tsserver/response通知写回给 vue_ls。整条链路要求两个服务器同时存活,缺一个就断。
可以把分工记成一句话:template 与 style 区归 vue_ls,script 区归 vtsls 加 Vue 插件,两者靠 tsserver 请求转发接力。理解了这条分工线,后面所有配置与排错都能对应上号。
动手前读配置:vue_ls.lua 与 vtsls.lua 的要点
正式操作前,花两分钟读一下 nvim-lspconfig 自带的两份预设,之后排错会快很多。
lsp/vue_ls.lua 的信息量不大,但每一行都有用:
| 字段 | 默认行为 | 对你的含义 |
|---|---|---|
| cmd | vue-language-server --stdio | 要安装的 npm 包是@vue/language-server,可执行文件名是vue-language-server |
| filetypes | vue | 只挂 .vue 文件,天然不与 vtsls 的默认文件类型冲突 |
| root_markers | package.json | 以 package.json 是否存在来定位项目根目录 |
| on_init | 注册 tsserver 请求转发器 | 自动把 TS 请求转发给 ts_ls/vtsls/typescript-tools,最多重试 10 次、间隔 100 毫秒,找不到 TS 客户端时会弹错误通知 |
lsp/vtsls.lua 的信息量更大,重点看四处:
- 默认 filetypes 只有
javascript、javascriptreact、typescript、typescriptreact,不包含vue,所以整合时必须手动扩展。 root_dir是自定义函数:用包管理器锁文件(package-lock.json、yarn.lock、pnpm-lock.yaml、bun.lock 等)识别项目根,找不到再退回.git目录。这使 vtsls 原生支持 monorepo——它会为你正在编辑的 package 自动找到对应的 tsconfig.json 或 jsconfig.json,无需为每个包起实例,省内存。- 自带 Deno 排除逻辑:文件附近的 deno.json、deno.lock 比项目锁文件更近时 vtsls 直接放弃启动。同样逻辑也意味着不建议 vtsls 与 ts_ls 同时启用。
- Vue 插件的集成点在
settings.vtsls.tsserver.globalPlugins,第 3 步会实际用到。
四步跑通:从安装到完整智能提示
第 1 步:安装语言服务器并验证路径
两个服务器都以 npm 包形式发布,一条命令装齐:
npm install -g @vue/language-server @vtsls/language-server安装完成后,vue-language-server与vtsls两个可执行文件应出现在 PATH 中。在 Neovim 里执行下面两行即可验证:输出路径(非空串)说明装好了;输出为空,多半是 npm 全局前缀不在 Neovim 进程的 PATH 里,需要先修正环境变量。
print(vim.fn.exepath('vue-language-server')) print(vim.fn.exepath('vtsls'))这个动作同时是排错的第一现场,后文会反复用到。
第 2 步:基础启用
在 init.lua(或任意配置入口)写两行,客户端就会按各自预设的文件类型自动挂载:
-- vue_ls 只挂 .vue 文件,接管 template 与 style 区 vim.lsp.enable('vue_ls') -- vtsls 挂 TS/JS 各文件类型 vim.lsp.enable('vtsls')此时打开一个 .vue 文件:template 区的 HTML 补全、style 区的样式提示已经由 vue_ls 提供。但<script setup>里只能得到编辑器层面的语法补全,不认你 tsconfig 里的类型与路径别名——这正是下一步要补上的缺口。
第 3 步:vtsls 深度整合 vue_ls
这一步做一件事:把 Vue 插件注册进 vtsls 的 tsserver,让 .vue 文件的 script 区由插件接管。插件本体位于@vue/language-server包的 node_modules 中,所以必须给它一个绝对路径:
-- location 指向 @vue/language-server 所在目录即可(mason 安装路径或 npm 全局目录) local vue_language_server_path = vim.fn.stdpath('data') .. '/mason/packages/vue-language-server/node_modules/@vue/language-server' local vue_plugin = { name = '@vue/typescript-plugin', location = vue_language_server_path, languages = { 'vue' }, -- 插件服务的文件类型,漏掉它 .vue 无提示 configNamespace = 'typescript', -- 从 typescript 命名空间读取配置 } vim.lsp.config('vtsls', { settings = { vtsls = { tsserver = { globalPlugins = { vue_plugin } }, }, }, -- 在默认基础上追加 vue,让 vtsls 直接挂到 .vue 缓冲区 filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' }, })两个细节最容易踩坑:其一,filetypes里写了 'vue' 也不能省languages,前者只决定 vtsls 挂哪些缓冲区,后者决定插件为谁服务,少了后者就会出现 .ts 正常、.vue 无提示的怪象;其二,如果不是用 mason 安装,把location改成 npm 全局 node_modules 下@vue/language-server的目录,指向含插件的目录即有效。
重启 Neovim 后,.vue 里的 TypeScript 代码具备补全、跳转定义与重命名能力,与 vue_ls 之间的接力由on_init自动完成,无需额外处理。
第 4 步:Vue 2 项目兼容处理
vue_ls 默认按 Vue 3 的编译器规则解析。Vue 2 项目需要显式声明目标版本,并确认项目依赖里装了 2.x 版本的@vue/compiler-sfc:
-- 项目仍是 Vue 2 时,为 vue_ls 追加目标版本声明 vim.lsp.config('vue_ls', { settings = { vue = { target = 2, -- 改用 Vue 2 编译器,不再以 Vue 3 规则误判语法 }, }, })如果设置target后报错换了形态(比如组合式 API 无法解析),通常是项目 node_modules 里的@vue/compiler-sfc版本与 2.x 不匹配,把它固定到对应 2.x 版本一般即可消除。
排错指南:3个高频问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| .vue 的 script 区无智能提示 | vtsls 未挂到 vue 文件类型,或 globalPlugins 配置不全(languages 缺 'vue'、location 指错目录) | 核对第 3 步的两个细节,再用:LspInfo确认 vtsls 客户端已附着 |
| 语言服务器启动失败,vue_ls 提示找不到 ts_ls/vtsls 客户端 | npm 包未装成功,或可执行文件不在 Neovim 的 PATH | 用vim.fn.exepath('vue-language-server')、vim.fn.exepath('vtsls')验证,为空则重装并检查 PATH |
| Vue 2 项目大面积误报语法错误 | vue_ls 默认走 Vue 3 编译器规则 | 设置settings.vue.target = 2,并核对@vue/compiler-sfc为 2.x |
第一行还有一个隐蔽场景:项目里没有 package.json 时,vue_ls 无法确定根目录,客户端根本不会挂载,表象同样是"没有提示"。在临时目录开发时放一个最小 package.json 即可恢复。
工程化优化:管理、按需加载与文件类型
用 mason.nvim 自动安装管理
每台机器手动执行npm install -g容易遗漏,交给 mason.nvim 统一托管更省心:
require('mason').setup() require('mason-lspconfig').setup({ ensure_installed = { 'vue_ls', 'vtsls' }, })装完后,第 3 步的location可以直接用 mason 的标准安装路径(vim.fn.stdpath('data') .. '/mason/packages/...'),不同环境不用改值。
FileType autocmd 按需加载
不做 Vue 开发时这两个服务器纯属浪费,把启动推迟到第一次打开 vue 文件的时刻:
vim.api.nvim_create_autocmd('FileType', { pattern = 'vue', callback = function() vim.lsp.enable('vue_ls') vim.lsp.enable('vtsls') end, })非 Vue 会话因此完全不产生这两个语言服务器进程,内存与启动时间都更省。
确保 .vue 文件类型识别
个别项目里扩展名被改名或识别规则被覆盖,filetype 落不到vue,两个服务器都不会挂载。显式登记映射可以兜底:
vim.filetype.add({ extension = { vue = 'vue', }, })怀疑此类问题时,在 .vue 缓冲区执行:set filetype?看一眼实际值即可确认。
总结
回顾全篇:vue_ls 负责 .vue 的 template 与 style 区,vtsls 加载@vue/typescript-plugin后负责 script 区,两者通过 tsserver 请求转发接力;安装并验证路径、两行启用、注册插件、声明 Vue 2 目标,四步走完,Neovim 中的 Vue 开发体验就齐了。卡住时先用vim.fn.exepath确认二进制在不在 PATH,再逐条比对本文的配置,多数问题都能收敛。
参考资料:
- nvim-lspconfig 全部配置列表:doc/configs.md
- vue_ls 预设配置:lsp/vue_ls.lua
- vtsls 预设配置:lsp/vtsls.lua
- vuejs/language-tools 官方文档中的 Neovim 配置章节,可查询最新兼容参数
【免费下载链接】nvim-lspconfigQuickstart configs for Nvim LSP项目地址: https://gitcode.com/GitHub_Trending/nv/nvim-lspconfig
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考