React入門學習

爲了得到更好的閱讀體驗,請訪問原地址: 傳送門

1、React 簡介


React 是什麼

React 是一個起源於 Facebook 的內部項目,由於當時 Facebook 對於市場上全部的 JavaScript MVC 框架都不太滿意,因此索性就本身寫了一套,用來架設 Instagram。作出來以後,發現這套東西還蠻好用的,因而就在 2013 年 5 月開源了html

在這裏咱們須要稍微注意一下 庫(Library)框架(Framework) 的區別,React 自己是一個用於構建用戶界面的 JavaScript 庫,而咱們平時所說的 React 框架實際上是指的是 React/ React-router 和 React-redux 的結合體,庫和框架的本質區別體如今於控制權:前端

  • 「庫」是一個封裝好的特定的集合,提供給開發者使用,並且是特定於某一方面的集合(方法和函數),庫沒有控制權,控制權徹底在於使用者自己;
  • 「框架」顧名思義是一套架構,會基於自身的特色向用戶提供一套比較完整的解決方案,若是使用者選定了一套框架,那麼就須要根據框架自己作出必定的適應。

爲何使用 React?

這是一個很是有趣的問題,也讓我困惑和苦惱。在筆者還在學校的時候嘗試用 Vue 搭建了一套簡單的博客系統,學習曲線平滑,讓只會一些基礎 HTML/ CSS 代碼的我經過一段時間學習就可以上手了,可是學習 React 以來,進展變得相對緩慢.. 一部分緣由是由於 React 創新性的開發模式以及讓我感到無所適從的 JSX 語法(菜纔是原罪)。react

Vue 做者尤雨溪在知乎上回答「Vue 和 React 的優勢分別是什麼?」這個問題的時候提到 :git

這裏我能夠大方地認可,若是多年之後要論歷史地位,React 確定是高於 Vue 的。事實上,我做爲一個開發者,也是由衷地佩服 Jordan Walke, Sebastian Markbage 這樣的,能從開發模式層面上提出突破性的新方向的人。

React 從一開始的定位就是提出 UI 開發的新思路。當年 Pete Hunt 最開始推廣 React 的時候的一句口號就叫 "Rethinking Best Practices",這樣的定位使得 React 打開了一些全新的思路,吸引了一羣喜歡折騰的早期核心用戶,並在這個基礎上經過社區迭代孵化出了許多今天被 React 開發者看成常識的 pattern。這是 React 偉大的地方,Vue 裏面也有不少地方是直接受到了 React 的啓發。React 敢作這樣的嘗試,是由於它是 Facebook。這樣的體量的公司,在 infrastructure 層面得到質的提高,收益是巨大的,並且 Facebook 的工程師們足夠聰明又要靠工資吃飯,改變他/她們的習慣並非什麼問題。而對外推廣,則是一種大公司纔有的 「改變業界」 的底氣。github

相比「爲何使用 React?」的理由,稱讚 React 的卻是明顯更多一些(React 確實是突破性的開發模式)。算法

是由於 React 組件化的思想嗎?不是。我以爲這跟多少跟微服務化之類的概念有點兒相似,這是屬於一個時代對於計算機工程的思想進步,是對於團隊協做提出的新一種成熟的解決方案,也是必然的一種趨勢。當前流行的無論是 Angular/ Vue 仍是 React,都自然的支持着組件化的概念。shell

那是由於 React 性能出衆嗎?我想也不是。或許 React 剛出世時由於其獨特高效的虛擬 DOM 設計,可以在前端江湖中平步青雲,可是如今前端技術都主鍵地趨於成熟(我也不懂,我亂說的..),從不少地方的對比數據中,都可以看獲得其實 React 與其餘框架的性能差別並非特別大。而且體如今平時的開發中,這樣對比不明顯的速度差別,根本沒有多大的用處。編程

還看到一種觀點,說 React 適用於構建大型的項目。從我並很少的瞭解中,我知道 React 體系中自然有着許多的約束,以及一些不成文的約定,這就好像是 SpringBoot 中默認提供給使用者的一些姿式,自然就有很強的工程性,加上一些約定俗成的代碼風格 or 歸約,這就使得 Java 很適合一些大型的團隊項目。但能不能開發大型的項目歷來都是取決於人,而不是採用了哪一種框架。redux

因此比較令我信服的理由是(我亂猜的):像 Java 同樣,React 體系足夠成熟,社區也很是活躍,你遇到的問題很容易在網絡上找到答案,而且也有一些成熟的實踐 or 輪子用以解決各類各樣的問題。並且 React 還有一個比較特別的特性是:你可以比較無痛地使用 React Native 開發原生移動應用。數組

2、React 核心概念


虛擬 DOM(Vitural Document Object Model)

要理解這個「虛擬 DOM」的概念,首先咱們就須要知道什麼是「DOM」。咱們先暫時忘掉什麼網頁之類的,咱們想象如今咱們須要編寫程序來對下列的 Markdown 文檔進行改變應該怎麼作:

# Title
## subtitle - 1
content - 1
## subtitle - 2
content - 2

