React ES6+ 入門指引

codepen 上的代碼請 fork 后再修改。

環境基礎

  • Chrome、FireFox等主流瀏覽器陸續支持 ES6+ 語法。
  • QQ、360、搜狗等瀏覽器已支持 ES6+ 語法。
  • 微軟全面轉移到 Edge。
  • 淘寶不再支持 IE 8 。
  • 項目已使用 webpack、babel 等來提供轉義支持。

簡介

A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
一個用于搭建用戶界面的js庫

核心思路:假定按照人機交互活動的不同狀態來設計UI,根據狀態的遷移來自動渲染頁面。同時,react 通過組件化來分治狀態。

Hello World

// 引入 js 類庫 
import React from 'react' 
import ReactDOM from 'react-dom' 

// 定義 HelloWorld 組件 
class HelloWorld extends React.Component {
  render() {
    // return 中是 JSX 語法
    return (
      <div>
        Hello, world!
      </div>
    );
  }
}

// 找到 HTML 中的 id="root" 的標簽,將 Hello 作為子元素插入 
ReactDOM.render(<HelloWorld/>, document.getElementById('root'));

codepen

ReactDOM.render()

在客戶端將 react 組件渲染成 HTML 的方法,在一般的 web app 中只在入口 js 文件中寫一處。

JSX

一種在 js 中書寫 HTML 的簡單方式,可以在其中通過 {} 來使用 js 表達式、變量、函數等。需要注意的是,由于 class 是 js 的關鍵字/保留字,所以 HTML 中的 class 需要寫成 className 。簡單語法如下。

const generateJSX = (arg) => {
  return <h3>{arg}</h3>
};

class HelloWorld extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      val: {key: 'JSX中使用對象的值'},
      ifelse: '與其寫那些匿名函數箭頭函數,不如提出來寫更好維護。'
    };
  }
  render() {
    return (
      <div>
        <h1>JSX</h1>
        <h3>{this.state.val.key}</h3>
        <h3>{true?'JSX中不能直接使用if-else,可以使用三元表達式':null}</h3>
        {(function() {
          if (true) {
            return <h3>在JSX中使用立即執行的匿名函數來寫if-else</h3>
          }
        })()}
        {true && <h3>單if的時候可以用表達式&&jsx的方式來寫</h3>}
        {(() => {
          if (true) 
            return <h3>在JSX中使用立即執行的箭頭函數來寫if-else</h3>
        })()}
        {generateJSX(this.state.ifelse)}
      </div>
    );
  }
}

ReactDOM.render(<HelloWorld/>, document.getElementById('root'));

codepen

需要注意的是,頂層只能有一對標簽。

// bad
return 
  <div></div>
  <div></div>

// good
return 
  <div>
    <div></div>
  </div>

為什么要使用 JSX

使用 JSX 時。

<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

不使用 JSX 時。

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

就一般的 web 開發來說,顯然第一種比較直觀;對于不一般的 web 開發者來說,也還是第一種比較直觀。

React 組件(Component)

React 支持自定義組件,而組件化,是工程化的基礎之一。React 支持自定義組件和傳參(props)。將自定義組件類比 HTML 標簽,參數就類似標簽屬性,能在 HTML 中怎么使用標簽,就能在 JSX 中怎么使用組件。

定義組件

一般由兩種方式:函數和 es 6 class 。

函數方式:

function genComA(props={}) {
  return <div>hello</div>
}

const genComB = (props={}) => {
  return <div>hello</div>
};

class:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

傳參

前面已經有過很多示例。

在 React 中,可以傳遞給組件的參數類型和 js 一致,包括 number、string、boolean、object、function 等。在組件中通過 this.props 獲取接收到的參數。

class HiName extends React.Component {
  render() {
    return <div>Hi, {this.props.name}</div>
  }
}

class HelloWorld extends React.Component {
  render() {
    return (
      <div>
        <HiName name={'sweetie'}/>
      </div>
    );
  }
}

// 輸出 hi, sweetie

以函數形式創建的組件,則沒有 this ,也就沒有 this.props ,是通過封裝成對象的形式傳遞,直接用 js 函數傳參的方式獲取即可。:必須首字母大寫,否則失效。

