React 從青銅到王者系列教程之倔強青銅篇

蝸牛老溼發表於2019-03-03

前端大陸乃是技術界近年來新興起的板塊,隨著人們對網站互動和效能越來越高, 前往前端大陸修煉 Javascript 的召喚師如過江之鯽,數不勝數,前端奇人異士之多,故修煉之法林林總總,俱不相同, Web 前端的未來尚無定論,內部卻已起了門戶之見, 幸而前端圈核心門派正道大昌,人氣鼎盛,其中尤以 React、Vue、Angular 為三大支柱,是為領袖,今天的故事,便是從 React 峽谷開始的

React 峽谷的每個前端召喚師,根據對 React 技術棧 和前端的理解,分別是青銅,白銀,黃金,鉑金,鑽石,星耀和王者段位,也對應這這個系列教程的七個模組

  1. 倔強青銅
    • 初入峽谷的初始段位,預設召喚師已經有了ES6nodejs 的基礎
    • 使用 create-react-app 建立 React 開發環境
    • 學習 React 世界裡的基本玩法,例如元件化,JSX,事件監聽,內部 state,元件的props、生命週期函式等
    • 這篇文章主要介紹 React 青銅升白銀需要的基礎知識,看完你就白銀啦
  2. 秩序白銀
    • 到了白銀段位,基本都是有了基本的操作,不會出現呆萌的站在地方塔下被打死的情況了
    • 我們需要買一個皮膚來提升頁面美觀並且多加練習
    • 學習使用螞蟻金服 ant-design 的使用
  3. 榮耀黃金

    • 到了這個階段,召喚師對 React 有了基本的認識,想進一步的提升段位,我們需要提高自己的大局觀
    • 學習使用 React-Router4 來讓我們有多面作戰能力
    • 學會使用 BrowserRouterRouterLink 等元件
    • 學會使用 Redux 和隊友配合,修煉大局觀
    • 瞭解單項資料流開發模式和 Redux 的各種概念,如 dispatch,action,reducers
    • 使用 react-redux 更好的和 Redux 配合有大局觀意識,上鉑金也是很 easy 了
  4. 尊貴鉑金

    • 很多召喚師卡在鉑金上不去,因為鉑金想上鑽石,需要了解更多的細節和原理
    • React 原理剖析
    • 對自己技能的原理有深刻的瞭解,上鑽石必備
  5. 永恆鑽石

    • 鑽石段位開始了徵兆模式,召喚師的技能池要足夠深才能更進一步,對自己擅長技能的理解也要更深刻
    • Redux 中介軟體機制,實現自己的中介軟體
    • 常見的React 效能優化方式
    • 服務端渲染
    • Redux 之外其他的資料解決方案如 mobxdva
  6. 至尊星耀

    • 這個段位已經是獨當一面的強者了,目標僅限於 React 這個庫很難更近一層,需要了解更底層的原理
    • Redux 原理剖析+實現自己的 Redux
    • React-Router+實現自己的 React-Router
    • Webppack 工作機制和原理剖析
    • Babel 工作機制和原理剖析
  7. 最強王者
    • 達到最強王者已經是頂尖召喚師,在整個 React 峽谷都是鼎鼎大名的存在,聽說上面還有傳說中的榮耀王者的段位,我這輩子是達不到了,也沒法教你們了,囧
    • 這個階段,我只推薦《程式設計師健康指南》一本書,保持身心健康,成為榮耀王者是早晚的事

開始之前,做個小廣告,歡迎大家關注我在慕課網的React實戰課程Redux+React Router+Node.js全棧開發

下面開始我們的正文,倔強青銅篇目錄

  1. 環境搭建
  2. 第一個元件
  3. 元件巢狀和屬性傳遞
  4. 狀態處理
  5. 生命週期

環境搭建

預設大家已經有 node 環境了,先安裝腳手架

npm install create-react-app -g複製程式碼

然後建立專案,並啟動

create-react-app bronze
cd bronze
npm start複製程式碼

看到下面的圖,意味著第一個 React 應用已經啟動起來了

我們開啟 src 目錄

src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js複製程式碼

index.js是入口檔案,核心內容就是分別引入 ReactReactDom,然後渲染了一個元件 App#root 這個元素上

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />,document.getElementById('root'));複製程式碼

然後重點看下 App.js 是我們元件的具體內容


import React, { Component } from 'react'

