為了獲得更好的閱讀體驗,請訪問原地址:傳送門
一睬涧、React 簡介
React 是什么
React 是一個起源于 Facebook 的內部項目,因為當時 Facebook 對于市場上所有的 JavaScript MVC 框架都不太滿意旗唁,所以索性就自己寫了一套畦浓,用來架設 Instagram。做出來之后检疫,發(fā)現(xiàn)這套東西還蠻好用的讶请,于是就在 2013 年 5 月開源了。
在這里我們需要稍微注意一下 庫(Library) 和 框架(Framework) 的區(qū)別屎媳,React 本身是一個用于構建用戶界面的 JavaScript 庫夺溢,而我們平時所說的 React 框架其實是指的是 React/ React-router 和 React-redux 的結合體,庫和框架的本質區(qū)別體現(xiàn)在于控制權:
- 「庫」是一個封裝好的特定的集合烛谊,提供給開發(fā)者使用风响,而且是特定于某一方面的集合(方法和函數(shù)),庫沒有控制權丹禀,控制權完全在于使用者本身状勤;
- 「框架」顧名思義是一套架構,會基于自身的特點向用戶提供一套比較完整的解決方案双泪,如果使用者選定了一套框架持搜,那么就需要根據(jù)框架本身做出一定的適應。
為什么使用 React焙矛?
這是一個非常有趣的問題朵诫,也讓我困惑和苦惱。在筆者還在學校的時候嘗試用 Vue 搭建了一套簡單的博客系統(tǒng)薄扁,學習曲線平滑剪返,讓只會一些基礎 HTML/ CSS 代碼的我通過一段時間學習就能夠上手了,但是學習 React 以來邓梅,進展變得相對緩慢.. 一部分原因是因為 React 創(chuàng)新性的開發(fā)模式以及讓我感到無所適從的 JSX 語法(菜才是原罪)脱盲。
Vue 作者尤雨溪在知乎上回答「Vue 和 React 的優(yōu)點分別是什么?」這個問題的時候提到 :
這里我可以大方地承認日缨,如果多年以后要論歷史地位钱反,React 肯定是高于 Vue 的。事實上匣距,我作為一個開發(fā)者面哥,也是由衷地佩服 Jordan Walke, Sebastian Markbage 這樣的,能從開發(fā)模式層面上提出突破性的新方向的人毅待。
React 從一開始的定位就是提出 UI 開發(fā)的新思路尚卫。當年 Pete Hunt 最開始推廣 React 的時候的一句口號就叫 "Rethinking Best Practices",這樣的定位使得 React 打開了一些全新的思路尸红,吸引了一群喜歡折騰的早期核心用戶吱涉,并在這個基礎上通過社區(qū)迭代孵化出了許多今天被 React 開發(fā)者當作常識的 pattern。這是 React 偉大的地方外里,Vue 里面也有很多地方是直接受到了 React 的啟發(fā)怎爵。React 敢做這樣的嘗試,是因為它是 Facebook盅蝗。這樣的體量的公司鳖链,在 infrastructure 層面獲得質的提升,收益是巨大的墩莫,而且 Facebook 的工程師們足夠聰明又要靠工資吃飯芙委,改變他/她們的習慣并不是什么問題。而對外推廣贼穆,則是一種大公司才有的 “改變業(yè)界” 的底氣题山。
相比「為什么使用 React?」的理由故痊,稱贊 React 的倒是明顯更多一些(React 確實是突破性的開發(fā)模式)顶瞳。
是因為 React 組件化的思想嗎?不是愕秫。我覺得這跟多少跟微服務化之類的概念有點兒類似慨菱,這是屬于一個時代對于計算機工程的思想進步,是對于團隊協(xié)作提出的新一種成熟的解決方案戴甩,也是必然的一種趨勢符喝。當前流行的不管是 Angular/ Vue 還是 React,都天然的支持著組件化的概念甜孤。
那是因為 React 性能出眾嗎协饲?我想也不是畏腕。或許 React 剛出世時因為其獨特高效的虛擬 DOM 設計茉稠,能夠在前端江湖中平步青云描馅,但是現(xiàn)在前端技術都主鍵地趨于成熟(我也不懂,我亂說的..)而线,從很多地方的對比數(shù)據(jù)中铭污,都能夠看得到其實 React 與其他框架的性能差異并不是特別大。并且體現(xiàn)在平時的開發(fā)中膀篮,這樣對比不明顯的速度差異嘹狞,根本沒有多大的用處。
還看到一種觀點誓竿,說 React 適用于構建大型的項目磅网。從我并不多的了解中,我知道 React 體系中天然有著許多的約束烤黍,以及一些不成文的約定知市,這就好像是 SpringBoot 中默認提供給使用者的一些姿勢,天然就有很強的工程性速蕊,加上一些約定俗成的代碼風格 or 歸約嫂丙,這就使得 Java 很適合一些大型的團隊項目。但能不能開發(fā)大型的項目從來都是取決于人规哲,而不是采用了哪種框架跟啤。
所以比較令我信服的理由是(我亂猜的):像 Java 一樣,React 體系足夠成熟唉锌,社區(qū)也非秤绶剩活躍,你遇到的問題很容易在網(wǎng)絡上找到答案袄简,并且也有一些成熟的實踐 or 輪子用以解決各種各樣的問題腥放。而且 React 還有一個比較特別的特性是:你能夠比較無痛地使用 React Native 開發(fā)原生移動應用。
二绿语、React 核心概念
虛擬 DOM(Vitural Document Object Model)
要理解這個「虛擬 DOM」的概念秃症,首先我們就需要知道什么是「DOM」。我們先暫時忘掉什么網(wǎng)頁之類的吕粹,我們想象現(xiàn)在我們需要編寫程序來對下列的 Markdown 文檔進行改變應該怎么做:
# Title
## subtitle - 1
content - 1
## subtitle - 2
content - 2
比如我現(xiàn)在就想要 content - 2
的內容進行改變种柑,那么我就需要一行一行的不斷遍歷直到最后遍歷到它才能進行操作,對內容改變的操作都差不多匹耕,所以如果我想對這個查找的操作進行優(yōu)化聚请,最簡單的想法就是把它樹化以減少高度,增加效率稳其。
DOM 的概念
DOM 是英文 Document Object Model 的縮寫驶赏,即文檔對象模型炸卑。它是一種跨平臺的、獨立于編程語言的 API母市,它把 HTML矾兜、XHTML 或 XML 文檔都當做一個樹結構,而每個節(jié)點視為一個對象患久,這些對象可以被編程語言操作,進而改變文檔的結構浑槽,映射到文檔的顯示蒋失。DOM 最開始的時候是和 JavaScript 交織在一起的,只是后來它們最終演變成了兩個獨立的實體桐玻。DOM 被設計成與特定編程語言相獨立篙挽,盡管絕大部分時候我們都是使用 JavaScript 來操作,但其實其他的語言一樣可以(如 Python)镊靴。
假如有這么一段 HTML 代碼:
<html>
<head>
<title>文檔標題</title>
</head>
<body>
<a href="">鏈接</a>
<h1>標題</h1>
</body>
</html>
那么它最終就應該會是下面這棵樹一樣的結構:
這里不對 DOM 節(jié)點的類型啊方法之類的進行討論铣卡,我們只需要對 DOM 有一個大致的概念就好了。
瀏覽器渲染 DOM 的流程
我們可以簡單了解一下瀏覽器渲染 DOM 的流程:
- 解析 HTML 建立 DOM 樹偏竟;
- 解析 CSS煮落,并結合 DOM 樹形成 Reander 樹;
- 布局 Render 樹(Layout/ reflow)踊谋,確定各節(jié)點的尺寸蝉仇、位置等信息;
- 繪制 Render 樹(Paint)殖蚕,繪制頁面像素信息轿衔;
- 瀏覽器將各層信息發(fā)給 GPU,GPU 會將各層合成(Composite)睦疫,顯示在屏幕上害驹;
操作 DOM 為什么慢
其實嚴格來說,單純的操作 DOM 并不慢蛤育,說它慢是帶有一定條件的宛官。
想象在一次事件循環(huán)中多次操作 DOM 時,有時希望 JS 代碼中能立刻獲取最新的 DOM 節(jié)點信息缨伊,這時瀏覽器不得不掛起 JS 引擎摘刑,轉而調用 DOM 引擎,計算渲染出最新的 DOM刻坊,以此來獲取最新的 DOM 節(jié)點信息枷恕,接著再重新激活 JS 引擎 繼續(xù)后續(xù)的操作。
可以預見谭胚,上述操作不僅需要多次進行引擎的切換徐块,還需要多次計算布局未玻,重新繪制 DOM。事實上paint
是一個耗時的過程胡控,然而layout
是一個更耗時的過程扳剿,我們無法確定layout
一定是自上而下或是自下而上進行的,甚至一次layout
會牽涉到整個文檔布局的重新計算昼激。
但是layout
是肯定無法避免的庇绽,所以我們主要是要最小化layout
的次數(shù)。
所以橙困,降低引擎切換頻率瞧掺、減小 DOM 變更規(guī)模才是 DOM 性能優(yōu)化方案的關鍵!
Virtual DOM 算法步驟
虛擬 DOM 正是解決了上述問題凡傅,它的本質就是用 JS 對象來模擬出我們真實的 DOM 樹辟狈,它的算法大致如下:
- 用 JavaScript 對象映射形成 DOM 樹的結構,然后用這個樹構建一個真正的 DOM 樹夏跷,插到文檔當中哼转;
- 當狀態(tài)變更的時候,重新構造一棵新的對象樹壹蔓,然后用新的樹和舊的樹進行比較(Diff 算法),記錄兩棵樹差異硼莽;
- 把第二步中所記錄的差異應用到步驟一所構建的真正的 DOM 樹上,視圖就更新懂鸵。
虛擬 DOM 和真實 DOM 的區(qū)別
我們由此可以對比出兩者的不同:
- 改變多個狀態(tài)偏螺,影響多個節(jié)點布局時匆光,只是頻繁的修改了內存中的 JS 對象,然后一次性比較并修改真實 DOM 中需要改的部分终息,最后在真實 DOM 中進行排版與重繪,減少過多 DOM 節(jié)點排版與重繪損耗周崭;
- 真實 DOM 頻繁排版與重繪的效率是相當?shù)偷模?/li>
- 虛擬 DOM 有效降低大面積(真實 DOM 節(jié)點)的重繪與排版柳譬,因為最終與真實 DOM 比較差異续镇,可以只渲染局部(同2);
使用虛擬DOM的損耗計算:
總損耗 = 虛擬DOM增刪改 + (與Diff算法效率有關)真實DOM差異增刪改 + (較少的節(jié)點)排版與重繪
直接使用真實DOM的損耗計算:
總損耗 = 真實DOM完全增刪改 + (可能較多的節(jié)點)排版與重繪
Diff 算法
虛擬 DOM 的核心在于 Diff,它自動幫你計算那些應該調整的制跟,然后只修改應該被調整的區(qū)域,省下的不是運行速度這種 "小速度"雨膨,而是開發(fā)速度/ 維護速度/ 邏輯簡練程度等 "總體速度"。
但虛擬 DOM 快也是在相對條件下的聊记,這里引用 @尤雨溪
大大在知乎問題《網(wǎng)上都說操作真實 DOM 慢撒妈,但測試結果卻比 React 更快排监,為什么?》上回答的一句話吧:
不要天真地以為 Virtual DOM 就是快社露,diff 不是免費的,batching 么 MVVM 也能做峭弟,而且最終 patch 的時候還不是要用原生 API。在我看來 Virtual DOM 真正的價值從來都不是性能脱拼,而是它 1) 為函數(shù)式的 UI 編程方式打開了大門;2) 可以渲染到 DOM 以外的 backend熄浓,比如 ReactNative。
Diff 大致可以分為三種類型:
- Tree Diff: 新舊兩棵 DOM 樹赌蔑,逐層對比的過程俯在,就是 Tree Diff娃惯,當整顆DOM逐層對比完畢,則所有需要被按需更新的元素趾浅,必然能夠找到;
-
Component Diff: 在進行 Tree Diff 的時候皿哨,每一層中浅侨,組件級別的對比证膨,叫做 Component Diff:
- 如果對比前后,組件的類型相同,則暫時認為此組件不需要被更新挨决;
- 如果對比前后,組件類型不同脖祈,則需要移除舊組件肆捕,創(chuàng)建新組件盖高,并追加到頁面上;
- Element Diff: 在進行組件對比的時候喻奥,如果兩個組件類型相同,則需要進行元素級別的對比撞蚕,這叫做 Element Diff润梯;
三甥厦、Hello World
- 引用自:http://www.ruanyifeng.com/blog/2015/03/react.html - 阮一峰 - React 入門實例教程
使用 React 的網(wǎng)頁源碼,結構大致如下(可以直接運行):
<!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.js
鬼悠、react-dom.js
和 Browser.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 節(jié)點收夸。
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
上面代碼將一個 h1
標題血崭,插入 example
節(jié)點,運行結果如下:
JSX 語法
- 引用自:https://www.runoob.com/react/react-jsx.html - RUNOOB.COM - React JSX
上一節(jié)的代碼夹纫, HTML 語言直接寫在 JavaScript 語言之中,不加任何引號捷凄,這就是 JSX 的語法,它允許 HTML 與 JavaScript 的混寫围来。我們先來看以下一段代碼:
const element = <h1>Hello, world!</h1>;
與瀏覽器的 DOM 元素不同跺涤,React 當中的元素事實上是普通的對象,React DOM 可以確保 瀏覽器 DOM 的數(shù)據(jù)內容與 React 元素保持一致监透。要將 React 元素渲染到根 DOM 節(jié)點中桶错,我們通過把它們都傳遞給 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 推薦使用內聯(lián)樣式困鸥。我們可以使用 camelCase 語法來設置內聯(lián)樣式. React 會在指定元素數(shù)字后自動添加 px 。以下實例演示了為 h1 元素添加 myStyle 內聯(lián)樣式:
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>菜鳥教程</h1>,
document.getElementById('example')
);
注釋
注釋需要寫在花括號中,實例如下:
ReactDOM.render(
<div>
<h1>菜鳥教程</h1>
{/*注釋...*/}
</div>,
document.getElementById('example')
);
數(shù)組
JSX 允許在模板中插入數(shù)組澜术,數(shù)組會自動展開所有成員:
var arr = [
<h1>菜鳥教程</h1>,
<h2>學的不僅是技術艺蝴,更是夢想鸟废!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
參考資料
- http://www.ruanyifeng.com/blog/2015/03/react.html - React 入門實例教程 - 阮一峰
- http://www.reibang.com/p/60100985dd7f - 前端框架與庫的區(qū)別
- https://www.zhihu.com/question/301860721/answer/545031906 - Vue 和 React 的優(yōu)點分別是什么猜敢?
- https://zhuanlan.zhihu.com/p/22184194 - 你真的理解 DOM 了嗎?
- https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Introduction - DOM 概述
- https://blog.huteming.site/posts/e0c41c5f/ - 為什么說虛擬DOM更快
按照慣例黏一個尾巴:
歡迎轉載侮攀,轉載請注明出處锣枝!
獨立域名博客:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693
錢