用 Vite 初始化一个 Vue 3 项目,跑起 dev server 一看浏览器标签页——挂着的还是默认的 Vite logo。修起来其实就藏在项目的 public/ 目录里,只要摸清 Vite 那几个特有的坑,三分钟左右就能搞定。
环境准备
一个跑在 Vite 上的 Vue 3 项目,加上准备放进去的 favicon 文件。如果还没准备,可以用 Mzu favicondl 一键生成完整的 favicon 套件(ICO、PNG、SVG),下下来就能直接用。不用折腾 webpack,也不用配 copy-webpack-plugin——Vite 默认就把 public 目录当静态资源处理,零配置。
第一步:打开 public/ 目录
每个 Vue + Vite 脚手架项目根目录都自带一个 public/ 目录。Vite 关于 public 目录的文档 明确说过:放在这里的任何文件都会原样拷贝到构建产物里,并以根路径的形式对外提供。
也就是说,public/favicon.ico 的访问地址是 /favicon.ico,而不是 /public/favicon.ico。分不清这两者的差别,是 Vue 项目上线后标签页图标失效最常见的原因,没有之一。
第二步:把 favicon 文件丢进去
把整包 favicon 文件直接放进 public/:
public/
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── site.webmanifest
└── favicon.svg
Vite 会把整个目录原样拷贝到 dist/——不用 import,也不经过任何转译。千万不要在 Vue 组件里 import 这些文件,它们的定位就是在打包产物之外。目前哪些尺寸真正有用,完整的拆解可以看我们的 Favicon 尺寸指南。
第三步:在 index.html 里加 link 标签
打开项目根目录下的 index.html——Vite 就是从这个文件挂载 Vue 应用的。在 <head> 里加上 link rel="icon" 声明:
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" href="/favicon-32x32.png" type="image/png" sizes="32x32" />
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
</head>
顺序很重要。浏览器会从前往后挑第一个支持的 rel="icon" 条目,所以把 SVG 放最前面保证矢量缩放效果,PNG 兜底放中间,老旧的 ICO 放最后。所有路径都以 / 开头,不要写成 /public/。
第四步:让 SVG 支持暗色模式
如果要做暗色模式切换,直接把媒体查询写在 SVG 文件里就行,没必要在 Vue 里搞响应式:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<style>
circle { fill: #1a1a1a; }
@media (prefers-color-scheme: dark) {
circle { fill: #fafafa; }
}
</style>
<circle cx="16" cy="16" r="14" />
</svg>
浏览器会自动切换 fill 颜色——标签页里不需要任何 watch() 调用,也不用生命周期钩子。想深入了解相关模式,可以看我们的 Favicon 暗色模式指南。
第五步:开发与生产环境分别验证
跑 npm run dev,确认三件事:
- DevTools → Network,过滤 "favicon",每个文件都返回
200 OK。 - 浏览器标签页显示的是你自己的图标,不是默认的 Vite logo。
curl http://localhost:5173/favicon.svg能拿到 SVG 的字节内容。
然后跑 npm run build,打开 dist/ 看看。public/ 里的每个文件都应该原样出现在 dist/ 根目录下。如果生产环境少了文件,大概率是托管平台的 rewrite 规则把路径吃掉了——查一下 vercel.json 或 _redirects。
常见坑
- 路径写错:写成了
/public/favicon.svg而不是/favicon.svg。Vite 会把/public/从访问 URL 里剥掉——文件永远解析不到。 - 放错文件夹:把文件丢进了
src/assets/而不是public/。被打包的资源需要import才能用,没法直接以/favicon.svg的形式渲染出来。 - 浏览器缓存顽固:浏览器对 ICO 缓存非常激进。用 Cmd/Ctrl + Shift + R 硬刷新,或者在图标 URL 后面加
?v=2这种查询参数。 - manifest 对不上:
site.webmanifest里引用的图标路径和public/里实际的文件名不一致。两边的文件名必须逐行核对。
收尾
Vue + Vite 的 public 目录流程,算是现代前端里最干净的 favicon 配置方式之一了。丢文件、在 index.html 里写好 link 标签,Vite 就把所有东西原封不动拷到 dist/。不用配插件,不用纠结 head 注入,Vue Router 也不会拦截你的请求——就是根路径下的静态资源。
用 Mzu favicondl 生成完整的 favicon 套件,把 link 标签粘到 index.html 里,你的 Vue 应用在每个浏览器的标签页里看着就规整了。