React 技術(shù)棧心得

前言

“步入前端兩年半畜晰,自覺菜雞懶又爛∪鹂穑” 近來想著寫寫一些前端學(xué)習(xí)的心得凄鼻,左思右想。還是從 React 入筆聚假。為什么是 React块蚌?身為小白,React 龐大的技術(shù)棧確實給我很多編程思想上的啟迪膘格,也讓我了解到更多前端領(lǐng)域的知識峭范。時至今日,自己仍在探索 React 的路上瘪贱。在此纱控,感謝一路上并肩作戰(zhàn)的戰(zhàn)友和捎帶一段車程的司機們。

零菜秦、初識 React

剛開始接觸 React甜害,大概是去年這個時候。當時 React 在 Github 的 stars 飛升球昨,超過了之前如火中天的 AngularJS 尔店。一開始會覺得 React 通過 jsx 實現(xiàn) HTML 模板的做法并不比 AngularJS 的模板好用,因為按照傳統(tǒng)的前端開發(fā)模式褪尝,HTML 負責結(jié)構(gòu)層闹获,CSS 負責渲染層,JavaScript 負責行為交互層河哑,這似乎有所違背避诽。但后來對 Virtual DOM 有了進一步的了解,才發(fā)現(xiàn)這是 React 的精髓所在璃谨。

一沙庐、Virtual DOM

Virtual DOM?說到 Virtual DOM 一開始是聽說它的性能很高佳吞,是啊拱雏,渲染性能確實很高,但是只是單純使用不去思考底扳,那我又能獲得什么呢铸抑?一個庫的使用方法?了解一門前端熱門的庫衷模?思前想后鹊汛,我覺得 Virtual DOM 給我的啟迪大致有以下兩方面:性能優(yōu)化策略(diff 算法)和 顛覆傳統(tǒng) DOM 編程思想蒲赂。

1.性能優(yōu)化策略

  • 傳統(tǒng)的瀏覽器渲染流程:瀏覽器中的渲染工作主要是由 渲染引擎 完成的。大致流程如下(不考慮 阻塞 等特殊情況)刁憋。
  • 解析 HTML 文件滥嘴,構(gòu)建 DOM 樹;
  • 解析 CSS 文件至耻,構(gòu)建 CSSOM 若皱;
  • 合并 DOM 樹和 CSSOM;
  • 布局(計算 DOM 節(jié)點在屏幕上的精確位置)尘颓,繪制(計算對應(yīng)節(jié)點的樣式)走触;
  • 重排(DOM結(jié)構(gòu)變化),重繪(CSS變化)泥耀;
  • 很明顯饺汹,通過上面的描述,可以看出每一次 DOM 結(jié)構(gòu)的改變?yōu)g覽器都會進行極大的計算量痰催,因此兜辞,Virtual DOM 做了大致以下兩個方面的處理:
  • 在瀏覽器內(nèi)存中,維護著一課與頁面 DOM 結(jié)構(gòu)一致的對象樹
  • 依賴 diff 算法極大提高了 DOM 操作的性能

  • 簡單地說夸溶,Virtual DOM 通過批量處理 DOM 操作逸吵,可以理解為對多次 DOM 操作起了一次緩存作用,從而合并多次的 DOM 操作為一次計算缝裁。(實現(xiàn)方法是將一個事件循環(huán)中發(fā)出的 DOM 操作全部收集起來扫皱,不立即在頁面上產(chǎn)生效果,而是在事件循環(huán)的結(jié)尾捷绑,才向頁面作用)

  • 那么韩脑,Virtual DOM 的計算過程是怎樣優(yōu)化的呢(即瀏覽器內(nèi)存中的 DOM)?不得不說粹污,React 做了一次大膽的嘗試段多。傳統(tǒng)標準的 Diff 算法 復(fù)雜度達到了 O(n^3),這就意味著要展示 1000 個節(jié)點壮吩,就要依次執(zhí)行上十億次的比較进苍。這是絕對無法滿足性能需求的。而 React 開發(fā)團隊通過制定大膽的策略鸭叙,使得 Diff 算法 復(fù)雜度降到 O(n)觉啊。但是,React 的優(yōu)化算法是基于以下三個前提的沈贝。

  • Web UI 中 DOM 節(jié)點跨層級的移動操作特別少杠人,可以忽略不計
  • 擁有相同類的兩個組件將會生成相似的樹形結(jié)構(gòu),擁有不同類的兩個組件將會生成不同的樹形結(jié)構(gòu)
  • 對于同一層級的一組子節(jié)點,它們可以通過唯一 id 進行區(qū)分