class App extends Component {
  render() {
    return (
      <div className="App">
        <p>
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;複製程式碼

這個基本上是最簡單的 React 元件了,自己實現元件也是分這麼幾個步驟

  1. import React
  2. 新建一個類,繼承 React.ComponentReact 裡每個元件都可以寫成一個類
  3. 類的 render 函式返回值,就是顯示在頁面的內容
  4. render 裡返回的是東西有點詭異,表面上是html 其實 Babel 會把 JSX 轉成 React.createElememt來執行
  5. 由於 JSX 本質就是 js,class 是 js 的關鍵字,所以要用 className 代替
  6. 如果想在 JSX 裡渲染變數,使用{}包裹即可

現在Babel官網],看下 JSX編譯後的程式碼,再劃下重點,所謂的 JSX,其實就是js 物件,再用 ReactDom.render 方法,渲染成dom

// jsx
<div className="App">
  <p>hello react</p>
</div>

// 轉換後
React.createElement(
  "div",
  { className: "App" },
  React.createElement(
    "p",
    null,
    "hello react"
  )
)複製程式碼

第一個元件

我們實現自己的第一個元件,修改 App.js

import React, { Component } from 'react'

class App extends Component {
  render() {
    const level='最強王者'
    return (
      <div>
        <h2>我們的目標是{level}</h2>
      </div>
    )
  }
}

export default App複製程式碼

由於JSX 本質上其實就是 js,所以我們可以在{}裡面使用 js 的表示式等功能,比如三元、函式、變數等等,還可以把陣列對映為列表,我們把程式碼修改為

import React, { Component } from 'react'

class App extends Component {
  render() {
    const level='最強王者'
    const isKing = true

    const title = isKing
                    ? <p>早睡早起,理性遊戲</p>
                    : <h2>我們的目標是{level}</h2>

     const wordList = ['俺老孫來也','有妖氣','取經之路,就在腳下']
    return (
      <div>
        {title}
        {isKing ? <p>我就是王者</p>: null}
        <ul>
          {wordList.map(v=><li key={v}>{v}</li>)}
        </ul>
      </div>
    )
  }
}

export default App複製程式碼

這裡要稍微注意一點,就是 render 裡,如果 return 多個元素,必須有一個父元素包裹,否則會有個報錯

我們在return 之外把 JSX 複製給變數,JSX 裡也可以在{}內部使用三元表示式,大家可以修改 isKingfalse 試一試

然後就是渲染列表,我們使用 map 函式直接對映為 JSX 的陣列,記得列表裡每個元素都有一個 key 屬性,關於它的作用,我們講虛擬dom 的時候會介紹

元件巢狀和屬性傳遞

如果我們繼續設計我們的應用,現在再設計一個Tank 元件,可以直接放在 App 裡使用,並且可以傳遞一個屬性,在元件內部,使用 this.props.key 獲取


import React, { Component } from 'react'

class App extends Component {
  render() {
    const level='最強王者'
    const isKing = true

    const title = isKing
                    ? <p>早睡早起,理性遊戲</p>
                    : <h2>我們的目標是{level}</h2>
    return (
      <div>
        {title}
        {isKing ? <p>我就是王者</p>: null}
        <Tank name='程咬金'></Tank> 
      </div>
    )
  }
}
class Tank extends Component {
  render() {
    return (
        <div>
          <h3>{this.props.name}是一個坦克</h3>
        </div>
    )
  }
}

export default App複製程式碼

如果我們的元件只有一個 render 方法,還可以寫成一個函式,props 是函式的引數,我們稱呼這種元件為無狀態元件,這種元件的特點,就是返回只和 props 有關,複用性高

function Tank(props){
  return (
    <div>
      <h3>{props.name}是一個坦克</h3>
    </div>
  )
}複製程式碼

這樣我們就可以把應用分成多個元件,然後用拼積木的形式拼接應用,但是現在的元件都沒法變化,下一步,我們學習 React 的狀態管理,也就是 state

React 狀態管理和事件監聽

我們通過在建構函式constructor裡初始 state,其實就是一個普通的 js 物件,然後可以呼叫 this.setState函式修改 state,每次 setState,都會重新渲染元件
元件裡可以使用 onClick來繫結一個函式,可以監聽使用者的事件,話不多說看程式碼

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      isKing:true
    }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick(){
    this.setState({
      isKing: !this.state.isKing
    })
  }
  render() {
    const level='最強王者'

    const title = this.state.isKing
                    ? <p>早睡早起,理性遊戲</p>
                    : <h2>我們的目標是{level}</h2>
    return (
      <div>
        <button onClick={this.handleClick}>點我</button>
        {title}
        {this.state.isKing ? <p>我就是王者</p>: null}
        <Tank name='程咬金'></Tank> 
      </div>
    )
  }
}複製程式碼

我們需要關注的點,一個是constructor,我們稱之為建構函式,元件初始化狀態放在這裡,設定了 isKingtrue,然後button 元素上的 onClick 的時候,執行handleClick,在handleClick內部,呼叫 this.setState 來修改 isKing

