元素的概念
元素是构成 React 应用的最小单位。元素描述了你在屏幕上想看到的内容。React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
将一个元素渲染为 DOM
假设我们的 HTML 文件有一个根元素 <div>
该节点内的所有内容都将由 React DOM 管理
<div id="root"></div>
注意: React 构建的应用通常只有单一的根 DOM 节点,如果你想要将 React 集成进一个已有搭建好的应用,那么你可以在应用中包含任意多的独立根 DOM 节点。
将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render():
// 已有的应用
<div>
<h1>Existing App</h1>
<div id="root1"></div>
<div id="root2"></div>
</div>
import ReactDOM from 'react-dom';
// React 组件
const element1 = <h1>Hello, world</h1>;
const element2 = <h1>Hello, world</h1>;
ReactDOM.render(element1, document.getElementById('root1'));
ReactDOM.render(element2, document.getElementById('root2'));
更新已渲染的元素
React 元素是不可变对象。一旦创建,React 元素的子元素和属性就不能直接更改
时钟组件示例:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
//组件挂载到 DOM 后,每一秒调用一次tick()方法
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
//使用 `setState` 方法更新组件的状态,将 `date` 属性更新为当前时间的 `Date` 对象
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
代码分析:
- 首先,我们定义了一个名为
Clock
的类,它继承了React.Component
。 - 在构造函数中,我们初始化了组件的状态(
state
),其中date
属性被设置为当前时间的Date
对象。 - 在组件挂载到 DOM 后,
componentDidMount
生命周期方法会被调用。在这个方法中,我们创建了一个定时器,并将定时器的 ID 存储在组件的实例变量timerID
中。定时器每秒触发一次tick
方法。 tick
方法被调用时,它会使用setState
方法更新组件的状态,将date
属性更新为当前时间的Date
对象。通过调用setState
,React 会检测到状态的变化,并触发重新渲染。render
方法定义了组件的渲染逻辑。它返回一个包含两个<h1>
和<h2>
元素的<div>
。其中<h1>
显示 “Hello, world!”,而<h2>
则显示当前时间,通过访问组件的状态中的date
属性来获取时间,并使用toLocaleTimeString
方法将其格式化为本地时间字符串。- 最后,我们使用
ReactDOM.createRoot
创建一个根节点,并将<Clock />
组件传递给root.render
方法,以将组件渲染到指定的根节点上。
这里的生命周期 componentWillUnmount()和 componentDidMount()以及State后面会讲到
注意: 在实践中,大多数 React 应用只会调用一次 ReactDOM.render()。
函数组件与 class 组件
React定义组件的方式主要有两种,函数组件(目前主流)和class组件
函数式组件示例:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”
class组件示例
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
使用自定义组件方式:
const element = <Welcome name="zayyo" />;
当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。在上面的示例中name的值“zayyo”就是组件中的的props.name获取到的值。
过程:
- 我们调用 root.render() 函数,并传入
<Welcome name="zayyo" />
作为参数。 - React 调用 Welcome 组件,并将
{name: 'zayyo'}
作为 props 值传入。 - Welcome 组件将
<h1>Hello, zayyo</h1>
元素作为返回值。 - React DOM 将 DOM 高效地更新为
<h1>Hello, zayyo</h1>
。
注意: 组件名称必须以大写字母开头。因为React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div />
代表 HTML 的 div 标签,而 <Welcome />
则代表一个组件,并且需在作用域内使用 Welcome
。
组合组件
我们可以使用多个自定义组件来组成我们的页面。
在下面的代码示例中,将会渲染三个 Welcome 组件,分别显示 “Hello, z”、“Hello, A” 和 “Hello, Y”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="z" />
<Welcome name="A" />
<Welcome name="Y" />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
在上面,我们使用 <Welcome name="z" />
、<Welcome name="A" />
和 <Welcome name="Y" />
分别创建了三个 Welcome
组件的实例,并将它们放置在 <div>
中。
每个 Welcome
组件都会接收不同的 name
属性值,通过 props.name
来访问该属性值,并在组件内部的 <h1>
元素中渲染相应的问候消息。