React入門學習

来源:https://www.cnblogs.com/wmyskxz/archive/2019/10/15/11681955.html
-Advertisement-
Play Games

為了獲得更好的閱讀體驗,請訪問原地址: "傳送門" 一、React 簡介 React 是什麼 React 是一個起源於 Facebook 的內部項目,因為當時 Facebook 對於市場上所有的 JavaScript MVC 框架都不太滿意,所以索性就自己寫了一套,用來架設 Instagram。做出 ...


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

一、React 簡介


React 是什麼

React 是一個起源於 Facebook 的內部項目,因為當時 Facebook 對於市場上所有的 JavaScript MVC 框架都不太滿意,所以索性就自己寫了一套,用來架設 Instagram。做出來之後,發現這套東西還蠻好用的,於是就在 2013 年 5 月開源了

在這裡我們需要稍微註意一下 庫(Library)框架(Framework) 的區別,React 本身是一個用於構建用戶界面的 JavaScript 庫,而我們平時所說的 React 框架其實是指的是 React/ React-router 和 React-redux 的結合體,庫和框架的本質區別體現在於控制權:

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

為什麼使用 React?

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

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

這裡我可以大方地承認,如果多年以後要論歷史地位,React 肯定是高於 Vue 的。事實上,我作為一個開發者,也是由衷地佩服 Jordan Walke, Sebastian Markbage 這樣的,能從開發模式層面上提出突破性的新方向的人。

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

相比「為什麼使用 React?」的理由,稱贊 React 的倒是明顯更多一些(React 確實是突破性的開發模式)。

是因為 React 組件化的思想嗎?不是。我覺得這跟多少跟微服務化之類的概念有點兒類似,這是屬於一個時代對於電腦工程的思想進步,是對於團隊協作提出的新一種成熟的解決方案,也是必然的一種趨勢。當前流行的不管是 Angular/ Vue 還是 React,都天然的支持著組件化的概念。

那是因為 React 性能出眾嗎?我想也不是。或許 React 剛出世時因為其獨特高效的虛擬 DOM 設計,能夠在前端江湖中平步青雲,但是現在前端技術都主鍵地趨於成熟(我也不懂,我亂說的..),從很多地方的對比數據中,都能夠看得到其實 React 與其他框架的性能差異並不是特別大。並且體現在平時的開發中,這樣對比不明顯的速度差異,根本沒有多大的用處。

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

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

二、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;

三、Hello World


  • 引用自:http://www.ruanyifeng.com/blog/2015/03/react.html - 阮一峰 - React 入門實例教程

使用 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 語法

  • 引用自:https://www.runoob.com/react/react-jsx.html - RUNOOB.COM - React 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/question/301860721/answer/545031906 - Vue 和 React 的優點分別是什麼?
  4. https://zhuanlan.zhihu.com/p/22184194 - 你真的理解 DOM 了嗎?
  5. https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Introduction - DOM 概述
  6. https://blog.huteming.site/posts/e0c41c5f/ - 為什麼說虛擬DOM更快

按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
獨立功能變數名稱博客:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關註公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在SQL Server中重建索引(Rebuild Index)與重組索引(Reorganize Index)會觸發統計信息更新嗎? 那麼我們先來測試、驗證一下: 我們以AdventureWorks2014為測試環境,如下所示: Person.Person表的統計信息最後一次更新為2014-07-17... ...
  • 本文基於 Android 9.0 , 代碼倉庫地址 : "android_9.0.0_r45" 文中源碼鏈接: "SystemServer.java" "ActivityManagerService.java" "Process.java" "ZygoteProcess.java" 對 和 啟動流程 ...
  • 1. 在對象內部讀取數據時,應該直接通過實例變數來讀,而寫入數據時,則應通過屬性來寫。 2. 在初始化方法及dealloc方法中,總是應該直接通過實例變數來讀寫數據。 3. 使用Lazy Initialization配置的數據,應該通過屬性來讀取數據。 4. 不要在setter/g... ...
  • 1. 可以用@property語法來定義對象中所封裝的數據。 2. 通過“修飾詞”來指定存儲數據所需的正確語義。 3. 在設置屬性所對應的實例變數時,一定要遵從該屬性所聲明的語義。 4. 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。 ...
  • 1. 應該用枚舉表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字,就像監聽網路狀態的枚舉。 2. 如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那麼就將各選項定義為2的冪,以便通過按位或操作將其組合起來。 3. 用 NS_ENUM 與 NS_O... ...
  • 本篇記錄的是使用Jsoup框架爬取網頁內容,結合Android的RecyclerView,從而實現批量下載小說的功能(也是我的APP "星之小說下載器Android版" 的核心功能), 思路僅供參考 本文使用了AsyncTask來實現下載功能,不懂使用的可以參考一下我的文章 "Android開發—— ...
  • 換膚思路: 1.什麼時候換膚? xml載入前換膚,如果xml載入後換膚,用戶將會看見換膚之前的色彩,用戶體驗不好。 2.皮膚是什麼? 皮膚就是apk,是一個資源包,包含了顏色、圖片等。 3.什麼樣的控制項應該進行換膚? 包含背景圖片的控制項,例如textView文字顏色。 4.皮膚與已安裝的資源如何匹配 ...
  • 隊列是常用的數據結構之一,只允許在表的前端(隊頭)進行刪除操作(出隊),在表的後端(隊尾)進行插入操作(入隊)。特點是先進先出,最先插入的元素最先被刪除。 在jQuery內部,隊列模塊為動畫模塊提供基礎功能,負責存儲動畫函數、自動出隊並執行動畫函數,同時還要確保動畫函數的順序執行。 jQuery的靜 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...