React學習(4)-理清React的工做方式

前言

在接觸React以前,咱們也許習慣了DOM編程,那它相比於原生JS,JQ編程方式,究竟有什麼區別?React的工做方式是什麼樣子的?所謂的虛擬DOM又指的是什麼?以及React的工做方式的優勢有哪些?javascript

那麼本篇就是你想要知道的css

若是想閱讀體驗更好,可戳React學習(4)-理清React的工做方式,內有視頻vue

從一個簡單的React組件開始

咱們先看一個加減數字框組件,具體效果以下所示,分別經過原生JS和JQ是怎麼實現的java

數字加減.gif
原生JS實現 DOM結構

<div>
		   	<button id = "reduce">-</button>
		   	<input  id = "input" type="text" value="0">
		   	<button id = "add">+</button>
	   </div>
複製代碼

CSS層疊樣式react

*{
		padding: 0;
		margin: 0;
	}

	div{
		width: 100%;
		display: flex;
		display: -webkit-flex;
		position:fixed;
		left: 40%;
		top:10%;
	}

	button {
		padding: 10px;
	}
	
	input {
		text-align:center;
	}

複製代碼

對應的JSjquery

// 獲取DOM元素
	   	     var oBtnReduce = document.querySelector("#reduce"),
	   	         oInput = document.querySelector("#input"),
	   	         oBtnAdd = document.querySelector("#add");
	   	        
 			
 			 // 添加事件
             oBtnAdd.onclick = function() {
             	oInput.value++;
             }

             oBtnReduce.onclick = function() {
             	oInput.value--;
             }
複製代碼

JQ實現:程序員

var  $reduce = $('#reduce'),
     			    $input = $('#input'),
     			    $add = $('#add'),
     			    $nowVal = $("#input").val();

     			    $reduce.click(function() {
     			    	$input.val($nowVal--);
     			    });

     			    $add.click(function() {
     			    	$input.val($nowVal++);
     			    })
複製代碼

固然,你把事件添加在內聯元素身上,能夠在行內元素裏面添加事件,經過傳參的方式去控制,以下代碼所示,也是能夠的web

<div>
		   	<button onclick = "handleClick('-')" id = "reduce">-</button>
		   	<input  id = "input" type="text" value="0">
		   	<button onclick = "handleClick('+')" id = "add">+</button>
	   </div>
       <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
       
       <script type="text/javascript">
       
             function handleClick(flag) {
					var nowVal =  $("#input").val();
				    if(flag == '+') {
						$("#input").val(parseInt(nowVal) +1);
					}else if(flag == '-' ) {
						$("#input").val(parseInt(nowVal) -1);
					}
			 }
       </script>
複製代碼

對於在原生JS,JQ中,經過內聯方式添加事件,是不推薦的,然而在現在的一些面向數據編程,例如React,Vue等框架中,這一方式卻獲得了支持與延續,要從面向DOM編程轉移到面向數據編程 React實現npm

import React, {  Component } from 'react';      
import ReactDOM from 'react-dom'; 


class CountNum extends Component {
    constructor(props) {
        super(props);

        this.state = {
            inputVal: 0
        }
       

    }
    
    render() {
        return (
            <div style = {{ textAlign: "center", marginTop: "50px"}}>
                 <button onClick = { this.handleClickReduce.bind(this) }>-</button>
                 <input style = {{ textAlign: "center"}} value = { this.state.inputVal } onChange = { this.handleInputChange.bind(this) } />
                 <button onClick = { this.handleCLickAdd }>+</button>
            </div>
        );
    }

    handleCLickAdd = () => {
      this.setState({
        inputVal: this.state.inputVal+1
      });
    }
    
    // handleCLickAdd(){
    //    this.setState({
    //        inputVal: this.state.inputVal+1 
    //    })
    // }
   
   handleClickReduce() {
      this.setState({
          inputVal: this.state.inputVal-1
      })
   }

   handleInputChange(e) {
      let changeVal = e.target.value;
      this.setState({
        inputVal: changeVal++
      });
   }
}


const container = document.getElementById('root');

ReactDOM.render(<CountNum />, container);
複製代碼

從上面一看,對於剛接觸React的小夥伴來講,可能以爲用原生JS,JQ實現起來很簡單呀,React寫起來的代碼,什麼玩意的,那麼一大堆的,JS裏面還寫HTML代碼,簡直噁心到不行,並未達到,內容結構,層疊樣式,邏輯的分離,若是對於這部份內容有疑惑的,能夠閱讀以前兩篇JSX的文章的編程

對於JS,JQ的實現方式,主要工做是在操做DOM,獲取元素,添加事件,執行操做。對於簡單的業務實現,是沒有什麼問題的,可是當DOM結構層級比較深,要進行一些複雜的邏輯操做時,此時,不斷的操做DOM就變得很是噁心了的,這裏並非忽視原生JS,即便有了一些上層的框架簡化了操做,但核心的邏輯代碼編寫仍然是要寫的,只是關注點不同了的

而在React中,咱們能夠發現,並無操做DOM的過程,一切以數據爲中心,數據是什麼,頁面就顯示什麼

