内容
- React元素渲染
- React JSX
- React 组件
- React State(状态)
1,元素渲染
元素是构成React应用的最小单位,用于描述屏幕输出的内容。注意:与浏览器的DOM元素不同,React中的元素事实上是普通的对象,React DOM 可以确保 浏览器DOM 的数据内容与React元素保持一致。
const element = <h1>Hello, world!</h1>;
1.1,将元素渲染到DOM中
首先需要在HTML页面中添加一个id="example" 的 <div> ,在此div中的所有的内容都将由React DOM 来管理,所以将其称作 “根”DOM节点。
<div id="example"></div>
说明:用React开发应用时,一般只会定义一个根节点,但如果是在一个已有的项目中引入React的话,可能会需要在不同的部分单独定义React根节点。
要将React元素渲染到DOM节点中,需要将元素都传递给ReactDOM.render() 的方法来将其渲染到页面上。 示例:
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
const element =<h1>Hello, world!</h1>;
ReactDOM.render(
element,
document.getElementById('example')
);
</script>
</body>
</html>
解释:在示例中引用了三个库:
- react.development.min.js:React的核心库
- react-dom.development.min.js :提供与DOM相关的功能
- babel.min.js:Babel可以将ES6代码转为ES5代码,这样就能在不支持ES6的浏览器上执行React代码.除此之外,Bable还内嵌了对JSX的支持.将Babel和babel-sublime 包一同使用,可以让源码的语法渲染上升到一个全新的水平
1.2,更新元素渲染
React元素都是不可变的,当元素被创建后,将无法改变其内容或属性。目前更新界面的唯一方式是创建一个新的元素,然后将其传入ReactDOM.render()方法中,如下示例:
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('example')
);
}
setInterval(tick, 1000);
</script>
</body>
</html>
代码解释:该示例通过 setInterval() 方法,每秒钟调用一次 ReactDOM.render()。可以将要展示的部分封装起来, 以下用一个函数表示该示例:
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('example') );
}
setInterval(tick, 1000);
除了函数外,还可以创建一个React.Component 的 ES6 类,该类封装了要展示的元素,需要注意的是在 render() 方法中,需要使用 this.props 替换 props。
React只会更新必要的部分:React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
2,JSX
React使用JSX来代替常规的JavaScript。JSX是一个看起来很像XML的JavaScript语法扩展。也可以不使用JSX,但使用JSX有以下优点:
- JSX执行更快,因为它在编译为JavaScript代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用JSX编写模板更加简单快速。
示例:下面的标签语法既不是字符串也不是HTML,它称为JSX,一种JavaScript的语法扩展,推荐使用JSX来描述用户界面。JSX是在JavaScript内部实现的。
const element = <h1>Hello, world!</h1>;
说明:元素是构成React应用的最小单位,JSX就是用来声明React当中的元素。与浏览器的DOM元素不同,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。要将 React 元素渲染到根 DOM 节点中,需要通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上。
说明:ReactDOM.render 是 React 的最基本方法用于将模板转为 HTML 语言,并插入指定的 DOM 节点。ReactDOM.render(template,targetDOM) 方法接收两个参数:
- template:创建的模板,多个 dom 元素外层需使用一个标签进行包裹,如 <div>。
- targetDOM:可以理解为插入该模板的目标位置。
注意:由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。如:给 input 添加 className 并更改样式
<input type="text" className="userName" value={value}/>
.userName{background: yellow} // 在CSS样式中定义
使用JSX示例:
ReactDOM.render(
<div>
<h1>菜鸟教程</h1>
<h2>欢迎学习 React</h2>
<p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>
</div>
,
document.getElementById('example')
);
代码解释:JSX类似一个HTML,如果需要嵌套多个HTML标签,需要使用一个div元素包裹。该示例中p元素添加了自定义属性data-myattribute,添加自定义属性需要使用data-前缀。
JavaScript表达式
可以在JSX中使用JavaScript表达式,表达式写在花括号{}中。如:
ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);
在JSX中不能使用if else语句,但可以使用三元运算表达式来替代。如:
ReactDOM.render(
<div>
<h1>{i == 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
样式
React推荐使用内嵌样式,可以使用camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。如:
<div id="example"></div>
<script type="text/babel">
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>菜鸟教程</h1>,
document.getElementById('example')
);
</script>
数组
JSX允许在模板中插入数组,数组会自动展开所有的成员。如:
<div id="example"></div>
<script type="text/babel">
var arr = [
<h1>菜鸟教程</h1>,
<h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
</script>
3,组件
React中有两种组件:
- 函数组件,传递参数只需要props 对象即可,不需要使用this,如:
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
const element = <HelloMessage />;
ReactDOM.render(
element,
document.getElementById('example')
);
- 类组件,需要继承React.Component(可能还需要写构造函数),传递参数时需要使用this.props 对象,如:
function HelloMessage(props) extends React.Component{
return <h1>Hello {this.props.name}!</h1>;
}
const element = <HelloMessage name="Runoob"/>;
ReactDOM.render(
element,
document.getElementById('example')
);
说明:const element = 为用户自定义组件(可以单标签也可双标签)。不能在组件名内使用style样式(不会生效)。
注意:原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
复合组件
可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。如:
function Name(props) {
return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
return <h1>网站地址:{props.url}</h1>;
}
function Nickname(props) {
return <h1>网站小名:{props.nickname}</h1>;
}
function App() {
return (
<div>
<Name name="菜鸟教程" />
<Url url="http://www.runoob.com" />
<Nickname nickname="Runoob" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
4,State(状态)
React把组件看作一个状态机(State Machines)。通过与用户进行交互,实现不同状态,然后渲染UI,使用户界面和数据保持一致。在React中,只需要更新组件的state,然后根据新的state重新渲染界面(不需要操作DOM)。如:
<div id="example"></div>
<script type="text/babel">
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
</script>
代码解释:创建一个组件类,在render()方法中使用this.state来修改当前的时间,并添加一个构造器来初始化this.state(需要调用父类的构造函数)。
将生命周期方法添加到类中
在具有多个组件的应用程序中,当销毁时释放组件所占用的资源非常重要。
- 挂载(Mount):每当组件第一次加载到DOM中时,我们想要生成定时器,这在React中称之为挂载。
- 卸载(UnMount):每当组件生成的这个DOM被移除时,我们也想要清除定时器,这在React中被称为卸载。
可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
代码解释:componentDidMount() 与 componentWillUnmount() 方法被称作生命周期钩子(hook)。在组件输出到 DOM 后会执行 componentDidMount() 钩子,我们就可以在这个钩子上设置一个定时器。this.timerID 为定时器的 ID,我们可以在 componentWillUnmount() 钩子中卸载定时器。
代码执行顺序:
- 当
<Clock />被传递给ReactDOM.render()时,React 调用Clock组件的构造函数。 由于Clock需要显示当前时间,所以使用包含当前时间的对象来初始化this.state。 稍后会更新此状态。 - React 然后调用
Clock组件的render()方法。这是 React 了解屏幕上应该显示什么内容,然后 React 更新 DOM 以匹配Clock的渲染输出。 - 当
Clock的输出插入到 DOM 中时,React 调用componentDidMount()生命周期钩子。 在该示例中,Clock组件要求浏览器设置一个定时器,每秒钟调用一次tick()。 - 浏览器每秒钟调用
tick()方法。 在该示例中,Clock组件通过使用包含当前时间的对象调用setState()来调度UI更新。 通过调用setState(),React 知道状态已经改变,并再次调用render()方法来确定屏幕上应当显示什么。 这一次,render()方法中的this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。 - 一旦
Clock组件被从 DOM 中移除,React 会调用componentWillUnmount()这个钩子函数,定时器也就会被清除。
数据自顶向下流动
父组件或子组件都不能知道某个组件是否有状态。并且它们不应该关心某一个组件是哪种(函数或类),这就是为什么状态通常被称为局部或封装。除了拥有并设置它的组件外,其他组件不可访问。示例:FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入:
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
这通常被称为自顶向下或单向数据流。任何状态始终由某些特定组件所拥有,并且从该状态导出的任何数据或UI只能影响树中下方的组件。可以将组件树看作属性的瀑布,每个组件的状态就是一个额外的水源,它连接在任意点,但也能留下来。