在這篇博客中,我們將逐步構建一個 求職網站應用程式,使用 React.js(搭配 Vite 進行設置)、Node.js(使用 Express)、SerpApi 從 Google 職位中獲取求職清單,以及使用 Material-UI (MUI) 進行樣式設計。
到本教程結束時,您將擁有一個功能性的求職網站,使用者可以搜尋工作並查看從 Google 求職清單中獲取的結果。
以下是此專案的演示:
先決條件
要跟隨進行,您需要:
網站連結: https://serpapi.com/
您可以創建一個帳戶或登錄(如果帳戶已存在)。
接下來,選擇左側邊欄中的 API 金鑰區域,選擇生成新金鑰、使用現有金鑰或創建新金鑰。
最終的專案結構如下,包含伺服器和客戶端:
job-board/
│
├── job-board-client/ # 前端 (React + Vite)
│ ├── node_modules/
│ ├── public/
│ ├── src/
│ │ ├── components/
│ │ │ ├── SearchBar.jsx # 搜尋欄元件
│ │ │ ├── JobList.jsx # 工作清單顯示元件
│ │ ├── App.jsx # 主應用程式元件
│ │ ├── main.jsx # React 應用程式的切入點
│ │ └── index.css # 全域 CSS
│ ├── .gitignore
│ ├── index.html # 主 HTML 文件
│ ├── package.json # 相依項目和腳本
│ ├── vite.config.js # Vite 設定
│ └── README.md
│
├── job-board-server/ # 後端 (Node.js + Express)
│ ├── node_modules/
│ ├── index.js # Express 伺服器的切入點
│ ├── .env # 環境變數(例如:SERP_API_KEY)
│ ├── package.json # 相依項目和腳本
│ ├── .gitignore
│ └── README.md
job-board
mkdir job-board
cd job-board/
首先使用 Vite 設置 React 專案。
# 使用 Vite 創建 React 專案
npm create vite@latest job-board-client --template react
# 進入專案目錄
cd job-board-client
# 安裝相依項目
npm install
安裝 Material-UI (MUI) 用於樣式設計,以及 axios 用於 API 調用。
# MUI Core 和圖示用於樣式設計
npm install axios @mui/material @emotion/react @emotion/styled @mui/icons-material
接下來,創建一個後端資料夾並初始化 Express 伺服器。要創建後端,您必須位於 job-board
中。可以透過運行 cd ..
命令達成。
之後運行:
# 創建後端目錄
mkdir job-board-server
cd job-board-server
# 初始化 Node.js 專案
npm init -y
# 安裝 Express
npm install express cors axios dotenv
index.js
)。在 job-board-server
資料夾中創建 index.js
文件,並創建返回工作的 API。
const express = require('express');
const cors = require('cors');
const axios = require('axios');
require('dotenv').config();
const app = express();
app.use(cors());
const PORT = process.env.PORT || 5000;
// 獲取工作清單的端點
app.get('/api/jobs', async (req, res) => {
const { query } = req.query;
try {
const serpApiUrl = `https://serpapi.com/search.json?engine=google_jobs&q=${query}&api_key=${process.env.SERP_API_KEY}`;
const response = await axios.get(serpApiUrl);
res.json(response.data.jobs_results || []);
} catch (error) {
res.status(500).json({ error: '獲取工作的過程中發生錯誤' });
}
});
app.listen(PORT, () => {
console.log(`伺服器正在端口 ${PORT} 上運行`);
});
在您的 job-board-server
目錄中創建一個 .env
文件,並添加您的 SerpApi API 金鑰。
SERP_API_KEY=your_serp_api_key_here
在 React 專案中,創建一個搜尋元件,允許用戶輸入搜尋詞並查看工作結果。
src/components/SearchBar.jsx
中創建一個 SearchBar
元件。import React, { useState } from 'react';
import { TextField, Button, CircularProgress } from '@mui/material';
const SearchBar = ({ onSearch, loading }) => {
const [query, setQuery] = useState('');
const handleSearch = () => {
if (query.trim()) {
onSearch(query);
}
};
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px' }}>
<TextField
label="搜尋工作"
variant="outlined"
value={query}
onChange={(e) => setQuery(e.target.value)}
style={{ marginRight: '10px', width: '300px' }}
/>
<Button
variant="contained"
color="primary"
onClick={handleSearch}
disabled={loading}
>
{loading ? <CircularProgress size={24} /> : '搜尋'}
</Button>
</div>
);
};
export default SearchBar;
src/components/JobList.jsx
中創建一個 JobList
元件,顯示工作結果。import React from 'react';
import { Card, CardContent, CardActions, Typography, Button, Grid, CircularProgress, Box } from '@mui/material';
import { WorkOutline } from '@mui/icons-material';
const JobCard = ({ job }) => {
return (
<Card
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
boxShadow: '0 4px 8px #1976d2',
p: 2,
mt:3
}}>
<CardContent>
<Typography variant="h5" component="div">
{job.title}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
{job.company_name} - {job.location}
</Typography>
<Typography variant="body2">
{job.description.slice(0, 150)}... {/* 預覽描述的一部分 */}
</Typography>
</CardContent>
<CardActions>
<Button
sx={{
backgroundColor: '#1976d2',
color: '#fff',
'&:hover': {
backgroundColor: '#1565c0',
},
width: '100%',
}}
size="small" href={job.share_link} target="_blank" rel="noopener">
申請
</Button>
</CardActions>
</Card>
);
};
const JobList = ({ jobs, loading }) => {
if (loading) {
return (
<Box display="flex" justifyContent="center" marginTop="20px">
<CircularProgress />
</Box>
);
}
if (jobs.length === 0) {
return (
<Box display="flex" justifyContent="center" alignItems="center" flexDirection="column" marginTop="20px">
<WorkOutline style={{ fontSize: 60, color: 'gray' }} />
<Typography variant="h6" color="textSecondary">
沒有可用的工作
</Typography>
</Box>
);
}
return (
<Grid container spacing={2}>
{jobs.map((job, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<JobCard job={job} />
</Grid>
))}
</Grid>
);
};
export default JobList;
import React, { useState } from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import SearchBar from './components/SearchBar';
import JobList from './components/JobList';
import axios from 'axios';
import { Container } from '@mui/material';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#ff4081',
},
},
});
const App = () => {
const [jobs, setJobs] = useState([]);
const [loading, setLoading] = useState(false);
const handleSearch = async (query) => {
try {
setLoading(true);
const response = await axios.get(`http://localhost:5000/api/jobs`, {
params: { query }
});
setJobs(response.data);
setLoading(false);
} catch (error) {
console.error('獲取工作清單時發生錯誤:', error);
setLoading(false);
}
};
return (
<ThemeProvider theme={theme}>
<Container>
<SearchBar onSearch={handleSearch} loading={loading} />
<JobList jobs={jobs} loading={loading} />
</Container>
</ThemeProvider>
);
};
export default App;
確保後端和前端都在運行。
# 在 job-board-server 資料夾中
node index.js
# 在 job-board-client 資料夾中
npm run dev
🎉 您現在已經構建了一個功能完整的求職網站應用程式!前端使用 React.js ⚛️ 构建,並使用 Material-UI 🎨 進行樣式設計,而後端則使用 Node.js 🚀 和 Express 來提供來自 SerpApi 🌐 的求職清單。
這是一個很好的起點,可以添加更先進的功能,例如按位置篩選 📍、添加工作細節頁面 📄,甚至允許用戶保存工作清單 💾。
這篇博客就到此為止!請持續關注更多更新,並繼續構建精彩的應用程式!💻✨
祝您編碼愉快!😊