Skip to content

Commit 6355b8c

Browse files
add News page
1 parent 691da0b commit 6355b8c

File tree

9 files changed

+669
-816
lines changed

9 files changed

+669
-816
lines changed

AI_Document.md

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,101 @@
1-
# Avatar 本地资源加载说明
1+
# GitHub Pages React Router 支持文档
2+
3+
## 概述
4+
5+
GitHub Pages 支持 React Router,但需要特殊配置来处理客户端路由。本项目已配置完成,支持以下路由:
6+
7+
- `/` - 友链列表页(原首页内容)
8+
- `/home` - 主页
9+
- `/about` - 关于页面
10+
11+
## 实现原理
12+
13+
### 问题
14+
GitHub Pages 是静态文件托管服务,当用户直接访问 `/home``/about` 时,服务器会寻找对应的物理文件,但这些路由是由 React Router 在客户端处理的,不存在实际的文件,因此会返回 404 错误。
15+
16+
### 解决方案
17+
使用 SPA(Single Page Application)GitHub Pages 解决方案:
18+
19+
1. **404.html 重定向**: 创建 `public/404.html` 文件,将所有 404 请求重定向到主应用
20+
2. **URL 参数处理**: 在 `index.html` 中添加脚本处理重定向的 URL 参数
21+
3. **React Router 配置**: 配置 BrowserRouter 和路由规则
22+
23+
## 配置文件
24+
25+
### public/404.html
26+
```html
27+
<!DOCTYPE html>
28+
<html>
29+
<head>
30+
<meta charset="utf-8">
31+
<title>Friends</title>
32+
<script type="text/javascript">
33+
// Single Page Apps for GitHub Pages
34+
var pathSegmentsToKeep = 0;
35+
var l = window.location;
36+
l.replace(
37+
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
38+
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
39+
l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
40+
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
41+
l.hash
42+
);
43+
</script>
44+
</head>
45+
<body>
46+
</body>
47+
</html>
48+
```
49+
50+
### index.html 中的处理脚本
51+
```javascript
52+
<script type="text/javascript">
53+
(function(l) {
54+
if (l.search[1] === '/' ) {
55+
var decoded = l.search.slice(1).split('&').map(function(s) {
56+
return s.replace(/~and~/g, '&')
57+
}).join('?');
58+
window.history.replaceState(null, null,
59+
l.pathname.slice(0, -1) + decoded + l.hash
60+
);
61+
}
62+
}(window.location))
63+
</script>
64+
```
65+
66+
## 路由组件
67+
68+
### src/components/Home.tsx
69+
主页组件,展示欢迎信息
70+
71+
### src/components/About.tsx
72+
关于页面组件,展示网站介绍信息
73+
74+
## 导航
75+
应用包含顶部导航栏,支持在不同页面间跳转:
76+
- 友链列表
77+
- 主页
78+
- 关于
79+
80+
## 部署
81+
运行 `npm run deploy` 命令会:
82+
1. 构建应用
83+
2. 复制 CNAME 文件到 dist 目录
84+
3. 复制 404.html 文件到 dist 目录
85+
86+
## 注意事项
87+
1. 所有路由在本地开发和 GitHub Pages 上都能正常工作
88+
2. 直接访问路由 URL(如 https://yoursite.github.io/home)会正确跳转
89+
3. 浏览器前进/后退按钮正常工作
90+
4. 页面刷新不会出现 404 错误
91+
92+
## 技术栈
93+
- React 19
94+
- React Router 7
95+
- Vite
96+
- TypeScript
97+
- Tailwind CSS
98+
- Radix UI
299

3100
`blogs.json` 中,有的条目的 `avatar` 字段是完整的远程 URL(以 https:// 开头),有的条目使用了相对文件名,比如 `avatar.png`
4101

index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Friends</title>
7+
<!-- GitHub Pages SPA support -->
8+
<script type="text/javascript">
9+
(function(l) {
10+
if (l.search[1] === '/' ) {
11+
var decoded = l.search.slice(1).split('&').map(function(s) {
12+
return s.replace(/~and~/g, '&')
13+
}).join('?');
14+
window.history.replaceState(null, null,
15+
l.pathname.slice(0, -1) + decoded + l.hash
16+
);
17+
}
18+
}(window.location))
19+
</script>
720
</head>
821
<body>
922
<div id="root"></div>

package-lock.json

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
"build": "tsc -b && vite build",
99
"lint": "eslint .",
1010
"preview": "vite preview",
11-
"deploy": "npm run build && cp CNAME dist/CNAME"
11+
"deploy": "npm run build && cp CNAME dist/CNAME && cp public/404.html dist/404.html"
1212
},
1313
"dependencies": {
1414
"@radix-ui/themes": "^3.2.1",
1515
"@tailwindcss/vite": "^4.1.13",
1616
"react": "^19.1.1",
1717
"react-dom": "^19.1.1",
18+
"react-router": "^7.9.1",
1819
"tailwindcss": "^4.1.13"
1920
},
2021
"devDependencies": {

public/404.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Friends</title>
6+
<script type="text/javascript">
7+
// Single Page Apps for GitHub Pages
8+
// https://github.com/rafgraph/spa-github-pages
9+
var pathSegmentsToKeep = 0;
10+
var l = window.location;
11+
l.replace(
12+
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
13+
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
14+
l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
15+
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
16+
l.hash
17+
);
18+
</script>
19+
</head>
20+
<body>
21+
</body>
22+
</html>

src/App.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Card } from '@radix-ui/themes/dist/cjs/components/index.js'
1+
import { Card, Text } from '@radix-ui/themes/dist/cjs/components/index.js'
2+
import { Routes, Route, Link } from 'react-router'
23
import './App.css'
34
import blogsData from './assets/blogs.json'
45
import avatar from './assets/avatar.webp'
5-
6+
import News from './components/News'
67

78
function resolveAvatar(path: string): string {
89
if (!path) return '';
@@ -19,8 +20,7 @@ interface Blog {
1920

2021
const blogs: Blog[] = blogsData as Blog[];
2122

22-
function App() {
23-
23+
function Them() {
2424
return (
2525
<div className="m-3">
2626
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@@ -48,4 +48,24 @@ function App() {
4848
)
4949
}
5050

51+
function App() {
52+
return (
53+
<div>
54+
{/* 导航栏 */}
55+
<nav className="bg-gray-100 p-4 mb-4">
56+
<div className="max-w-7xl mx-auto flex gap-10">
57+
<Link to="/"><Text weight="light" size="5" color='gray'>Them</Text></Link>
58+
<Link to="/news"><Text weight="light" size="5" color='green' className='hover:underline underline-offset-2'>NEWS</Text></Link>
59+
</div>
60+
</nav>
61+
62+
{/* 路由内容 */}
63+
<Routes>
64+
<Route path="/" element={<Them />} />
65+
<Route path="/news" element={<News />} />
66+
</Routes>
67+
</div>
68+
)
69+
}
70+
5171
export default App

src/components/News.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
function News(){
3+
return (
4+
<div className="m-3">
5+
<h1 className="text-4xl font-bold mb-4">News Page</h1>
6+
<p className="text-lg">Welcome to the News page! Here you can find the latest updates and articles.</p>
7+
</div>
8+
);
9+
}
10+
11+
export default News;

src/main.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import './index.css'
44
import App from './App.tsx'
55
import "@radix-ui/themes/styles.css";
66
import { Theme } from '@radix-ui/themes/dist/cjs/components/index.js';
7+
import { BrowserRouter } from 'react-router';
78

89
createRoot(document.getElementById('root')!).render(
910
<StrictMode>
10-
<Theme>
11-
<App />
12-
</Theme>
11+
<BrowserRouter>
12+
<Theme>
13+
<App />
14+
</Theme>
15+
</BrowserRouter>
1316
</StrictMode>,
1417
)

0 commit comments

Comments
 (0)