公司官网
使用 VitePress 构建专业的公司官网,全面展示企业形象、核心业务、品牌价值和发展历程。
项目概述
公司官网是企业数字化形象的核心载体,承载着品牌传播、业务展示、客户获取等重要功能。VitePress 凭借其现代化的技术架构和灵活的定制能力,为企业官网建设提供了理想的解决方案。
核心特性
- 🎨 品牌展示 - 专业的视觉设计和品牌形象展示
- 🏢 企业介绍 - 全面的公司信息和发展历程
- 💼 业务展示 - 详细的产品服务和解决方案
- 📈 成功案例 - 客户案例和项目展示
- 👥 团队风采 - 企业文化和团队介绍
- 📰 新闻资讯 - 企业动态和行业资讯
- 📞 联系我们 - 多渠道联系方式和在线咨询
- 🌍 全球化 - 多语言和多地区支持
技术架构
核心技术栈
json
{
"framework": "VitePress",
"language": "TypeScript",
"styling": "SCSS + CSS Modules",
"components": "Vue 3 Composition API",
"animations": "GSAP + Lottie",
"tools": [
"Intersection Observer",
"Swiper.js",
"Chart.js",
"Three.js"
]
}
项目结构
company-website/
├── docs/
│ ├── .vitepress/
│ │ ├── config.ts
│ │ ├── theme/
│ │ │ ├── index.ts
│ │ │ ├── Layout.vue
│ │ │ ├── components/
│ │ │ │ ├── Hero/
│ │ │ │ ├── About/
│ │ │ │ ├── Services/
│ │ │ │ ├── Cases/
│ │ │ │ └── Contact/
│ │ │ └── styles/
│ │ └── public/
│ │ ├── images/
│ │ ├── videos/
│ │ └── animations/
│ ├── about/
│ ├── services/
│ ├── cases/
│ ├── news/
│ ├── careers/
│ └── contact/
└── package.json
实现步骤
1. 项目初始化
bash
# 创建项目
npm create vitepress@latest company-website
cd company-website
# 安装依赖
npm install
npm install -D sass gsap lottie-web swiper chart.js three
2. 主题配置
typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
title: '创新科技集团',
description: '引领行业创新,创造无限可能',
head: [
['link', { rel: 'icon', href: '/favicon.ico' }],
['meta', { name: 'theme-color', content: '#3b82f6' }],
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:locale', content: 'zh_CN' }],
['meta', { property: 'og:site_name', content: '创新科技集团' }],
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
['link', { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap' }]
],
themeConfig: {
logo: {
light: '/logo-light.svg',
dark: '/logo-dark.svg'
},
nav: [
{ text: '首页', link: '/' },
{
text: '关于我们',
items: [
{ text: '公司简介', link: '/about/' },
{ text: '发展历程', link: '/about/history' },
{ text: '企业文化', link: '/about/culture' },
{ text: '资质荣誉', link: '/about/honors' }
]
},
{
text: '产品服务',
items: [
{ text: '解决方案', link: '/services/' },
{ text: '产品中心', link: '/services/products' },
{ text: '技术支持', link: '/services/support' }
]
},
{ text: '成功案例', link: '/cases/' },
{ text: '新闻中心', link: '/news/' },
{ text: '人才招聘', link: '/careers/' },
{ text: '联系我们', link: '/contact/' }
],
footer: {
message: '© 2024 创新科技集团 版权所有',
copyright: '京ICP备12345678号-1 | 京公网安备11010802012345号'
}
},
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
}
})
3. 首页英雄区域
vue
<!-- .vitepress/theme/components/Hero/HeroSection.vue -->
<template>
<section class="hero-section" ref="heroRef">
<div class="hero-background">
<canvas ref="canvasRef" class="hero-canvas"></canvas>
<div class="hero-overlay"></div>
</div>
<div class="hero-content">
<div class="container">
<div class="hero-text">
<h1 class="hero-title" ref="titleRef">
<span class="title-line">引领行业创新</span>
<span class="title-line highlight">创造无限可能</span>
</h1>
<p class="hero-subtitle" ref="subtitleRef">
我们致力于通过前沿技术和创新思维,为客户提供卓越的解决方案,
推动行业发展,共创美好未来。
</p>
<div class="hero-actions" ref="actionsRef">
<a href="/services/" class="btn btn-primary">
<span>了解服务</span>
<i class="icon-arrow"></i>
</a>
<a href="/contact/" class="btn btn-outline">
<span>联系我们</span>
</a>
</div>
</div>
<div class="hero-stats" ref="statsRef">
<div class="stat-item" v-for="stat in stats" :key="stat.label">
<div class="stat-number" :data-target="stat.value">0</div>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
</div>
</div>
<div class="scroll-indicator" @click="scrollToNext">
<div class="scroll-mouse">
<div class="scroll-wheel"></div>
</div>
<span>向下滚动</span>
</div>
</section>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { gsap } from 'gsap'
import * as THREE from 'three'
const heroRef = ref()
const canvasRef = ref()
const titleRef = ref()
const subtitleRef = ref()
const actionsRef = ref()
const statsRef = ref()
const stats = ref([
{ label: '服务客户', value: 500, suffix: '+' },
{ label: '成功项目', value: 1200, suffix: '+' },
{ label: '团队成员', value: 200, suffix: '+' },
{ label: '行业经验', value: 15, suffix: '年' }
])
let scene, camera, renderer, particles
onMounted(() => {
initThreeJS()
initAnimations()
initCounters()
})
onUnmounted(() => {
if (renderer) {
renderer.dispose()
}
})
function initThreeJS() {
// 创建 Three.js 场景
scene = new THREE.Scene()
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value, alpha: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
// 创建粒子系统
const geometry = new THREE.BufferGeometry()
const positions = new Float32Array(1000 * 3)
for (let i = 0; i < 1000; i++) {
positions[i * 3] = (Math.random() - 0.5) * 100
positions[i * 3 + 1] = (Math.random() - 0.5) * 100
positions[i * 3 + 2] = (Math.random() - 0.5) * 100
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
const material = new THREE.PointsMaterial({
color: 0x3b82f6,
size: 2,
transparent: true,
opacity: 0.6
})
particles = new THREE.Points(geometry, material)
scene.add(particles)
camera.position.z = 50
animate()
}
function animate() {
requestAnimationFrame(animate)
if (particles) {
particles.rotation.x += 0.001
particles.rotation.y += 0.002
}
renderer.render(scene, camera)
}
function initAnimations() {
const tl = gsap.timeline()
tl.from(titleRef.value.querySelectorAll('.title-line'), {
duration: 1,
y: 100,
opacity: 0,
stagger: 0.2,
ease: 'power3.out'
})
.from(subtitleRef.value, {
duration: 0.8,
y: 50,
opacity: 0,
ease: 'power2.out'
}, '-=0.5')
.from(actionsRef.value.children, {
duration: 0.6,
y: 30,
opacity: 0,
stagger: 0.1,
ease: 'back.out(1.7)'
}, '-=0.3')
.from(statsRef.value.children, {
duration: 0.8,
y: 40,
opacity: 0,
stagger: 0.1,
ease: 'power2.out'
}, '-=0.4')
}
function initCounters() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCounters()
observer.disconnect()
}
})
})
observer.observe(statsRef.value)
}
function animateCounters() {
statsRef.value.querySelectorAll('.stat-number').forEach((el, index) => {
const target = parseInt(el.dataset.target)
const suffix = stats.value[index].suffix
gsap.to({ value: 0 }, {
duration: 2,
value: target,
ease: 'power2.out',
onUpdate: function() {
el.textContent = Math.round(this.targets()[0].value) + suffix
}
})
})
}
function scrollToNext() {
const nextSection = document.querySelector('.about-section')
if (nextSection) {
nextSection.scrollIntoView({ behavior: 'smooth' })
}
}
</script>
<style lang="scss" scoped>
.hero-section {
position: relative;
height: 100vh;
min-height: 800px;
display: flex;
align-items: center;
overflow: hidden;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.hero-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.hero-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.hero-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
}
.hero-content {
position: relative;
z-index: 2;
width: 100%;
color: white;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
display: grid;
grid-template-columns: 2fr 1fr;
gap: 60px;
align-items: center;
}
.hero-text {
max-width: 600px;
}
.hero-title {
font-size: 4rem;
font-weight: 800;
line-height: 1.1;
margin-bottom: 2rem;
.title-line {
display: block;
overflow: hidden;
}
.highlight {
background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
.hero-subtitle {
font-size: 1.25rem;
line-height: 1.7;
margin-bottom: 3rem;
opacity: 0.9;
}
.hero-actions {
display: flex;
gap: 1.5rem;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 1rem 2rem;
border-radius: 50px;
font-weight: 600;
text-decoration: none;
transition: all 0.3s ease;
border: 2px solid transparent;
&.btn-primary {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
color: white;
&:hover {
transform: translateY(-3px);
box-shadow: 0 20px 40px rgba(59, 130, 246, 0.4);
}
}
&.btn-outline {
background: transparent;
color: white;
border-color: rgba(255, 255, 255, 0.5);
&:hover {
background: white;
color: #1f2937;
border-color: white;
}
}
}
.hero-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.stat-item {
text-align: center;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 16px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.stat-number {
font-size: 2.5rem;
font-weight: 800;
color: #fbbf24;
margin-bottom: 0.5rem;
}
.stat-label {
font-size: 0.9rem;
opacity: 0.8;
}
.scroll-indicator {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: white;
transform: translateX(-50%) translateY(-5px);
}
}
.scroll-mouse {
width: 24px;
height: 40px;
border: 2px solid currentColor;
border-radius: 12px;
position: relative;
}
.scroll-wheel {
width: 4px;
height: 8px;
background: currentColor;
border-radius: 2px;
position: absolute;
top: 6px;
left: 50%;
transform: translateX(-50%);
animation: scroll 2s infinite;
}
@keyframes scroll {
0% { opacity: 0; transform: translateX(-50%) translateY(0); }
50% { opacity: 1; }
100% { opacity: 0; transform: translateX(-50%) translateY(12px); }
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
gap: 3rem;
text-align: center;
}
.hero-title {
font-size: 2.5rem;
}
.hero-subtitle {
font-size: 1.1rem;
}
.hero-actions {
justify-content: center;
}
.hero-stats {
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
}
</style>
4. 关于我们组件
vue
<!-- .vitepress/theme/components/About/AboutSection.vue -->
<template>
<section class="about-section" ref="sectionRef">
<div class="container">
<div class="section-header">
<span class="section-tag">关于我们</span>
<h2 class="section-title">专业团队,卓越服务</h2>
<p class="section-subtitle">
15年行业深耕,我们始终坚持以客户为中心,以创新为驱动,
为全球客户提供专业的技术解决方案。
</p>
</div>
<div class="about-content">
<div class="about-text">
<div class="text-block" v-for="(block, index) in textBlocks" :key="index">
<h3>{{ block.title }}</h3>
<p>{{ block.content }}</p>
</div>
<div class="about-features">
<div class="feature-item" v-for="feature in features" :key="feature.title">
<div class="feature-icon">
<i :class="feature.icon"></i>
</div>
<div class="feature-content">
<h4>{{ feature.title }}</h4>
<p>{{ feature.description }}</p>
</div>
</div>
</div>
</div>
<div class="about-visual">
<div class="visual-container">
<div class="timeline">
<div class="timeline-item" v-for="(item, index) in timeline" :key="index">
<div class="timeline-year">{{ item.year }}</div>
<div class="timeline-content">
<h4>{{ item.title }}</h4>
<p>{{ item.description }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
const sectionRef = ref()
const textBlocks = ref([
{
title: '我们的使命',
content: '通过技术创新推动行业进步,为客户创造价值,为社会贡献力量。我们相信技术的力量能够改变世界,让生活更美好。'
},
{
title: '我们的愿景',
content: '成为全球领先的技术解决方案提供商,以卓越的产品和服务赢得客户信任,推动数字化转型的浪潮。'
}
])
const features = ref([
{
icon: 'icon-innovation',
title: '技术创新',
description: '持续投入研发,保持技术领先优势'
},
{
icon: 'icon-quality',
title: '品质保证',
description: '严格的质量管控,确保产品可靠性'
},
{
icon: 'icon-service',
title: '专业服务',
description: '7×24小时技术支持,快速响应'
},
{
icon: 'icon-partnership',
title: '合作共赢',
description: '与客户建立长期战略合作关系'
}
])
const timeline = ref([
{
year: '2009',
title: '公司成立',
description: '在北京成立,专注于企业级软件开发'
},
{
year: '2012',
title: '业务扩展',
description: '拓展云计算和大数据业务领域'
},
{
year: '2016',
title: '技术突破',
description: '推出自主研发的核心技术平台'
},
{
year: '2020',
title: '国际化',
description: '业务拓展至海外市场,服务全球客户'
},
{
year: '2024',
title: 'AI赋能',
description: '全面拥抱人工智能,引领行业变革'
}
])
onMounted(() => {
initAnimations()
})
function initAnimations() {
gsap.fromTo('.section-header > *',
{ y: 50, opacity: 0 },
{
y: 0,
opacity: 1,
duration: 0.8,
stagger: 0.2,
ease: 'power2.out',
scrollTrigger: {
trigger: sectionRef.value,
start: 'top 80%'
}
}
)
gsap.fromTo('.text-block',
{ x: -50, opacity: 0 },
{
x: 0,
opacity: 1,
duration: 0.8,
stagger: 0.3,
ease: 'power2.out',
scrollTrigger: {
trigger: '.about-text',
start: 'top 80%'
}
}
)
gsap.fromTo('.feature-item',
{ y: 30, opacity: 0 },
{
y: 0,
opacity: 1,
duration: 0.6,
stagger: 0.1,
ease: 'back.out(1.7)',
scrollTrigger: {
trigger: '.about-features',
start: 'top 80%'
}
}
)
gsap.fromTo('.timeline-item',
{ x: 50, opacity: 0 },
{
x: 0,
opacity: 1,
duration: 0.8,
stagger: 0.2,
ease: 'power2.out',
scrollTrigger: {
trigger: '.timeline',
start: 'top 80%'
}
}
)
}
</script>
<style lang="scss" scoped>
.about-section {
padding: 120px 0;
background: #f8fafc;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.section-header {
text-align: center;
margin-bottom: 80px;
}
.section-tag {
display: inline-block;
padding: 8px 20px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
color: white;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 1rem;
}
.section-title {
font-size: 3rem;
font-weight: 800;
color: #1f2937;
margin-bottom: 1.5rem;
line-height: 1.2;
}
.section-subtitle {
font-size: 1.2rem;
color: #6b7280;
line-height: 1.7;
max-width: 600px;
margin: 0 auto;
}
.about-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: start;
}
.text-block {
margin-bottom: 3rem;
h3 {
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 1rem;
}
p {
color: #6b7280;
line-height: 1.7;
font-size: 1.1rem;
}
}
.about-features {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.feature-item {
display: flex;
gap: 1rem;
padding: 1.5rem;
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
}
.feature-icon {
flex-shrink: 0;
width: 48px;
height: 48px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
}
.feature-content {
h4 {
font-size: 1.1rem;
font-weight: 600;
color: #1f2937;
margin-bottom: 0.5rem;
}
p {
color: #6b7280;
font-size: 0.9rem;
line-height: 1.5;
}
}
.visual-container {
position: relative;
background: white;
border-radius: 20px;
padding: 3rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.timeline {
position: relative;
&::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, #3b82f6, #1d4ed8);
}
}
.timeline-item {
position: relative;
padding-left: 60px;
margin-bottom: 2.5rem;
&::before {
content: '';
position: absolute;
left: 11px;
top: 8px;
width: 18px;
height: 18px;
background: #3b82f6;
border-radius: 50%;
border: 4px solid white;
box-shadow: 0 0 0 4px #3b82f6;
}
&:last-child {
margin-bottom: 0;
}
}
.timeline-year {
font-size: 1.2rem;
font-weight: 800;
color: #3b82f6;
margin-bottom: 0.5rem;
}
.timeline-content {
h4 {
font-size: 1.1rem;
font-weight: 600;
color: #1f2937;
margin-bottom: 0.5rem;
}
p {
color: #6b7280;
font-size: 0.9rem;
line-height: 1.5;
}
}
@media (max-width: 768px) {
.about-content {
grid-template-columns: 1fr;
gap: 3rem;
}
.section-title {
font-size: 2rem;
}
.about-features {
grid-template-columns: 1fr;
}
.visual-container {
padding: 2rem;
}
}
</style>
高级功能
1. 数据可视化
vue
<!-- .vitepress/theme/components/Charts/BusinessChart.vue -->
<template>
<div class="chart-container">
<canvas ref="chartRef"></canvas>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Chart from 'chart.js/auto'
const chartRef = ref()
const props = defineProps({
type: {
type: String,
default: 'line'
},
data: {
type: Object,
required: true
},
options: {
type: Object,
default: () => ({})
}
})
onMounted(() => {
new Chart(chartRef.value, {
type: props.type,
data: props.data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
},
...props.options
}
})
})
</script>
<style scoped>
.chart-container {
position: relative;
height: 400px;
width: 100%;
}
</style>
2. 3D 展示效果
vue
<!-- .vitepress/theme/components/3D/ProductShowcase.vue -->
<template>
<div class="showcase-container" ref="containerRef">
<canvas ref="canvasRef"></canvas>
<div class="showcase-controls">
<button @click="rotateLeft">向左旋转</button>
<button @click="rotateRight">向右旋转</button>
<button @click="resetView">重置视角</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
const containerRef = ref()
const canvasRef = ref()
let scene, camera, renderer, model, controls
onMounted(() => {
initThreeJS()
loadModel()
animate()
})
onUnmounted(() => {
if (renderer) {
renderer.dispose()
}
})
function initThreeJS() {
scene = new THREE.Scene()
scene.background = new THREE.Color(0xf0f0f0)
camera = new THREE.PerspectiveCamera(
75,
containerRef.value.clientWidth / containerRef.value.clientHeight,
0.1,
1000
)
renderer = new THREE.WebGLRenderer({
canvas: canvasRef.value,
antialias: true
})
renderer.setSize(
containerRef.value.clientWidth,
containerRef.value.clientHeight
)
// 添加光源
const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
directionalLight.position.set(1, 1, 1)
scene.add(directionalLight)
camera.position.z = 5
}
function loadModel() {
// 创建一个简单的几何体作为示例
const geometry = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshPhongMaterial({
color: 0x3b82f6,
shininess: 100
})
model = new THREE.Mesh(geometry, material)
scene.add(model)
}
function animate() {
requestAnimationFrame(animate)
if (model) {
model.rotation.y += 0.005
}
renderer.render(scene, camera)
}
function rotateLeft() {
if (model) {
model.rotation.y -= 0.2
}
}
function rotateRight() {
if (model) {
model.rotation.y += 0.2
}
}
function resetView() {
if (model) {
model.rotation.set(0, 0, 0)
}
camera.position.set(0, 0, 5)
}
</script>
<style scoped>
.showcase-container {
position: relative;
width: 100%;
height: 500px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.showcase-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.showcase-controls button {
padding: 8px 16px;
background: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
.showcase-controls button:hover {
background: white;
transform: translateY(-2px);
}
</style>
3. 动画效果库
vue
<!-- .vitepress/theme/components/Animations/LottiePlayer.vue -->
<template>
<div ref="animationRef" class="lottie-container"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import lottie from 'lottie-web'
const animationRef = ref()
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
}
})
let animation
onMounted(() => {
animation = lottie.loadAnimation({
container: animationRef.value,
renderer: 'svg',
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData
})
})
onUnmounted(() => {
if (animation) {
animation.destroy()
}
})
defineExpose({
play: () => animation?.play(),
pause: () => animation?.pause(),
stop: () => animation?.stop()
})
</script>
<style scoped>
.lottie-container {
width: 100%;
height: 100%;
}
</style>
部署与优化
1. 构建优化
javascript
// .vitepress/config.ts
export default defineConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'three': ['three'],
'gsap': ['gsap'],
'chart': ['chart.js']
}
}
}
},
optimizeDeps: {
include: ['three', 'gsap', 'chart.js', 'lottie-web']
}
}
})
2. PWA 支持
javascript
// .vitepress/config.ts
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
vite: {
plugins: [
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg}']
},
manifest: {
name: '创新科技集团',
short_name: '创新科技',
description: '引领行业创新,创造无限可能',
theme_color: '#3b82f6',
background_color: '#ffffff',
display: 'standalone',
icons: [
{
src: '/icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
})
]
}
})
3. 性能监控
javascript
// .vitepress/theme/utils/performance.js
export class PerformanceMonitor {
constructor() {
this.metrics = {}
this.init()
}
init() {
// 监控页面加载性能
window.addEventListener('load', () => {
this.measurePageLoad()
})
// 监控用户交互
this.observeUserInteractions()
// 监控资源加载
this.observeResourceLoading()
}
measurePageLoad() {
const perfData = performance.getEntriesByType('navigation')[0]
this.metrics.pageLoad = {
dns: perfData.domainLookupEnd - perfData.domainLookupStart,
tcp: perfData.connectEnd - perfData.connectStart,
request: perfData.responseStart - perfData.requestStart,
response: perfData.responseEnd - perfData.responseStart,
dom: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
load: perfData.loadEventEnd - perfData.loadEventStart,
total: perfData.loadEventEnd - perfData.fetchStart
}
this.sendMetrics('page_load', this.metrics.pageLoad)
}
observeUserInteractions() {
['click', 'scroll', 'keydown'].forEach(event => {
document.addEventListener(event, (e) => {
this.trackInteraction(event, e.target)
}, { passive: true })
})
}
observeResourceLoading() {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'resource') {
this.trackResourceLoad(entry)
}
})
})
observer.observe({ entryTypes: ['resource'] })
}
trackInteraction(type, element) {
const data = {
type,
element: element.tagName,
timestamp: Date.now()
}
this.sendMetrics('user_interaction', data)
}
trackResourceLoad(entry) {
const data = {
name: entry.name,
duration: entry.duration,
size: entry.transferSize,
type: entry.initiatorType
}
this.sendMetrics('resource_load', data)
}
sendMetrics(type, data) {
// 发送到分析服务
if (typeof gtag !== 'undefined') {
gtag('event', type, {
event_category: 'Performance',
event_label: JSON.stringify(data),
value: data.duration || data.total
})
}
}
}
// 初始化性能监控
export function initPerformanceMonitoring() {
if (typeof window !== 'undefined') {
new PerformanceMonitor()
}
}
最佳实践
1. 代码组织
.vitepress/theme/
├── components/
│ ├── common/ # 通用组件
│ ├── layout/ # 布局组件
│ ├── business/ # 业务组件
│ └── ui/ # UI 组件
├── composables/ # 组合式函数
├── utils/ # 工具函数
├── styles/ # 样式文件
└── types/ # 类型定义
2. 状态管理
javascript
// .vitepress/theme/composables/useGlobalState.js
import { ref, reactive } from 'vue'
const globalState = reactive({
user: null,
theme: 'light',
language: 'zh-CN'
})
export function useGlobalState() {
const setUser = (user) => {
globalState.user = user
}
const setTheme = (theme) => {
globalState.theme = theme
document.documentElement.setAttribute('data-theme', theme)
}
const setLanguage = (lang) => {
globalState.language = lang
}
return {
state: globalState,
setUser,
setTheme,
setLanguage
}
}
3. 错误处理
javascript
// .vitepress/theme/utils/errorHandler.js
export class ErrorHandler {
static init() {
// 全局错误捕获
window.addEventListener('error', this.handleError)
window.addEventListener('unhandledrejection', this.handlePromiseRejection)
// Vue 错误处理
if (typeof app !== 'undefined') {
app.config.errorHandler = this.handleVueError
}
}
static handleError(event) {
console.error('Global Error:', event.error)
this.reportError({
type: 'javascript',
message: event.error.message,
stack: event.error.stack,
filename: event.filename,
lineno: event.lineno,
colno: event.colno
})
}
static handlePromiseRejection(event) {
console.error('Unhandled Promise Rejection:', event.reason)
this.reportError({
type: 'promise',
message: event.reason.message || event.reason,
stack: event.reason.stack
})
}
static handleVueError(error, instance, info) {
console.error('Vue Error:', error, info)
this.reportError({
type: 'vue',
message: error.message,
stack: error.stack,
info
})
}
static reportError(errorInfo) {
// 发送错误报告到监控服务
if (typeof gtag !== 'undefined') {
gtag('event', 'exception', {
description: errorInfo.message,
fatal: false
})
}
}
}
示例项目
完整的示例项目可以在 GitHub 上查看。
在线演示
- 公司官网: company-demo.kehuanli.com
- 源代码: GitHub Repository
功能特色
- ✅ 现代化设计
- ✅ 3D 视觉效果
- ✅ 动画交互
- ✅ 数据可视化
- ✅ 性能优化
- ✅ PWA 支持
- ✅ 错误监控
- ✅ 多语言支持
通过这个案例,你可以学习如何使用 VitePress 构建功能丰富的公司官网,包括3D展示、动画效果、数据可视化等高级功能,打造令人印象深刻的企业形象展示平台。