Hello World
最简单的代码示例如下
ReactDOM.render(<h1>Hello world</h1>, document.getElementById('root'))
什么是jsx
概念
jsx是javascript的语法扩展,通常与react一起使用,用来描述UI的外观,jsx具有javascript的全部功能。
简单示例
这是一个简单的jsx示例
const element = <h1>Hello world</h1>
等同于
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
为什么选择jsx
react本质上是一个渲染器,通过将标记与逻辑松散耦合的组件来实现,react不需要使用jsx,也可以使用React.createElement去描述UI,但是jsx用来描述UI更加方便简洁,而且还允许react显示更多有用的错误和警告信息。
复杂示例
jsx编译后成为常规的javascript函数调用,这意味着如果使用函数方法,可以在jsx中使用if语句和for循环进行变量的处理,jsx中还可以嵌入表达式、指定属性、绑定事件等
const formatName = (user) => {
return user.name1 + ' ' + user.name2
}
const buttonClick = (e) => {
alert(e)
}
const user = {
name1: 'name1',
name2: 'name2',
}
const buttonType = "button"
const element = (
<button
type={buttonType}
onClick={buttonClick}
>
Hello, {formatName(user)}
</button>
)
ReactDOM.render(element, document.getElementById('root'))
进阶示例
下方示例展示了
- 绑定事件
- 使用表达式
- 遍历数据
- 嵌套结构
- 嵌入变量
- 设置属性
// 使用表达式
const formatName = (user) => {
return user.name1 + ' ' + user.name2
}
// 绑定事件
const buttonClick = (e) => {
alert(e)
}
// 遍历数据
const list = ['list1', 'list2', 'list3', 'list4']
const renderList = (list) => {
return list.map((item,index)=>{
return <span key={index}>{item}</span>
})
}
// 按钮内容
const user = {
name1: 'name1',
name2: 'name2',
}
// 按钮颜色
const color = '#ccc'
// 设置属性
const buttonType = "button"
const element = (
<div>
{renderList(list)}
<button
// 设置css
style={{'color': color}}
// 设置属性
type={buttonType}
// 绑定事件
onClick={buttonClick}
>
Hello, {formatName(user)}
</button>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
当然,上面的示例都只是一些简单的示例,后面还有更复杂的示例
组件
概念
组件就像javascript函数,它们接受任意的输入(props),并返回描述应该在屏幕上显示的内容的react元素
需要注意的是,组件需要以大写为开头
在react中组件的形式分为两种,一种是函数式组件,另外一种是类组件
函数式组件
函数式组件有以下特性:
- 自身没有状态
- 返回一个元素
- 没有生命周期
在react中所有的函数组件,必须是纯函数,即不改变原有属性
function Welcome(props){
return <h1>Hello, {props.name}</h1>
}
类组件
类组件有以下特性:
- 使用es6的class方式声明组件
- 需要继承 React.Component 来使用react的生命周期
- 有声明周期
- 自身有状态
- 可以通过调用setState方法去更新组件
- render函数中通过 retur一个元素进行渲染
class Welcom extends React.Component {
render(){
return <h1>Hello, {this.props.name}</h1>
}
}
示例
在jsx中不只是html标签可以使用,我们声明的组件也可以使用
function Welcome(props){
return <h1>Hello, {props.name}</h1>
}
const element = <Welcome name="name1" />
ReactDOM.render(
element,
document.getElementById('root')
)
复杂示例
组件可以复用,可以嵌套,我们可以把结构相同或者某一功能封装成一个组件进行使用,不要害怕将组件拆分成较小的组件
function Nav (props){
return (
<div className="nav">
<img src={props.logo} alt="logo" />
<ul>
<li><a href={props.nav[0].href}>{props.nav[0].name}</a></li>
<li><a href={props.nav[1].href}>{props.nav[1].name}</a></li>
<li><a href={props.nav[2].href}>{props.nav[2].name}</a></li>
<li><a href={props.nav[3].href}>{props.nav[3].name}</a></li>
</ul>
<div className="user">
<img src={props.user.pic} alt="user" />
<span>{props.user.name}</span>
</div>
</div>
)
}
const element = (
<div className="nav-warp">
<Nav logo="" nav={[]} user={{pic:'', name:''}} />
</div>
)
ReactDOM.render(
element,
document.getElementById('root')
)
上面是一个导航栏的结构,我们可以将它细分一下
function Logo(props) {
return <img src={props.logo} alt="logo" />
}
function NavList(props) {
return (
<ul>
{props.nav.map((item, index) => {
return (
<li key={index}><a href={item.href}>{item.name}</a></li>
)
})}
</ul>
)
}
function User(props) {
return (
<div className="user">
<img src={props.user.pic} alt="user" />
<span>{props.user.name}</span>
</div>
)
}
function Nav(props){
return (
<div className="nav">
<Logo logo="" />
<NavList nav={[]} />
<User user={{pic:'', name:''}}/>
</div>
)
}
const element = (
<div className="nav-warp">
<Nav />
</div>
)
ReactDOM.render(
element,
document.getElementById('root')
)
状态和生命周期
以es6的class声明的组件继承了 React.Component 会拥有自己的状态和生命周期
state && setState
state是指组件自身的状态
用状态控制组件变化 可以把一个组件看做一个状态机, 每一次状态对应于组件的一个 ui
需要注意的是setState是一个异步函数,并不是更改以后就立刻会生效的
import React, { Component } from 'react';
class StateDemo extends Component {
state = {
secondsElapsed: 0
}
tick(){
this.setState({ secondsElapsed: this.state.secondsElapsed + 1 });
}
componentDidMount(){
this.interval = setInterval( this.tick.bind(this), 1000 );
}
componentWillUnmount(){
clearInterval(this.interval);
}
render(){
return (
<div>目前已经计时:{this.state.secondsElapsed}秒</div>
)
}
}
export default StateDemo;
props
props是指父组件传递给子组件的状态
通过 this.props 可以获取传递给该组件的属性值,还可以通过定义 getDefaultProps 来指定默认属性值(这是ES5的写法,ES6定义组件的默认props可以直接写props) 下面几个是props的常用API: this.props.children this.props.map this.props.filter props是调用组件的时候传递进去的数据,一般用于组件树数据传递
import React, { Component } from 'react';
class PropsDemo extends Component {
props = {
title: '这是默认的title属性值'
}
render(){
console.log(this.props);
return <b>{this.props.title}</b>
}
}
export default PropsDemo;
// 组件调用方式
// <PropsDemo title="设置的标题" />
函数组件更新页面
在不使用生命周期的情况下更更新状态我们只能通过不断的去执行ReactDOM.render函数去进行更新
var count = 0
function buttonClick() {
count = count + 1
render()
}
const render = () => {
const element = (
<div className="warp">
<div>点击了{count}次</div>
<button type="button" onClick={buttonClick}>点击</button>
</div>
)
ReactDOM.render(
element,
document.getElementById('root')
)
}
render()
类组件更新状态
在内组件内部可通过调用 setState 函数去更新状态,以达到更新页面的目的
class App extends React.Component {
state = {
count: 0
}
buttonClick = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
alert(this.state.count)
return (
<div className="warp">
<div>点击了{this.state.count}次</div>
<button type="button" onClick={this.buttonClick}>点击</button>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
生命周期
在上面的环节里,我们使用函数组件以及类组件实现了一个计数器的组件,使用到了render以及setState,在实际应用中,我们需要对组件的状态有更新详细的掌控,对组件性能的优化,以及实现更多的需求,这个时候就需要用到react的生命周期函数
下面篇幅较多,如果想看生命周期图,请直接往下翻
React生命周期主要包括三个阶段:初始化阶段、运行中阶段和销毁阶段;
初始化阶段
getDefaultProps()
设置组件的默认属性;可以用static defaultProps设置默认的props
getInitialState()
在使用es6的class语法时是没有这个钩子函数的,使用constructor代替;
一个react组件需要继承react Component这个基类,才能有render(),生命周期等方法可以使用,这也是为什么函数组件不能使用这些方法的原因; 而constructor里的super(props)用来调用基类的构造方法, 也将父组件的props注入给子组件,供子组件读取(组件中props只读不可变,state可变)。 而constructor()用来做一些组件的初始化工作,如定义this.state的初始内容,使用bing()方法绑定一些函数,使其this指向当前的这个类,这样就可以在函数中访问到这个类的所有方法;
componentWillMount() react 16.7移除
组件即将被渲染到页面之前触发;只会调用一次,调用this.setState()不会触发render 因为componentWillMount()将被删除,所以官方推荐使用constructor()替代该方法
render()
组件渲染到页面中,此时可以进行开启定时器、向服务器发送请求等操作 父组件重新render,props、state改变都会重新render,需要return出一个返回结果
componentDidMount()
组件已经被渲染到页面中后触发;只会调用一次; 此时页面中有了真正的DOM的元素,可以进行DOM相关的操作; 服务器端渲染时不会被触发;
运行中阶段
componentWillReceiveProps()
组件接收到属性时触发;react 16.7中被 static getDerivedStateFromProps() 替代
shouldComponentUpdate()
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候
一个父组件的重新更新会造成它旗下所有的子组件重新执行render()方法,形成新的虚拟DOM,再用diff算法对新旧虚拟DOM进行结构和属性的比较,决定组件是否需要重新渲染。
例如React中的就提供了一个PureComponent的类,当我们的组件继承于它时,组件更新时就会默认先比较新旧属性和状态,从而决定组件是否更新。值得注意的是,PureComponent进行的是浅比较,所以组件状态或属性改变时,都需要返回一个新的对象或数组
常见的用例有:根据state的变化设置变量,派发事件,开始动画
componentWillUpdate()
组件即将被更新时触发;react 16.7中被 getSnapshotBeforeUpdate() 替代
被调用于render之后,可以读取但无法使用DOM的时候。可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()的第三个参数。
componentDidUpdate()
组件被更新完成后触发。页面中产生了新的DOM的元素,可以进行DOM操作 有两个参数,分别是更新前的props和更新前的state
销毁阶段
componentWillUnmount()
组件被销毁时触发。这里我们可以进行一些清理操作,取消Redux的订阅事件,删除在componentDidMount或其他地方添加的事件监听,断开网络请求,清空计时器,清理在componentDidMount中创建的 DOM 元素
整个流程如下图所示
16.7之前
16.7之后
有兴趣的可以加群 885356143 react前端交流群