在网页的一般开发流程中,我们通常会通过 JS 操作 DOM (对应 HTML 的描述产生的树),以引起界面的一些变化响应用户的行为。例如,用户点击某个按钮的时候,JS 会记录一些状态到 JS 变量里边,同时通过 DOM API 操控 DOM 的属性或者行为,进而引起界面一些变化。
当项目越来越大的时候,你的代码会充斥着非常多的界面交互逻辑和程序的各种状态变量,显然这不是一个很好的开发模式,因此就有了 MVVM 的开发模式(例如 React, Vue),提倡把渲染和逻辑分离。
简单来说就是不要再让 JS 直接操控 DOM,JS 只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系即可。
对于props
props 是 React 组件的输入。它们是从父组件向下传递给子组件的数据。
要有产品的思想,你要实现在屏幕上输出一行字,可以使用react 的通用构造:
<div id="root"></div>
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(element, document.getElementById('root'));
1、直接到最后的ReactDOM.render() 函数,传入了前面定义的参数element,里面的值是 <Welcome name="Sara" />
。
2、element是一个标签(作用就是包裹,相当于div加/div),程序运行到这里时会调用Welcome 组件( 组件名称必须以大写字母开头),组件在最顶上早就编译好,里面包装好了各种标签,样式,等详细信息。
3、element标签启用的时候,同时传入props (也就是{name: 'Sara'}
),如果Welcome 组件用到,就会有返回,这里是返回一个标签,标签里的内容是通过props(也就是{name: 'Sara'}
)传递进去的。
4、返回出来最后会到达ReactDOM.render()
函数对应的document.getElementById('root')
会找到public
文件夹下index.html的root
节点位置,整个react渲染完成。
上面是介绍props,下面举个时钟的例子来承上启下,官方链接
<div id="root"> //这里是Html
<!-- This element's contents will be replaced with your component. -->
</div>
function tick() { //这里是JS
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2> //根据本地时间把 Date 对象的时间部分转换为字符串,就是输出时间而已
</div>
);
ReactDOM.render( element, document.getElementById('root') );
}
setInterval(tick, 1000); //这是一个每秒钟运行的永动机
在实践中,大多数 React 应用只会调用一次 ReactDOM.render()。
这里是直接每秒钟调用tick函数,函数里有ReactDOM.render(),所以也就是每秒都调用 ReactDOM.render()。
每次调用Tick函数,都会自动把新日期给常量element赋值更新,这样的话简单粗暴。
对于state
下面是最终代码,也和上面一样,不过是在组件里实现更新实时时间。
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
class Clock extends React.Component { //这里定义了一个组件,包括继承谁(设置默认值),生命周期函数,渲染返回。
constructor(props) {
super(props);
this.state = {date: new Date()};
}
//componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行,所以,最好在这里设置计时器
componentDidMount() { //永动机启动,每秒去调用tick()更新时间,这个等于号把计时器的 ID 保存在 this 之中(this.timerID)
this.timerID = setInterval( () => this.tick(), 1000 );
}
componentWillUnmount() { //组件在DOM渲染以后启动上面的挂载函数,卸载的时候就要把timerID也清除掉
clearInterval(this.timerID);
}
tick() { //react发现state改变,就会自动重新render渲染
this.setState({ date: new Date() });
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render( <Clock />, document.getElementById('root'));
每次组件更新时 render 方法都会被调用,但只要在相同的 DOM 节点中渲染 ,就仅有一个 Clock 组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。
下面是程序运行流程,要注意的是,渲染过程它是自动进行的。