react众所周知的前端3大主流框架之一,因为出色的性能,完善的周边设施风头一时无两。本文就带你们一块儿掌握react。javascript
前端MVVM主流框架都有一套本身的模板处理方法,react则使用它独特的jsx语法。在组件中插入html相似的语法,简化建立view的流程。html
下面让咱们来认识一下构建的两种元素前端
ReactDOM.render(( <div> <h1>标题</h1> </div> ), document.getElementById('root'))
经过简单的语法页面就会被插入一个div+一个h1标签。原生的html元素能够被直接使用。以上的语法并非js支持的语法,须要被转换以后才能运行。vue
react强大之处就在于能够组件的自定义,实现组件的复用。若是咱们建立了一个组件。咱们也能够经过jsx语法调用。java
import * as React from 'react' class Page extends React.Component { render() { return (<div> home111 © © \ua9 </div>) } } ReactDOM.render(( <div> <Page/> </div> ), document.getElementById('root'))
咱们定义了一个Page组件,能够在jsx里面像调用html同样直接调用。react
let name = 'hi' ReactDOM.render(( <div> {name} </div> ), document.getElementById('root'))
使用{}就能够插入数据,可是{}中间的必须是js表达式,不能是语句。若是表达式的执行结果是一个数组,则会自动join。程序员
jsx语法和html语法同样,也是能够插入注释,只不过写的时候有一些区别typescript
let name = 'hi' ReactDOM.render(( <div> {/* 注释 */} {name} </div> ), document.getElementById('root'))
在子组件中插入注释,须要使用{}包裹起来,在/ /之间插入注释文字。redux
let name = 'hi' ReactDOM.render(( <div> {name} <img /* 多行注释 */ src="1.jpg"/> </div> ), document.getElementById('root'))
在标签中间,能够插入一个多行注释,相似上面的代码。api
let name = 'hi' ReactDOM.render(( <div> <img src="1.png"/> </div> ), document.getElementById('root'))
let props = { src: '1.png', alt: '1图片' } ReactDOM.render(( <div> <img src={"1.png"}/> <img {...props}/> </div> ), document.getElementById('root'))
由于class和for是javascript关键字,因此这里须要用转换以后名称
ReactDOM.render(( <div className="tab"> <label htmlFor="name">姓名:</label><input id="name"/> </div> ), document.getElementById('root'))
若是一个属性的值是布尔值,当这个值是true的时候则能够省略=后面的值,只保留key。
ReactDOM.render(( <div className="tab"> <input type="text" required/> <input type="text" required={true}/> </div> ), document.getElementById('root'))
react对元素属性作了校验,若是在原生属性上使用此元素不支持的属性,则不能编译成功。必须使用data-前缀
ReactDOM.render(( <div className="tab"> <input type="text" data-init="22"/> </div> ), document.getElementById('root'))
若是动态的插入html元素,react出于安全性考虑会自动帮咱们转义。因此必定要动态的插入元素的话,使用dangerouslySetInnerHTML
ReactDOM.render(( <div className="tab"> <div dangerouslySetInnerHTML={{__html: '<span>test</span>'}}></div> </div> ), document.getElementById('root'))
这是旧版本的api,使用React.createClass建立组件,配套的一些api,有getDefaultProps, getinitialstate。官方已经不建议使用了,使用下面新的api替代。
import * as React from 'react' class Page extends React.Component { render() { return (<div> home </div>) } }
这是一个实现了render方法的class。也是一个基本的react组件。
function Button(props, context) { return ( <button> <em>{props.text}</em> <span>{context.name}</span> </button> ); }
纯函数,不存在state,只接受props和state。纯函数有优势,优势就是易于测试,无反作用。
state是组件的内部状态,须要在视图里面用到的状态,才须要放到state里面去。以下,咱们在类上建立一个state属性,在视图里面经过使用this.state.name去引用。而这里的state定义则代替的是getinitialstate方法。
import * as React from 'react' class Page extends React.Component { state = { name: '小明' } render() { return (<div> {this} </div>) } }
如何更新state呢,直接更改state其实能够能够的,不过这样子没法触发组件视图的更新机制。因此使用setState()
api。值得注意的是setState是异步的,缘由是react内部须要对setState作优化,不是state变了马上去更新视图,而是拦截一部分state的改变,等到合适的时机再去更新视图。
import * as React from 'react' class Page extends React.Component { state = { name: '小明' } render() { setTimeout(() => this.setState({name: '小明儿子'}), 5000) return (<div> {this.state.name} </div>) } }
真实开发中毫不要在render函数里面去更改state,以上只是为了演示
props是组件之间传递数据的最主要api, react推崇的是自顶向下的数据流向,也就是组件的数据要从父组件传给子组件。若是子组件须要向父组件传递数据,则须要使用回调函数的方式。
import * as React from 'react' class Child extends React.Component { render() { return (<div> {this.props.parentName} </div>) } } class Parent extends React.Component { state = { name: '小明' } render() { setTimeout(() => this.setState({name: '小明儿子'}), 5000) return (<div> <Child parentName={this.state.name}/> </div>) } }
能够看到Child组件显示了父组件的name。当父组件状态更新了,子组件同步更新。那如何在子组件中更改父组件状态呢?答案是回调函数。
import * as React from 'react' class Child extends React.Component { update() { this.props.onChange('小明名字改了') } render() { return (<div> {this.props.parentName} <button onClick={this.update.bind(this)}>更新</button> </div>) } } class Parent extends React.Component { state = { name: '小明' } changeName(name) { this.setState({ name }) } render() { setTimeout(() => this.setState({name: '小明儿子'}), 5000) return (<div> <Child onChange={this.changeName.bind(this)} parentName={this.state.name}/> </div>) } }
注意哈:props是不能够更改的,这既不符合react单向数据流思想,也为维护带来灾难。
react里面的用户事件都是合成事件,被React封装过。内部使用的仍是事件的委托机制。
经常使用的事件有点击事件onClick,input的onChange事件等,官网均可以查到。
就像上文同样,咱们绑定事件的方式很奇怪,使用了bind来显示绑定this的指向。由于传递到组件内部的只是一个函数,而脱离了当前对象的函数的this指向是不能指到当前组件的,须要显示指定。
<button onClick={this.update.bind(this)}>更新</button>
import * as React from 'react' class Child extends React.Component { constructor(props) { super(props) this.update = this.update.bind(this) } update() { this.props.onChange('小明名字改了') } render() { return (<div> {this.props.parentName} <button onClick={this.update}>更新</button> </div>) } }
import * as React from 'react' class Child extends React.Component { update => e = { this.props.onChange('小明名字改了') } render() { return (<div> {this.props.parentName} <button onClick={this.update}>更新</button> </div>) } }
import * as React from 'react' class Child extends React.Component { constructor(props) { super(props) } @autoBind update() { this.props.onChange('小明名字改了') } render() { return (<div> {this.props.parentName} <button onClick={this.update}>更新</button> </div>) } }
装饰器是es7语法,若是须要使用须要安装对应的babel:present版本。而typescript则原生支持。
autoBind原理大概就是劫持get方法,get时改变this指向
经过e.nativeEvent获取原生事件对象
import * as React from 'react' class Child extends React.Component { constructor(props) { super(props) this.update = this.update.bind(this) } update(e) { console.log(e.nativeEvent) } render() { return (<div> <button onClick={this.update}>更新</button> </div>) } }
e.preventDefault() //取消默认行为
e.stopPropagation() //取消冒泡
这个和浏览器原生事件处理方案是一致的。问题是咱们只能够调合成事件的e
的方法,不能够经过e.nativeEvent
方法作这些操做,缘由是上文讲过的委托。
特殊的props,ref组件对象的引用,如今官方也不建议直接给ref赋值,须要经过函数来赋值。
ReactDOM.render(( <div> <Calendar ref={ref => this.c = ref} any-ss="text"/> </div> ), document.getElementById('root'))
顶层api,只有在根组件时候才须要使用。第一个参数是Component,第二个参数是dom节点
经过传入component实例获取此component根dom节点,在这里能够去dom节点进行操做了,虽然极其不建议这么作,可是你确实能够作。
卸载此组件,并销毁组件state和事件
接收组件的引用,也就是ref。仅仅是取消挂载,组件还在,若是须要完全清除的话,须要手动删掉此dom。
与vue框架不一样的是,react若是要实现表单元素变化,状态同步更新,必需要本身去监听表单事件。
import * as React from 'react' class Child extends React.Component { state = { name: '小明' } constructor(props) { super(props) this.update = this.update.bind(this) } update(e) { this.setState({ name: e.target.value }) } render() { return (<div> <input onChange={this.update} value={this.state.name}/> </div>) } }
受控组件和非受控组件这些都是指的表单组件,当一个表单的值是经过value改变的而不是经过defaultValue是受控组件,不然就是非受控组件。
下面组件中的input就是受控组件
import * as React from 'react' class Child extends React.Component { state = { name: '小明' } constructor(props) { super(props) this.update = this.update.bind(this) } update(e) { this.setState({ name: e.target.value }) } render() { return (<div> <input onChange={this.update} value={this.state.name}/> </div>) } }
下面组件中的input是非受控组件
import * as React from 'react' class Child extends React.Component { state = { name: '小明' } constructor(props) { super(props) this.update = this.update.bind(this) } update(e) { this.setState({ name: e.target.value }) } render() { return (<div> <input onChange={this.update} defaultValue={this.state.name}/> </div>) } }
父子之间通信又分为父->子,子->父。
由于react单向数据流向的缘故,父->子通讯的话直接经过props。父组件数据变更,直接传递给子组件。
子->父组件之间就要经过回调函数来通讯了,父组件传递一个回调函数给子组件,子组件经过调用此函数的方式通知父组件通讯。
react为了实现祖先组件和后辈组件之间的通讯问题,引入了contextApi。
class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } Button.contextTypes = { color: React.PropTypes.string }; class Message extends React.Component { render() { return ( <div> {this.props.text} <Button>Delete</Button> </div> ); } } class MessageList extends React.Component { getChildContext() { return {color: "purple"}; } render() { const children = this.props.messages.map((message) => <Message text={message.text} /> ); return <div>{children}</div>; } } MessageList.childContextTypes = { color: React.PropTypes.string };
MessageList中的color会自动更新到儿孙组件里面去,实现跨级啊通讯。若是须要反过来通讯,则须要借助其余工具,好比事件系统(Pub/Sub)。
组件之间通讯最主流的两种方式脱胎于观察这模式和中介者模式这两种。
跨级之间通讯如今最主流的方式就是观察这模式的实现Pub/Sub,react社区中的redux也是使用这种方式实现的。
vue2.X版本也去掉了跨组件通讯的功能。那如何在2.x中作跨组件通讯呢?若是不借助外力的话,是否是可使用$parent和$childen的递归调用实现全局组件通讯呢?好比我想广播一个事件,我就查找到全部的子组件,挨个触发$emit(xx),上报一个事件也是同理,只不过须要查找全部的$parent。结合起来就能够实现组件之间的通讯,只不过这种查找效率比较低,须要慎用和优化
建立了一个程序员交流微信群,你们进群交流IT技术
若是已过时,能够添加博主微信号15706211347,拉你进群
个人博客即将同步至腾讯云+社区,邀请你们一同入驻:https://cloud.tencent.com/dev...