国庆值班把假期拆了个稀碎, 正好不用去看人潮人海, 趁机会赶个晚集入门一下都火这么久的 React 前端技术. 话说其实 n 年前也了解过一丢丢来着, 当时看到一上来就用 JS 写 DOM 的套路直接就给吓退了, 扭头还去看 Vue 了, 现在从市场份额来看, 确实 React 还是占有率更高 (数据来源) 加上单位现在也都在推 CloudScape 做项目, 有必要再试一试了.
本次入门主要围绕 React 前端开发的环境准备和基本功能实现, 后端用 https://reqres.in/api/users?page=2 提供的 Mock 数据, 以后有空再尝尝 FastAPI (挖个坑先). Mock 数据长这样:
{
"page": 2,
"per_page": 6,
"total": 12,
"total_pages": 2,
"data": [
{
"id": 7,
"email": "[email protected]",
"first_name": "Michael",
"last_name": "Lawson",
"avatar": "https://reqres.in/img/faces/7-image.jpg"
},
// 省略更多其他相同结构数据
],
"support": {
"url": "https://reqres.in/#support-heading",
"text": "To keep ReqRes free, contributions towards server costs are appreciated!"
}
}
计划使用这些数据制作一个通信录页面, 包含每个人物的基本信息, 以卡片形式排列展示.
# 检查 nodejs 版本
node -v
v20.8.0
# 修改镜像源
npm config set registry https://registry.npmmirror.com
# 创建文件夹
mkdir my-react; cd my-react
# 创建项目
npm create vite
# 完成向导, 使用 React + TypeScript 组合
Need to install the following packages:
create-vite@4.4.1
Ok to proceed? (y)
√ Project name: ... frontend
√ Select a framework: » React
√ Select a variant: » TypeScript
Scaffolding project in C:\Users\lpwm\Desktop\react-study\my-react\frontend...
Done. Now run:
cd frontend
npm install
npm run dev
# 安装基础的 React 依赖
cd frontend; npm install
# 使用 VScode 打开项目
code .
安装以下插件可以使 Coding 的快乐(效率)加倍
快捷键 Ctrl
+ ,
打开 VScode 设置, 搜索 “format on save”, 勾选上 Editor: Format On Save
; 搜索 “default formatter” , Editor: Default Formatter
选择 Prettier - Code formatter
配置完的项目 Folder 长这个样子:
通过 Vite 脚手架创建的项目中乍一看好多文件, 其实大多数都不需要动, 主要 focus 在 src
里面就中.
React 其实并不会包含和 UI 相关的 Component / Style, 这部分还是需要使用其他的前端 UI 框架, 这里使用老朋友 Bootstrap 适配 React 的版本 React Bootstrap, 在 VScode 中通过快捷键 Ctrl
+ Shift
+ `
打开 PowerShell 终端:
npm install react-bootstrap bootstrap
安装完成后可以检查 package.json
可以看到内容已经自动更新, 非高端玩家其实项目中的大多数配置文件都不需要手动修改的.
"dependencies": {
"bootstrap": "^5.3.2",
"react": "^18.2.0",
"react-bootstrap": "^2.9.0",
"react-dom": "^18.2.0"
},
Vite 创建的项目中包含了一个演示的页面, 对于新手来说, 看起来会比较混乱, 先删除 src 中的 App.css, index.css 这两个自定义的样式文件.
修改 src/main.tsx
删除 import './index.css'
这行相关的引用.
修改 src/App.tsx
删除所有内容, 输入 tsrafce
按 TAB 键自动补全填充代码片段 (其实不用输入完就能联想出来了) 这里其实是调用前面装的插件 ES7 React/Redux/Styled-components snippets
Creates a React Arrow Function Component with ES7 module system and TypeScript interface (ES7 React/Redux/Styled-components snippets)
自动补全的 React Component 代码(真省事):
import React from 'react'
interface Props {
}
const App = (props: Props) => {
return (
<div>
</div>
)
}
export default App
其实 JSX 也没有像之前想象的那么晦涩, 当成 XML 来对待就行了. 修改上面的代码, 删除没有用到的部分, 添加一个静态的 Bootstrap Navbar 和 Card. 在 return ()
中输入 import
代码, 就很欢快了有木有! 另外得益于之前在 VScode 中设置的 Editor: Format On Save
, 保存文件时会自动通过 Prettier 进行格式化.
参考 React Bootstrap 文档 完成页面设计.
import { Card, Col, Container, Navbar, Row } from "react-bootstrap";
// 别忘了添加 Bootstrap CSS 文件引用
import "bootstrap/dist/css/bootstrap.min.css";
const App = () => {
return (
<Container fluid>
<Row>
<Navbar expand="lg" className="bg-primary-subtle">
<Container>
<Navbar.Brand>Contacts</Navbar.Brand>
</Container>
</Navbar>
</Row>
<Container className="p-3">
{/* Container 加上 padding 类 */}
<Row md="4">
{/* 在 Row 上定义 Grid Column (每行最多 4 列) */}
<Col>
<Card className="m-2">
{/* 加上 margin 类 */}
<Card.Img variant="top" src="holder.js/100px180"></Card.Img>
<Card.Body>
<Card.Title>Card Title</Card.Title>
<Card.Text>Some text in the card.</Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
</Container>
);
};
export default App;
在 Terminal 中启动开发服务器
npm run dev
VITE v4.4.9 ready in 200 ms
➜ Local: http://localhost:5174/
➜ Network: use --host to expose
➜ press h to show help
浏览器访问 http://localhost:5174/
预览效果
多复制几个 Card 所在的
查看 Grid Column 效果:
静态页面已经没问题了, 接下来使用 https://reqres.in/api/users?page=2 模拟后端 API 返回的数据进行页面动态渲染, 修改后的 App.tsx
import { Card, Col, Container, Navbar, Row } from "react-bootstrap";
// 别忘了添加 Bootstrap CSS 文件引用
import "bootstrap/dist/css/bootstrap.min.css";
import { useEffect, useState } from "react";
import React from "react";
// 使用 Context 管理组件内部的上下文内容
const AppContext = React.createContext({
users: [],
fetchUsers: () => {},
});
const App = () => {
// 模拟异步获取 API 数据
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
await fetch("https://reqres.in/api/users?page=2")
.then((response) => {
return response.json();
})
.then((json_data) => {
setUsers(json_data["data"]);
});
};
useEffect(() => {
fetchUsers();
}, []);
return (
<AppContext.Provider value={{ users, fetchUsers }}>
<Container fluid>
<Row>
<Navbar expand="lg" className="bg-primary-subtle">
<Container>
<Navbar.Brand>Contacts</Navbar.Brand>
</Container>
</Navbar>
</Row>
<Container className="p-3">
{/* Container 加上 padding 类 */}
<Row md="4">
{/* 在 Row 上定义 Grid Column (每行最多 4 列) */}
{users.map((user) => (
<Col key={user["id"]}>
<Card className="m-2">
{/* 加上 margin 类 */}
<Card.Img variant="top" src={user["avatar"]}></Card.Img>
<Card.Body>
<Card.Title>{user["first_name"]}</Card.Title>
<Card.Text>Some text in the card.</Card.Text>
</Card.Body>
</Card>
</Col>
))}
</Row>
</Container>
</Container>
</AppContext.Provider>
);
};
export default App;
刷新浏览器查看效果:
由于图片的高度不一致, 导致显示的 Card 大小也不一样, 先通过自定义 inline 样式的方式解决, 修改 Card.Img
添加 style
属性
<Card.Img
variant="top"
src={user["avatar"]}
style={{ height: "100px", objectFit: "cover" }}
></Card.Img>
刷新页面, 制式~
换种方式, 将自定义样式放到单独的文件中, 然后为 Card.Img
添加 className
属性. 创建文件 src/App.css
(就是开头删掉的那个)
.card-avatar {
height: 100px;
object-fit: cover;
}
修改 App.tsx
, 在开头加入引用 import "./App.css";
, 并调整 Card.Img
<Card.Img
variant="top"
src={user["avatar"]}
className="card-avatar"
></Card.Img>
再次刷新浏览器, 格式保持一致. 至此, 一个简单的 React 页面就开发完成了. 其实后面涉及到的 React.createContext
, useState
, useEffect
还属于一知半解, 也没好意思展开强行解释. 能先跑出来效果就打消了不少之前对 React 的恐惧了, 后面还是得花时间好好看一遍文档和基础学习.
现在都是在本地通过 npm run dev
跑的开发测试环境, 生产部署的简单过程:
dist
目录npm run build
dist
目录中的文件部署到 Web Server, 以 WSL2 (Ubuntu 22.04) 中的 nginx 为例# 先安装 nginx
sudo apt install nginx -y
# 从 Windows 的资源管理器中
# 将 dist 文件夹复制到 WSL2 的 Home 文件夹: /home/lpwm/dist
# 修改 nginx 配置
sudo vim /etc/nginx/sites-enabled/default
nginx 配置文件简单示意:
server {
listen 80;
root /home/lpwm/dist;
}
启动 nginx 服务
sudo systemctl restart nginx
此时通过浏览器访问 http://localhost
返回 403 Forbidden
, 查看 /var/log/nginx/error.log
发现:
2023/10/01 01:09:59 [error] 1117#1117: *1 “/home/lpwm/dist/index.html” is forbidden (13: Permission denied), client: 127.0.0.1, server: , request: “GET / HTTP/1.1”, host: “localhost”
使用 sudo ps -ef |grep nginx
检查当前运行 nginx 的用户是 www-data
, 没有对 /home/lpwm/dist
文件夹的访问权限. 这个好说, 把 www-data
用户添加到我自己用户的 lpwm
组就行了呗
sudo usermod -aG lpwm www-data
sudo nginx -s reload
齐活儿~
本次入门就先到这里吧, 后面抽时间再细琢磨 React 里面的各种基础概念.
React Tutorial for Beginners - Programming with Mosh