React我的入門總結《一》

前言

首先我說說我爲何學React,React能夠說是三大框架中思想最爲先進的一款,在國內Vue雖然很火,可是Vue也開始引入了React的一些思想,好比虛擬DOM,接着Vue3.0還會引入JSX,React很是注重組件化和複用性,學習React能夠比之前更熟悉一些架構知識。

工做時間也有3個多月了,該學會寫東西總結了,最近利用工做時間學習React,今天又有空寫一下總結,怕過了一段時間忘記,這篇文章我是看了 React小書 總結下來的,若是有人跟我同樣是React入門的話,建議去React小書學起,從腳手架學起真的是快不少,咱們老師教咱們學東西首先要學會用,再學原理。

這篇文章不適合大神看,若是有大神想指導能夠在下面發佈評論。

概念

React是個幫你構建頁面UI的庫,他至關於MVC裏面的View,React將咱們界面分紅各個獨立的小塊,每一個小塊都是一個組件,而這些組件能夠互相嵌套丶結合,而後就造成了頁面css

腳手架安裝(Create-react-app)

因爲React要配合其餘工具和庫輔助,例如編譯須要babel,組織代碼須要redux等第三方狀態管理工具,寫單頁面應用須要應用到React-router,這也至關於React的全家桶。html

  • 使用npm安裝Create-react-app

    npm install -g create-react-app前端

  • 使用create-react-app建立項目

    create-react-app my-app
    node

    我第一次使用時報了一個這樣的錯誤,而後我上網查才知道是使用腳手架建立項目時要下載許多依賴文件,而後它是默認使用npm下載的,這時候主要將npm切換成cnpm就能夠解決了。

    • npm config set registry https://registry.npm.taobao.org //將npm切換成cnpm
    • 還有一解決方法就是找到resource下面的app裏面的extensions打開後看到有git文件夾,刪除,重啓。

    建立完成以後而後cd到項目根目錄而後npm start就能夠啓動項目了react

  • 項目結構

    建立完成以後cd到建立的目錄而後能夠看到腳手架幫咱們建立的結構git

    node_modules public scripts src
    依賴文件 靜態庫 項目啓動配置 開發目錄

    開發時候在src目錄上開發就好了,若是要查看項目配置,能夠輸入npm run ejectconfig目錄暴露出來,這個是默認隱藏的,你也能夠在node_modules目錄上找到,這個目錄是項目的配置。若是運行npm run eject報錯的話,把代碼儲存到本地就好了git add .,而後git commit -m '',接着從新運行指令,這執行是不可逆的。npm

    public有個index.html這是整個項目的頁面,以後咱們要把寫好的東西渲染到裏面去。
    src能夠看到一個index.js,因爲寫React幾乎都是組件,而index通常用來渲染其餘組件redux


編寫React

首先咱們把index.js的代碼改爲這樣再看看效果數組

