基于 Vue 3 + Vite + Tailwind CSS 的官网框架搭建步骤
一、搭建基础框架
nodejs版本:20.19.4
1.创建项目并安装依赖
pnpm create vue@latest
cd vue-demo
pnpm install -D tailwindcss@^3 postcss autoprefixer
npx tailwindcss init -p
2. 配置 Tailwind
- 修改 tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#4f46e5',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
},
},
plugins: [],
}
- 创建 src/assets/css/tailwind.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body { @apply text-gray-800 bg-gray-50; }
h1 { @apply text-[clamp(2rem,5vw,3.5rem)] font-bold; }
h2 { @apply text-[clamp(1.5rem,3vw,2.5rem)] font-semibold; }
}
3.配置路由
- 创建 src/router/index.js:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
// import About from '../views/About.vue'
// import Contact from '../views/Contact.vue'
export default createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
// { path: '/about', component: About },
// { path: '/contact', component: Contact }
]
})
4.入口文件配置
- 修改 src/main.ts:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './assets/css/tailwind.css'
const app = createApp(App)
app.use(router)
app.mount('#app')
- 若没有安装typescript,修改 src/main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './assets/css/tailwind.css'
createApp(App)
.use(router)
.mount('#app')
二、核心组件实现
1.根组件(src/App.vue)
<template>
<div class="min-h-screen flex flex-col">
<Navbar class="fixed top-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-sm shadow-sm" />
<main class="flex-grow pt-16">
<router-view />
</main>
<Footer class="bg-gray-900 text-white py-12" />
</div>
</template>
<script setup>
import Navbar from './components/common/Navbar.vue'
import Footer from './components/common/Footer.vue'
</script>
2. 导航栏组件(src/components/common/Navbar.vue)
<template>
<nav class="container mx-auto px-4 py-4 flex justify-between items-center">
<!-- Logo -->
<a href="/" class="flex items-center gap-2">
<div class="w-10 h-10 bg-primary rounded-md flex items-center justify-center">
<span class="text-white font-bold">C</span>
</div>
<span class="text-xl font-bold text-primary">Company</span>
</a>
<!-- 桌面菜单 -->
<ul class="hidden md:flex gap-8">
<li><router-link to="/" class="hover:text-primary transition-colors">首页</router-link></li>
<li><router-link to="/about" class="hover:text-primary transition-colors">关于我们</router-link></li>
<li><router-link to="/contact" class="hover:text-primary transition-colors">联系我们</router-link></li>
</ul>
<!-- 咨询按钮 -->
<button class="hidden md:block bg-primary hover:bg-primary/90 text-white px-6 py-2 rounded-md transition-colors">
立即咨询
</button>
<!-- 移动端菜单按钮 -->
<button class="md:hidden" @click="isMenuOpen = !isMenuOpen">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
</nav>
<!-- 移动端菜单 -->
<div v-if="isMenuOpen" class="md:hidden bg-white border-t p-4">
<ul class="flex flex-col gap-4">
<li><router-link to="/" class="block py-2">首页</router-link></li>
<li><router-link to="/about" class="block py-2">关于我们</router-link></li>
<li><router-link to="/contact" class="block py-2">联系我们</router-link></li>
<li><button class="w-full bg-primary text-white px-6 py-2 rounded-md">立即咨询</button></li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { RouterLink } from 'vue-router'
const isMenuOpen = ref(false)
</script>
3. 首页实现(src/views/Home.vue)
<template>
<div>
<!-- 英雄区 -->
<section class="relative h-[90vh] overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-r from-primary/90 to-secondary/90 z-10"></div>
<img
src="https://picsum.photos/id/1067/1920/1080"
alt="企业办公环境"
class="absolute inset-0 w-full h-full object-cover"
>
<div class="container mx-auto px-4 h-full flex items-center relative z-20">
<div class="max-w-2xl text-white">
<h1 class="mb-6 leading-tight">
创新解决方案<br>
驱动企业未来
</h1>
<p class="text-xl mb-8 text-white/90">
10年行业经验,为500+企业提供数字化转型服务
</p>
<div class="flex flex-col sm:flex-row gap-4">
<button class="bg-white text-primary hover:bg-gray-100 px-8 py-3 rounded-md font-medium transition-colors">
了解服务
</button>
<button class="bg-transparent border border-white text-white hover:bg-white/10 px-8 py-3 rounded-md font-medium transition-colors">
查看案例
</button>
</div>
</div>
</div>
</section>
<!-- 服务展示 -->
<section class="py-20">
<div class="container mx-auto px-4">
<div class="text-center mb-16">
<h2 class="mb-4">我们的服务</h2>
<p class="text-gray-600 max-w-2xl mx-auto">专业团队提供全方位企业解决方案</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- 服务卡片 1 -->
<div class="bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="w-14 h-14 bg-primary/10 text-primary rounded-full flex items-center justify-center mb-6">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
</div>
<h3 class="text-xl font-semibold mb-3">数字化转型</h3>
<p class="text-gray-600">帮助企业实现业务流程数字化,提升运营效率</p>
</div>
<!-- 服务卡片 2 -->
<div class="bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="w-14 h-14 bg-primary/10 text-primary rounded-full flex items-center justify-center mb-6">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
</div>
<h3 class="text-xl font-semibold mb-3">云服务部署</h3>
<p class="text-gray-600">提供云平台搭建与迁移服务,降低IT基础设施成本</p>
</div>
<!-- 服务卡片 3 -->
<div class="bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="w-14 h-14 bg-primary/10 text-primary rounded-full flex items-center justify-center mb-6">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
</svg>
</div>
<h3 class="text-xl font-semibold mb-3">数据分析</h3>
<p class="text-gray-600">通过大数据分析,为企业决策提供数据支持</p>
</div>
</div>
</div>
</section>
</div>
</template>
4. 页脚组件(src/components/common/Footer.vue)
<template>
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8 mb-12">
<div>
<h3 class="text-xl font-bold mb-4">Company</h3>
<p class="text-gray-400 mb-4">专注于企业数字化转型的专业服务提供商</p>
<div class="flex gap-4">
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fab fa-facebook"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fab fa-twitter"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fab fa-linkedin"></i>
</a>
</div>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">快速链接</h4>
<ul class="space-y-2">
<li><a href="/" class="text-gray-400 hover:text-white transition-colors">首页</a></li>
<li><a href="/about" class="text-gray-400 hover:text-white transition-colors">关于我们</a></li>
<li><a href="/contact" class="text-gray-400 hover:text-white transition-colors">联系我们</a></li>
</ul>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">服务</h4>
<ul class="space-y-2">
<li><a href="#" class="text-gray-400 hover:text-white transition-colors">数字化转型</a></li>
<li><a href="#" class="text-gray-400 hover:text-white transition-colors">云服务部署</a></li>
<li><a href="#" class="text-gray-400 hover:text-white transition-colors">数据分析</a></li>
</ul>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">联系我们</h4>
<ul class="space-y-2 text-gray-400">
<li>邮箱:contact@company.com</li>
<li>电话:400-123-4567</li>
<li>地址:北京市朝阳区XX大厦</li>
</ul>
</div>
</div>
<div class="border-t border-gray-800 pt-8 text-center text-gray-500">
<p>© 2023 Company. 保留所有权利</p>
</div>
</div>
</template>
<script setup>
// 引入Font Awesome图标库(需安装)
import '@fortawesome/fontawesome-free/css/all.min.css'
</script>
三、运行与预览
1.安装 Font Awesome 图标库(用于页脚社交图标):
pnpm install @fortawesome/fontawesome-free
2.启动开发服务器:
npm run dev
目录结构
company-website/
├─ public/ # 静态资源
├─ src/
│ ├─ assets/css/ # 样式文件
│ ├─ components/ # 组件
│ │ └─ common/ # 通用组件
│ ├─ views/ # 页面视图
│ ├─ router/ # 路由配置
│ ├─ App.vue # 根组件
│ └─ main.js # 入口文件
├─ tailwind.config.js # Tailwind配置
└─ vite.config.js # Vite配置
以下是基于 Vue 3 + Axios 的接口封装方案:
一、安装 Axios
pnpm install axios
二、创建接口封装文件
- 在 src/utils/ 目录下创建 request.js,封装 Axios 实例和通用方法:
// src/utils/request.js
import axios from 'axios'
// 创建 Axios 实例
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 基础地址(从环境变量获取)
timeout: 10000, // 超时时间
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 请求拦截器:添加 token、处理请求参数等
request.interceptors.request.use(
(config) => {
// 示例:添加认证 token(根据实际需求修改)
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
// 处理请求错误
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器:处理响应数据、统一错误提示等
request.interceptors.response.use(
(response) => {
// 假设后端返回格式为 { code: 200, data: ..., message: '' }
const res = response.data
// 非成功状态(根据实际后端约定调整)
if (res.code !== 200) {
// 错误提示(可结合 UI 库的消息组件,如 Element Plus 的 ElMessage)
console.error('接口错误:', res.message || '请求失败')
return Promise.reject(new Error(res.message || '请求失败'))
}
// 只返回数据部分,简化使用
return res.data
},
(error) => {
// 处理网络错误、超时等
console.error('响应错误:', error)
// 分类错误提示
if (error.message.includes('timeout')) {
console.error('请求超时,请稍后重试')
} else if (error.response) {
const status = error.response.status
switch (status) {
case 401:
console.error('未授权,请登录')
// 可跳转登录页
break
case 404:
console.error('接口不存在')
break
case 500:
console.error('服务器错误')
break
default:
console.error(`请求错误:${status}`)
}
} else {
console.error('网络错误,请检查网络连接')
}
return Promise.reject(error)
}
)
// 封装常用请求方法
export default {
// GET 请求
get(url, params = {}) {
return request({
url,
method: 'get',
params
})
},
// POST 请求
post(url, data = {}) {
return request({
url,
method: 'post',
data
})
},
// PUT 请求
put(url, data = {}) {
return request({
url,
method: 'put',
data
})
},
// DELETE 请求
delete(url, params = {}) {
return request({
url,
method: 'delete',
params
})
}
}
三、创建接口管理文件
- 按模块划分接口,在 src/api/ 目录下创建对应文件(如官网常用的 home.js、contact.js 等):
// src/api/home.js
import request from '@/utils/request'
// 获取首页轮播图数据
export const getBannerList = () => {
return request.get('/home/banner')
}
// 获取服务
export const getServiceList = () => {
return request.get('/home/service')
}
// 获取客户案例
export const getCaseList = (params) => {
return request.get('/home/cases', params)
}
// 提交联系表单
export const submitContactForm = (data) => {
return request.post('/contact/submit', data)
}
四、配置环境变量(可选)
- 在项目根目录创建 .env.development 和 .env.production 文件,区分开发 / 生产环境的接口地址:
# .env.development(开发环境)
VITE_API_BASE_URL = 'http://dev-api.company.com'
# .env.production(生产环境)
# VITE_API_BASE_URL = 'http://api.company.com'
五、在组件中使用
- 在 Vue 组件中引入接口方法并使用:
<!-- src/views/Home.vue -->
<template>
<div>
<!-- 轮播图区域 -->
<div v-if="banners.length">
<div v-for="banner in banners" :key="banner.id">
<img :src="banner.imageUrl" :alt="banner.title">
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { getBannerList, getServiceList } from '@/api/home'
// 轮播图数据
const banners = ref([])
// 服务列表数据
const services = ref([])
// 页面加载时获取数据
onMounted(async () => {
try {
// 并行请求多个接口
const [bannerData, serviceData] = await Promise.all([
getBannerList(),
getServiceList()
])
banners.value = bannerData
services.value = serviceData
} catch (error) {
console.error('获取首页数据失败:', error)
}
})
</script>
六、关键特性说明
- 统一基础配置:通过 Axios 实例设置基础地址、超时时间等,避免重复配置。
- 拦截器处理:
- 请求拦截器:统一添加认证信息(如 token)、处理参数格式。
- 响应拦截器:统一解析响应数据、处理错误(如网络错误、权限问题)。
- 模块化管理:按业务模块拆分接口,便于维护和查找。
- 环境区分:通过环境变量区分开发 / 生产接口地址,避免手动修改。
- 错误处理:集中处理各类错误,可结合 UI 库的消息组件(如 Element Plus 的 ElMessage)提供友好提示。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。