constructor函式裡的 bind 是強行把handleClickthis 繫結在元件上,否則 onClick 的時候會獲取 this 引用出錯,解決這個問題還有其他的形式,可以不用寫 bind 這一行

import React, { Component } from 'react'

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      isKing:true
    }
    this.handleClick = this.handleClick.bind(this)
  }
  // 在constructor裡手動 bind
  handleClick(){

    this.setState({
      isKing: !this.state.isKing
    })
  }
  // 繫結的時候傳遞箭頭函式
  handleClick1(){
    this.handleClick()
  }
  // 定義的時候是剪頭函式
  handleClick2 = ()=>{
    this.handleClick()
  }
  // onClick 的時候直接繫結
  handleClick3(){
    this.handleClick()
  }
  render() {
    const level='最強王者'

    const title = this.state.isKing
                    ? <p>早睡早起,理性遊戲</p>
                    : <h2>我們的目標是{level}</h2>
    return (
      <div>
        <button onClick={this.handleClick}>點我</button>
        <button onClick={()=>this.handleClick1()}>點我1</button>
        <button onClick={this.handleClick2}>點我2</button>
        <button onClick={this.handleClick3.bind(this)}>點我3</button>
        {title}
        {this.state.isKing ? <p>我就是王者</p>: null}
        <Tank name='程咬金'></Tank> 
      </div>
    )
  }
}複製程式碼

生命週期

最後要介紹的,就是 React 元件的生命週期,每個元件在不同的時期,會有不同的鉤子函式執行,比如元件載入完畢後,會執行 componentDidMount 鉤子函式


class App extends Component{
  componentDidMount(){
    console.log('元件渲染完畢')
  }
  render(){
    console.log('元件正在渲染')
    return <h2>倔強青銅</h2>
  }
}

// 元件正在渲染
// 元件渲染完畢複製程式碼

React 在不同的階段,會執行不同的函式,我從網上找了個圖,很清晰的說明各個生命週期函式執行的時機,

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      isKing:true
    }
    this.handleClick = this.handleClick.bind(this)
    console.log('constructor App 的建構函式,初始化先執行')
  }
  handleClick(){
    this.setState({
      isKing: !this.state.isKing
    })
  }
  componentWillMount(){
    console.log('componentWillMount,元件 App 準備渲染')
  }
  componentDidMount(){
    console.log('componentDidMount,元件 App 渲染完畢')
  }
  shouldComponentUpdate(){
    console.log('shouldComponentUpdate,判斷 App 元件是否應該渲染, 預設返回 true')
    return true
  }
  componentWillUpdate(){
    console.log('componentWillUpdate,元件 App 準備更新了')
  }
  componentDidUpdate(){
    console.log('componentDidUpdate, 元件 App 更新完畢了')
  }
  render() {
    const level='最強王者'

    const title = this.state.isKing
                    ? <p>早睡早起,理性遊戲</p>
                    : <h2>我們的目標是{level}</h2>
    const wordList = ['俺老孫來也','有妖氣','取經之路,就在腳下']
    console.log('元件 App 正在渲染')
    return (
      <div>
        <button onClick={this.handleClick}>點我</button>
        {title}
        {this.state.isKing ? <p>我就是王者</p>: null}

        <ul>
          {wordList.map(v=><li key={v}>{v}</li>)}
        </ul>
        <Tank name='程咬金'></Tank> 
      </div>
    )
  }
}

// 首次載入列印
constructor App 的建構函式,初始化先執行
componentWillMount,元件 App 準備渲染
元件 App 正在渲染
componentDidMount,元件 App 渲染完畢

// 點選按鈕修改狀態時列印
shouldComponentUpdate,判斷 App 元件是否應該渲染, 預設返回 true
componentWillUpdate,元件 App 準備更新了
元件 App 正在渲染
componentDidUpdate, 元件 App 更新完畢了複製程式碼

除了上面介紹的,還有需要注意的

  1. React16新增的componentDidCatch生命週期,用來處理報錯
  2. shouldComponentUpdate返回 false,那麼元件就不會渲染
  3. 如果是子元件,還有個componentWillReceiveProps
  4. 元件解除安裝有componentWillUnmount,用來做資源的清理
  5. 合理利用生命週期,在不同的階段做不同的事情
    如果你能看到這裡,那真的對 React 是真愛,恭喜你,你已經是秩序白銀啦,領取徽章

歡迎大家關注我在慕課網的實戰課程Redux+React Router+Node.js全棧開發

下期預告:秩序白銀篇-- 使用 ant-design UI庫,有問題留言,我們還可以開黑,一起上王者

相關文章