import React, { Component } from 'react' // 寫React必需要引入這兩樣東西
import ReactDOM from 'react-dom' // 組件不須要引入,他只有一個做用,就是渲染DOM
import './index.css' // 能夠直接引入css或者是sass,也能夠引入圖片
<!-- 使用組件繼承Component類 -->
class Header extends Component {
  render () { // render方法把裏面東西jsx返回
    return (
      <!-- jsx -->
      <div>
        <h1>React大法好</h1>
      </div>
    )
  }
}
<!-- 渲染出去 -->
ReactDOM.render(
  <Header />,
  document.getElementById('root')
)
複製代碼
  • JSX

    JSX 是 JavaScript 語言的一種語法擴展,長得像 HTML,但並非 HTML。瀏覽器

    平時咱們寫html是這樣寫的

    <div class='box' id='content'>
          <div class='title'>Hello</div>
          <button>Click</button>
        </div>
    複製代碼

    若是咱們用JS對象來表示的話

    {
      tag: 'div',
      attrs: { className: 'box', id: 'content'},
      children: [
        {
          tag: 'div',
          arrts: { className: 'title' },
          children: ['Hello']
        },
        {
          tag: 'button',
          attrs: null,
          children: ['Click']
        }
      ]
    }
    複製代碼

    沒錯,其實JSX也至關於JS對象,React也會根據這樣編譯,轉化爲HTML結構以後就能夠拿去構建真正的DOM,這是最後那段渲染DOM所作的事情

    ReactDOM.render(
      <Header />,
      document.getElementById('root')
    )
    複製代碼

    這是整個過程流程圖,單獨把ReactDOM抽出來的緣由是由於咱們能夠渲染到Canvas或者轉化原生App(ReactNative)

  • 組件render方法

    React中一切皆爲組件,寫組件的時候通常都須要繼承React.js的Component,這個方法必須返回一個JSX元素,而返回並列JSX元素是不合法的

    <!-- bad -->
    render() {
        return {
            <div>one</div>
            <div>two</div>
        }
    }
    複製代碼

    必需要用一個外層元素包起來,不能有多個外層元素

    <!-- good -->
    render() {
        return {
            <div>
                <div>one</div>
                <div>two</div>
            </div>
        }
    }
    複製代碼
    • 表達式插入

      表達式能夠插入變量,還能夠計算,還能夠條件返回,而且能夠寫函數,render會把真實的內容返回,特別的靈活

      render() {
          const word  = 'word'
          const isGoodWord = true
          return(
              <div>
                  <h1>HELLO {word}</h1> // 變量
                  <h1>{1 + 2}</h1> // 計算
                  <h1>{(function(){ return 'React' })()}</h1> // 函數
                  // 條件返回
                  {  
                     isGoodWord 
                     ? <span>好文章</span>
                     : <span>壞文章</span>
                  }
              </div>
          )
      }
      複製代碼

      表達式不只能夠插入標籤內部,還能夠插入屬性

      render() {
          const className  = 'header'
          return(
              <div className={ className }></div>
          )
      }
      複製代碼

      因爲class是JS的關鍵字,React.js換成了className,還有一個就是for換成了htmlfor

    • 組件組合

      自定義的組件都必需要用大寫字母開頭

      class Title extends Component {
        render () {
          return (
            <h1>標題標題標題</h1>
          )
        }
      }
      
      class Header extends Component {
        render () {
          return (
            <div>
              <!-- 渲染三次 -->
              <Title />
              <Title />
              <Title />
            </div>
          )
        }
      }
      複製代碼

      能夠直接在Header標籤裏面使用,React.js會在<Title />,組件的render方法表示的JSX內容渲染出來,
      它會顯示在相應的位置上

    • 事件監聽

      在須要監聽事件的元素加上屬性相似 onClick onKeyDown這樣的屬性便可,事件屬性必須使用駝峯命名法。

      class Title extends Component {
        handleOnClickTitle(hello) {
            console.log(hello)
            console.log(this)
        }
        render () {
          return (
            <!-- 綁定事件時須要綁定this,不綁定則拿不到 -->
            <h1 onClick={this.handleOnClickTitle.bind(this, 'hello')}>標題標題標題</h1>
          )
        }
      }
      複製代碼

      在 React.js 不須要手動調用瀏覽器原生的addEventListener進行事件監聽。React.js 幫咱們封裝好了一系列的 on* 的屬性,當你須要爲某個元素監聽某個事件的時候,只須要簡單地給它加上 on* 就能夠了。並且你不須要考慮不一樣瀏覽器兼容性的問題,React.js 都幫咱們封裝好這些細節了。

      React.js 將瀏覽器原生的event對象封裝了一下,對外提供統一的 API 和屬性,這樣你就不用考慮不一樣瀏覽器的兼容性問題。

      若是你不手動綁定this在函數裏面打印的會是undefined,這是由於 React.js 調用你所傳給它的方法的時候,並非經過對象方法的方式調用,而是直接經過函數調用,因此事件監聽函數內並不能經過this 獲取到實例若是你想在事件函數當中使用當前的實例,你須要手動地將實例方法 bind 到當前實例上再傳入給 React.js

    • 組件的 state 和 setState

      state是組件的狀態,咱們能夠用它來進行狀態切換,如顯示或隱藏,或者是改變className等操做。

      class Index extends Component {
        constructor(props) {
          super(props)
          <!-- 狀態 -->
          this.state = {
              isShow: true
          }
        }
        render () {
          handleState() {
              this.setState({
                  isShow: !this.state.isShow
              })
          }
          return (
              <div className='index'>
                  {
                      this.state.isShow === true
                      ? <h1>顯示<h1/> : null
                  }
                  <button onClick={this.handleState.bind(this)}></button>
              </div>
          )
        }
      }
      複製代碼

      setState 方法由父類 Component 所提供。當咱們調用這個函數的時候,React.js 會更新組件的狀態 state ,而且從新調用 render 方法,而後再把 render 方法所渲染的最新的內容顯示到頁面上。

      setState能夠接受函數或者對象做爲參數,這裏要注意一個問題,當你調用setState時React並不會立刻修改state,而是把這個對象放在一個更新隊列,稍後纔會從隊列當中把新的狀態提取出來合併到 state 當中,而後再觸發組件更新。

      handleState() {
          console.log(this.state.isShow) // true
          this.setState({
              isShow: !this.state.isShow
          })
          console.log(this.state.isShow) // true
      }
      複製代碼

      若是要作到後續操做依賴前一個setState的這種操做的話,咱們能夠把參數換成函數。

      handleState() {
          console.log(this.state.isShow) // true
          this.setState((prevState) => {
              return { isShow: !this.state.isShow }
          })
          console.log(this.state.isShow) // false
      }
      複製代碼

      在一個函數裏面同步使用setState它只會執行一個,React.js 內部會把js 事件循環中的消息隊列的同一個消息中的setState都進行合併之後再從新渲染組件,若是你先調用一個setState而後用定時器調用的話,就會觸發兩次

      說到setState還要說到一個React的受控組件,好比 inputtextarea這種輸入框,若是你直接寫這種標籤時候React會報一個錯誤。

      React.js 認爲全部的狀態都應該由 React.js 的 state 控制,只要相似於 <input /><textarea /> 這樣的輸入控件被設置了 value 值,那麼它們的值永遠以被設置的值爲準。這時候只要給他綁定一個onChange就好了。

      class InputWithUserName extends Component {
        constructor() {
          super()
          this.state = {
            value: ''
          }
        }
        changeValue(event) {
          console.log(event.target.value)
          this.setState({
            value: event.target.value
          })
        }
        render() {
          return (
            <div>
              {this.props.content}
              <input type="text" value={this.state.value} onChange={this.changeValue.bind(this)}/>
            </div>
          )
        }
      }
      複製代碼
    • Props

      每一個組件均可以接受一個 props 參數,它是一個對象,包含了全部你對這個組件的配置。

      在使用一個組件的時候,能夠把參數放在標籤的屬性當中,全部的屬性都會做爲 props 對象的鍵值

      class Index extends Component {
        render () {
          return (
              <div className='index'>
                  <!-- 這裏兩個花括號只是JSX裏面再嵌套一個對象而已 -->
                  <!-- 還能夠傳函數 -->
                  <Main options={{title: 'React', content: 'React大法好'}} onClick={()=> console.log('Click in Index')} />
              </div>
          )
        }
      }
      
      class Main extends Component {
          <!-- 若是沒有東西傳過來能夠在這裏設置默認值 -->
          static defaultProps = {
              options = {
                  title: '默認頭部',
                  content: '默認內容'
              }
          }    
          <!-- 這裏能夠省略,由於React會自動生成 -->
          constructor(props) {
              super(props)
          }
          
          handleIndexClick() {
              if(this.props.onClick) {
                  <!-- 執行Index傳過來的函數 -->
                  this.props.onClick()
              }
          }
          
          render() {
              const options = this.props.options
              <div className='main'>
                  <h1>{options.title}</h1>
                  <p>{options.content}</p>
                  <button onClick={this.handleIndexClick.bind(this)}></button
              </div>
          }
      }
      複製代碼

      不要試圖去改變傳過來的props,React.js 但願一個組件在輸入肯定的 props 的時候,可以輸出肯定的 UI 顯示形態。若是 props 渲染過程當中能夠被修改,那麼就會致使這個組件顯示形態和行爲變得不可預測,這樣會可能會給組件使用者帶來困惑。

      handleIndexClick() {
          this.props.options = null; // 報錯
          if(this.props.onClick) {
              <!-- 執行Index傳過來的函數 -->
              this.props.onClick()
          }
      }
      複製代碼

      但這並不意味着由props決定的顯示形態不能被修改。組件的使用者能夠主動地經過從新渲染的方式把新的props 傳入組件當中,這樣這個組件中由props決定的顯示形態也會獲得相應的改變。

      class Index extends Component {
        constructor (props) {
          super(props)
          this.state = {
            options: {
              title: 'React',
              content: 'React大法好'
            },
          }
        }
        <!-- 修改傳過去的值 -->
        handleOptionsChange() {
            this.setState({
              options: {
                  title: 'Vue',
                  content: 'Vue大法好'
              },
            })
        }
        render () {
          return (
              <div className='index'>
                  <Main options={ this.state.options } onClick={()=> console.log('Click in Index')} />
                  <!-- 點擊修改 -->
                  <button onClick={this.handleOptionsChange.bind(this)}></button>
              </div>
          )
        }
      }
      複製代碼

      因爲setState會致使Index從新渲染,因此<Main />也會接受到新的值而且從新渲染,這樣就能作到修改<Main />的顯示效果

    • stateprops

      state 的主要做用是用於組件保存、控制、修改本身的可變狀態。state 在組件內部初始化,能夠被組件自身修改,而外部不能訪問也不能修改。你能夠認爲 state 是一個局部的、只能被組件自身控制的數據源。state 中狀態能夠經過 this.setState 方法進行更新,setState 會致使組件的從新渲染。

      props 的主要做用是讓使用該組件的父組件能夠傳入參數來配置該組件。它是外部傳進來的配置參數,組件內部沒法控制也沒法修改。除非外部組件主動傳入新的 props,不然組件的 props 永遠保持不變。

      stateprops 有着千絲萬縷的關係。它們均可以決定組件的行爲和顯示形態。一個組件的 state 中的數據能夠經過 props 傳給子組件,一個組件可使用外部傳入的 props 來初始化本身的 state。可是它們的職責其實很是明晰分明:state 是讓組件控制本身的狀態,props 是讓外部對組件本身進行配置。

      若是你以爲仍是搞不清 stateprops 的使用場景,那麼請記住一個簡單的規則:儘可能少地用 state,儘可能多地用 props。沒有 state 的組件叫 無狀態組件(stateless component),設置了 state 的叫作 有狀態組件件(stateful component)。由於狀態會帶來管理的複雜性,咱們儘可能多地寫無狀態組件,儘可能少地寫有狀態的組件。這樣會下降代碼維護的難度,也會在必定程度上加強組件的可複用性。前端應用狀態管理是一個複雜的問題。

      有狀態組件

      class HelloWorld extends Component {
        constructor() {
          super()
          this.state = {
              isShow: true
          }
        }
      
        sayHi () {
          alert('Hello World')
        }
      
        render () {
          return (
            <div onClick={this.sayHi.bind(this)}>Hello World</div>
          )
        }
      }
      複製代碼

      無狀態組件

      const HelloWorld = (props) => {
        const sayHi = (event) => alert('Hello World')
        return (
          <div onClick={sayHi}>Hello World</div>
        )
      }
      複製代碼

      函數式的組件編寫方式是一個函數就是一個組件,你能夠和之前同樣經過<HellWorld /> 使用該組件。不一樣的是,函數式組件只能接受 props 而沒法像跟類組件同樣能夠在 constructor 裏面初始化 state。你能夠理解函數式組件就是一種只能接受 props 和提供render 方法的類組件。

    • 渲染列表數據

      直接使用ES6的map遍歷代碼比較簡潔,也能夠手動寫循環構建列表的JSX,在React遍歷使用map是很是常見的。

      const users = [
        { username: 'Jerry', age: 21, gender: 'male' },
        { username: 'Tomy', age: 22, gender: 'male' },
        { username: 'Lily', age: 19, gender: 'female' },
        { username: 'Lucy', age: 20, gender: 'female' }
      ]
      
      class Index extends Component {
        render () {
          return (
            <div>
              {users.map((user, i) => <User user={user} key={i} />)}
            </div>    
          )
        }
      }
      
      class User extends Component {
        render () {
          const { user } = this.props
          return (
              <div>
                  <div>姓名:{user.username}</div>
                  <div>年齡:{user.age}</div>
                  <div>性別:{user.gender}</div>
                  <hr />
              </div>
          )
        }
      }
      複製代碼

      React.js 的是很是高效的,它高效依賴於所謂的 Virtual-DOM 策略。簡單來講,能複用的話 React.js 就會盡可能複用,沒有必要的話絕對不碰 DOM。對於列表元素來講也是這樣,可是處理列表元素的複用性會有一個問題:元素可能會在一個列表中改變位置。但其實 React.js 只須要交換一下 DOM 位置就好了,可是它並不知道其實咱們只是改變了元素的位置,因此它會從新渲染後面兩個元素(再執行 Virtual-DOM 策略),這樣會大大增長 DOM 操做。但若是給每一個元素加上惟一的標識,React.js 就能夠知道這兩個元素只是交換了位置

      render () {
          return (
            <div>
              {users.map((user, i) => <User user={user} key={i} />)}
            </div>    
          )
      }
      複製代碼

      這樣 React.js 就簡單的經過 key 來判斷出來,這兩個列表元素只是交換了位置,能夠儘可能複用元素內部的結構。
      對於用表達式套數組羅列到頁面上的元素,都要爲每一個元素加上 key 屬性,這個 key 必須是每一個元素惟一的標識,通常後臺會返回。


下一篇 --- React我的入門總結《二》

相關文章
相關標籤/搜索