iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Modern Web

Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站系列 第 28

# Day 28 React 起手式 – 用 Vite + TypeScript 初始化專案,建立骨架 ## 今日目標

  • 分享至 

  • xImage
  •  

今日目標

  • Vite + React + TypeScript 建立新專案
  • 認識 React Function Component 與 JSX
  • 建立基本元件(Header / Hero / About / Skills / Projects / Contact / Footer)
  • 把骨架放進 App.tsx,跑起開發伺服器

1) 建立專案

# 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 初始頁就成功了。


2) 專案結構

resume-site-react/
├─ index.html              # 整站入口
├─ src/
│  ├─ App.tsx              # 根元件
│  ├─ main.tsx             # React 入口
│  ├─ components/          # 共用元件
│  ├─ styles/              # 全域 CSS
│  └─ assets/              # 圖片
└─ tsconfig.json


3) 建立基本樣式

先放一份全域 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>,
)


4) 建立元件骨架

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 對齊),後面幾天再逐步「資料化」、「加互動」。


5) App.tsx 組裝

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,看到完整骨架頁面
  • 頁面含 Header / Hero / About / Skills / Projects / Contact / Footer
  • 樣式套用成功,排版與 Vue/Angular 版一致

小心踩雷

  1. 忘了引入 CSS → 畫面全白

    ✅ 確認 main.tsximport './styles/base.css'

  2. JSX 語法錯誤 → 例如 class 改成 className

    ✅ React 中要用 classNamehtmlFor

  3. 圖片路徑錯誤 → 確認放在 public/assets/,存取 /assets/...


下一步(Day 29 預告)

我們要把靜態骨架 資料化

  • useState 管理 Skills / Projects 陣列
  • .map() 取代硬寫列表
  • 加上 props 傳遞,讓元件更可重用

上一篇
Day 27 Vue 版履歷網站收尾與部署
系列文
Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言