选型理由

之前做公司官网,有几个硬需求:

  • SEO 友好(需要搜索引擎收录)
  • 组件库好看但不要模板感
  • 能快速迭代,别被框架束缚
  • Vue 技术栈(团队主力方向)

对比了几套方案:

方案 SEO 自定义程度 开发效率
Vite + Vue SPA
Nuxt.js + Element Plus 低(模板感重)
Nuxt.js + shadcn-vue
Astro + Vue

选了 Nuxt.js + shadcn-vue。Nuxt 负责 SSR/SSG 和路由,shadcn-vue 提供无头组件(拿过来直接用但可以随便改),自由度拉满。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
my-site/
├── app.vue # 根布局
├── nuxt.config.ts # 核心配置
├── assets/css/main.css # 全局样式
├── components/
│ ├── ui/ # shadcn-vue 组件
│ │ ├── Button.vue
│ │ ├── Card.vue
│ │ └── ...
│ └── site/ # 业务组件
│ ├── HeroSection.vue
│ ├── FeatureGrid.vue
│ └── Footer.vue
├── pages/
│ ├── index.vue # 首页
│ ├── about.vue # 关于
│ └── products/[id].vue # 产品详情
└── server/
└── api/ # Nitro 服务端 API

shadcn-vue 的组件直接复制源码到 components/ui/,想改就改,没有「组件库覆盖不了设计稿」的烦恼。

关键实践

1. CSS 变量驱动主题

shadcn-vue 的核心是 CSS 变量,换主题只需要改几行变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 221 83% 53%;
--primary-foreground: 0 0% 100%;
--muted: 210 40% 96%;
--radius: 0.5rem;
}

.dark {
--background: 222 47% 11%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
}

不写死颜色值,全项目通过 hsl(var(--primary)) 引用。暗色模式一键切换。

2. SSR 下的客户端交互

Nuxt 默认 SSR,但像轮播图、动画库这种依赖 window 的组件需要特殊处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup>
// ClientOnly 包裹,只在浏览器端渲染
</script>

<template>
<ClientOnly>
<Swiper :modules="[Autoplay]" :slides-per-view="1">
<SwiperSlide v-for="item in banners" :key="item.id">
<img :src="item.image" :alt="item.title" />
</SwiperSlide>
</Swiper>
<template #fallback>
<div class="h-64 bg-muted animate-pulse rounded-lg" />
</template>
</ClientOnly>
</template>

#fallback 插槽提供 SSR 时的骨架屏,避免布局抖动。

3. SEO 元数据管理

Nuxt 的 useSeoMeta 直接搞定:

1
2
3
4
5
6
7
8
// pages/index.vue
useSeoMeta({
title: '公司名称 - 产品标语',
description: '官网描述,150 字以内',
ogTitle: '公司名称',
ogDescription: '分享时的描述',
ogImage: 'https://cdn.example.com/og-image.png',
})

不需要额外插件,Nuxt 内置的 SEO 能力已经很够用。

4. 静态生成 (SSG)

官网不需要服务端实时渲染,直接预渲染成静态文件:

1
2
3
4
5
6
7
8
9
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/', '/about', '/products'],
crawlLinks: true,
},
},
})

npx nuxt generate 之后产出纯静态 HTML,扔到 Nginx 上就行。

5. Tailwind CSS 的克制使用

shadcn-vue 基于 Tailwind,但官网页面容易写得全是 class,可读性差:

1
2
3
4
5
<!-- 不推荐:class 地狱 -->
<div class="flex flex-col items-center justify-center min-h-screen px-4 py-16 bg-gradient-to-b from-slate-50 to-white">

<!-- 推荐:抽成语义化组件 -->
<HeroSection />

页面级组件保持干净,工具类只在 components/ui/components/site/ 内部使用。

部署

CI/CD 流程:

1
2
3
4
5
# 本地构建
npx nuxt generate

# 产物在 .output/public/,直接上传服务器
scp -r .output/public/* user@server:/var/www/site/

配合 Nginx 配置 Gzip + 静态资源强缓存:

1
2
3
4
location /_nuxt/ {
expires 1y;
add_header Cache-Control "public, immutable";
}

感受

shadcn-vue 最大的价值不是组件多——是组件属于你。复制到项目里的代码,你想怎么改就怎么改,不会因为组件库升级导致 UI 崩掉。

Nuxt 4 的 DevTools 也比以前好用很多,热更新快,Vue DevTools 对接丝滑。

官网这种项目,选型对了就成功了一半。剩下的一半是把组件和样式管清楚。