react學習篇

react衆所周知的前端3大主流框架之一,因爲出色的性能,完善的周邊設施風頭一時無兩。javascript

jsx語法

前端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 &copy; ©\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。typescript

註釋

jsx語法和html語法同樣,也是能夠插入註釋,只不過寫的時候有一些區別redux

子組件註釋

let name = 'hi'
ReactDOM.render((  
  <div>    
  {/* 註釋 */}
    {name}  
</div>), document.getElementById('root'))

在子組件中插入註釋,須要使用{}包裹起來,在//之間插入註釋文字。segmentfault

屬性註釋

let name = 'hi'
ReactDOM.render((
  <div>
    {name}    
  <img /*        
  多行註釋
  */ src="1.jpg"/> </div>), document.getElementById('root'))

在標籤中間,能夠插入一個多行註釋,相似上面的代碼。api

屬性props

1. 能夠像使用html的attr同樣使用屬性,就像下面img的src同樣

let name = 'hi'
ReactDOM.render((  
  <div>
    <img src="1.png"/>  
  </div>
), document.getElementById('root'))

2.若是須要傳遞動態屬性,使用{},多個屬性,使用展開運算符

let props = {
    src: '1.png',
    alt: '1圖片'
}
ReactDOM.render((
  <div>
    <img src={"1.png"}/>
    <img {...props}/>
  </div>
), document.getElementById('root'))

3.兩個轉換,class-->className for-->htmlFor

由於class和for是javascript關鍵字,因此這裏須要用轉換以後名稱

ReactDOM.render((
  <div className="tab">
    <label htmlFor="name">姓名:</label>
   <input id="name"/> </div>
), document.getElementById('root'))

4.布爾屬性

若是一個屬性的值是布爾值,當這個值是true的時候則能夠省略=後面的值,只保留key。

ReactDOM.render((
  <div className="tab">
    <input type="text" required/>
    <input type="text" required={true}/>
  </div>
), document.getElementById('root'))

5.原生元素的自定義屬性

react對元素屬性作了校驗,若是在原生屬性上使用此元素不支持的屬性,則不能編譯成功。必須使用data-前綴

ReactDOM.render((
  <div className="tab">
    <input type="text" data-init="22"/>
  </div>
), document.getElementById('root'))

插入html

若是動態的插入html元素,react出於安全性考慮會自動幫咱們轉義。因此必定要動態的插入元素的話,使用dangerouslySetInnerHTML

ReactDOM.render((
  <div className="tab">
    <div dangerouslySetInnerHTML={{__html: '<span>test</span>'}}></div>
  </div>
), document.getElementById('root'))

React組件建立

React.createClass

這是舊版本的api,使用React.createClass建立組件,配套的一些api,有getDefaultProps, getinitialstate。官方已經不建議使用了,使用下面新的api替代。

ES6 classes

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。純函數有優勢,優勢就是易於測試,無反作用。

React數據流

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

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事件等,官網均可以查到。

合成事件的this指向問題

就像上文同樣,咱們綁定事件的方式很奇怪,使用了bind來顯示綁定this的指向。由於傳遞到組件內部的只是一個函數,而脫離了當前對象的函數的this指向是不能指到當前組件的,須要顯示指定。

經過bind

<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指向

如何得到event原生事件

經過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方法作這些操做,緣由是上文講過的委託。

ReactDom

ref

特殊的props,ref組件對象的引用,如今官方也不建議直接給ref賦值,須要經過函數來賦值。

ReactDOM.render((  
  <div> <Calendar ref={ref => this.c = ref} any-ss="text"/> </div> ), document.getElementById('root'))

render

頂層api,只有在根組件時候才須要使用。第一個參數是Component,第二個參數是dom節點

findDOMNode

經過傳入component實例獲取此component根dom節點,在這裏能夠去dom節點進行操做了,雖然極其不建議這麼作,可是你確實能夠作。

unmountComponentAtNode

卸載此組件,並銷燬組件state和事件

接收組件的引用,也就是ref。僅僅是取消掛載,組件還在,若是須要完全清除的話,須要手動刪掉此dom。

表單

onchange配合value

與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。結合起來就能夠實現組件之間的通訊,只不過這種查找效率比較低,須要慎用和優化。

參考:https://segmentfault.com/a/1190000016281174

相關文章
相關標籤/搜索