App.tsx
,跑起開發伺服器# 1) 建立 Vite + React + TS 專案
npm create vite@latest resume-site-react -- --template react-ts
# 2) 安裝依賴
cd resume-site-react
npm install
# 3) 啟動開發伺服器
npm run dev
打開瀏覽器(預設 http://localhost:5173
)能看到 React 初始頁就成功了。
resume-site-react/
├─ index.html # 整站入口
├─ src/
│ ├─ App.tsx # 根元件
│ ├─ main.tsx # React 入口
│ ├─ components/ # 共用元件
│ ├─ styles/ # 全域 CSS
│ └─ assets/ # 圖片
└─ tsconfig.json
先放一份全域 CSS,方便骨架套用。
src/styles/base.css
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; font-family: system-ui, -apple-system, "Noto Sans TC", Arial, sans-serif; line-height: 1.7; }
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
.container { max-width: 960px; margin: 0 auto; padding: 0 16px; }
.section { padding: 56px 0 40px; }
.muted { color: #6b7280; }
.btn { display:inline-block; padding:10px 16px; background:#2563eb; color:#fff; border:1px solid #2563eb; border-radius:8px; cursor:pointer; }
.btn:hover { opacity:.92; }
.btn-outline { background:transparent; color:#2563eb; border-color:#2563eb; }
.btn.small { padding:6px 10px; font-size:14px; }
.site-header { position:sticky; top:0; background:#fff; border-bottom:1px solid #e5e7eb; z-index:10; }
.site-header .container { display:flex; align-items:center; gap:16px; padding:12px 16px; }
.brand { font-weight:700; }
.site-nav { margin-left:auto; }
.site-nav ul { list-style:none; margin:0; padding:0; display:flex; gap:12px; }
.site-nav a:hover { color:#2563eb; }
.hero { display:grid; gap:20px; padding:40px 0; }
.hero-text h1 { margin:8px 0 6px; font-size: clamp(24px, 5vw, 34px); }
.hero-cta { display:flex; gap:12px; margin-top:8px; }
.hero-photo { max-width:240px; }
.hero-photo img { border-radius:50%; border:4px solid #e5e7eb; }
.skill-grid, .project-grid { display:grid; gap:12px; }
.skill-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); list-style:none; padding:0; }
.skill-grid li { border:1px solid #e5e7eb; padding:10px 12px; border-radius:10px; }
.project-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.card { border:1px solid #e5e7eb; border-radius:12px; padding:16px; }
.site-footer { margin-top:48px; border-top:1px solid #e5e7eb; }
.site-footer .container { padding:16px; text-align:center; color:#6b7280; }
在 src/main.tsx
引入:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './styles/base.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
在 src/components/
下建立 7 個檔案:
SiteHeader.tsx
Hero.tsx
About.tsx
Skills.tsx
Projects.tsx
Contact.tsx
SiteFooter.tsx
src/components/SiteHeader.tsx
import React from 'react'
export default function SiteHeader() {
return (
<header className="site-header">
<div className="container">
<a className="brand" href="#home">Chiayu</a>
<nav className="site-nav">
<ul>
<li><a href="#about">關於我</a></li>
<li><a href="#skills">技能</a></li>
<li><a href="#projects">作品</a></li>
<li><a href="#contact">聯絡</a></li>
</ul>
</nav>
</div>
</header>
)
}
其他元件可以先做靜態版:
src/components/Hero.tsx
import React from 'react'
export default function Hero() {
return (
<section className="hero container" id="home">
<div className="hero-text">
<h1>哈囉,我是 Chiayu</h1>
<p>前端工程師|專長 React / Vue / Angular / TypeScript</p>
<div className="hero-cta">
<a className="btn" href="#projects">看作品</a>
<a className="btn btn-outline" href="#contact">聯絡我</a>
</div>
</div>
<div className="hero-photo">
<img src="/assets/me-formal.jpg" alt="Chiayu 的照片" width="240" height="240" />
</div>
</section>
)
}
About.tsx / Skills.tsx / Projects.tsx / Contact.tsx / SiteFooter.tsx
都先放靜態 HTML 骨架(和 Vue/Angular 對齊),後面幾天再逐步「資料化」、「加互動」。
import React from 'react'
import SiteHeader from './components/SiteHeader'
import Hero from './components/Hero'
import About from './components/About'
import Skills from './components/Skills'
import Projects from './components/Projects'
import Contact from './components/Contact'
import SiteFooter from './components/SiteFooter'
export default function App() {
return (
<><SiteHeader />
<main>
<Hero />
<About />
<Skills />
<Projects />
<Contact />
</main>
<SiteFooter />
</>
)
}
npm run dev
,看到完整骨架頁面忘了引入 CSS → 畫面全白
✅ 確認 main.tsx
有 import './styles/base.css'
JSX 語法錯誤 → 例如 class 改成 className
✅ React 中要用 className
、htmlFor
等
圖片路徑錯誤 → 確認放在 public/assets/
,存取 /assets/...
我們要把靜態骨架 資料化:
useState
管理 Skills / Projects 陣列.map()
取代硬寫列表