于是搜吧,React 基于以上三個前提策略市俊,分別對 Tree diffComponent diff滤奈,Element diff 進行算法優(yōu)化,事實也證明這三個前提策略是合理且準確的撩满,它保證了整體界面構(gòu)建性能蜒程。

  • Tree diff:只會對同一父節(jié)點下的所有子節(jié)點進行比較,當發(fā)現(xiàn)節(jié)點不存在伺帘,則該節(jié)點及其子節(jié)點會被完全刪除掉昭躺,不會用于進一步的比較
  • Component diff:React 是基于組件構(gòu)建,如果同一類型的組件伪嫁,則按照原策略繼續(xù)比較 Virtual DOM tree领炫,如果不是,則將該組件判斷為 dirty component张咳,從而替換整個組件下的所有子節(jié)點(另外帝洪,React 提供了一個函數(shù)鉤子 shouldComponentUpdate() 來判斷該組件是否需要進行 diff)
  • Element diff:當節(jié)點處于同一層級時,React 提供了三種節(jié)點操作脚猾,分別為插入(INSERT——MARKUP)葱峡、移動(MOVE_EXISTING)、刪除(REMOVE_NODE)

2.抽象——編程思想的顛覆龙助?

一開始思考 React 跟 Vue 的區(qū)別的時候砰奕,就只有直觀的架構(gòu)模式 MVC,MVVM 的區(qū)別提鸟,但是 MVC 只要加個 事件監(jiān)聽數(shù)據(jù)劫持 不也可以實現(xiàn) 數(shù)據(jù)雙向綁定军援?當然,后來就想到了 Vue 比較適合中小型項目称勋,而 React 比較適合大型項目胸哥。但是,究其根本铣缠,似乎沒有明確的理由烘嘱。近來又似乎找到了答案。

  • UI 層與業(yè)務(wù)邏輯的低耦:肯定很多人都有同感蝗蛙,高耦合的 DOM 編程模式蝇庭,對于項目的維護以及團隊合作都很不友好。而 Virtual DOM 對真實 DOM 進行了一層抽象捡硅,它幫助我們?nèi)ゲ僮髡鎸?DOM哮内,而我們通過操作 Virtual DOM 來控制頁面 UI。
  • 當系統(tǒng)復(fù)雜度上升,模塊間的低耦也是勢在必行”狈ⅲ現(xiàn)在的前端開發(fā)更加注重用戶體驗纹因,很多傳統(tǒng)在服務(wù)端解決的功能也被遷移到客戶端,因此前端開發(fā)的復(fù)雜度勢必會逐漸上升琳拨,React 的 VIrtual DOM 可以說是一個革命性的創(chuàng)新瞭恰。

二、模塊化開發(fā)

模塊化開發(fā)在軟件工程里邊也算是一個老生長談的話題了狱庇,而在 JavaScript 這門語言中惊畏,一直都是通過 <script></script> 標簽在 HTML 文檔中引入文件,而且模塊(文件)間的依賴關(guān)系也沒有通過很多的方法去處理密任⊙掌簦或許是前期前端開發(fā)的改革注重于新功能的實現(xiàn),所以很長一段時間都花在瀏覽器 API 的優(yōu)化上面了浪讳,但是隨著前端開發(fā)工程化缰盏,模塊化開發(fā)成了一個不可以回避的問題。

1.模塊化規(guī)范

前端的模塊化規(guī)范有 AMD 和 CMD(國內(nèi)提出的)淹遵,后端的模塊化規(guī)范有 CommonJS(Nodejs 采用這種規(guī)范)口猜。

  • AMD 規(guī)范(Asynchronous Module Definition):一個瀏覽器端模塊化開發(fā)的規(guī)范,主要的思想有以下三點合呐。
  • 模塊將被異步加載
  • 模塊加載不影響后面語句的運行
  • 所有依賴某些模塊的語句均放置在回調(diào)函數(shù)中
  • AMD 是 require.js 在推廣過程中對模塊定義的規(guī)范化的產(chǎn)出暮的。AMD 規(guī)范只定義了一個全局函數(shù) define
