React基礎(chǔ)
1.1. React基本認識
1.2. React基本使用
1.3. JSX的理解和使用
React組件化編程
2.1. 基本理解和使用
2.2. 組件的3大屬性: state, props, refs
2.3. 組件中的事件處理
2.4. 組件的組合使用
2.5. 組件收集表單數(shù)據(jù)
2.6. 組件的生命周期
Create React App 創(chuàng)建React 前端工程
3.1 工程目錄結(jié)構(gòu)
3.2 試運行
3.2 打包編譯
題外話題:頁面性能
由一道面試題引發(fā)的思考:從用戶瀏覽器輸入URL到頁面最后呈現(xiàn)有哪些過程署咽?
網(wǎng)絡(luò)傳輸階段:
用戶輸入URL地址
瀏覽器解析URL解析出主機名
瀏覽器將主機名轉(zhuǎn)換成服務(wù)器ip地址(瀏覽器先查找本地DNS緩存列表 沒有的話 再向瀏覽器默認的DNS服務(wù)器發(fā)送查詢請求 同時緩存)
瀏覽器將端口號從URL中解析出來
瀏覽器建立一條與目標Web服務(wù)器的TCP連接(三次握手)
瀏覽器向服務(wù)器發(fā)送一條HTTP請求報文
服務(wù)器向瀏覽器返回一條HTTP響應(yīng)報文
如果文檔中有資源 重復(fù)6 7 動作 直至資源全部加載完畢
頁面渲染階段:
HTML解析出DOM Tree
CSS解析出Style Rules
將二者關(guān)聯(lián)生成Render Tree
Layout 根據(jù)Render Tree計算每個節(jié)點的信息
Painting 根據(jù)計算好的信息繪制整個頁面
我們關(guān)心的性能問題
排除網(wǎng)絡(luò)問題,頁面加載消耗時間在哪里生音?
主要消耗時間:解析DOM 樹宁否、解析Style Rules、計算布局(排布)缀遍、繪制過程慕匠。
綜合平臺首頁
加載時間分析:
對瀏覽器來說,我們?nèi)粘i_發(fā)中做的最多的事情是什么域醇?
1台谊、根據(jù)業(yè)務(wù)數(shù)據(jù),添加譬挚、修改或者刪除DOM元素
2青伤、改變元素CSS樣式,以滿足不同操作場景
結(jié)論:
? 當解析的html文件很大時(復(fù)雜頁面)殴瘦,生成DOM樹占用內(nèi)存也較大,同時遍歷元素耗時也很長号杠。DOM的更核心問題是:DOM修改導(dǎo)致的頁面重繪蚪腋、重新排版!重新排版是用戶阻塞的操作,直接影響用戶體驗姨蟋,同時屉凯,如果頻繁重排,瀏覽器CPU使用率也將大漲眼溶!
? 為了提高頁面性能悠砚,我們努力的目標:減少或者避免DOM操作,減輕或者減少瀏覽器重繪堂飞、重排的過程
我們前端技術(shù)(基于DOM操作多少)的演進:
1灌旧、asp.net 服務(wù)器端技術(shù)绑咱,每次交互重新獲取頁面所有數(shù)據(jù),重新渲染界面枢泰。
2描融、ajax階段,獲取數(shù)據(jù)衡蚂,局部更新頁面窿克。比如EasyUI\Extjs
3、虛擬DOM毛甲,DIFF后更細粒度更新DOM
1. React基礎(chǔ)
ReactJS的背景和原理
? 在Web開發(fā)中年叮,我們總需要將變化的數(shù)據(jù)實時反應(yīng)到UI上,這時就需要對DOM進行操作玻募。而復(fù)雜或頻繁的DOM操作通常是性能瓶頸產(chǎn)生的原因只损。
“Any problem in computer science can be solved by anther layer of indirection.”
計算機科學(xué)領(lǐng)域的任何問題都可以通過增加一個間接的中間層來解決。
找對象也是如此补箍,單身的你也許就缺一個中間層:介紹人改执。
? 為了解決和優(yōu)化性能,React為此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現(xiàn)了一套DOM API】友牛基于React進行開發(fā)時所有的DOM構(gòu)造都是通過虛擬DOM進行辈挂,每當數(shù)據(jù)變化時,React都會重新構(gòu)建整個DOM樹裹粤,然后React將當前整個DOM樹和上一次的DOM樹進行對比终蒂,得到DOM結(jié)構(gòu)的區(qū)別,然后僅僅將需要變化的部分進行實際的瀏覽器DOM更新(減少遥诉、減輕DOM更新)拇泣。而且React能夠批處理虛擬DOM的刷新,在一個事件循環(huán)(Event Loop)內(nèi)的兩次數(shù)據(jù)變化會被合并矮锈,例如你連續(xù)的先將節(jié)點內(nèi)容從A變成B霉翔,然后又從B變成A,React會認為UI不發(fā)生任何變化苞笨,而如果通過手動控制债朵,這種邏輯通常是極其復(fù)雜的。
? 另外瀑凝,盡管每一次都需要構(gòu)造完整的虛擬DOM樹序芦,但是因為虛擬DOM是內(nèi)存數(shù)據(jù),性能是極高的粤咪,而對實際DOM進行操作的僅僅是Diff
部分谚中,因而能達到提高性能的目的。這樣,在保證性能的同時宪塔,開發(fā)者將不再需要關(guān)注某個數(shù)據(jù)的變化如何更新到一個或多個具體的DOM元素磁奖,而只需要關(guān)心在任意一個數(shù)據(jù)狀態(tài)下姊氓,整個界面是如何Render的臭埋。
百度百科
React 起源于 Facebook 的內(nèi)部項目盘寡,因為該公司對市場上所有 JavaScript MVC 框架不跟,都不滿意浩习,就決定自己寫一套吝羞,用來架設(shè)Instagram 的網(wǎng)站尿背。做出來以后逛万,發(fā)現(xiàn)這套東西很好用弟疆,就在2013年5月開源了戚长。
虛擬DOM與DOM diff算法
虛擬DOM是什么?
React會在內(nèi)存中構(gòu)建一個相對應(yīng)的DOM樹,基于React開發(fā)時所有的DOM構(gòu)造都是通過虛擬DOM進行怠苔。每當組件的狀態(tài)發(fā)生變化時同廉,React都會重新構(gòu)建整個DOM數(shù)據(jù),然后將當前的整個DOM樹和上一次的DOM樹進行對比柑司,得出DOM結(jié)構(gòu)變化的部分(Patchs)迫肖,然后將這些Patchs 再更新到真實DOM中。整個過程都是在內(nèi)存中進行攒驰,因此是非常高效的蟆湖。
進一步理解
Virtual DOM 本質(zhì)上就是在 JS 和 DOM 之間做了一個緩存。
可以類比 CPU 和硬盤玻粪,既然硬盤這么慢隅津,我們就在它們之間加個緩存:既然 DOM 這么慢,我們就在它們 JS 和 DOM 之間加個緩存劲室。CPU(JS)只操作內(nèi)存(Virtual DOM)伦仍,最后的時候再把變更寫入硬盤(DOM)。
? 初始渲染時很洋,首先將數(shù)據(jù)渲染為 Virtual DOM充蓝,然后由 Virtual DOM 生成 DOM。
? 數(shù)據(jù)更新時喉磁,渲染得到新的 Virtual DOM棺克,與上一次得到的 Virtual DOM 進行 diff,得到所有需要在 DOM 上進行的變更线定,然后在 patch 過程中應(yīng)用到 DOM 上實現(xiàn)UI的同步更新。
又是結(jié)論
? 我們基于React開發(fā)頁面确买,其實就是在往虛擬DOM里面添加元素而已斤讥。
比較現(xiàn)行框架后的直觀認識
1.1. React的基本認識
- 1). Facebook開源的一個javascript庫
- 2). 一個用來動態(tài)
構(gòu)建用戶界面
的javascript庫(最終還是和Dom打交道) - 3). React的特點
- Declarative(聲明式編碼)
- Component-Based(組件化編碼) :通過 React 構(gòu)建組件,使得代碼更加容易得到復(fù)用,能夠很好的應(yīng)用在大項目的開發(fā)
- Learn Once, Write Anywhere(支持客戶端與服務(wù)器渲染)
- 高效
- 單向數(shù)據(jù)流: React 實現(xiàn)了單向響應(yīng)的數(shù)據(jù)流芭商,從而減少了重復(fù)代碼派草,這也是它為什么比傳統(tǒng)數(shù)據(jù)綁定更簡單
- 4). React高效的原因
- 虛擬(virtual)DOM, 不總是直接操作DOM(批量更新, 減少更新的次數(shù))
- 高效的DOM Diff算法, 最小化頁面重繪(減小頁面更新的區(qū)域)
1.2. React的基本使用
導(dǎo)入相關(guān)js庫文件(react.js, react-dom.js)
-
編碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>HelloWorld_React</title> </head> <body> <div id="test"></div> <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js"></script> <script type="text/javascript"> const hello = 'Hello React' //構(gòu)建虛擬DOM(JS對象),這種寫法稍顯尷尬铛楣,不能直接寫HTML標簽么近迁? const vDom = React.createElement('h1', {id: 'myId'}, hello.toUpperCase()) // 渲染虛擬DOM到頁面真實DOM容器中 ReactDOM.render(vDom, document.getElementById('test')) </script> </body> </html>
問題:這種虛擬DOM的定義是不是不夠直觀?
1.3. JSX的理解和使用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>02_JSX</title>
</head>
<body>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript">
// 1. 創(chuàng)建虛擬DOM
/*方式一: 純JS(一般不用)創(chuàng)建虛擬DOM元素對象*/
const myId = 'react'
var li1 = React.createElement('li', { key: 'one' }, 'one');
var li2 = React.createElement('li', { key: 'two' }, 'two');
var vDom = React.createElement('ul', { id: myId }, [li1, li2]);
// 2. 渲染到真實頁面
const domContainer = document.getElementById('test1')
ReactDOM.render(vDom, domContainer)
</script>
<script type="text/babel">
// 1. 創(chuàng)建虛擬DOM
/*方式二: JSX創(chuàng)建虛擬DOM元素對象*/
const vDOM2 =
<ul id={myId.toUpperCase()}>
<li key={'one'}>one</li>
<li key={'two'}>two</li>
</ul>
// 2. 渲染到真實頁面
ReactDOM.render(vDOM2, document.getElementById('test2'))
</script>
</body>
</html>
? 從上面的代碼可以看到將 HTML 直接嵌入了 JS 代碼里面簸州,這個就是 React 提出的一種叫 JSX 的語法鉴竭,這應(yīng)該是最開始接觸 React 最不能接受的設(shè)定之一,因為前端被“表現(xiàn)和邏輯層分離”這種思想“洗腦”太久了岸浑。但實際上組件的 HTML 是組成一個組件不可分割的一部分搏存,能夠?qū)?HTML 封裝起來才是組件的完全體,React 發(fā)明了 JSX 讓 JS 支持嵌入 HTML 不得不說是一種非常聰明的做法矢洲,讓前端實現(xiàn)真正意義上的組件化成為了可能璧眠。(其實Easyui的控件,都是將Html代碼包含在了js里面读虏。)
- 理解
- 全稱: JavaScript XML( JSX簡介)
- react定義的一種類似于XML的JS擴展語法: XML+JS
- 作用: 用來創(chuàng)建react虛擬DOM(元素)對象
- 編碼相關(guān)
- js中直接可以套標簽, 但標簽要套js需要放在{}中
- 在解析顯示js數(shù)組時, 會自動遍歷顯示
- 把數(shù)據(jù)的數(shù)組轉(zhuǎn)換為標簽的數(shù)組:
var liArr = dataArr.map(function(item, index){
return <li key={index}>{item}</li>
})
- 注意:
- 標簽必須有結(jié)束
- 標簽的class屬性必須改為className屬性
- 標簽的style屬性值必須為: {{color:'red', width:12}}
再結(jié)論
JSX解決了React定義虛擬DOM時责静,JS腳本寫HTML標簽的尷尬
2. react組件化開發(fā)
VM定義問題:能不能通過定義個函數(shù)來返回虛擬DOM結(jié)構(gòu)呢?
2.1. 基本理解和使用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>03_component_basic</title>
</head>
<body>
<div id="example1"></div>
<div id="example2"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1. 定義組件
/*方式1: 工廠函數(shù)組件(簡單組件)*/
function MyComponent () {
return <h2>工廠函數(shù)組件(簡單組件)</h2>
}
/*方式2: ES6類組件(復(fù)雜組件)*/
class MyComponent2 extends React.Component {
render () {
console.log(this) // MyComponent2的實例對象
return <h2>ES6類組件(復(fù)雜組件)</h2>
}
}
// 2. 渲染組件標簽
ReactDOM.render(<MyComponent />, document.getElementById('example1'))
ReactDOM.render(<MyComponent2 />, document.getElementById('example2'))
</script>
</body>
</html>
ReactDOM.render()渲染組件標簽的基本流程
- React內(nèi)部會通過創(chuàng)建
組件實例對象/調(diào)用組件函數(shù)
, 得到虛擬DOM對象(雷同React.createElement對象) - 將虛擬DOM解析為真實DOM
- 插入到指定的頁面元素內(nèi)部
2.2 組件的3大屬性
問題:對比Easyui的控件盖桥,我們能給React組件傳遞些參數(shù)么灾螃?如何轉(zhuǎn)遞和接受參數(shù)?React為什么不是MVVM葱轩?特殊情況真實DOM元素我該如何獲饶阑馈?
2.2.1. 組件的3大屬性: props
? 當 React 元素為用戶自定義組件時靴拱,它會將 JSX 所接收的屬性(attributes)轉(zhuǎn)換為單個對象傳遞給組件垃喊,這個對象被稱之為 “props”。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
2.2.2. 組件的3大屬性: state
? 組件的state是組件內(nèi)部的狀態(tài)袜炕,state的變化最終將反映到組件UI的變化上(React單向數(shù)據(jù)流的體現(xiàn))本谜。我們在組件的構(gòu)造方法constructor中通過this.state定義組件的初始狀態(tài),并通過調(diào)用this.setState方法改變組件狀態(tài)(也是改變組件狀態(tài)的唯一方式)偎窘,進而組件UI也會隨之重新渲染乌助。
//初始化指定:
constructor() {
super()
this.state = {
stateName1 : stateValue1,
stateName2 : stateValue2
}
}
//讀取顯示:
this.state.stateName1
//更新狀態(tài)-->更新界面 :
this.setState({stateName1 : newValue})
State 與 Props 區(qū)別
? props 是組件對外的接口,state 是組件對內(nèi)的接口陌知。組件內(nèi)可以引用其他組件他托,組件之間的引用形成了一個樹狀結(jié)構(gòu)(組件樹),如果下層組件需要使用上層組件的數(shù)據(jù)或方法仆葡,上層組件就可以通過下層組件的props屬性進行傳遞赏参,因此props是組件對外的接口。組件除了使用上層組件傳遞的數(shù)據(jù)外,自身也可能需要維護管理數(shù)據(jù)把篓,這就是組件對內(nèi)的接口state纫溃。根據(jù)對外接口props 和對內(nèi)接口state,組件計算出對應(yīng)界面的UI韧掩。
? 主要區(qū)別:
- State是可變的紊浩,是一組用于反映組件UI變化的狀態(tài)集合;
- 而Props對于使用它的組件來說疗锐,是只讀的坊谁,要想修改Props,只能通過該組件的父組件或者外圍修改窒悔。在組件狀態(tài)上移的場景中呜袁,父組件正是通過子組件的Props, 傳遞給子組件其所需要的狀態(tài)。
2.2.3. 組件的3大屬性: refs
? 特殊情況下需要獲取組件內(nèi)部元素的DOM對象简珠。
2.3. 組件中的事件處理
問題:DOM事件怎么在虛擬DOM里面定義和使用阶界?
-
給標簽添加屬性:
onXxx={this.eventHandler}
在組件中添加事件處理方法
eventHandler = (event) => {
//處理過程
}
React 元素的事件處理和 DOM 元素的很相似,但是有一點語法上的不同:
React 事件的命名采用小駝峰式(camelCase)聋庵,而不是純小寫膘融。
使用 JSX 語法時你需要傳入一個函數(shù)作為事件處理函數(shù),而不是一個字符串祭玉。
//例如氧映,傳統(tǒng)的 HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
//在 React 中略微不同:
<button onClick={activateLasers}>
Activate Lasers
</button>
?
2.4. 組件的組合使用
問題:組件內(nèi)部能不能使用組件,形成新的組件脱货?組件嵌套
2.5. 組件收集表單數(shù)據(jù)
問題:表單數(shù)據(jù)怎么收集岛都?
2.6. 組件的生命周期
問題:既然都組件化了,每個組件是不是有生命周期振峻,這樣我們就可以控制組件的初始化和更新臼疫?
? 組件從被創(chuàng)建到被銷毀的過程稱為組件的生命周期。React為組件在不同的生命周期階段提供不同的生命周期方法扣孟,讓開發(fā)者可以在組件的生命周期過程中更好地控制組件的行為烫堤。通常,組件的生命周期可以被分為三個階段:掛載階段凤价、更新階段鸽斟、卸載階段。
2.6.1. 掛載階段
? 這個階段組件被創(chuàng)建利诺,執(zhí)行初始化富蓄,并被掛載到DOM中,完成組件的第一次渲染慢逾。依次調(diào)用的生命周期方法有:
constructor
? 這是ES 6 class的構(gòu)造方法立倍,組件被創(chuàng)建時躏吊,會首先調(diào)用組件的構(gòu)造方法。這個構(gòu)造方法接收一個props參數(shù)帐萎,props是從父組件中傳入的屬性對象,如果父組件中沒有傳入屬性而組件自身定義了默認屬性胜卤,那么這個props指向的就是組件的默認屬性疆导。你必須在這個方法中首先調(diào)用super(props)才能保證props被傳入組件中。constructor通常用于初始化組件的state以及綁定事件處理方法等工作葛躏。
componentWillMount
? 這個方法在組件被掛載到DOM前調(diào)用澈段,且只會被調(diào)用一次。這個方法在實際項目中很少會用到舰攒,因為可以在該方法中執(zhí)行的工作都可以提前到constructor中败富。在這個方法中調(diào)用this.setState不會引起組件的重新渲染。
render
? 這是定義組件時唯一必要的方法(組件的其他生命周期方法都可以省略)摩窃。在這個方法中兽叮,根據(jù)組件的props和state返回一個React元素,用于描述組件的UI猾愿,通常React元素使用JSX語法定義鹦聪。需要注意的是,render并不負責(zé)組件的實際渲染工作蒂秘,它只是返回一個UI的描述泽本,真正的渲染出頁面DOM的工作由React自身負責(zé)。render是一個純函數(shù)姻僧,在這個方法中不能執(zhí)行任何有副作用的操作规丽,所以不能在render中調(diào)用this.setState,這會改變組件的狀態(tài)撇贺。
componentDidMount
? 在組件被掛載到DOM后調(diào)用赌莺,且只會被調(diào)用一次。這時候已經(jīng)可以獲取到DOM結(jié)構(gòu)显熏,因此依賴DOM節(jié)點的操作可以放到這個方法中雄嚣。這個方法通常還會用于向服務(wù)器端請求數(shù)據(jù)。在這個方法中調(diào)用this.setState會引起組件的重新渲染喘蟆。
2.6.2. 更新階段
? 組件被掛載到DOM后缓升,組件的props或state可以引起組件更新。props引起的組件更新蕴轨,本質(zhì)上是由渲染該組件的父組件引起的港谊,也就是當父組件的render方法被調(diào)用時,組件會發(fā)生更新過程橙弱,這個時候歧寺,組件props的值可能發(fā)生改變燥狰,也可能沒有改變,因為父組件可以使用相同的對象或值為組件的props賦值斜筐。但是龙致,無論props是否改變,父組件render方法每一次調(diào)用顷链,都會導(dǎo)致組件更新目代。State引起的組件更新,是通過調(diào)用this.setState修改組件state來觸發(fā)的嗤练。組件更新階段榛了,依次調(diào)用的生命周期方法有:
componentWillReceiveProps
? 這個方法只在props引起的組件更新過程中,才會被調(diào)用煞抬。State引起的組件更新并不會觸發(fā)該方法的執(zhí)行霜大。方法的參數(shù)nextProps是父組件傳遞給當前組件的新的props。但如上文所述革答,父組件render方法的調(diào)用
并不能保證傳遞給子組件的props發(fā)生變化战坤,也就是說nextProps的值可能和子組件當前props的值相等,因此往往需要比較nextProps和this.props來決定是否執(zhí)行props發(fā)生變化后的邏輯蝗碎,比如根據(jù)新的props調(diào)用this.setState觸發(fā)組件的重新渲染湖笨。
shouldComponentUpdate
? 這個方法決定組件是否繼續(xù)執(zhí)行更新過程。當方法返回true時(true也是這個方法的默認返回值)蹦骑,組件會繼續(xù)更新過程慈省;當方法返回false時,組件的更新過程停止眠菇,后續(xù)的componentWillUpdate边败、render、componentDidUpdate也不會再被調(diào)用捎废。一般通過比較nextProps笑窜、nextState和組件當前的props、state決定這個方法的返回結(jié)果登疗。這個方法可以用來減少組件不必要的渲染排截,從而優(yōu)化組件的性能。
componentWillUpdate
? 這個方法在組件render調(diào)用前執(zhí)行辐益,可以作為組件更新發(fā)生前執(zhí)行某些工作的地方断傲,一般也很少用到。
render
? 更新Render
componentDidUpdate
? 組件更新后被調(diào)用智政,可以作為操作更新后的DOM的地方认罩。這個方法的兩個參數(shù)prevProps、prevState代表組件更新前的props和state续捂。
2.6.3. 卸載階段
? 組件從DOM中被卸載的過程垦垂,這個過程中只有一個生命周期方法:componentWillUnmount
這個方法在組件被卸載前調(diào)用宦搬,可以在這里執(zhí)行一些清理工作,比如清除組件中使用的定時器劫拗,清除componentDidMount中手動創(chuàng)建的DOM元素等间校,以避免引起內(nèi)存泄漏。
只有類組件才具有生命周期方法页慷,函數(shù)組件是沒有生命周期方法的撇簿,因此永遠不要在函數(shù)組件中使用生命周期方法。
Gitee源碼:https://gitee.com/BeautifulHao/react-simple
3. Create React App 創(chuàng)建React 前端工程
問題:React組件化了差购,一個業(yè)務(wù)頁面N多個組件組成,組件怎么復(fù)用汉嗽,組件怎么發(fā)布等等都是問題
3.1 工程構(gòu)建
提前node 和 npm 安裝欲逃,安裝腳手架工具:
npm install -g create-react-app
創(chuàng)建項目:
create-react-app react-demo
3.2 調(diào)試運行
yarn start
3.2 打包編譯
yarn build