Appearance
第12章:企业级进阶实战(综合应用)
实战 3:企业官网
1. 项目架构设计(布局拆分、组件规划)
项目初始化:
bash
# 使用 npx nuxi init 命令创建项目
npx nuxi init enterprise-website
# 进入项目目录
cd enterprise-website
# 安装依赖
npm install
# 启动开发服务器
npm run dev目录结构设计:
enterprise-website/
├── assets/ # 静态资源
│ ├── css/ # 样式文件
│ ├── images/ # 图片资源
│ └── fonts/ # 字体文件
├── components/ # 组件
│ ├── ui/ # 通用 UI 组件
│ ├── layout/ # 布局组件
│ └── sections/ # 页面区块组件
├── composables/ # 组合式函数
├── layouts/ # 布局
├── pages/ # 页面
├── public/ # 公共静态资源
├── server/ # 服务器端代码
├── utils/ # 工具函数
├── nuxt.config.ts # 配置文件
└── package.json # 项目配置布局拆分:
- 默认布局:包含导航栏和页脚
- 首页布局:包含英雄区、产品展示、客户案例等区块
- 产品页布局:包含产品列表和详情
- 关于页布局:包含公司介绍、团队成员等
- 联系页布局:包含联系表单和地图
组件规划:
- UI 组件:按钮、卡片、表单、导航栏、页脚等
- 布局组件:容器、网格、分隔符等
- 区块组件:英雄区、产品展示、客户案例、团队介绍、联系表单等
2. 核心功能实现(导航栏、轮播图、产品展示、新闻动态)
创建导航栏组件:
vue
<!-- components/layout/Navbar.vue -->
<template>
<nav class="navbar" :class="{ 'scrolled': scrolled }">
<div class="container">
<div class="navbar-brand">
<NuxtLink to="/" class="logo">
<img src="/logo.png" alt="Company Logo" />
</NuxtLink>
</div>
<div class="navbar-menu">
<ul class="navbar-links">
<li><NuxtLink to="/" class="navbar-link">首页</NuxtLink></li>
<li><NuxtLink to="/products" class="navbar-link">产品</NuxtLink></li>
<li><NuxtLink to="/about" class="navbar-link">关于我们</NuxtLink></li>
<li><NuxtLink to="/news" class="navbar-link">新闻动态</NuxtLink></li>
<li><NuxtLink to="/contact" class="navbar-link">联系我们</NuxtLink></li>
</ul>
<button class="navbar-button">
免费咨询
</button>
</div>
<button class="navbar-toggle" @click="toggleMenu">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="mobile-menu" v-if="menuOpen">
<ul class="mobile-links">
<li><NuxtLink to="/" class="mobile-link" @click="menuOpen = false">首页</NuxtLink></li>
<li><NuxtLink to="/products" class="mobile-link" @click="menuOpen = false">产品</NuxtLink></li>
<li><NuxtLink to="/about" class="mobile-link" @click="menuOpen = false">关于我们</NuxtLink></li>
<li><NuxtLink to="/news" class="mobile-link" @click="menuOpen = false">新闻动态</NuxtLink></li>
<li><NuxtLink to="/contact" class="mobile-link" @click="menuOpen = false">联系我们</NuxtLink></li>
<li><button class="mobile-button">免费咨询</button></li>
</ul>
</div>
</nav>
</template>
<script setup>
const scrolled = ref(false)
const menuOpen = ref(false)
function toggleMenu() {
menuOpen.value = !menuOpen.value
}
function handleScroll() {
scrolled.value = window.scrollY > 50
}
onMounted(() => {
window.addEventListener('scroll', handleScroll)
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
</script>
<style scoped>
.navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
transition: all 0.3s ease;
padding: 1rem 0;
}
.navbar.scrolled {
background-color: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 0.5rem 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.navbar-brand .logo img {
height: 40px;
}
.navbar-menu {
display: flex;
align-items: center;
gap: 2rem;
}
.navbar-links {
display: flex;
gap: 2rem;
list-style: none;
}
.navbar-link {
color: #333;
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
.navbar-link:hover {
color: #007bff;
}
.navbar-button {
background-color: #007bff;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
.navbar-button:hover {
background-color: #0056b3;
}
.navbar-toggle {
display: none;
flex-direction: column;
gap: 0.3rem;
background: none;
border: none;
cursor: pointer;
}
.navbar-toggle span {
width: 24px;
height: 2px;
background-color: #333;
transition: all 0.3s ease;
}
.mobile-menu {
position: absolute;
top: 100%;
left: 0;
width: 100%;
background-color: white;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
padding: 1rem 0;
animation: slideDown 0.3s ease;
}
.mobile-links {
list-style: none;
display: flex;
flex-direction: column;
gap: 1rem;
padding: 0 1rem;
}
.mobile-link {
color: #333;
text-decoration: none;
font-weight: 500;
padding: 0.5rem 0;
transition: color 0.3s ease;
}
.mobile-link:hover {
color: #007bff;
}
.mobile-button {
background-color: #007bff;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
width: 100%;
margin-top: 0.5rem;
}
.mobile-button:hover {
background-color: #0056b3;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.navbar-menu {
display: none;
}
.navbar-toggle {
display: flex;
}
}
</style>创建轮播图组件:
vue
<!-- components/sections/HeroSlider.vue -->
<template>
<div class="hero-slider">
<div class="slider-container" ref="sliderContainer">
<div
v-for="(slide, index) in slides"
:key="index"
class="slide"
:class="{ active: currentSlide === index }"
:style="{ transform: `translateX(-${currentSlide * 100}%)` }"
>
<div class="slide-content">
<h1>{{ slide.title }}</h1>
<p>{{ slide.description }}</p>
<button class="cta-button">{{ slide.buttonText }}</button>
</div>
<div class="slide-image">
<img :src="slide.image" :alt="slide.title" />
</div>
</div>
</div>
<div class="slider-controls">
<button
class="control-button prev"
@click="prevSlide"
>
<span><</span>
</button>
<button
class="control-button next"
@click="nextSlide"
>
<span>></span>
</button>
</div>
<div class="slider-indicators">
<button
v-for="(slide, index) in slides"
:key="index"
class="indicator"
:class="{ active: currentSlide === index }"
@click="currentSlide = index"
></button>
</div>
</div>
</template>
<script setup>
const sliderContainer = ref(null)
const currentSlide = ref(0)
const slides = ref([
{
title: '创新科技解决方案',
description: '为企业提供全方位的技术支持和创新解决方案,助力企业数字化转型',
buttonText: '了解更多',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=modern%20technology%20solution%20business%20hero%20image&image_size=landscape_16_9'
},
{
title: '专业服务团队',
description: '拥有一支经验丰富的专业团队,为客户提供高质量的服务和支持',
buttonText: '联系我们',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=professional%20team%20working%20together%20business%20setting&image_size=landscape_16_9'
},
{
title: '客户成功案例',
description: '已为众多企业提供解决方案,帮助客户实现业务增长和数字化转型',
buttonText: '查看案例',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=business%20success%20case%20data%20visualization&image_size=landscape_16_9'
}
])
function nextSlide() {
currentSlide.value = (currentSlide.value + 1) % slides.value.length
}
function prevSlide() {
currentSlide.value = (currentSlide.value - 1 + slides.value.length) % slides.value.length
}
// 自动轮播
let interval = null
onMounted(() => {
interval = setInterval(nextSlide, 5000)
})
onUnmounted(() => {
if (interval) {
clearInterval(interval)
}
})
</script>
<style scoped>
.hero-slider {
position: relative;
height: 100vh;
overflow: hidden;
}
.slider-container {
display: flex;
height: 100%;
transition: transform 0.5s ease;
}
.slide {
flex: 0 0 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 2rem;
background-color: #f8f9fa;
}
.slide-content {
flex: 0 0 50%;
padding: 2rem;
}
.slide-content h1 {
font-size: 3rem;
font-weight: 700;
margin-bottom: 1rem;
color: #333;
}
.slide-content p {
font-size: 1.2rem;
margin-bottom: 2rem;
color: #666;
}
.cta-button {
background-color: #007bff;
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
.cta-button:hover {
background-color: #0056b3;
}
.slide-image {
flex: 0 0 50%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.slide-image img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
}
.slider-controls {
position: absolute;
top: 50%;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
padding: 0 2rem;
}
.control-button {
background-color: rgba(255, 255, 255, 0.8);
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.control-button:hover {
background-color: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}
.slider-indicators {
position: absolute;
bottom: 2rem;
left: 0;
right: 0;
display: flex;
justify-content: center;
gap: 0.5rem;
}
.indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
border: none;
cursor: pointer;
transition: all 0.3s ease;
}
.indicator.active {
background-color: white;
width: 30px;
border-radius: 5px;
}
@media (max-width: 768px) {
.slide {
flex-direction: column;
text-align: center;
padding: 2rem 1rem;
}
.slide-content {
flex: 0 0 100%;
padding: 1rem;
}
.slide-content h1 {
font-size: 2rem;
}
.slide-image {
flex: 0 0 100%;
height: 50%;
}
}
</style>创建产品展示组件:
vue
<!-- components/sections/ProductShowcase.vue -->
<template>
<section class="product-showcase">
<div class="container">
<div class="section-header">
<h2>Our Products</h2>
<p>Discover our range of innovative solutions designed to meet your business needs</p>
</div>
<div class="products-grid">
<div v-for="product in products" :key="product.id" class="product-card">
<div class="product-image">
<img :src="product.image" :alt="product.name" />
</div>
<div class="product-content">
<h3>{{ product.name }}</h3>
<p>{{ product.description }}</p>
<button class="product-button">Learn More</button>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup>
const products = ref([
{
id: 1,
name: 'Cloud Solutions',
description: 'Scalable cloud infrastructure and services for businesses of all sizes',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=cloud%20computing%20technology%20abstract%20concept&image_size=square'
},
{
id: 2,
name: 'Data Analytics',
description: 'Advanced data analytics tools to gain insights and make informed decisions',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=data%20analytics%20dashboard%20visualization&image_size=square'
},
{
id: 3,
name: 'Cybersecurity',
description: 'Comprehensive security solutions to protect your business from threats',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=cybersecurity%20digital%20protection%20shield&image_size=square'
},
{
id: 4,
name: 'AI Services',
description: 'Artificial intelligence solutions to automate and optimize business processes',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=artificial%20intelligence%20technology%20concept&image_size=square'
}
])
</script>
<style scoped>
.product-showcase {
padding: 4rem 0;
background-color: #f8f9fa;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: #333;
}
.section-header p {
font-size: 1.2rem;
color: #666;
max-width: 600px;
margin: 0 auto;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}
.product-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
}
.product-image {
height: 200px;
overflow: hidden;
}
.product-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.product-card:hover .product-image img {
transform: scale(1.05);
}
.product-content {
padding: 1.5rem;
}
.product-content h3 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
.product-content p {
color: #666;
margin-bottom: 1.5rem;
line-height: 1.6;
}
.product-button {
background-color: #007bff;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
.product-button:hover {
background-color: #0056b3;
}
@media (max-width: 768px) {
.section-header h2 {
font-size: 2rem;
}
.products-grid {
grid-template-columns: 1fr;
}
}
</style>创建新闻动态组件:
vue
<!-- components/sections/NewsSection.vue -->
<template>
<section class="news-section">
<div class="container">
<div class="section-header">
<h2>Latest News</h2>
<p>Stay updated with our latest news and events</p>
</div>
<div class="news-grid">
<div v-for="news in newsItems" :key="news.id" class="news-card">
<div class="news-image">
<img :src="news.image" :alt="news.title" />
</div>
<div class="news-content">
<span class="news-date">{{ news.date }}</span>
<h3>{{ news.title }}</h3>
<p>{{ news.excerpt }}</p>
<NuxtLink to="/news" class="news-link">Read More</NuxtLink>
</div>
</div>
</div>
<div class="section-footer">
<NuxtLink to="/news" class="view-all-button">View All News</NuxtLink>
</div>
</div>
</section>
</template>
<script setup>
const newsItems = ref([
{
id: 1,
title: 'Company Wins Innovation Award',
excerpt: 'Our company has been recognized for its innovative approach to technology solutions',
date: '2024-01-15',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=business%20award%20ceremony%20celebration&image_size=landscape_4_3'
},
{
id: 2,
title: 'New Product Launch',
excerpt: 'We are excited to announce the launch of our new cloud-based solution',
date: '2024-01-10',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=product%20launch%20event%20technology&image_size=landscape_4_3'
},
{
id: 3,
title: 'Partnership with Global Tech Giant',
excerpt: 'We have formed a strategic partnership with a leading global technology company',
date: '2024-01-05',
image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=business%20partnership%20agreement%20handshake&image_size=landscape_4_3'
}
])
</script>
<style scoped>
.news-section {
padding: 4rem 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: #333;
}
.section-header p {
font-size: 1.2rem;
color: #666;
max-width: 600px;
margin: 0 auto;
}
.news-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.news-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.news-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
}
.news-image {
height: 200px;
overflow: hidden;
}
.news-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.news-card:hover .news-image img {
transform: scale(1.05);
}
.news-content {
padding: 1.5rem;
}
.news-date {
display: block;
font-size: 0.9rem;
color: #999;
margin-bottom: 1rem;
}
.news-content h3 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
.news-content p {
color: #666;
margin-bottom: 1.5rem;
line-height: 1.6;
}
.news-link {
color: #007bff;
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
.news-link:hover {
color: #0056b3;
text-decoration: underline;
}
.section-footer {
text-align: center;
margin-top: 2rem;
}
.view-all-button {
display: inline-block;
background-color: #007bff;
color: white;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-weight: 500;
text-decoration: none;
transition: background-color 0.3s ease;
}
.view-all-button:hover {
background-color: #0056b3;
}
@media (max-width: 768px) {
.section-header h2 {
font-size: 2rem;
}
.news-grid {
grid-template-columns: 1fr;
}
}
</style>3. SEO 优化(元标签配置、SSR 渲染、结构化数据)
使用 useHead 配置元标签:
vue
<!-- pages/index.vue -->
<template>
<div>
<HeroSlider />
<ProductShowcase />
<NewsSection />
</div>
</template>
<script setup>
useHead({
title: '企业官网 - 创新科技解决方案',
meta: [
{ name: 'description', content: '为企业提供全方位的技术支持和创新解决方案,助力企业数字化转型' },
{ name: 'keywords', content: '科技,解决方案,数字化转型,企业服务' },
{ name: 'robots', content: 'index, follow' },
{ property: 'og:title', content: '企业官网 - 创新科技解决方案' },
{ property: 'og:description', content: '为企业提供全方位的技术支持和创新解决方案,助力企业数字化转型' },
{ property: 'og:type', content: 'website' },
{ property: 'og:url', content: 'https://example.com' },
{ property: 'og:image', content: 'https://example.com/og-image.jpg' }
],
link: [
{ rel: 'canonical', href: 'https://example.com' }
]
})
</script>添加结构化数据:
vue
<!-- components/layout/StructuredData.vue -->
<template>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Company Name",
"url": "https://example.com",
"logo": "https://example.com/logo.png",
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+86-123-4567-8910",
"contactType": "customer service"
},
"sameAs": [
"https://www.facebook.com/company",
"https://twitter.com/company",
"https://www.linkedin.com/company/company"
]
}
</script>
</template>
<script setup>
// 内容
</script>在布局中使用:
vue
<!-- layouts/default.vue -->
<template>
<div>
<Navbar />
<StructuredData />
<main>
<slot />
</main>
<Footer />
</div>
</template>
<script setup>
// 内容
</script>4. 性能优化(图片懒加载、组件懒加载、资源压缩)
图片懒加载:
vue
<template>
<img
v-lazy="imageUrl"
:alt="altText"
class="lazy-image"
/>
</template>
<script setup>
defineProps({
imageUrl: {
type: String,
required: true
},
altText: {
type: String,
default: ''
}
})
</script>
<style scoped>
.lazy-image {
width: 100%;
height: auto;
transition: opacity 0.3s ease;
}
.lazy-image:not([src]) {
opacity: 0;
}
</style>组件懒加载:
vue
<template>
<div>
<h2>Our Team</h2>
<LazyTeamMembers />
</div>
</template>
<script setup>
const LazyTeamMembers = defineAsyncComponent(() => import('~/components/sections/TeamMembers.vue'))
</script>资源压缩:
在 nuxt.config.ts 中配置:
typescript
export default defineNuxtConfig({
build: {
terser: {
compress: {
drop_console: process.env.NODE_ENV === 'production'
}
}
},
vite: {
build: {
minify: 'terser',
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue'],
'nuxt-core': ['@nuxt/kit', '@nuxt/schema'],
'nuxt-utils': ['@nuxt/utils']
}
}
}
}
}
})5. 部署上线(服务器部署、静态站点部署、CDN 配置)
服务器部署:
构建项目:
bashnpm run build部署到服务器:
- 将
dist目录上传到服务器 - 使用 PM2 管理进程:bash
npm install -g pm2 pm2 start npm --name "enterprise-website" -- run start
- 将
Nginx 配置:
nginxserver { listen 80; server_name example.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }
静态站点部署:
生成静态站点:
bashnpm run generate部署到 GitHub Pages:
- 将
dist目录推送到 GitHub 仓库的gh-pages分支 - 在 GitHub 仓库设置中启用 GitHub Pages
- 将
部署到 Netlify:
- 连接 GitHub 仓库
- 配置构建命令:
npm run generate - 配置发布目录:
dist
CDN 配置:
使用 Cloudflare CDN:
- 将域名的 DNS 解析指向 Cloudflare
- 在 Cloudflare 控制面板中启用 CDN
配置缓存策略:
nginxlocation ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires 30d; add_header Cache-Control "public, max-age=2592000"; }
小结
本章介绍了企业官网的实战案例,包括项目架构设计、核心功能实现、SEO 优化、性能优化和部署上线等内容。通过这个实战项目,你应该已经掌握了如何使用 Nuxt.js 构建企业级应用,包括:
- 项目架构设计:合理的目录结构和组件规划
- 核心功能实现:导航栏、轮播图、产品展示、新闻动态等
- SEO 优化:元标签配置、SSR 渲染、结构化数据
- 性能优化:图片懒加载、组件懒加载、资源压缩
- 部署上线:服务器部署、静态站点部署、CDN 配置
这些内容涵盖了企业级应用开发的各个方面,通过实践,你应该能够更深入地理解和使用 Nuxt.js 构建各种类型的企业应用。
在接下来的章节中,我们将学习 Nuxt.js 的进阶配置与优化、部署与上线、常见问题与面试题等内容,帮助你进一步提升 Nuxt.js 开发技能。