好比我如今就想要 content - 2 的內容進行改變,那麼我就須要一行一行的不斷遍歷直到最後遍歷到它才能進行操做,對內容改變的操做都差很少,因此若是我想對這個查找的操做進行優化,最簡單的想法就是把它樹化以減小高度,增長效率。

DOM 的概念

DOM 是英文 Document Object Model 的縮寫,即文檔對象模型。它是一種跨平臺的、獨立於編程語言的 API,它把 HTML、XHTML 或 XML 文檔都當作一個樹結構,而每一個節點視爲一個對象,這些對象能夠被編程語言操做,進而改變文檔的結構,映射到文檔的顯示。DOM 最開始的時候是和 JavaScript 交織在一塊兒的,只是後來它們最終演變成了兩個獨立的實體。DOM 被設計成與特定編程語言相獨立,儘管絕大部分時候咱們都是使用 JavaScript 來操做,但其實其餘的語言同樣能夠(如 Python)。

假若有這麼一段 HTML 代碼:

<html>
  <head>
    <title>文檔標題</title>
  </head>
  
  <body>
    <a href="">連接</a>
    <h1>標題</h1>
  </body>
</html>

那麼它最終就應該會是下面這棵樹同樣的結構:

這裏不對 DOM 節點的類型啊方法之類的進行討論,咱們只須要對 DOM 有一個大體的概念就行了。

瀏覽器渲染 DOM 的流程

咱們能夠簡單瞭解一下瀏覽器渲染 DOM 的流程:

  1. 解析 HTML 創建 DOM 樹;
  2. 解析 CSS,並結合 DOM 樹造成 Reander 樹;
  3. 佈局 Render 樹(Layout/ reflow),肯定各節點的尺寸、位置等信息;
  4. 繪製 Render 樹(Paint),繪製頁面像素信息;
  5. 瀏覽器將各層信息發給 GPU,GPU 會將各層合成(Composite),顯示在屏幕上;

操做 DOM 爲何慢

其實嚴格來講,單純的操做 DOM 並不慢,說它慢是帶有必定條件的。

想象在一次事件循環中屢次操做 DOM 時,有時但願 JS 代碼中能馬上獲取最新的 DOM 節點信息,這時瀏覽器不得不掛起 JS 引擎,轉而調用 DOM 引擎,計算渲染出最新的 DOM,以此來獲取最新的 DOM 節點信息,接着再從新激活 JS 引擎 繼續後續的操做。

能夠預見,上述操做不只須要屢次進行引擎的切換,還須要屢次計算佈局,從新繪製 DOM。事實上paint是一個耗時的過程,然而layout是一個更耗時的過程,咱們沒法肯定layout必定是自上而下或是自下而上進行的,甚至一次layout會牽涉到整個文檔佈局的從新計算。

可是layout是確定沒法避免的,因此咱們主要是要最小化layout的次數。

因此,下降引擎切換頻率、減少 DOM 變動規模纔是 DOM 性能優化方案的關鍵!

Virtual DOM 算法步驟

虛擬 DOM 正是解決了上述問題,它的本質就是用 JS 對象來模擬出咱們真實的 DOM 樹,它的算法大體以下:

  1. 用 JavaScript 對象映射造成 DOM 樹的結構,而後用這個樹構建一個真正的 DOM 樹,插到文檔當中;
  2. 當狀態變動的時候,從新構造一棵新的對象樹,而後用新的樹和舊的樹進行比較(Diff 算法),記錄兩棵樹差別;
  3. 把第二步中所記錄的差別應用到步驟一所構建的真正的 DOM 樹上,視圖就更新。

虛擬 DOM 和真實 DOM 的區別

咱們由此能夠對比出二者的不一樣:

  1. 改變多個狀態,影響多個節點佈局時,只是頻繁的修改了內存中的 JS 對象,而後一次性比較並修改真實 DOM 中須要改的部分,最後在真實 DOM 中進行排版與重繪,減小過多 DOM 節點排版與重繪損耗;
  2. 真實 DOM 頻繁排版與重繪的效率是至關低的;
  3. 虛擬 DOM 有效下降大面積(真實 DOM 節點)的重繪與排版,由於最終與真實 DOM 比較差別,能夠只渲染局部(同2);

使用虛擬DOM的損耗計算:

總損耗 = 虛擬DOM增刪改 + (與Diff算法效率有關)真實DOM差別增刪改 + (較少的節點)排版與重繪

直接使用真實DOM的損耗計算:

總損耗 = 真實DOM徹底增刪改 + (可能較多的節點)排版與重繪

Diff 算法

虛擬 DOM 的核心在於 Diff,它自動幫你計算那些應該調整的,而後只修改應該被調整的區域,省下的不是運行速度這種 "小速度",而是開發速度/ 維護速度/ 邏輯簡練程度等 "整體速度"。

但虛擬 DOM 快也是在相對條件下的,這裏引用 @尤雨溪大大在知乎問題《網上都說操做真實 DOM 慢,但測試結果卻比 React 更快,爲何?》上回答的一句話吧:

不要天真地覺得 Virtual DOM 就是快,diff 不是免費的,batching 麼 MVVM 也能作,並且最終 patch 的時候還不是要用原生 API。在我看來 Virtual DOM 真正的價值歷來都不是性能,而是它 1) 爲函數式的 UI 編程方式打開了大門;2) 能夠渲染到 DOM 之外的 backend,好比 ReactNative。

Diff 大體能夠分爲三種類型:

  • Tree Diff: 新舊兩棵 DOM 樹,逐層對比的過程,就是 Tree Diff,當整顆DOM逐層對比完畢,則全部須要被按需更新的元素,必然可以找到;
  • Component Diff: 在進行 Tree Diff 的時候,每一層中,組件級別的對比,叫作 Component Diff:

    • 若是對比先後,組件的類型相同,則暫時認爲此組件不須要被更新;
    • 若是對比先後,組件類型不一樣,則須要移除舊組件,建立新組件,並追加到頁面上;
  • Element Diff: 在進行組件對比的時候,若是兩個組件類型相同,則須要進行元素級別的對比,這叫作 Element Diff;

3、Hello World


使用 React 的網頁源碼,結構大體以下(能夠直接運行):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Hello React!</title>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>

<div id="example"></div>
<script type="text/babel">
    ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
    );
</script>

</body>
</html>

上面代碼有兩個地方須要注意。首先,最後一個 <script> 標籤的 type 屬性爲 text/babel 。這是由於 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"

其次,上面代碼一共用了三個庫: react.jsreact-dom.jsBrowser.js ,它們必須首先加載。其中,react.js是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的做用是將 JSX 語法轉爲 JavaScript 語法,這一步很消耗時間,實際上線的時候,應該將它放到服務器完成。

$ babel src --out-dir build

上面命令能夠將 src 子目錄的 js 文件進行語法轉換,轉碼後的文件所有放在 build 子目錄。

ReactDOM.render()

ReactDOM.render 是 React 的最基本方法,用於將模板轉爲 HTML 語言,並插入指定的 DOM 節點。

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

上面代碼將一個 h1 標題,插入 example 節點,運行結果以下:

JSX 語法

上一節的代碼, HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它容許 HTML 與 JavaScript 的混寫。咱們先來看如下一段代碼:

const element = <h1>Hello, world!</h1>;

與瀏覽器的 DOM 元素不一樣,React 當中的元素事實上是普通的對象,React DOM 能夠確保 瀏覽器 DOM 的數據內容與 React 元素保持一致。要將 React 元素渲染到根 DOM 節點中,咱們經過把它們都傳遞給 ReactDOM.render() 的方法來將其渲染到頁面上:

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

JSX 看起來相似 HTML ,你也能夠在上面代碼中嵌套多個 HTML 標籤,可是須要使用一個 div 元素包裹它。

JavaScript 表達式

咱們能夠在 JSX 中使用 JavaScript 表達式。表達式寫在花括號 {} 中。實例以下:

ReactDOM.render(
    <div>
      <h1>{1+1}</h1>
    </div>
    ,
    document.getElementById('example')
);

在 JSX 中不能使用 if else 語句,但可使用 conditional (三元運算) 表達式來替代。如下實例中若是變量 i 等於 1 瀏覽器將輸出 true, 若是修改 i 的值,則會輸出 false.

ReactDOM.render(
    <div>
      <h1>{i == 1 ? 'True!' : 'False'}</h1>
    </div>
    ,
    document.getElementById('example')
);

樣式

React 推薦使用內聯樣式。咱們可使用 camelCase 語法來設置內聯樣式. React 會在指定元素數字後自動添加 px 。如下實例演示了爲 h1 元素添加 myStyle 內聯樣式:

var myStyle = {
    fontSize: 100,
    color: '#FF0000'
};
ReactDOM.render(
    <h1 style = {myStyle}>菜鳥教程</h1>,
    document.getElementById('example')
);

註釋

註釋須要寫在花括號中,實例以下:

ReactDOM.render(
    <div>
    <h1>菜鳥教程</h1>
    {/*註釋...*/}
     </div>,
    document.getElementById('example')
);

數組

JSX 容許在模板中插入數組,數組會自動展開全部成員:

var arr = [
  <h1>菜鳥教程</h1>,
  <h2>學的不只是技術,更是夢想!</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

參考資料


  1. http://www.ruanyifeng.com/blog/2015/03/react.html - React 入門實例教程 - 阮一峯
  2. https://www.jianshu.com/p/60100985dd7f - 前端框架與庫的區別
  3. https://www.zhihu.com/questio... - Vue 和 React 的優勢分別是什麼?
  4. https://zhuanlan.zhihu.com/p/... - 你真的理解 DOM 了嗎?
  5. https://developer.mozilla.org... - DOM 概述
  6. https://blog.huteming.site/po... - 爲何說虛擬DOM更快

按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
獨立域名博客:wmyskxz.com
簡書ID: @我沒有三顆心臟
github: wmyskxz
歡迎關注公衆微信號:wmyskxz
分享本身的學習 & 學習資料 & 生活
想要交流的朋友也能夠加qq羣:3382693

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索