- A+
react众所周知的前端3大主流框架之一,由于出色的性能,完善的周边设施风头一时无两。本文就带大家一起掌握react。
jsx语法
前端MVVM主流框架都有一套自己的模板处理方法,react则使用它独特的jsx语法。在组件中插入html类似的语法,简化创建view的流程。
下面让我们来认识一下构建的两种元素
原生元素
1 2 3 4 5 |
[crayon-65c4e58463cdf446854569 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>标题<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
通过简单的语法页面就会被插入一个div+一个h1标签。原生的html元素可以被直接使用。以上的语法并不是js支持的语法,需要被转换之后才能运行。
自定义元素
react强大之处就在于可以组件的自定义,实现组件的复用。如果我们创建了一个组件。我们也可以通过jsx语法调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[crayon-65c4e58463ce4941758425 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Page</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> home111 <span class="hljs-symbol">&copy;</span> © \ua9 <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">Page</span>/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
我们定义了一个Page组件,可以在jsx里面像调用html一样直接调用。
插入动态数据
1 2 3 4 5 6 7 |
[crayon-65c4e58463ce9928623397 inline="true" ]<span class="hljs-keyword">let</span> name = <span class="hljs-string">'hi'</span> ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {name} <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
使用{}就可以插入数据,但是{}中间的必须是js表达式,不能是语句。如果表达式的执行结果是一个数组,则会自动join。
注释
jsx语法和html语法一样,也是可以插入注释,只不过写的时候有一些区别
子组件注释
1 2 3 4 5 6 7 8 |
[crayon-65c4e58463ced501948382 inline="true" ]<span class="hljs-keyword">let</span> name = <span class="hljs-string">'hi'</span> ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {/* 注释 */} {name} <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
在子组件中插入注释,需要使用{}包裹起来,在/ /之间插入注释文字。
属性注释
1 2 3 4 5 6 7 8 9 10 |
[crayon-65c4e58463cf1938933797 inline="true" ]<span class="hljs-keyword">let</span> name = <span class="hljs-string">'hi'</span> ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {name} <span class="hljs-tag"><<span class="hljs-name">img</span> /* 多行注释 */ <span class="hljs-attr">src</span>=<span class="hljs-string">"1.jpg"</span>/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
在标签中间,可以插入一个多行注释,类似上面的代码。
属性props
- 可以向使用html的attr一样使用属性,就像下面img的src一样
1 2 3 4 5 6 7 |
[crayon-65c4e58463cf5118294850 inline="true" ]<span class="hljs-keyword">let</span> name = <span class="hljs-string">'hi'</span> ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"1.png"</span>/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
- 如果需要传递动态属性,使用{},多个属性,使用展开运算符
1 2 3 4 5 6 7 8 9 10 11 |
[crayon-65c4e58463cf9291715613 inline="true" ]<span class="hljs-keyword">let</span> props = { <span class="hljs-attr">src</span>: <span class="hljs-string">'1.png'</span>, <span class="hljs-attr">alt</span>: <span class="hljs-string">'1图片'</span> } ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">1.png</span>"}/></span> <span class="hljs-tag"><<span class="hljs-name">img</span> {<span class="hljs-attr">...props</span>}/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
- 两个转换,class-->className for-->htmlFor
因为class和for是javascript关键字,所以这里需要用转换之后名称
1 2 3 4 5 |
[crayon-65c4e58463cfd028446751 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab"</span>></span> <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span>></span>姓名:<span class="hljs-tag"></<span class="hljs-name">label</span>></span><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span>/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
- 布尔属性
如果一个属性的值是布尔值,当这个值是true的时候则可以省略=后面的值,只保留key。
1 2 3 4 5 6 |
[crayon-65c4e58463d01358796704 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab"</span>></span> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">required</span>/></span> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">required</span>=<span class="hljs-string">{true}/</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
- 原生元素的自定义属性
react对元素属性做了校验,如果在原生属性上使用此元素不支持的属性,则不能编译成功。必须使用data-前缀
1 2 3 4 5 |
[crayon-65c4e58463d05141702723 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab"</span>></span> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">data-init</span>=<span class="hljs-string">"22"</span>/></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
插入html
如果动态的插入html元素,react出于安全性考虑会自动帮我们转义。所以一定要动态的插入元素的话,使用dangerouslySetInnerHTML
1 2 3 4 5 |
[crayon-65c4e58463d08350505763 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab"</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">dangerouslySetInnerHTML</span>=<span class="hljs-string">{{__html:</span> '<<span class="hljs-attr">span</span>></span>test<span class="hljs-tag"></<span class="hljs-name">span</span>></span>'}}><span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
React组件创建
React.createClass
这是旧版本的api,使用React.createClass创建组件,配套的一些api,有getDefaultProps, getinitialstate。官方已经不建议使用了,使用下面新的api替代。
ES6 classes
1 2 3 4 5 6 7 8 9 |
[crayon-65c4e58463d0c346834448 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Page</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> home <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
这是一个实现了render方法的class。也是一个基本的react组件。
无状态函数
1 2 3 4 5 6 7 8 |
[crayon-65c4e58463d10968700762 inline="true" ]<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params">props, context</span>) </span>{ <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">button</span>></span> <span class="hljs-tag"><<span class="hljs-name">em</span>></span>{props.text}<span class="hljs-tag"></<span class="hljs-name">em</span>></span> <span class="hljs-tag"><<span class="hljs-name">span</span>></span>{context.name}<span class="hljs-tag"></<span class="hljs-name">span</span>></span> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span> ); } |
[/crayon]
纯函数,不存在state,只接受props和state。纯函数有优点,优点就是易于测试,无副作用。
React数据流
state
state是组件的内部状态,需要在视图里面用到的状态,才需要放到state里面去。如下,我们在类上创建一个state属性,在视图里面通过使用this.state.name去引用。而这里的state定义则代替的是getinitialstate方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[crayon-65c4e58463d14270479078 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Page</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { <span class="hljs-attr">name</span>: <span class="hljs-string">'小明'</span> } render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this} <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
如何更新state呢,直接更改state其实可以可以的,不过这样子无法触发组件视图的更新机制。所以使用
setState()api。值得注意的是setState是异步的,原因是react内部需要对setState做优化,不是state变了立刻去更新视图,而是拦截一部分state的改变,等到合适的时机再去更新视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[crayon-65c4e58463d1a696575845 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Page</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { <span class="hljs-attr">name</span>: <span class="hljs-string">'小明'</span> } render() { setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =></span> <span class="hljs-keyword">this</span>.setState({<span class="hljs-attr">name</span>: <span class="hljs-string">'小明儿子'</span>}), <span class="hljs-number">5000</span>) <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this.state.name} <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
真实开发中绝不要在render函数里面去更改state,以上只是为了演示
props
props是组件之间传递数据的最主要api, react推崇的是自顶向下的数据流向,也就是组件的数据要从父组件传给子组件。如果子组件需要向父组件传递数据,则需要使用回调函数的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[crayon-65c4e58463d1e010139386 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this.props.parentName} <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Parent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { <span class="hljs-attr">name</span>: <span class="hljs-string">'小明'</span> } render() { setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =></span> <span class="hljs-keyword">this</span>.setState({<span class="hljs-attr">name</span>: <span class="hljs-string">'小明儿子'</span>}), <span class="hljs-number">5000</span>) <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">Child</span> <span class="hljs-attr">parentName</span>=<span class="hljs-string">{this.state.name}/</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
可以看到Child组件显示了父组件的name。当父组件状态更新了,子组件同步更新。那如何在子组件中更改父组件状态呢?答案是回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[crayon-65c4e58463d23217837962 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React from <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ update() { <span class="hljs-keyword">this</span>.props.onChange(<span class="hljs-string">'小明名字改了'</span>) } render() { <span class="hljs-keyword">return</span> (<div> {<span class="hljs-keyword">this</span>.props.parentName} <button onClick={<span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>)}>更新</button> </div>) } } <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Parent</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { name: <span class="hljs-string">'小明'</span> } changeName(name) { <span class="hljs-keyword">this</span>.setState({ name }) } render() { setTimeout(() => <span class="hljs-keyword">this</span>.setState({name: <span class="hljs-string">'小明儿子'</span>}), <span class="hljs-number">5000</span>) <span class="hljs-keyword">return</span> (<div> <Child onChange={<span class="hljs-keyword">this</span>.changeName.bind(<span class="hljs-keyword">this</span>)} parentName={<span class="hljs-keyword">this</span>.state.name}/> </div>) } } |
[/crayon]
注意哈:props是不可以更改的,这既不符合react单向数据流思想,也为维护带来灾难。
事件
react里面的用户事件都是合成事件,被React封装过。内部使用的还是事件的委托机制。
常用的事件有点击事件onClick,input的onChange事件等,官网都可以查到。
合成事件的this指向问题
就像上文一样,我们绑定事件的方式很奇怪,使用了bind来显示绑定this的指向。因为传递到组件内部的只是一个函数,而脱离了当前对象的函数的this指向是不能指到当前组件的,需要显示指定。
通过bind
1 |
[crayon-65c4e58463d27068708915 inline="true" ]<button onClick={<span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>)}>更新</button> |
[/crayon]
构造器内部指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[crayon-65c4e58463d2b421586116 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React from <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) <span class="hljs-keyword">this</span>.update = <span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>) } update() { <span class="hljs-keyword">this</span>.props.onChange(<span class="hljs-string">'小明名字改了'</span>) } render() { <span class="hljs-keyword">return</span> (<div> {<span class="hljs-keyword">this</span>.props.parentName} <button onClick={<span class="hljs-keyword">this</span>.update}>更新</button> </div>) } } |
[/crayon]
箭头函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[crayon-65c4e58463d2f352019192 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ update => e = { <span class="hljs-keyword">this</span>.props.onChange(<span class="hljs-string">'小明名字改了'</span>) } render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this.props.parentName} <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.update}</span>></span>更新<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[crayon-65c4e58463d33375237129 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) } @autoBind update() { <span class="hljs-keyword">this</span>.props.onChange(<span class="hljs-string">'小明名字改了'</span>) } render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this.props.parentName} <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.update}</span>></span>更新<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
装饰器是es7语法,如果需要使用需要安装对应的babel:present版本。而typescript则原生支持。
autoBind原理大概就是劫持get方法,get时改变this指向
如何获得evnt原生事件
通过e.nativeEvent获取原生事件对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[crayon-65c4e58463d37354361772 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) <span class="hljs-keyword">this</span>.update = <span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>) } update(e) { <span class="hljs-built_in">console</span>.log(e.nativeEvent) } render() { <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.update}</span>></span>更新<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>) } } |
[/crayon]
解决冒泡和取消默认事件
1 |
[crayon-65c4e58463d3c850259348 inline="true" ]<span class="hljs-selector-tag">e</span><span class="hljs-selector-class">.preventDefault</span>() <span class="hljs-comment">//取消默认行为</span> |
[/crayon]
1 |
[crayon-65c4e58463d3f968599342 inline="true" ]<span class="hljs-selector-tag">e</span><span class="hljs-selector-class">.stopPropagation</span>() <span class="hljs-comment">//取消冒泡</span> |
[/crayon]
这个和浏览器原生事件处理方案是一致的。问题是我们只可以调合成事件的
e的方法,不可以通过
e.nativeEvent方法做这些操作,原因是上文讲过的委托。
ReactDom
ref
特殊的props,ref组件对象的引用,现在官方也不建议直接给ref赋值,需要通过函数来赋值。
1 2 3 4 5 |
[crayon-65c4e58463d47312583401 inline="true" ]ReactDOM.render(( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">Calendar</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{ref</span> =></span> this.c = ref} any-ss="text"/> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ), <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)) |
[/crayon]
render
顶层api,只有在根组件时候才需要使用。第一个参数是Component,第二个参数是dom节点
findDOMNode
通过传入component实例获取此component根dom节点,在这里可以去dom节点进行操作了,虽然极其不建议这么做,但是你确实可以做。
unmountComponentAtNode
卸载此组件,并销毁组件state和事件
接收组件的引用,也就是ref。仅仅是取消挂载,组件还在,如果需要彻底清除的话,需要手动删掉此dom。
表单
onchange配合value
与vue框架不同的是,react如果要实现表单元素变化,状态同步更新,必须要自己去监听表单事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[crayon-65c4e58463d4b146377465 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React from <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { name: <span class="hljs-string">'小明'</span> } <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) <span class="hljs-keyword">this</span>.update = <span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>) } update(e) { <span class="hljs-keyword">this</span>.setState({ name: e.target.value }) } render() { <span class="hljs-keyword">return</span> (<div> <input onChange={<span class="hljs-keyword">this</span>.update} value={<span class="hljs-keyword">this</span>.state.name}/> </div>) } } |
[/crayon]
受控组件和非受控组件
受控组件和非受控组件这些都是指的表单组件,当一个表单的值是通过value改变的而不是通过defaultValue是受控组件,否则就是非受控组件。
下面组件中的input就是受控组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[crayon-65c4e58463d4f933428252 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React from <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { name: <span class="hljs-string">'小明'</span> } <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) <span class="hljs-keyword">this</span>.update = <span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>) } update(e) { <span class="hljs-keyword">this</span>.setState({ name: e.target.value }) } render() { <span class="hljs-keyword">return</span> (<div> <input onChange={<span class="hljs-keyword">this</span>.update} value={<span class="hljs-keyword">this</span>.state.name}/> </div>) } } |
[/crayon]
下面组件中的input是非受控组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[crayon-65c4e58463d53261107119 inline="true" ]<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React from <span class="hljs-string">'react'</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Child</span> <span class="hljs-title">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ state = { name: <span class="hljs-string">'小明'</span> } <span class="hljs-keyword">constructor</span>(props) { <span class="hljs-keyword">super</span>(props) <span class="hljs-keyword">this</span>.update = <span class="hljs-keyword">this</span>.update.bind(<span class="hljs-keyword">this</span>) } update(e) { <span class="hljs-keyword">this</span>.setState({ name: e.target.value }) } render() { <span class="hljs-keyword">return</span> (<div> <input onChange={<span class="hljs-keyword">this</span>.update} defaultValue={<span class="hljs-keyword">this</span>.state.name}/> </div>) } } |
[/crayon]
组件之间通讯
父子之间通讯
父子之间通讯又分为父->子,子->父。
因为react单向数据流向的缘故,父->子通信的话直接通过props。父组件数据变动,直接传递给子组件。
子->父组件之间就要通过回调函数来通信了,父组件传递一个回调函数给子组件,子组件通过调用此函数的方式通知父组件通信。
跨级组件通信
react为了实现祖先组件和后辈组件之间的通信问题,引入了contextApi。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
[crayon-65c4e58463d58347826230 inline="true" ]<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Button</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ render() { <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{background:</span> <span class="hljs-attr">this.context.color</span>}}></span> {this.props.children} <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span> ); } } Button.contextTypes = { <span class="hljs-attr">color</span>: React.PropTypes.string }; <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ render() { <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> {this.props.text} <span class="hljs-tag"><<span class="hljs-name">Button</span>></span>Delete<span class="hljs-tag"></<span class="hljs-name">Button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ); } } <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MessageList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{ getChildContext() { <span class="hljs-keyword">return</span> {<span class="hljs-attr">color</span>: <span class="hljs-string">"purple"</span>}; } render() { <span class="hljs-keyword">const</span> children = <span class="hljs-keyword">this</span>.props.messages.map(<span class="hljs-function">(<span class="hljs-params">message</span>) =></span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">{message.text}</span> /></span></span> ); <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span>{children}<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>; } } MessageList.childContextTypes = { <span class="hljs-attr">color</span>: React.PropTypes.string }; |
[/crayon]
MessageList中的color会自动更新到儿孙组件里面去,实现跨级啊通信。如果需要反过来通信,则需要借助其他工具,比如事件系统(Pub/Sub)。
没有嵌套关系组件之间通信
组件之间通信最主流的两种方式脱胎于观察这模式和中介者模式这两种。
跨级之间通信现在最主流的方式就是观察这模式的实现Pub/Sub,react社区中的redux也是使用这种方式实现的。
vue2.X版本也去掉了跨组件通信的功能。那如何在2.x中做跨组件通信呢?如果不借助外力的话,是不是可以使用parent和childen的递归调用实现全局组件通信呢?比如我想广播一个事件,我就查找到所有的子组件,挨个触发emit(xx),上报一个事件也是同理,只不过需要查找所有的parent。结合起来就可以实现组件之间的通信,只不过这种查找效率比较低,需要慎用和优化