define(id?, dependencies?, factory);  // id:模塊名字; dependcies: 依賴模塊字面量; factory: 模塊初始化需要執(zhí)行的函數(shù)或?qū)ο?
  • CMD規(guī)范(Common Module Definition):這是國內(nèi)發(fā)展出來的,該規(guī)范明確了模塊的基本書寫格式和基本交互規(guī)則淌实。AMD 是依賴關(guān)系前置冻辩,CMD 是按需加載。在 CMD 規(guī)范中拆祈,一個模塊就是一個文件恨闪,代碼書寫格式如下:
define(factory);  
  • factory 為函數(shù)時,表示是模塊的構(gòu)造方法放坏。執(zhí)行該構(gòu)造方法咙咽,可以得到模塊向外提供的接口。factory 方法在執(zhí)行時淤年,默認會傳入三個參數(shù):require 钧敞、exportsmodule
// require 是可以把其他模塊導(dǎo)入進來的一個參數(shù)
// exports 是可以把模塊內(nèi)的一些屬性和方法導(dǎo)出的一個參數(shù)
define(function(require, exports, module) {
   // 模塊代碼
})
  • CommonJS 規(guī)范是服務(wù)端模塊的規(guī)范,Node.js 采用了這個規(guī)范麸粮。Node.js 首先采用了 js 模塊化的概念溉苛,根據(jù) CommonJS 規(guī)范,一個單獨的文件就是一個模塊弄诲。每一個模塊都是一個單獨的作用域愚战,也就是說,在該模塊內(nèi)部定義的變量,無法被其他模塊讀取寂玲,除非定義為 global 屬性塔插。如下代碼:
// module.exports 就是模塊外部與內(nèi)部通信的橋梁
// 加載模塊使用 require 方法,該方法讀取一個文件并執(zhí)行拓哟,最后返回文件內(nèi)部的 module.exports 對象
let i = 1;
let max = 30;

module.exports = function () {
   for(i -= 1; i++ < max; ) {
    console.log(i); 
  }
  max *=1.1;
}

2.React 開發(fā)中使用的模塊化

  • 組件化開發(fā):想必很多前端開發(fā)的道友都會有這樣一個感觸想许,就是同一個項目中經(jīng)常有很多相似的模塊(結(jié)構(gòu)相似或樣式相似,甚至一模一樣)彰檬,那么我們就會考慮到這些模塊復(fù)用的問題伸刃。這個時候我們就會聯(lián)想到類似 Bootstrap , Foundation 等前端框架,這些框架提供了很多便利的組件逢倍,提高了開發(fā)效率。但是我們會想到一個問題就是景图,第一這些組件都是別人開發(fā)好的较雕,第二復(fù)用組件的方式就是引入 代碼庫,復(fù)制粘貼 HTML 代碼挚币。想解決這些問題亮蒋,一開始就會想到用 模板引擎 (比如 Nunjuck Template)來解決這個問題,但是我們就會想到引入分模塊引入樣式表的問題妆毕,當然也可以通過自己封裝一個類來實現(xiàn)模塊化管理(比如正則匹配url慎玖,執(zhí)行相關(guān)文件的函數(shù))。而我們看看下面 React 的組件化 jsx 文件代碼結(jié)構(gòu):
