nvim-lspconfig 中 Vue 语言配置:vue_ls 与 vtsls 协同的完整指南
2026/9/16 19:32:43 网站建设 项目流程

nvim-lspconfig 中 Vue 语言配置:vue_ls 与 vtsls 协同的完整指南

【免费下载链接】nvim-lspconfigQuickstart configs for Nvim LSP项目地址: https://gitcode.com/GitHub_Trending/nv/nvim-lspconfig

在 Neovim 里打开一个.vue文件,模板里的样式提示正常,<script setup>里的 TypeScript 却没有任何补全——这是 nvim-lspconfig 用户用 Vue 项目时最常见的"半残"状态。本文讲清两件事:nvim-lspconfig 的vue_ls为什么必须和一个 TypeScript 服务器搭档工作,以及从安装到排错的完整落地路径。你可以直接拿到可复制的配置、每个关键字段的作用说明,和 4 个高频问题的自检方法。

一个服务器管不了整个 .vue 文件

Vue 单文件组件(SFC)里同时住着三类代码:<template>的 HTML、<style>的 CSS、<script>的 TypeScript。早期方案(volar 的 takeover 模式)让一个服务器接管整份文件的所有请求,简单但职责边界模糊。Vue 语言服务器 v3.0.0 起移除了 takeover 模式,改为"混合模式":

旧做法(takeover 模式)新做法(混合模式,v3.0.0 起)
架构单一服务器处理模板、样式、脚本全部请求vue_ls只管 CSS/HTML 部分,TS 请求转发给 tsserver
TypeScript 能力内嵌,能力跟随语言服务器版本vtsls(或 Neovim 内置ts_ls)提供,独立升级
配置成本一处配置两个服务器 + 一个 tsserver 插件
多语言混用容易和独立 TS 服务器冲突天然与 JS/TS 生态共存

理解这一点后,后面的配置都是顺理成章的:vue_ls遇到 TypeScript 请求时,自己不会处理,而是转发给 tsserver 通道。仓库里 lsp/vue_ls.lua 的文件头注释明确写着:需要vtsls配合@vue/typescript-plugin才能覆盖.vue文件里的 TypeScript。

从安装到跑通:三步完成配置

第一步:安装语言服务器

用 npm 全局安装两个包,Neovim 侧不需要额外依赖:

# vue_ls 的启动命令是 vue-language-server,vtsls 的是 vtsls npm install -g @vue/language-server @vtsls/language-server

第二步:先给一个最小可运行版本

最小配置只需 2 行,先让它跑起来:

-- 两个服务器缺一不可:vue_ls 管模板/样式,vtsls 管 TS vim.lsp.enable('vue_ls') vim.lsp.enable('vtsls')

此时打开.vue文件,<template>的补全和<script>的基础 TS 能力应该都能用。如果<script setup>里项目类型、组合式 API 的提示还是残缺,继续第三步。

第三步:完整版,接入 @vue/typescript-plugin

让 tsserver 认识 Vue 单文件组件,需要注册一个全局插件:

-- @vue/typescript-plugin 的宿主包路径。 -- 用 mason.nvim 安装时,Mason 默认把包装在 data 目录下 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', -- tsserver 按这个名字加载插件 location = vue_language_server_path, -- 必须是包的真实路径 languages = { 'vue' }, -- 关键:即使 filetypes 里有 vue 也要写这里 configNamespace = 'typescript', } vim.lsp.config('vtsls', { settings = { vtsls = { tsserver = { globalPlugins = { vue_plugin }, -- 插件在此注入 tsserver }, }, }, -- 默认只覆盖 js/ts 四种类型,补上 vue 才能接管 SFC filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' }, }) vim.lsp.enable('vue_ls') vim.lsp.enable('vtsls')

如果你没有用 mason,而是npm install -g,把vue_language_server_path改成@vue/language-server在 npm 全局目录下的实际路径即可(可用npm root -g查到前缀)。

拆开看内部:on_init 的转发逻辑

真正让这套配置"自动工作"的,是 lsp/vue_ls.lua 里的on_init钩子。逐字段拆解:

  • cmd = { 'vue-language-server', '--stdio' }:启动命令。作用是用 stdio 模式拉起官方语言服务器;一般不用改,除非你用了自定义安装路径,此时才需要补全可执行文件位置。
  • filetypes = { 'vue' }root_markers = { 'package.json' }:前者决定哪个文件触发服务器,后者决定它把项目根目录识别为哪里。monorepo 里根目录识别错了会导致它找不到正确的vue版本。
  • on_init里的typescriptHandler:核心逻辑。它监听tsserver/request通知,然后依次查找当前缓冲区上的ts_lsvtslstypescript-tools三个候选客户端,找到后调用typescript.tsserverRequest命令把请求转过去,再把结果通过tsserver/response回传给vue_ls。这里有个值得注意的细节:Neovim 0.11 内置的ts_ls排在第一位,如果你环境里已有内置 TS 服务器,vue_ls会优先转发给它。
  • 找不到任何 TS 客户端时的兜底:重试 10 次(每次间隔 100ms),仍失败则弹出Could not find ts_ls, vtsls, or typescript-tools lsp client...错误。这条提示本身就是最重要的排查线索。