並無像JS,JQ同樣獲取元素,添加事件而後執行一些操做的動做.

對於大型項目迭代開發,這種方式編寫的代碼會更容易的管理,由於React只是用做於視圖UI層的渲染工做,咱們關心的是渲染成什麼樣子,而不須要關心如何實現渲染,怎麼進行DOM操做

這就比如在業界裏有這麼一句話,優秀的程序員關心數據結構,平凡的程序員操心代碼同樣,若是把JQ,與React作這樣一個對比,前者就是React,在這裏沒有任何貶低JQ的意思.

JQ仍然是無比強悍的,每一個技術都有與之對應的應用場景.

何況也沒有JQ實現不了的,只不過是略繁瑣一些而已.

至少在沒有出現React,vue,Angular等這些框架以前,它仍然是霸主統治性地位存在的,然而如今真的不得不說,它的確是在走向落寞.

從上面的React代碼中,咱們能夠歸結出,React的理念能夠用這麼一個公式表示:

UI = render(data)

複製代碼

這個等號左邊UI用戶界面的顯示取決於等號右邊的render函數,這個render函數接收一個數據data做爲參數,這個函數是一個純函數,也能夠稱爲是無狀函數(函數式組件)

換而言之,相似這種只用做UI顯示的函數,咱們能夠用無狀態函數去定義,這在後續若使用了redux作公共數據管理時,把組件裏面的state數據抽離到store當中時,可使用無狀態組件的

由於它只負責頁面的渲染,沒有去作任何邏輯操做的時候,UI組件咱們通常均可以用無狀態組件來定義,UI組件只負責頁面的渲染,固然這並非絕對的,有時候,也能夠作一些簡單邏輯的操做

使用無狀態組件(函數組件),它的性能是高於普通組件的,由於它是函數,而用class類定義的組件,類生成的對象裏面有生命週期函數,因此它執行起來確定沒有函數組件(UI組件)快

對於咱們開發來講,最重要的是區分哪些是屬於data,哪些是屬於render,想要更新用戶界面,要作的是更新data,用戶的界面天然會作出響應,因此把React稱爲響應式編程(面向數據編程)

注意:render函數返回的值,組件生成的 HTML 結構只能有一個單一的根節點

Virtual(虛擬) DOM

元素(JSX)是構成React應用的最小磚塊,它描述了你在在屏幕上看到的UI內容

與瀏覽器的DOM元素不一樣,React元素時建立開銷極小的普通對象,並不會跟原生操做DOM同樣,影響整個DOM的重繪渲染,React DOM會負責更新DOM與React元素保持一致

React只更新它須要更新的部分,React DOM會將元素和它的子元素與它們以前的狀態進行比較,並只會進行必要的更新,例如:以下示例

虛擬DOM更新.gif
具體代碼以下所示

import React, {  Fragment, Component } from 'react';      
import ReactDOM from 'react-dom'; 

function tick() {
  const element = (
    <Fragment>
      <div style = {{ textAlign: 'center' }}>
        <h1>歡迎關注微信itclancoder公衆號</h1>
        <p>如今北京時間是 { new Date().toLocaleTimeString() }</p>
      </div>
    </Fragment>
  );
  
const container = document.getElementById('root');

ReactDOM.render(element, container);

}

setInterval(tick, 1000);  
  
複製代碼

固然,咱們能夠對它進一步的優化,寫成一個組件,以下所示:

import React, {  Fragment, Component } from 'react';      
import ReactDOM from 'react-dom'; 

class Clock extends Component {
  constructor(props) {
    super(props);

    this.state = {
       date: new Date()
    }

  }

  render() {
    return (
       <Fragment>
            <div style = {{ textAlign: "center" }}>
                <h1>歡迎關注微信itclanCoder公衆號</h1>
                <p>如今是北京時間:{ this.state.date.toLocaleTimeString() }</p>
            </div>
       </Fragment>
    );
  }

 // 生命週期函數,組件掛載時自動執行這個方法,組件已經被渲染到 DOM 中後運行
  componentDidMount() {
    this.timer = setInterval(() => {
      this.tick()
    }, 1000)
  }
  // 組件卸載時,清除定時器
  componentWillUnmount(){
    clearInterval(this.timer);
  }

  tick() {
    this.setState({
      date: new Date()
    })
  }
}

const container = document.getElementById('root');


ReactDOM.render(<Clock  />, container);
複製代碼

對於上面的代碼,涉及到初始化state狀態數據,以及componentDidMount和componentWillUnmount兩個生命週期函數,在組件掛載時設置一個定時器函數,自動更新時間,在組件卸載時,清除定時器,經過setState這個方法,實時更新state數據。更多相關state以及props,生命週期的知識,暫時知道這麼用就能夠了,後續會有更詳細的內容介紹的​

儘管每一秒咱們都會新建一個描述整個 UI 樹的元素,可是React DOM 只會更新實際改變了的內容,也就是上面中的文本節點

這是由於React利用Virtual DOM,讓每次渲染都只從新渲染最少的DOM元素

