React教程
一、demo
<!DOCTYPE html>
<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">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
</body>
</html>
二、安装
官方CDN,生成环境不要用
<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>
需要3个库:
- react.min.js React的核心库
- react-dom.min.js react的dom相关库
- babel.min.js 对不支持es6的浏览器的兼容
使用npm构建环境
三、元素渲染
<div id="example"></div>
<script type="text/babel">
function tick() {
const element = (
<div>
<h2>now is {new Date().toLocaleTimeString()}</h2>
</div>
)
ReactDOM.render(element, document.getElementById('example'))
}
setInterval(tick, 1000)
</script>
- 定义一个element,它是React的元素对象
- 通过
ReactDOM.render
,把元素渲染到dom里面 - 渲染后,修改元素不会同步到dom,如果要修改dom,需要重新渲染,也就是调用render函数
- element只能是const,也就是只读类型
每次修改dom,就要重新获取元素,所以把元素的获取封装起来,就比较方便了。可以通过元素函数,或者元素类来封装。名字必须大写开头。
元素函数
function Clock(props) {
return (
<div>
<h2>now is {props.date.toLocaleTimeString()}</h2>
</div>
);
}
function tick() {
ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('example'));
}
setInterval(tick, 1000);
- 定义了获取元素的函数Clock
- 通过
<Clock date={new Date()}/>
,这个代码会执行Clock函数,然后把入参存在函数的props属性
元素类
除了可以封装为函数外,也可以封装为类。
class Clock extends React.Component {
render() {
return (
<div>
<h2>now is {this.props.date.toLocaleTimeString()}</h2>
</div>
);
}
}
function tick() {
ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('example'));
}
- 定义Clock类,继承React.Component
- 定义render返回元素对象
- 通过this.props访问props属性
- 调用方法和函数一样,都是
<Clock date={new Date()}/>
四、JSX
React使用JSX来代替js
JSX是很像XML的js语法拓展
JSX优点:
- 执行更快
- 类型安全,会检查编译错误
- 使用模板更加快捷简单
JSX是用来在js语法中,声明React的元素的(也就是html代码),但是又不像字符串。
var arr = [
<h1>h111</h1>,
<h2>h222</h2>,
3
]
var style = {
fontSize: 100,
color : 'blue'
}
const element1 = <h1>
<p style={style} font='1px' data-myattribute="kevinlu">{1 + 1},{1 == 1 ? 2 : 3}</p>
element1
{/*注释...*/}
<div>{arr}</div>
</h1>
function tick() {
ReactDOM.render(element1, document.getElementById('example'));
}
- 这样就声明了一个元素element1 ,但是又不需要用引号包裹。声明时可以换行。最外层只能是一个标签。
- 例如这样是不行的:
const ele = <p>1</p><p>2</p>
因为最外层有2个标签 - 这样可以:
const ele = <div><p>1</p><p>2</p></div>
,通过外层加一个div标签。
- 例如这样是不行的:
- 如果要定义自定义属性,可以:
<p font='15pt' data-myattribute = "kevinlu"></p>
data-前缀 - 可以在元素中使用js表达式,用{}包裹
- 不支持if else,但可以用三元运算符
- 支持内联样式,把样式定义在一个字典里面,然后再元素里引用字典
- 支持数组,数据里面的元素可以是元素,元素会被逐一展示
五、组件
一个元素可以以组件形式被另一个元素引用,而且可以传参数。
function Con(props) {
return (
<div>
<h2>I am Con name={props.name}</h2>
</div>
);
}
const ele = <div><Con name="kevinlu"></Con><h3>content</h3></div>
ReactDOM.render(ele, document.getElementById('example'));
- Con是一个组件,通过
props.name
获取入参。组件也可以用类的形式来定义 - ele 是一个元素,引用了Con组件,然后传输了入参name="kevinlu"
- 也可以创建复合组件,就是多个组件组合为一个新组件
- 组件必须大写开头,区别于html默认标签都是小写开头
六、组件状态
为组件赋予状态属性,通过修改状态来动态更新DOM
class Clock extends React.Component {
constructor(props) { //构建函数
super(props);
this.state = {date: new Date()}
}
componentDidMount() {//当DOM被创建(挂载),执行该函数
this.timerId = setInterval(() => this.tick(), 1000) //() => this.tick()相当于定义了一个箭头函数,()表示无入参
}
componentWillUnmount() { // 当DOM被删除(卸载),执行改函数
clearInterval(this.timerId)
}
tick() {//通过setState来更新state,如果直接更新,例如this.state.date=new Date(),不会同步到DOM
this.setState({date: new Date()})
}
render() {
return <div>
<h1>Time</h1>
<h2>Now is {this.state.date.toLocaleTimeString()}</h2>
</div>
}
}
ReactDOM.render(
<Clock></Clock>,
document.getElementById('example')
)
- 执行ReactDOM.render 渲染Clock元素到
- 生成一个Clock实例
- 执行构建函数constructor
- 执行render生成元素的html代码
- 渲染元素的html到DOM
- 执行componentDidMount钩子,里面设置了一个定时器,定时器ID存储在实例的变量this.timerId中
- 定时器定时执行
() => this.tick()
,- 这是一个箭头函数,直接传this.tick是不行的,因为会丢失this参数。
- 箭头函数执行this.tick也就是Clock实例的tick函数,
- tock函数通过setState更新Clock实例的state里面的date属性,更新为当前时间
- setState函数会做:
- 更新变量
- 重新这些render生成html
- 把html更新到DOM
- 所以如果直接更新变量,例如this.state.date=new Date(),是不会同步到DOM的
- 当DOM被删除componentWillUnmount函数会被执行,然后删除定时器
七、props
类型检查,元素组件支持入参,props支持类型检查,检查入参是否符合要求,如果不符合,或早console显示warning信息。
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
function Clock(props) {
return <div>{props.name}</div>
}
Clock.propTypes = {
name: PropTypes.string
}
ReactDOM.render(
<Clock name={123}></Clock>,
document.getElementById('example')
)
- Clock要求的name入参类型是string
- 要加入babel文件的引入
- 传入了number,所以警告:
react.development.js:372 Warning: Failed prop type: Invalid prop `name` of type `number` supplied to `Clock`, expected `string`.
in Clock
支持的类型:
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
八、事件
元素里面的事件,需要调用函数来触发。
class Toggle extends React.Component{
constructor(props){
super(props);
this.state={isToggleOn:true}
this.handleClick=this.handleClick.bind(this) //需要绑定this
}
handleClick(){
this.setState({isToggleOn:!this.state.isToggleOn})
}
render(){
return <button onClick={this.handleClick}>
{this.state.isToggleOn?'ON':'OFF'}
</button>
}
}
ReactDOM.render(
<Toggle></Toggle>,
document.getElementById('example')
)
- 这里写了一个开关功能,点击变ON,再次点击变OFF,以此循环。
- 设置buttom的
onClick={this.handleClick}
,表示当buttom被点击,会调用handleClick函数 - 这里需要事先为handleClick函数绑定this,否则在handleClick函数里面访问this会返回undefined,而不是Toggle的实例
不需要绑定this的方法
方法1:
class ToggleTest extends React.Component {
handleClick = () => {
console.log('this is ', this)
}
render() {
return <button onClick={this.handleClick}>xxx</button>
}
}
ReactDOM.render(<ToggleTest ></ToggleTest >,document.getElementById('example'))
- 修改定义handleClick 的方法,加上
() =>
,这个叫:属性初始化器语法
方法2:
class ToggleTest extends React.Component {
handleClick () {
console.log('this is ', this)
}
render() {
return <button onClick={(e)=>this.handleClick(e)}>xxx</button>
}
}
ReactDOM.render(<ToggleTest ></ToggleTest >,document.getElementById('example'))
- onclick的时候,填入箭头函数。这里相当于重新定义了一个函数。如果向更低阶的元素组件传递函数,会重复构建函数,导致性能问题。
传递参数
class ToggleParam extends React.Component {
handleClick(name, e) { // e是React的事件实例,需要放在最后面
alert(name)
}
handleClick1(name) { // 也可以不要e参数
}
render() {
return <button onClick={this.handleClick.bind(this, 'kevinlu')}>xxx</button>
}
}
ReactDOM.render(<ToggleParam></ToggleParam>,document.getElementById('example'))
- e是React的事件实例,需要放在所有参数最后面,也可以不设置e参数。
- 这里通过bind的方法,绑定this和name参数,bind会返回一个新函数,当按钮被点击,React会调用该函数
- 可以通过
e.preventDefault();
来阻止默认的操作。例如点击链接会跳转到新页面,这个命令可以阻止跳转操作。
九、条件渲染
1. if条件判断
function If(props) {
if (props.isLogin) {
return <h1>is Login</h1>
} else {
return <h1>is not Login</h1>
}
}
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {isLogin: false}
}
login() {
this.setState({isLogin: true})
}
logout() {
this.setState({isLogin: false})
}
render() {
if (this.state.isLogin)
return <div>now is login <button onClick={this.logout.bind(this)}>logout</button></div>
else
return <div>now is logout <button onClick={this.login.bind(this)}>login</button></div>
}
}
ReactDOM.render(<If isLogin={false}></If>, document.getElementById('example'))
- If和Login两个元素组件,都是根据属性,来判断render的返回html
2.&&运算符
function At(props) {
return <div>kkkkk {props.number > 0 && <h2>number is {props.number} </h2>}</div>
}
- 如果props.number,显示元素
<h2>number is {props.number} </h2>
3. 三目运算符
和IF类似,不过它可以在元素里面通过{}的方式定义:{props.number ?"large 0":'least 0'}
4. 阻止渲染
直接让元素组件返回null,就不会渲染这个元素。
function NoneTest(props) {
if (props.number > 0)
return <div>number is {props.number}</div>
else
return null
}
十、数组
var l = [1, 2, 3, 4, 5]
const ele = <ul>
{l.map(
(number) => <li key={number}>{number}</li>
)}
</ul>
ReactDOM.render(ele, document.getElementById('example'))
- 通过JS的map方法,遍历l数组,对每个元素执行
(number) => <li>{number}</li>
函数,返回的是元素数组 - 对于元素列表,React会遍历数组,逐个显示,所以url标签里面会有5个li标签
- 数组构建的元素,需要包含一个唯一的key,不然会报错。key一般是数组元素的id,如果没有id可以用下标,即
(number,index) => <li key={index}>{number}</li>
十一、组件API
- setState(object nextState[, function callback]) 设置状态
*callback可选,会在修改状态后,渲染DOM后被调用- 没有设置的key,不会被替换
- replaceState(object nextState[, function callback]) 替换状态
- 和setState类似,区别是没有设置的key,会被设置为undefined
- setProps(object nextProps[, function callback]) 设置props
- replaceProps(object nextProps[, function callback])
- forceUpdate([function callback]) 通知组件重绘DOM
- DOMElement findDOMNode() 获取组件对应的DOM元素,如果没有,返回null
- bool isMounted() 判断组件挂载状态
十二、组件生命周期
生命周期有:
- Mounting 已挂载,也就是已插入到真实的DOM
- Updating 更新中,重新渲染
- Unmounting 已移出真实的DOM
方法:
- componentWillMount 组件将要挂载前调用
- componentDidMount 组件挂载完成调用
- componentWillReceiveProps 组件接收一个新的prop时被调用,第一次渲染不会调用。主要用户组件的更新
- shouldComponentUpdate 返回bool值,在组件接收到新的props或者state时被调用,第一次渲染不调用
- componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用
- componentDidUpdate 更新完成后调用,第一次渲染不调用
- componentWillUnmount 组件被移除前调用
十三、AJAX
一般在componentDidMount 中通过AJAX获取数据,然后通过setState更新组件。
class AjaxTest extends React.Component{
constructor(props) {
super(props);
this.state={content:"test"}
}
componentDidMount() {
this.req = $.get('/demo/react_demo/data', function (res) {
this.setState({content: res})
}.bind(this))
}
componentWillUnmount(){
this.req.abort()
}
render(){
return <div>{this.state.content}</div>
}
}
- 这里用了jq的ajax方法,导入:`
- get的回调函数需要bind this 不然回调函数里面访问this,不会执行组件实例
- componentWillUnmount当组件被卸载,关闭http请求
`
十四、表单
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {name: "unknow"}
}
onAttrChange(key, e) {
var state = {}
state[key] = e.target.value
this.setState(state, () => console.info('new name', this.state.name))
}
render() {
return <div>名字:<input value={this.state.name} onChange={this.onAttrChange.bind(this, 'name')}></input></div>
}
}
- 表单的值修改后,需要通过onChange事件,更新新的值到组件的变量
如果是嵌套组件,需要把父组件的this传给子组件
function Input(props) {
return <input value={props.this.state.name} onChange={props.this.onAttrChange.bind(props.this, 'name')}></input>
}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {name: "unknow"}
}
onAttrChange(key, e) {
var state = {}
state[key] = e.target.value
this.setState(state, () => console.info('new name', this.state.name))
}
render() {
return <div>名字:<Input this={this}></Input></div>
}
}
Select
class Select extends React.Component {
constructor(props) {
super(props);
this.state = {value: "unknow"}
}
onAttrChange(key, e) {
var state = {}
state[key] = e.target.value
this.setState(state, () => console.info('new value', this.state.value))
}
render() {
return <div><select value={this.state.value} onChange={this.onAttrChange.bind(this, 'value')}>
<option value="gg">Google</option>
<option value="fb">Facebook</option>
</select></div>
}
}
十五、Ref
我们可以在元素里面的某个标签,设置一个id,然后通过ref,获取到这个标签。
class RefTest extends React.Component {
constructor(props) {
super(props);
}
onClick() {
var name = this.refs.myRef //获取到input实例
console.info(name.value)//获取input的值
}
render() {
return <div>
name: <input ref="myRef"></input>
<button onClick={this.onClick.bind(this)}>BTN</button>
</div>
}
}
- 这里我们为input 加了一个ref属性
- 在onClick里,通过
this.refs.myRef
可以获取到这个input的实例,然后获取它的value值