React 基础
1. React 简介
React 是由 Facebook 开发并开源的一个用于构建用户界面的 JavaScript 库。它主要用于构建单页应用(SPA),其核心理念是组件化和声明式编程,即 ui = render(data)。
核心特点
声明式
- React 使用声明式编程范式来描述 UI。开发者只需描述界面在某一特定状态下的样子,React 会负责在状态变化时高效地更新和渲染界面。
- React 的声明式,通过 jsx 语法定义实现,开发者只需时刻关注 state、props 的变化,React 会自动更新渲染视图。
组件化
- React 应用由多个组件组成,每个组件对应页面中的一个部分。组件是可重用的独立代码块,使得开发和维护大型应用变得更加高效。
虚拟 DOM(Virtual DOM)
- React 使用虚拟 DOM 来优化性能。虚拟 DOM 是一个轻量级的 JavaScript 对象,作为实际 DOM 的抽象副本。当状态或数据发生变化时,React 首先更新虚拟 DOM,然后计算出最小的 DOM 变更并将其应用到实际 DOM 上,从而提高性能。
- 在 React 中,引入了 fiber 架构用于桥接数据变更与 dom 更新。
单向数据流
- React 中的数据流是单向的。这种数据流方式使得数据管理更加清晰和可预测,有助于调试和维护。
JSX 语法
- JSX 是 JavaScript 的一种语法扩展,允许在 JavaScript 代码中编写类似 HTML 的标签。JSX 使得定义组件结构更加直观和简洁。
核心概念
组件
- 组件是 React 应用的基础单元。可以是类组件(使用 ES6 类语法)或函数组件(使用函数语法)。
状态
- 状态是组件内部的数据,用于控制组件的行为和渲染。
属性(Props)
- 属性是从父组件传递到子组件的数据。属性是不可变的,子组件只能读取属性,而不能修改它们。
React 主要生态系统
React Router
- React Router 是一个用于在 React 应用中实现路由的库,允许开发者根据 URL 的变化渲染不同的组件。
Redux -> zustand
- Redux 是一个用于管理应用状态的库,常与 React 结合使用。它通过全局状态树和单向数据流来简化状态管理。
Create React App -> Vite
- Create React App 是一个官方的脚手架工具,帮助开发者快速创建和配置 React 项目。
2. React 项目初始化
React 项目初始化方式有很多种选择,我们可以选择已有脚手架工具,直接创建 React 项目,也可以通过自定义项目结构,然后使用构建工具实现 React 项目的构建打包流程。
脚手架方案
Vite
通过 Vite 搭建 React 工程只需要一行命令即可,命令如下:
pnpm create vite react-vite --template react-tscreate-react-app
命令示例:
npx create-react-app my-app --template typescript已不推荐,官网地址:https://create-react-app.dev/
自搭建
Vite
1. 创建项目目录并初始化
mkdir react-custom
cd react-custom
pnpm init2. 安装 Vite 相关依赖
pnpm install react react-dom
pnpm install --save-dev vite @vitejs/plugin-react3. 创建项目结构
react-custom/
├── src/
│ ├── main.jsx
│ ├── App.jsx
│ └── index.css
├── index.html
├── vite.config.js
├── package.json
└── .gitignore4. 创建 Vite 配置文件
在根目录创建vite.config.js文件。
vite.config.js :
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
open: true,
},
});5. 创建 React 组件
在src下创建 main.jsx、App.jsx、index.css 文件。
src/main.jsx :
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("app")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);src/App.jsx :
import React from "react";
export default function App() {
return (
<div>
<h1>Hello World</h1>
</div>
);
}src/index.css :
body {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #f1f1f1;
}6. 创建 HTML 模块
在项目根目录下创建 index.html文件。
index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.jsx"></script>
</body>
</html>7. 更新 package.json 脚本
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},8. 启动开发服务器
pnpm dev3. 认识 JSX
JSX 是 React 中最核心的一个概念,它也是区别于 Vue 最核心的一个概念(Vue 现也已支持 JSX 语法)。JSX 全称 JavaScript And XML,是一种在 JavaScript 中编写 HTML 代码片段的语法协议,不过这种协议同样回归到编译阶段,jsx 最终通过编译转为 jsx 以在运行时工作。
在 React 应用中编写的 jsx 会先通过编译工具转换为 React 提供的运行时方法,从而在应用执行时工作。
jsx -> _jsx -> jsxProd -> createElement
JSX 基础示例
const element = <h1>Hello World</h1>;
export default function App() {
return element;
}在这个例子中, <h1>Hello World</h1> 是 JSX, 它表示一个文本为 "Hello World" 的 "h1" 标签。
JSX 的特点
1. 标签语法
- 类似 HTML 标签,但可以在 Javascript 中使用。
- 必须闭合标签,即使是自闭合标签也需要
/, 例如<br/>。
2. 嵌入表达式
- JSX 允许在标签内部嵌入使用
{}JavaScript 表达式。
const name = "Json";
const element = <h1>Hello, {name}</h1>;3. JSX 是一个表达式
- JSX 编译后变成了普通的 JavaScript 对象,可以在条件语句、循环中使用,赋值给变量,作为函数的参数,返回值等。
function greet(user) {
if (user) {
return <h1>Hello, {user}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}JSX 的转化原理
- JSX 不能直接在浏览器中运行,需要通过编译工具(如 Babel)转换为纯 JavaScript。Babel 是一个 JavaScript 编译器,可以将 JSX 和其他新语法转换为兼容性更好的旧版本 JavaScript。
以下是 JSX 转换为 JavaScript 的过程:
1. JSX 代码
const element = <h1>Hello World</h1>;2. 转换后的 JavaScript 代码
import { jsx as _jsx } from "react/jsx-runtime";
var element = /*#__PURE__*/ _jsx("h1", {
children: "Hello, world!"
});3. 运行时
var element = React.createElement("h1", null, "Hello, world!");4. React 基础语法
新版本 React 推荐使用函数式组件设计,状态相关处理推荐使用 Hooks,接下来我们通过 class component 和 function component 进行对比讲解。 React 的基础语法和概念是构建 React 应用程序的核心。以下是对 React 基础语法的详细说明,包括 props、state、条件渲染、事件处理和组件传值等。
Function Component
目前 React 应用主流编码方式是函数式组件。
1. State
函数式组件使用 useState Hook 来管理状态。
示例
import React from "react";
export default function UserStateDemo() {
const [date, setDate] = React.useState(new Date());
React.useEffect(() => {
const timerID = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timerID);
}, []);
return (
<div>
UserStateDemo
<h1>时间:{date.toLocaleTimeString()}</h1>
</div>
)
}2. Props
函数式组件通过参数来接收 props。
示例
import React from "react";
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
export default function PropsDemo() {
return (
<>
<div>PropsDemo</div>
<Welcome name="React"></Welcome>
<Welcome name="Vue"></Welcome>
</>
);
}3. 事件处理
React 的事件处理类似于 DOM 事件处理,但有一些语法上的不同。事件命名采用小驼峰命名法,处理函数通过 {} 传递。
示例
import React from "react";
export default function EventDemo() {
const handleClick = () => {
console.log("点击了按钮");
};
return (
<>
<div>EventDemo</div>
<button onClick={handleClick}>点击</button>
</>
);
}4. 列表和Key
在 React 中渲染列表时,需要为每个列表项提供一个唯一的 key 属性,以便 React 能够有效地更新和重新渲染列表。
示例
import React from 'react'
export default function ListDemo() {
const list = [
{ id: 1, name: "张三" },
{ id: 2, name: "张三" },
{ id: 3, name: "张三" },
{ id: 4, name: "张三" },
{ id: 5, name: "张三" },
];
return (
<>
<div>ListDemo</div>
<ul>
{list.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</>
)
}5. 表单处理
- 受控组件: React 表单元素的值通常由组件的
state控制,这些表单元素称为“受控组件”。 - 非受控组件:不由React 的
state控制。
示例
import React from "react";
export default function FormDemo() {
const [name, setName] = React.useState("");
return (
<>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</>
);
}Class Component
React 的老项目可能会使用 class component 进行开发,相关知识需要学习一下。
1. State
state 是组件内部的数据状态。state 是可变的,可以通过调用 this.setState 方法来更新状态,从而触发重新渲染。
示例
import { Component } from "react";
export default class StateDemo extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<>
<p>当前计数: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
增加
</button>
</>
);
}
}2. Props
props 是组件的属性,是从父组件传递到子组件的数据。props 是只读的,子组件只能读取而不能修改它们。
示例
import { Component } from "react";
class Welcome extends Component {
constructor(props) {
super(props);
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
export default class PropsDemo extends Component {
render() {
return (
<>
<Welcome name="React" />
<Welcome name="Vue" />
</>
);
}
}3. 事件处理
React 的事件处理类似于 DOM 事件处理,但有一些语法上的不同。事件命名采用小驼峰命名法,处理函数通过 {} 传递。
示例
import React, { Component } from "react";
export default class EventDemo extends Component {
handleClick = () => {
console.log("点击了");
};
render() {
return <div onClick={this.handleClick}>EventDemo按钮</div>;
}
}5. React Hooks基础
在 React 16.8 版本之前,函数组件也称为“无状态组件”,函数组件没有自己的内部数据,也没有自己的生命周期函数。
从 16.8 开始,React 中专门为函数组件新增了 Hook 方法,这些方法就是用来解决函数组件没有状态、没有生命周期的问题。
1. useState
useState 是最常用的 Hook,用于在函数组件中添加 state。
用法:
import React from "react";
export default function UserStateDemo() {
const [date, setDate] = React.useState(new Date());
React.useEffect(() => {
const timerID = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timerID);
}, []);
return (
<div>
UserStateDemo
<h1>时间:{date.toLocaleTimeString()}</h1>
</div>
)
}2. useEffect
useEffect 用于在函数组件中执行副作用操作,例如数据获取、订阅或手动更改 DOM。
useEffect 接收两个参数,第一个为回调函数,会在指定时机执行。第二参数为数组,当数组指定的数据发送改变便执行第一个参数传递的函数。 useEffect return 一个函数,此函数会在销毁的时候执行。
import {useEffect } from 'react';
useEffect(() => {
// 组件挂载完成时执行 和 组件更新时执行
})
useEffect(() => {
// 组件挂载完成时执行
}, [])
useEffect(() => {
// 组件挂载完成时执行
// 数组中任意一条数据发生改变时执行
}, [数据1, 数据2])import React from "react";
export default function UserStateDemo() {
const [date, setDate] = React.useState(new Date());
React.useEffect(() => {
const timerID = setInterval(() => {
setDate(new Date());
}, 1000);
// 清理副作用
return () => clearInterval(timerID);
}, []);
return (
<div>
UserStateDemo
<h1>时间:{date.toLocaleTimeString()}</h1>
</div>
)
}3. useRef
useRef 返回一个可变的 ref 对象,该对象的 .current 属性被初始化为传入的参数(initialValue)。
useRef 常用于访问 DOM 元素直接或存储某个可变值,该值在组件的整个生命周期内保持不变。
import { useEffect, useRef, useState } from "react";
// 用来获取 DOM 元素
export const UseRefDemo = () => {
const inputRef = useRef<HTMLInputElement>(null);
return (
<div>
<input ref={inputRef} type="text" />
<button
onClick={() => {
console.log(inputRef.current?.value);
}}
>
提交
</button>
</div>
);
};4. useImperativeHandle
useImperativeHandle 是 React 中的一个 Hook,它能让你自定义由 ref 暴露出来的属性。
import { useImperativeHandle } from 'react';
const Child = (props, ref) => {
useImperativeHandle(ref, () => {
return {
// 返回给父组件的数据或方法 ,方法可以有形参
showModal(params){
}
}
})
}5. useReducer
出自 Redux 作者,可以理解为 React 内部基于 Hooks 实现的类似 Redux 状态管理逻辑。 useReducer 是 useState 的替代方案,适用于 state 逻辑较复杂且包含多个子值,或下一个 state 依赖于之前的 state 的情况。
import {useReducer} from 'react'
function reducer(state, action) {
switch (action.type) {
case 'add':
return {count: state.count + 1}
case 'minus':
return {count: state.count - 1}
default:
return state
}
}
export const UseReducerDemo = () => {
const [state, dispatch] = useReducer(reducer, {count: 0})
return (
<>
<div>useReducerDemo</div>
<div>{state.count}</div>
<button onClick={() => dispatch({type: 'add'})}>+</button>
<button onClick={() => dispatch({type: 'minus'})}>-</button>
</>
)
}6. useMemo
useMemo 用来缓存数据。
import { useMemo } from 'react';
const App = () => {
const result = useMemo(() => {
return 计算的结果;
}, [依赖的数据项])
}7. useCallback
useCallback 用来缓存方法。
const handleAdd = useCallback(() => {
// 就近取值
setCount((c) => c + 1);
}, []);
const handleSub = useCallback(() => {
setCount((c) => c - 1);
}, []);8. React.memo
memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。
import { memo } from 'react';
const App = () => {
}
export default memo(App);6. React 样式方案
1. 内联样式 (Inline Styles)
通过 style 属性直接在组件中定义样式。
<h1 style={{ color: 'red', backgroundColor: '#eee' }}>你好</h1>2. 全局样式
外部的全局样式文件,对于文件名没有特殊要求,按照常规的样式文件后缀名(例如:.css、.scss、.less)即可。 在任意一个组件中通过 import 引入后,都会作用于当前项目中所有的组件。
import "index.css";3. CSS 模块 (CSS Modules)
CSS 模块允许你为 CSS 类名添加局部作用域,避免样式冲突。文件名通常以 .module.css 结尾。
/* style.module.css */
.container {
color: red;
}import style from "./style.module.css";
<div className={style.container}>{count}</div>4. Styled Components
使用 styled-components 库可以在 JavaScript 中编写实际的 CSS,提供了组件级别的样式管理。
5. Emotion
Emotion 是一个强大的 CSS-in-JS 库,提供了灵活的样式管理方案。
6. Tailwind CSS
Tailwind CSS 是一个实用工具优先的 CSS 框架,可以在 React 项目中使用。
补充资料
React 中文官网:https://zh-hans.react.dev/
React Hooks 套件:https://github.com/streamich/react-use
React 基础架构社区方案:https://github.com/kriasoft/react-starter-kit