自定义 404 页面
创建一个独特的 404 页面,提升用户体验。
方式一:使用主题插槽
创建 404 组件
vue
<!-- .vitepress/theme/components/NotFound.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
const { site } = useData()
</script>
<template>
<div class="not-found">
<div class="not-found-content">
<p class="code">404</p>
<h1>页面未找到</h1>
<p class="desc">抱歉,您访问的页面不存在或已被移动。</p>
<div class="actions">
<a href="/" class="home-link">返回首页</a>
</div>
</div>
</div>
</template>
<style scoped>
.not-found {
padding: 96px 24px;
text-align: center;
}
.code {
font-size: 8rem;
font-weight: 700;
line-height: 1;
background: linear-gradient(120deg, #6366f1, #8b5cf6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
h1 {
font-size: 2rem;
margin: 1rem 0;
}
.desc {
color: var(--vp-c-text-2);
margin-bottom: 2rem;
}
.actions {
display: flex;
gap: 1rem;
justify-content: center;
}
.home-link {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.5rem;
background: var(--vp-c-brand-1);
color: white;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
}
.home-link:hover {
background: var(--vp-c-brand-2);
transform: translateY(-2px);
}
</style>注册组件
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import NotFound from './components/NotFound.vue'
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
'not-found': () => h(NotFound)
})
}
}方式二:创建 404.md 页面
创建页面文件
markdown
<!-- docs/404.md -->
---
layout: page
title: 404
---
<div class="not-found">
<div class="not-found-content">
<p class="code">404</p>
<h1>页面未找到</h1>
<p class="desc">抱歉,您访问的页面不存在。</p>
<a href="/" class="home-link">返回首页</a>
</div>
</div>
<style>
.not-found {
padding: 96px 24px;
text-align: center;
}
.code {
font-size: 8rem;
font-weight: 700;
background: linear-gradient(120deg, #6366f1, #8b5cf6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.home-link {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--vp-c-brand-1);
color: white;
border-radius: 8px;
text-decoration: none;
margin-top: 2rem;
}
.home-link:hover {
background: var(--vp-c-brand-2);
}
</style>高级:动画 404 页面
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const isLoaded = ref(false)
onMounted(() => {
setTimeout(() => {
isLoaded.value = true
}, 100)
})
</script>
<template>
<div class="not-found" :class="{ loaded: isLoaded }">
<div class="animation">
<div class="ghost">
<div class="ghost-face">
<div class="eye left"></div>
<div class="eye right"></div>
<div class="mouth"></div>
</div>
</div>
</div>
<h1>页面跑丢了</h1>
<p>别担心,我们可以帮你找回来</p>
<a href="/" class="btn">返回首页</a>
</div>
</template>
<style scoped>
.not-found {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
opacity: 0;
transform: translateY(20px);
transition: all 0.6s ease;
}
.not-found.loaded {
opacity: 1;
transform: translateY(0);
}
.ghost {
width: 120px;
height: 140px;
background: linear-gradient(180deg, #e8e8e8 0%, #f5f5f5 100%);
border-radius: 60px 60px 40px 40px;
position: relative;
animation: float 3s ease-in-out infinite;
}
.ghost::before,
.ghost::after {
content: '';
position: absolute;
bottom: -20px;
width: 30px;
height: 30px;
background: inherit;
border-radius: 50%;
}
.ghost::before {
left: 10px;
}
.ghost::after {
right: 10px;
}
.ghost-face {
position: absolute;
top: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
}
.eye {
width: 15px;
height: 20px;
background: #333;
border-radius: 50%;
animation: blink 4s infinite;
}
.mouth {
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 10px;
background: #333;
border-radius: 0 0 20px 20px;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes blink {
0%, 95%, 100% { transform: scaleY(1); }
97% { transform: scaleY(0.1); }
}
h1 {
font-size: 2rem;
margin: 2rem 0 1rem;
}
p {
color: var(--vp-c-text-2);
margin-bottom: 2rem;
}
.btn {
padding: 0.75rem 1.5rem;
background: var(--vp-c-brand-1);
color: white;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
}
.btn:hover {
background: var(--vp-c-brand-2);
transform: translateY(-2px);
}
</style>配置重定向
在构建后自动重定向:
ts
// .vitepress/config.mts
export default defineConfig({
transformHead({ pageData }) {
if (pageData.relativePath === '404.md') {
return [
['meta', { 'http-equiv': 'refresh', content: '5;url=/' }]
]
}
}
})