而操做DOM會引發瀏覽器對網頁進行重排重繪。

DOM樹是對HTML的抽象,而vitrtual DOM就是對DOM樹的抽象,虛擬DOM不會觸及瀏覽器,虛擬DOM本質上就是javascript對象,還記得前面說過的JSX是React.createElement()方法的一個語法糖?

它是存在於javascript空間樹形結構,每次自上而下渲染React組件時,會對比這一次產生的virtual DOM和上一次渲染的virtual DOM,對比就會發現差異,而後修改真正的DOM樹時就只須要修改中的部分就能夠了的

React的工做方式及優勢

在沒有組件化React,Vue,Angular以前,毫無疑問,JQ是最直觀易懂的,可是當項目逐漸變得複雜龐大時,用JQ寫出來的代碼耦合度就沒那麼高了的,正是這樣,也就誕生了一些requirejs以及Seajs解決一些問題,可是使用JQ寫出來的代碼每每互相糾纏 以下圖所示

JQ工做方式易形成的代碼結構.png

使用React的方式,就能夠避免構建這樣複雜的程序結構,不管何種事件,引起的都是React組件的從新渲染,它只會修改數據變化的的DOM部分,並不須要去關心怎麼去操做DOM

以下圖所示

React的工做流程.png

在React中,對JSX元素上添加事件,是經過on*EventType 這種內聯方式添加的,不須要手動調用瀏覽器原生的 addEventListener 進行事件監聽,在React中,它已經幫咱們封裝好了一些事件類型屬性,當須要給某個元素監聽事件的時候,只須要經過內聯方式,React元素上加on*EventType就能夠了,注意這裏事件類型的寫法,駝峯式命名法

也無需考慮瀏覽器的兼容性,這裏要格外注意的是,這些 on*EventType的事件監聽只能用在普通的 HTML 的標籤上(div,input,p,a等原生瀏覽器支持的標籤),而不能用在組件標籤上。也就是說,<Button onClick={…} /> 這樣的寫法是不起做用的

若是想要作到這一點,在組件標籤上監聽事件起做用,也能夠作到,就是結合第三方模塊styled-components樣式組件進行使用,是能夠作到的,更多內容,能夠參考styled-components官方文檔 這裏簡單提一下:

  • 終端裏,安裝styled-components: npm install --save styled-components
  • 在文件中引入styled-components模塊
  • 樣式組件定義使用,以下所示
import React, {  Fragment, Component } from 'react';      
import ReactDOM from 'react-dom'; 
import styled from 'styled-components';

export const Button = styled.button`
   outline: none;
`

// class Button extends Component {

//      render() {
//         return (
//           <button>按鈕</button>
//         );

//      }

// }

class CountNum extends Component {
    constructor(props) {
        super(props);

        this.state = {
            inputVal: 0
        }
       

    }
   
    render() {
        return (
            <div style = {{ textAlign: "center", marginTop: "50px"}}>
                 <button onClick = { this.handleClickReduce.bind(this) }>-</button>
                 <input style = {{ textAlign: "center"}} value = { this.state.inputVal } onChange = { this.handleInputChange.bind(this) } />
                 <button onClick = { this.handleCLickAdd }>+</button>
                 <Button onClick = { this.handleBtnClick.bind(this) }>按鈕</Button>
            </div>
        );
    }

    handleBtnClick() {
      alert("我是樣式組件,簡直帥呆了");
    }

    handleCLickAdd = () => {
      this.setState({
        inputVal: this.state.inputVal+1
      });
    }
    
    // handleCLickAdd(){
    //    this.setState({
    //        inputVal: this.state.inputVal+1 
    //    })
    // }
   
   handleClickReduce() {
      this.setState({
          inputVal: this.state.inputVal-1
      })
   }

   handleInputChange(e) {
      let changeVal = e.target.value;
      this.setState({
        inputVal: changeVal++
      });
   }
}


const container = document.getElementById('root');

ReactDOM.render(<CountNum />, container);
複製代碼

具體效果以下所示

樣式化組件定義.gif

React的編程模式是函數式編程來解決用戶界面渲染問題的,也稱爲面向數據編程,一切皆是JS,基於組件開發模式

結語

本文主要從一個簡單的React數字框組件應用開始,分別用原生JS,JQ,React進行了實現,在React中UI視圖取決於render函數返回的內容,數據是什麼,就讓頁面顯示什麼,無需關注DOM操做,而且React引入了虛擬DOM

它是對DOM樹的一種抽象,本質上就是一js對象,當進行視圖的改變時,當React的子元素內容發生改變時,並不會引發整個瀏覽器的重繪和重排,只會更改變化的數據部分,而且在給JSX添加事件監聽時,使用on*EnentType的方式

而且這種事件的監聽,它只做用於原生HTML元素上,若放在自定義的組件上時,是不起做用的,具體解決辦法,能夠引入第三方styled-components模塊的,後續單獨拿一篇幅來講也不爲過的,涉及到的知識仍是挺多的

相關文章
相關標籤/搜索