React是Facebook开发并开源的前端框架。javascript
当时他们的团队在市面上没有找到合适的MVC框架,就本身写了一个Js框架,用来架设Instagram(图片分享社交网络)。2013年React开源。html
React解决的是前端MVC框架中的View视图层的问题。前端
DOM(文档对象模型Document Object Model)java
文档react
<html> <body> <h1>Title</h1>
<p>A <em>word</em></p> </body> </html>
将网页内全部内容映射到一棵树型结构的层级对象模型上,浏览器提供对DOM的支持,用户能够是用脚本调用DOM API来动态的修改DOM结点,从而达到修改网页的目的,这种修改是浏览器中完成,浏览器会根据DOM的改变重绘改变的DOM结点部分。算法
修改DOM从新渲染代价过高,前端框架为了提升效率,尽可能减小DOM的重绘,提出了Virtual DOM,全部的修改都是如今Virtual DOM上完成的,经过比较算法,找出浏览器DOM之间的差别,使用这个差别操做DOM,浏览器只须要渲染这部分变化就好了。浏览器
React实现了DOM Diff算法能够高效比对Virtual DOM和DOM的差别。前端框架
JSX是一种JavaScript和XML混写的语法,是JavaScript的扩展。网络
React.render( <div> <div> <div>content</div> </div> </div>, document.getElementById('example') );
替换 /src/index.js 为下面的代码框架
import React from "react"; import ReactDom from "react-dom"; class Root extends React.Component{ render() { return <div>Hello World</div>; } } ReactDom.render(<Root/>,document.getElementById("root"));
保存后就会自动编译,并从新装载刷新浏览器端页面
import React from 'react'; 导入react模块
import ReactDOM from 'react-dom'; 导入react的DOM模块
class Root extends React.Component 组件类定义,从React.Component类上继承。这个类生成JSXElement对象即React元素。
render() 渲染函数。返回组件中渲染的内容。注意,只能返回惟一 一个顶级元素回去。
ReactDom.render(<Root/>, document.getElementById('root')); 第一个参数是JSXElement对象,第二个是DOM的Element元素。将React元素添加到DOM的Element元素中并渲染。
还可使用React.createElement建立react元素,第一参数是React组件或者一个HTML的标签名称(例如div、span)。
return React.createElement('div', null, 'why are you so cool.'); ReactDom.render(React.createElement(Root), document.getElementById('root'));
改写的代码为
import React from "react"; import ReactDom from "react-dom"; class Root extends React.Component{ render() { // return <div>Hello World</div>; return React.createElement('div', null, 'why are you so cool.'); } } // ReactDom.render(<Root/>,document.getElementById("root")); ReactDom.render(React.createElement(Root),document.getElementById("root"));
使用JSX更简洁易懂,推荐使用JSX语法。
增长一个子元素
import React from "react"; import ReactDom from "react-dom"; class Sub extends React.Component{ render() { return <div>Sub content</div> } } class Root extends React.Component{ render() { return (<div> <h2>hello, I'm Riper</h2> <br /> <Sub /> </div>); } } ReactDom.render(<Root/>,document.getElementById("root"));
注意:
一、React组件的render函数return,只能是一个顶级元素
二、JSX语法是XML,要求全部元素必须闭合,注意 <br /> 不能写成 <br>
每个React组件都有一个状态属性state,它是一个JavaScript对象,能够为它定义属性来保存值。
若是状态变化了,会触发UI的从新渲染。使用setState()方法能够修改state值。
注意:state是每一个组件本身内部使用的,是组件本身的属性。
依然修改/src/index.js
import React from "react" import ReactDom from "react-dom" class Root extends React.Component{ state = { p1 : "hello ", p2 : "world." }; render () { this.state.p1 = "where is "; //不推荐在render中修改state // this.setState({p1: "where is"}) // 递归了,不能够对还在更新中的state使用setState console.log("!!!!!!!!!!!!!!!!") // setTimeout(() => this.setState({p1:"where is "}),5000) //控制台会每隔五秒打印一次!!!!!!!!!!!,由于setState会致使render重绘,这里仍是会循环 return ( <div> <div>this say {this.state.p1}{this.state.p2}</div> </div>); } } ReactDom.render(<Root />,document.getElementById("root"));
若是将 this.state.p1 = 'where is ' 改成 this.setState({p1:'where is '}); 就会出警告。
可使用延时函数 setTimeout(() => this.setState({ p1: 'where is ' }), 5000); 便可。若是网页须要每一段时间更新一次,也可使用该方法。
import React from "react" import ReactDom from "react-dom" class Root extends React.Component{ state = { count : 0 }; handleClick(event) { console.log(event) console.log(event.target); this.setState({'count': ++this.state.count}) }; render () { return ( <div onClick={this.handleClick.bind(this)}> // 绑定,不然会有传递的this的坑 {this.state.count} </div>); } } ReactDom.render(<Root />,document.getElementById("root"));
将脚本放在网页中,传统网页实现
<html> <head> <script type="text/javascript"> function getEventTrigger(event) { x = event.target; // 从事件中获取元素 alert("触发的元素的id是:" + x.id); } </script> </head> <body> <div id="t1" onmousedown="getEventTrigger(event)"> 点击这句话,会触发一个事件,并弹出一个警示框 </div> </body> </html>
div的id是t1,鼠标按下事件捆绑了一个函数,只要鼠标按下就会触发调用getEventTrigger函数,浏览器会送给它一个参数event。event是事件对象,当事件触发时,event包含触发这个事件的对象。
属性 此事件发生在什么时候
onabort 图像的加载被中断
onblur 元素失去焦点
onchange 域的内容被改变
onclick 当用户点击某个对象时调用的事件句柄
ondblclick 当用户双击某个对象时调用的事件句柄
onerror 在加载文档或图像时发生错误
onfocus 元素得到焦点
onkeydown 某个键盘按键被按下
onkeypress 某个键盘按键被按下并松开
onkeyup 某个键盘按键被松开
onload 一张页面或一幅图像完成加载
onmousedown 鼠标按钮被按下
onmousemove 鼠标被移动
onmouseout 鼠标从某元素移开
onmouseover 鼠标移到某元素之上
onmouseup 鼠标按键被松开
onreset 重置按钮被点击
onresize 窗口或框架被从新调整大小
onselect 文本被选中
onsubmit 确认按钮被点击
onunload 用户退出页面
import React from 'react'; import ReactDom from 'react-dom'; class Toggle extends React.Component { state = {flag : true}; handleClick(event) { console.log(event.targer === this, event.target.id); console.log(this, this.state); this.setState({flag: !this.state.flag}) }; render() { return <div id="t1" onClick={this.handleClick.bind(this)}> 点击按钮会发生事件 {this.state.flag.toString()} </div> } } class Root extends React.Component { state = {p1: "Are you kidding me"} render () { setTimeout(() => this.setState({p1: "Yes!"}),5000) return ( <div> <div>{this.state.p1}</div> <br /> <Toggle /> </div> ) } } ReactDom.render(<Root />, document.getElementById('root'));
分析:
Toggle类,有本身的state属性
当render完成后,网页上有一个div标签,div标签对象捆邦了一个click事件,div标签内有文本内容。
点击左键后,触发了click方法关联的handleClick函数,改变了状态值。
状态的改变会致使render的重绘。
组件的state变化只会致使本身的render方法重绘。
注意:
{this.handleClick.bind(this)},不能外加引号
this.handleClick.bind(this) 必定要绑定this,不然当触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了。
console.log(event.target.id),取回的产生事件的对象的id,可是这不是咱们封装的组件对象。因此,console.log(event.target===this)是false。因此这里必定要用this,而这个this是经过绑定来的。
React中的事件
使用小驼峰命名
使用JSX表达式,表达式中指定事件处理函数
不能使用return false,若是要阻止事件默认行为,使用event.preventDefault()
props就是组件的属性properties。
把React组件当作标签使用,能够为其增长属性,以下
<Toggle name="school" parent={this} />
为上面的Toggle元素增长属性:
一、 name = "school" ,这个属性会做为一个单一的对象传递给组件,加入到组件的props属性中
二、 parent = {this} ,注意这个this是在Root元素中,指的是Root组件自己
三、在Root中为使用JSX语法为Toggle增长子元素,这些子元素也会被加入Toggle组件的props.children中
import React from 'react'; import ReactDom from 'react-dom'; class Toggle extends React.Component { state = { flag: true }; // 类中定义state handleClick(event) { // event.preventDefult() console.log(event.target.id); console.log(event.target === this); console.log(this); console.log(this.state); this.setState({ flag: !this.state.flag }); } render() {/* 注意必定要绑定this onClick写成小驼峰 */ return <div id="t1" onClick={this.handleClick.bind(this)}> 点击这句话,会触发一个事件。{this.state.flag.toString()}<br /> {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br /> {this.props.children} </div>; } } class Root extends React.Component { // 定义一个对象 state = { p1: '会发生', p2: '变化' }; // 构造函数中定义state // handleClick(event) { // console.log(this.state) // }; render() { // setTimeout(() => this.setState({ p1: '已经' }), 5000); setInterval(() => this.setState({ p1: '已经' }), 5000); return ( <div> <div>这里5秒 {this.state.p1}{this.state.p2}</div> <br /> <Toggle name="teststr" parent={this}> <hr /> <span>我是Toggle元素的子元素</span> </Toggle> </div>); } } ReactDom.render(<Root />, document.getElementById('root'));
有了props,能够在子组件中控制父组件中的state。
尝试修改props中的属性值,会抛出 TypeError: Cannot assign to read only property 'name' of object '#<Object>' 异常。
应该说,state是私有private的属于组件本身的属性,组件外没法直接访问。能够修改state,可是建议使用setState方法。
props是公有public属性,组件外也能够访问,但只读。
在修改state属性时,注意
react在处理事件时,会在传递给子组件后,再传递给父组件。好比子组件和父组件都绑了onclick事件。通常不会同时接受。
使用ES6的构造器,要提供一个参数props,并把这个参数使用super传递给父类
import React from 'react'; import ReactDom from 'react-dom'; class Toggle extends React.Component { constructor(prpos){ super(prpos) this.state = { flag: true }; // 类中定义state } handleClick(event) { this.setState({ flag: !this.state.flag }); console.log(this.props.parent); let r = this.props.parent; r.setState({p1:"hello !!!"}) } render() {/* 注意必定要绑定this onClick写成小驼峰 */ return <div style={{padding:"20px"}}> <span id="t1" onClick={this.handleClick.bind(this)} >点击这句话,会触发一个事件。{this.state.flag.toString()}</span> <br /> {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br /> {this.props.children} </div>; } } class Root extends React.Component { constructor(prpos){ super(prpos) this.state = { p1: '会发生', p2: '变化' }; // 构造函数中定义state } render() { // setTimeout(() => this.setState({ p1: '已经' }), 5000); setInterval(() => this.setState({ p1: '已经' }), 5000); return ( <div> <div>这里5秒 {this.state.p1}{this.state.p2}</div> <br /> <Toggle name="teststr" parent={this}> <hr /> <span style={{backgroundColor:"white"}}>我是Toggle元素的子元素</span> </Toggle> </div>); } } ReactDom.render(<Root />, document.getElementById('root'));
组件的生命周期可分红三个状态:
组件的生命周期的方法
装载时触发
componentWillMount: 渲染前调用,只在装载前调用一次,在客户端和服务端都执行
componentDidMount: 在第一次渲染后执行,只在客户端执行,以后组件已经生成了对应的DOM结构,能够经过this.getDOMNode()来进行访问。 若是你想和其余JavaScript框架一块儿使用,能够在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操做(防止异部操做阻塞UI)。只在装载完成后调用一次,在render以后
更新时触发,不会再首次render时调用
componentWillReceiveProps(nextProps) 在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用
shouldComponentUpdate(nextProps, nextState) 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
能够在你确认不须要更新组件时使用。
若是设置为false,就是不容许更新组件,那么componentWillUpdate、componentDidUpdate不会执行。
componentWillUpdate(nextProps, nextState) 在组件接收到新的props或者state但尚未render时被调用。在初始化时不会被调用。
componentDidUpdate(prevProps, prevState) 在组件完成更新后当即调用。在初始化时不会被调用。
卸载组件触发
componentWillUnmount在组件从 DOM 中移除的时候马上被调用。
由图可知
constructor构造器是最先执行的函数。
触发 更新生命周期函数 ,须要更新state或props。
所以,从新编写/src/index.js。
构造两个组件,在子组件Sub中,加入全部生命周期函数。
下面的例子添加是装载、卸载组件的生命周期函数
import React from 'react'; import ReactDom from 'react-dom'; class Sub extends React.Component { constructor(props) { console.log('Sub constructor') super(props); // 调用父类构造器 this.state = { count: 0 }; } handleClick(event) { this.setState({ count: this.state.count + 1 }); } render() { console.log('Sub render'); return (<div style={{ height: 200 + 'px', color: 'red', backgroundColor: '#f0f0f0' }}> <a id="sub" onClick={this.handleClick.bind(this)}> Sub's count = {this.state.count} </a> </div>); } componentWillMount() { // constructor以后,第一次render以前 console.log('Sub componentWillMount'); } componentDidMount() { // 第一次render以后 console.log('Sub componentDidMount'); } componentWillUnmount() { // 清理工做 console.log('Sub componentWillUnmount'); } componentWillReceiveProps(nextProps) { // props变动时,接到新props了,交给shouldComponentUpdate。 // props组件内只读,只能从外部改变 console.log(this.props); console.log(nextProps); console.log('Sub componentWillReceiveProps', this.state.count); } shouldComponentUpdate(nextProps, nextState) { // 是否组件更新,props或state方式改变时,返回布尔值,true才会更新 console.log('Sub shouldComponentUpdate', this.state.count, nextState); return true; // return false将拦截更新 } componentWillUpdate(nextProps, nextState) { // 赞成更新后,真正更新前,以后调用render console.log('Sub componentWillUpdate', this.state.count, nextState); } componentDidUpdate(prevProps, prevState) { // 赞成更新后,真正更新后,在render以后调用 console.log('Sub componentDidUpdate', this.state.count, prevState); } } class Root extends React.Component { constructor(props) { console.log('Root Constructor') super(props); // 调用父类构造器 // 定义一个对象 this.state = { flag: true, name: 'root' }; } handleClick(event) { this.setState({ flag: !this.state.flag, name: this.state.flag ? this.state.name.toLowerCase() : this.state.name.toUpperCase() }); } render() { return ( <div id="root" onClick={this.handleClick.bind(this)}> My Name is {this.state.name} <hr /> <Sub /> {/*父组件的render,会引发下一级组件的更新流程,致使props从新发送,即便子组件props没有 改变过*/} </div>); } } ReactDom.render(<Root />, document.getElementById('root'));
componentWillMount 第一次装载,在首次render以前。例如控制state、props
componentDidMount 第一次装载结束,在首次render以后。例如控制state、props
componentWillReceiveProps 在组件内部,props是只读不可变的,可是这个函数能够接收到新的props,能够对props作一些处理,this.props = {name:'roooooot'};这就是偷梁换柱。componentWillReceiveProps触发,也会走shouldComponentUpdate。
shouldComponentUpdate 判断是否须要组件更新,就是是否render,精确的控制渲染,提升性能。
componentWillUpdate 在除了首次render外,每次render前执行,componentDidUpdate在render以后调用。
不过,大多数时候,用不上这些函数,这些钩子函数是为了精确的控制。shouldComponentUpdate能够在程序中拦截,控制属性的值。
import React from "react"; import ReactDom from "react-dom" let Root = props => <div>{props.schoolName}</div> ReactDom.render(<Root schoolName="magedu" />, document.getElementById('root'));
无状态组件,也叫函数式组件
开发中,不少状况下,组件其实很简单,不须要state状态,也不须要使用生命周期函数。无状态组件很好的知足了须要。
无状态组件函数应该提供一个参数props,返回一个React元素。
无状态组件函数自己就是render函数。