import '../welcome.scss'
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • 正如前面所說的笛粘,React 是 js 主導(dǎo)的一門框架趁怔,所以我們可以在 jsx 文件中通過 js 的模塊化機制引入其他文件(比如 scss 文件),這樣就可以很好的解決組件化開發(fā)的問題(樣式表在組件內(nèi)部編寫)薪前。當然润努,js 的模塊化機制只是針對 js 的,所以類似 css示括,scss铺浇,圖片的引入,我們就得借助 Loader (比如 webpackstyle-loader, css-loader, sass-loader, url-loader)進行轉(zhuǎn)化垛膝,把其他形式的文件轉(zhuǎn)化成 js 可以進行模塊化管理的文件鳍侣。而且我們可以實現(xiàn)一個文件一個自定義組件的效果,從而借助文件結(jié)構(gòu)更方便的管理組件結(jié)構(gòu)吼拥。

  • ES6:一開始 React 出現(xiàn)的時候倚聚,使用的是 ES5 語法,但是這種通過 原型鏈 實現(xiàn)面向?qū)ο缶幊痰恼Z法對于跟常見的面向?qū)ο笳Z言語法還是有很多不同扔罪,因此對開發(fā)的編碼效率以及代碼的可讀性也不是很友好(個人覺得)秉沼,因此 React 官方文檔也推薦開發(fā)者使用 ES6 推出 的 class 語法糖。此外,ES6 的 exportimport Module 語法糖也幫助解決 js 文件依賴關(guān)系的模塊化管理問題唬复。

  • 當然了矗积,在接觸 React 的時候接觸到了 ES6 這些新的語法特性,就不由得去了解一下 ES6 的其他語法特性敞咧,其中很多語法特性應(yīng)用到 React 項目中棘捣,比如 箭頭表達式(Arrow functions),letconst 命令休建,還要對 數(shù)組方法及字符串方法 的鞏固應(yīng)用乍恐。也通過 React 的編碼規(guī)范了解了 建造者模式 等設(shè)計模式,對于 JavaScript 的學(xué)習(xí)之路有了進一步的規(guī)劃测砂。

  • SCSS:SCSS 是 CSS 的一門預(yù)編譯語言茵烈,由于 CSS 是一門面向設(shè)計的語言,所以對于龐大的項目來說砌些,CSS 代碼的模塊化管理還有編碼規(guī)范方面問題很難解決呜投,于是就會有 SASSLESS 等預(yù)編譯語言的出現(xiàn)存璃。SCSS 是 SASS 的一個改進版本仑荐,主要是語法方面向 CSS 靠近。使用 SCSS 模塊化管理的方法很簡單纵东,如下:

.welcome{
  h1{
    ...
  }
  p{
    ...
  }
}

// 編譯結(jié)果
.welcome h1{...}
.welcome p{...}
  • 很明顯可以看出粘招,我們只要保證頂層的類與其他模塊不同,則編譯出來的 CSS 代碼就不會有模塊化沖突的問題了偎球。當然洒扎,我們通常以組件名給頂層類命名,便于管理甜橱。但是這樣就會有一個問題逊笆,不同模塊可能存在相似或相同的樣式表,那這樣編譯出來的樣式就會存在冗余的部分了岂傲,對此的解決方法难裆,目前想到的就是:
  • 設(shè)置 Common.scss ,定義好全局復(fù)用的樣式效果(比如字體大小的幾款樣式镊掖,樣色乃戈,陰影效果,動畫效果)
  • 組件劃分盡量劃分到不能再細分的組件亩进,這樣代碼的復(fù)用率比較高

3.包管理工具(npm)和自動化打包工具(webpack)

  • npm:(node package manager)症虑,顧名思義即 NodeJS 的一個包管理工具。NodeJS 除了官方提供的 核心模塊 归薛,還有大量的第三方模塊谍憔,因此 npm 的誕生就是為了更好管理和使用這些第三方模塊的匪蝙。使用的方法也很簡單,安裝需要的模塊并將此模塊作為項目的依賴模塊的信息寫入 package.json 文件中习贫,方便其他開發(fā)者直接安裝模塊逛球,也方便自己后期查看依賴模塊信息(比如版本信息)。在需要引用該模塊的文件中苫昌,通過 requrie('') 語句進行調(diào)用即可颤绕。

  • webpack:一個自動化打包工具,webpack 也就花了兩三天看看文檔祟身,寫個小 demo 測試一下而已奥务。要說感觸的話,主要是覺得 loader, plugin 對開發(fā)過程很友好袜硫,而且打包文件進行了一系列優(yōu)化處理氯葬,比如代碼壓縮、圖片進行 base64 處理等婉陷。

  • 當然溢谤,剛使用 webpack 的時候也想過這樣一些問題。

  • webpack 的打包原理憨攒?

  • webpack 打包慢的處理方式?

  • webpack-dev-server 的監(jiān)聽原理阀参?

  • 第一個問題肝集,由于 webpack 是依賴于 NodeJS 的,因此模塊規(guī)范就是 CommonJS蛛壳。一個文件就是一個模塊杏瞻,require 就是引入模塊,module.exports 就是對外暴露的接口衙荐。而打包的方式也就是常見的 管道模式(即 require 的文件中的 require 繼續(xù)打包)捞挥。另外,同一個模塊不會被引入兩次忧吟,因為在入口文件中砌函,每個 require 都配置了一個 id,因此即使同個模塊被引入多次溜族,由于其 id 一致讹俊,故不會被打包多次。

  • 第二個問題煌抒,目前想到也就以下兩種方法仍劈。

  • 使用 webpack --watch 命令代替 webpack ,這樣在第一次打包之后打包速度會比較快
  • 將常用的庫等靜態(tài)資源打包成一個文件寡壮,開發(fā)時不再動態(tài)打包

  • 第三個問題贩疙,官方文檔有給出答案讹弯。也就是建立一個小的服務(wù)器進行監(jiān)聽。

