React.js是一个开源的JavaScript库,由Facebook于2013年开发,用于构建用户界面或UI组件。它主要用于构建单页应用程序或SPA,并且可以在Web和移动应用程序中使用。React.js使用虚拟DOM来提高性能,并使用组件化的方式来实现代码的重用和组织。
create-react-app创建
在终端中输入以下命令来创建一个新的React项目:
npx create-react-app my-app
其中,"my-app"是要创建的项目的名称。
Vite
使用 NPM:
npm create vite@latest
使用 Yarn:
yarn create vite
使用 PNPM:
pnpm create vite
然后按照提示操作即可!
目录结构
my-app
├─ .eslintrc.cjs
├─ .gitignore
├─ index.html
├─ package.json
├─ public
│ └─ vite.svg
├─ README.md
├─ src
│ ├─ App.css
│ ├─ App.jsx
│ ├─ assets
│ │ └─ react.svg
│ ├─ index.css
│ └─ main.jsx
└─ vite.config.js
React 应用程序是由组件组成的。一个组件是 UI(用户界面)的一部分,它拥有自己的逻辑和外观。组件可以小到一个按钮,也可以大到整个页面。
React 组件是返回标签的 JavaScript 函数
函数组件和类组件:这是根据定义组件的方法来划分的。
无状态组件和有状态组件:这是根据组件内部是否维护state来划分的。
展示型组件和容器型组件:这是根据组件的功能来划分的。
// button.jsx
function MyButton() {
return (
<button>button</button>
);
}
export default MyButton;
// button.jsx
// import React from 'react';
import React, { Component } from 'react';
// class MyButton extends React.Component {
class MyButton extends Component {
constructor() {}
render() {
return (
<button>button</button>
);
}
}
export default MyButton;
import MyButton from './button';
function MyComponent() {
return (
<div>
<p>这是一个按钮</p>
<MyButton />
</div>
);
}
上面所使用的标签语法被称为 JSX。它是可选的,但大多数 React 项目会使用 JSX,主要是它很方便。
JSX 比 HTML 更加严格。你必须闭合标签,如
。你的组件也不能返回多个 JSX 标签。你必须将它们包裹到一个共享的父级中,比如
或使用空的 <>...>
包裹。
在 React 中,你可以使用 className
来指定一个 CSS 的 class。它与 HTML 的 class
属性的工作方式相同:
<img className="avatar" />
然后,你可以在一个单独的 CSS 文件中为它编写 CSS 规则:
/* In your CSS */
.avatar {
border-radius: 50%;
}
JSX 会让你把标签放到 JavaScript 中。而大括号会让你 “回到” JavaScript 中,这样你就可以从你的代码中嵌入一些变量并展示给用户。例如:
function MyComponent(props) {
const count = 0;
return (
<div>
<p>Count: {count}</p>
<button>Button</button>
</div>
);
}
你还可以将 JSX 属性 “转义到 JavaScript”,但你必须使用大括号 而非 引号。例如,className=“avatar” 是将 “avatar” 字符串传递给 className,作为 CSS 的 class。但 src={user.imageUrl} 会读取 JavaScript 的 user.imageUrl 变量,然后将该值作为 src 属性传递:
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
在上面示例中,style={{}}
并不是一个特殊的语法,而是 style={ }
JSX 大括号内的一个普通 {}
对象。当你的样式依赖于 JavaScript 变量时,你可以使用 style
属性。
React 没有特殊的语法来编写条件语句,因此使用的就是普通的 JavaScript 代码。例如,if
语句、&&
和 ? :
运算符来选择性地渲染 JSX:
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
{isLoggedIn ? (<AdminPanel />) : (<LoginForm />)}
{/* 切勿将数字放在 && 左侧 */}
{isLoggedIn && <AdminPanel />}
</div>
);
切勿将数字放在 && 左侧.
JavaScript 会自动将左侧的值转换成布尔类型以判断条件成立与否。然而,如果左侧是 0,整个表达式将变成左侧的值(0),React 此时则会渲染 0 而不是不进行渲染。
依赖 JavaScript 的特性,例如 for
循环 和 array 的 map()
函数 来渲染组件列表。
在你的组件中,使用 map()
函数将这个数组转换为 标签构成的列表:
function CustomUl() {
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
直接放在 map() 方法里的 JSX 元素一般都需要指定 key 值!
使用 React 可以在 JSX 中添加 事件处理函数。其中事件处理函数为自定义函数,它将在响应交互(如点击、悬停、表单输入框获得焦点等)时触发。
如需添加一个事件处理函数,需要先定义一个函数,然后 将其作为 prop
传入 合适的 JSX 标签。
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<>
<button onClick={handleClick}>
Click me
</button>
<button onClick={() => {
alert('You clicked me!')
}}>
Click me
</button>
</>
);
}
注意,onClick={handleClick}
的结尾没有小括号!不要 调用 事件处理函数:你只需 把函数传递给事件 即可。当用户点击按钮时 React 会调用你传递的事件处理函数。
通常你会希望你的组件 “记住” 一些信息并展示出来,比如一个按钮被点击的次数。要做到这一点,你需要在你的组件中添加 state。
首先,从 React 引入 useState
:
import { useState } from 'react';
现在可以在组件中声明一个 state 变量:
function MyButton() {
const [count, setCount] = useState(0);
// ...
将从 useState
中获得两样东西:当前的 state(count
),以及用于更新它的函数(setCount
)。可以给它们起任何名字,但按照惯例会像 [something, setSomething]
这样为它们命名。
这里的
[
和]
语法称为数组解构,它允许你从数组中读取值。useState
返回的数组总是正好有两项。
第一次显示按钮时,count
的值为 0
,因为你把 0
传给了 useState()
。当你想改变 state 时,调用 setCount()
并将新的值传递给它。点击该按钮计数器将递增:
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
React 将再次调用你的组件函数。第一次 count
变成 1
。接着点击会变成 2
。继续点击会逐步递增。
如果你多次渲染同一个组件,每个组件都会拥有自己的 state。
注意,每个按钮会 “记住” 自己的
count
,而不影响其他按钮。
import { useState } from 'react';
export default function MovingDot() {
const [userInfo, setUserInfo] = useState({
age: 14,
height: 150
});
const addAge = () => {
// userInfo.age += 1; 数据改变了,但是不会触发视图更新
setUserInfo({
...userInfo,
age: age + 1,
})
}
const addHeight = () => {
// userInfo.height += 1; 数据改变了,但是不会触发视图更新
setUserInfo({
...userInfo,
height: height + 1,
})
}
return (
<div>
<p>
<span>年龄</span>: {userInfo.age}
</p>
<p>
<span>身高</span>: {userInfo.height}
</p>
<button onClick={addAge}>年龄 + 1</button>
<button onClick={addHeight}>身高 + 1</button>
</div>
);
}
避免使用 (会改变原始数组) | 推荐使用 (会返回一个新数组) | |
---|---|---|
添加元素 | push ,unshift |
concat ,[...arr] 展开语法 |
删除元素 | pop ,shift ,splice |
filter ,slice |
替换元素 | splice ,arr[i] = ... 赋值 |
map |
排序 | reverse ,sort |
先将数组复制一份 |
const [artists, setArtists] = useState([]);
setArtists(
[...artists, '张杰', '周杰伦', '李荣浩']
)
以 use
开头的函数被称为 Hook。useState
是 React 提供的一个内置 Hook。你可以在 React API 参考 中找到其他内置的 Hook。你也可以通过组合现有的 Hook 来编写属于你自己的 Hook。
Hook 比普通函数更为严格。你只能在你的组件(或其他 Hook)的 顶层 调用 Hook。如果你想在一个条件或循环中使用 useState
,请提取一个新的组件并在组件内部使用它。
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
// 给 prop 指定一个默认值
function Avatar({ person, size = 100 }) {
// ...
}
这种语法被称为 “解构”,等价于于从函数参数中读取属性:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
有时候,传递 props 会变得非常重复:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
重复代码没有错(它可以更清晰)。但有时你可能会重视简洁。一些组件将它们所有的 props 转发给子组件,正如 Profile
转给 Avatar
那样。因为这些组件不直接使用他们本身的任何 props,所以使用更简洁的“展开”语法是有意义的:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
将内容嵌套在 JSX 标签中时,父组件将在名为 children
的 prop 中接收到该内容。
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}