Neovim nvim-lspconfig Vue语言服务器完整指南:4步跑通 vue_ls 与 vtsls 协同
2026/9/12 17:11:32 网站建设 项目流程

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 的信息量不大,但每一行都有用:

字段默认行为对你的含义
cmdvue-language-server --stdio要安装的 npm 包是@vue/language-server,可执行文件名是vue-language-server
filetypesvue只挂 .vue 文件,天然不与 vtsls 的默认文件类型冲突
root_markerspackage.json以 package.json 是否存在来定位项目根目录
on_init注册 tsserver 请求转发器自动把 TS 请求转发给 ts_ls/vtsls/typescript-tools,最多重试 10 次、间隔 100 毫秒,找不到 TS 客户端时会弹错误通知

lsp/vtsls.lua 的信息量更大,重点看四处:

  • 默认 filetypes 只有javascriptjavascriptreacttypescripttypescriptreact,不包含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-servervtsls两个可执行文件应出现在 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 的 PATHvim.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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询