The webpack-dev-server is a little Node.js Express server, which uses the webpack-dev-middleware to serve a webpack bundle. It also has a little runtime which is connected to the server via Sock.js.

三这溅、React-router

  • react-router 簡單地說组民,就是 URLComponents 的映射,其實現(xiàn)原理深究無非就是 UI 與 URL 的同步實現(xiàn)芍躏,下圖便很清晰地解釋其實現(xiàn)原理邪乍。而 react-router 有一點是覺得比較有趣的。就是一開始以為 URL 的更新是通過 window.location 進行設(shè)置对竣,但是后來意識到 window.location 對象在更新時會出現(xiàn)頁面跳轉(zhuǎn)的現(xiàn)象庇楞。于是查了一下,發(fā)現(xiàn) 單頁面應(yīng)用 的 URL 更細是通過 window.history.pushState() 方法實現(xiàn)的否纬,這樣一來吕晌,historygo()
    , back() 等問題自然也能夠得到解決。

  • 后來又發(fā)現(xiàn)临燃,window.history 對象是高版本瀏覽器才有的睛驳,于是查了一下,發(fā)現(xiàn) react-router 是基于一個開源的第三方 js 庫 history 實現(xiàn)的膜廊。主要的兼容方式為:

  • 老版本瀏覽器的 history:主要通過 hash 來實現(xiàn)乏沸,對應(yīng) createHashHistory
  • 高版本瀏覽器:通過 HTML5 里面的 history,對應(yīng) createBrowerHistory
  • Node 環(huán)境下:主要存儲在 memeory 里面爪瓜,對于 createMemoryHistory

READ MORE

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹬跃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子铆铆,更是在濱河造成了極大的恐慌蝶缀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薄货,死亡現(xiàn)場離奇詭異翁都,居然都是意外死亡,警方通過查閱死者的電腦和手機谅猾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門柄慰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赊瞬,你說我怎么就攤上這事先煎。” “怎么了巧涧?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵薯蝎,是天一觀的道長。 經(jīng)常有香客問我谤绳,道長占锯,這世上最難降的妖魔是什么袒哥? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮消略,結(jié)果婚禮上堡称,老公的妹妹穿的比我還像新娘。我一直安慰自己艺演,他們只是感情好却紧,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胎撤,像睡著了一般晓殊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伤提,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天巫俺,我揣著相機與錄音,去河邊找鬼肿男。 笑死介汹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的舶沛。 我是一名探鬼主播嘹承,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼如庭!你這毒婦竟也來了赶撰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤柱彻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后餐胀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哟楷,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年否灾,在試婚紗的時候發(fā)現(xiàn)自己被綠了卖擅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡墨技,死狀恐怖惩阶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扣汪,我是刑警寧澤断楷,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站崭别,受9級特大地震影響冬筒,放射性物質(zhì)發(fā)生泄漏恐锣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一舞痰、第九天 我趴在偏房一處隱蔽的房頂上張望土榴。 院中可真熱鬧,春花似錦响牛、人聲如沸玷禽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矢赁。三九已至,卻和暖如春聚磺,著一層夾襖步出監(jiān)牢的瞬間坯台,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工瘫寝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜒蕾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓焕阿,卻偏偏與公主長得像咪啡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子暮屡,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容