对比 lsp/vtsls.lua 的root_dir函数:它用package-lock.jsonyarn.lockpnpm-lock.yamlbun.lockb等锁文件定位项目根,并主动排除 Deno 项目(检测到deno.json或更近的deno.lock就直接放弃)。所以 vtsls 和 ts_ls 二选一即可,不要同时启用两个

高频问题速查

现象一:<script setup>里补全残缺或报错

  • 根因@vue/typescript-plugin没有注册到 tsserver,典型是globalPlugins漏配、location路径错误,或languages里少了'vue'
  • 解法:核对第三步配置。自检::checkhealth vtsls确认服务器健康,再在.vue文件里跑:lua =vim.lsp.get_clients({ bufnr = 0, name = 'vtsls' }),应返回一个客户端对象而非空表。

现象二:弹出 "Could not find ts_ls, vtsls, or typescript-tools"

  • 根因vue_ls已启动,但缓冲区上没有任何 TypeScript 客户端。常见于只启用了vue_ls忘了vtsls,或 vtsls 因根目录判定失败没有附着。
  • 解法:确认vim.lsp.enable('vtsls')存在;项目根要有package.json和至少一种锁文件(vtsls 的root_dir靠它定位)。自检命令同上,另跑:checkhealth vtsls看 "Server is active" 状态。

现象三:服务器压根没启动

  • 根因:npm 包没装或不在 PATH 里,vue-language-server/vtsls可执行文件找不到。
  • 解法:终端里跑which vue-language-server && which vtsls;在 Neovim 里等价检查:
print(vim.fn.exepath('vue-language-server')) -- 非空路径才算装上 print(vim.fn.exepath('vtsls'))

现象四:Vue 2 项目行为异常

  • 根因vue_ls默认只支持 Vue 3 项目(见 lsp/vue_ls.lua 文件头注释),Vue 2 项目需要按官方 language-tools 文档做额外配置,核心是让语言服务器找到项目内的 Vue 2 编译器依赖。
  • 解法:先确认项目里装的是vue@2.x:lua =vim.fn.system('node -p "require(\'vue/package.json\').version"')。版本对但行为异常时,对照官方 README 的 Vue 2 章节补齐项目侧依赖,而不是改 Neovim 配置。

进阶:性能与工程化

以下两条针对多项目或大型仓库场景,小项目可跳过。

  1. monorepo 零额外配置:vtsls 本身支持 monorepo,会为每个包自动找到对应的tsconfig.json/jsconfig.json,无需为每个包起一个服务器实例。建议把统一版本的 TypeScript 放在 workspace 根,让 vtsls 只解析一次 TS 二进制路径。
  2. 按需加载(可选):只在打开 Vue 相关缓冲区时启用服务器,减少纯 TS/JS 项目里无关进程的占用:
vim.api.nvim_create_autocmd('FileType', { pattern = { 'vue', 'typescript', 'javascript', 'typescriptreact', 'javascriptreact' }, callback = function() if not vim.lsp.get_clients({ bufnr = 0, name = 'vtsls' })[1] then vim.lsp.enable('vtsls') if vim.bo.filetype == 'vue' then vim.lsp.enable('vue_ls') end end end, })
  1. 管理方式二选一:mason.nvim 适合统一管理路径(上文的stdpath('data')写法就是配合它),npm 全局安装则更直接。两者混用时注意location指向要和实际安装位置一致,否则插件静默失效。

小结

回顾主线:Vue 3.0.0 起语言服务器不再"包打天下",vue_ls负责模板与样式,TypeScript 能力通过on_init里的转发机制交给vtsls(或内置ts_ls);配置上的全部工作量,就是装好两个包、注册@vue/typescript-plugin、并保证languages里有'vue'。出问题先看:checkhealth vtsls和缓冲区客户端列表,80% 的故障在这一步现形。

延伸阅读:doc/configs.md 中vue_lsvtsls的完整条目,以及 lsp/vue_ls.lua、lsp/vtsls.lua 的源码注释。如果你的项目里还有别的语言组合(比如 Nuxt + Solidity 这种混搭),转发机制是怎么配合的,欢迎在讨论区留言交流你的配置。

【免费下载链接】nvim-lspconfigQuickstart configs for Nvim LSP项目地址: https://gitcode.com/GitHub_Trending/nv/nvim-lspconfig

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询