function Bye(props) {
  return <div>{props.name}, goodbye.</div>
}

class HelloWorld extends React.Component {
  render() {
    return (
      <div>
        <Bye name={'sweetie'}/>
      </div>
    );
  }
}

this.props.children

React 中有個保留的參數,叫 this.props.children ,主要用于封裝和動態加載子組件。常見的用法有單頁面 web app 中 header 和 footer 固定,內部內容動態變化等。

class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div>
          {this.props.children}
        </div>
        <Footer />
      </div>
    )
  }
}

參數校驗

React 支持參數校驗,包括 js 的數據類型、自定義類型、非空、枚舉等。

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: React.PropTypes.string
};

具體參考官網

狀態(state)

React 組件基于自身的 state 變化來觸發 render ,并進行相應的計算,達到渲染的效果。所以 state 的使用,是 React 中最重要的部分。

  • React 組件的生命周期節點為 mount -> (update) -> unmount
  • 其中 mountunmount 在生命周期中只執行一次,update 執行 0 到多次。
  • mountupdate 都會觸發 render
  • 對于 mountupdate 都有 willdid 兩種處理函數,對于 unmount 只有 componentWillUnmount
  • 提供 shouldComponentUpdate 來處理比較復雜的情況下組件 state 變化是否渲染,以提升性能。緊急逃生,慎用
  • 提供 componentWillReceiveProps 用于組件 mount 之后接收參數再次更新 state 。
class PropsCount extends React.Component {
  componentWillMount() {
    console.log('PropsCount will mount')
  }

  componentDidMount() {
    console.log('PropsCount did mount')
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('PropsCount will update')
  }

  componentDidUpdate(prevProps, provState) {
    console.log('PropsCount did update')
  }
  
  render() {
    console.log('PropsCount render')
    return <div>count update by props: {this.props.count}</div>
  }
}

class StateCount extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.count};
  }
  
  componentWillMount() {
    console.log('StateCount will mount')
  }

  componentDidMount() {
    console.log('StateCount did mount')
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('StateCount will update')
  }

  componentDidUpdate(prevProps, provState) {
    console.log('StateCount did update')
  }
  
  componentWillReceiveProps(nextProps) {
    this.setState({count: nextProps.count});
  }
  
  render() {
    console.log('StateCount render')
    return <div>count update by state: {this.state.count}</div>
  }
}

class Init extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.count}
  }
  
  componentWillMount() {
    console.log('Init will mount')
  }

  componentDidMount() {
    console.log('Init did mount')
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('Init will update')
  }

  componentDidUpdate(prevProps, provState) {
    console.log('Init did update')
  }
  
  render() {
    console.log('Init render')
    return <div>init: {this.state.count}</div>
  }
}

class HelloWorld extends React.Component {
  addOne = () => {
    this.setState({count: this.state.count + 1});
  };
  
  constructor(props) {
    super(props);
    this.state = {count: 0};
  }

  componentWillMount() {
    console.log('HelloWorld will mount')
  }

  componentDidMount() {
    console.log('HelloWorld did mount')
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('HelloWorld will update')
  }

  componentDidUpdate(prevProps, provState) {
    console.log('HelloWorld did update')
  }
  
  render() {
    console.log('HelloWorld render')
    return (
      <div>
        <Init count={this.state.count} />
        <button value="addOne" onClick={this.addOne}>add</button>
        <PropsCount count={this.state.count} />
        <StateCount count={this.state.count} />
      </div>
    );
  }
}

ReactDOM.render(<HelloWorld/>, document.getElementById('root'));

codepen

注意:在 componentWillMount componentWillUpdate 中不要使用 setState。初始化的異步請求最好放在 componentDidMount 里,其他初始化的同步操作放在 constructor 里。

處理事件和獲取值

官網參考

class A extends React.Component {
  constructor (props) {
    super(props)
    this.state={name:null}
  }
  
  onChange = (e) => {
    this.setState({name: e.target.value})
  };
  
  render () {
    return <div>
      <input type="text" onChange={this.onChange} />
      <br />
      {this.state.name && <label>hi, {this.state.name}</label>}
    </div>;
  }
}

codepen

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容