簡(jiǎn)介
React是一個(gè)用于構(gòu)建用戶界面的JavaScript庫(kù)
用戶界面(User Interface称鳞,簡(jiǎn)稱UI)是系統(tǒng)和用戶之間進(jìn)行交互和信息交換的介質(zhì),在這里指代的就是網(wǎng)頁(yè)前端界面
傳統(tǒng)的JS的問(wèn)題
- 需要頻繁的操作DOM欠窒,而操作DOM對(duì)于不同的瀏覽器之間存在一定的兼容性問(wèn)題
- 而過(guò)多的兼容性代碼素征,會(huì)在一定程度上造成業(yè)務(wù)邏輯代碼的冗余
- 代碼組織和規(guī)范的問(wèn)題
- 我們編寫頁(yè)面的時(shí)候崭参,需要過(guò)多的關(guān)注界面的細(xì)節(jié)穆役,也就是說(shuō)我們即需要操作DOM民镜,還需要去管理和維護(hù)頁(yè)面的狀態(tài)(數(shù)據(jù))
- 數(shù)據(jù)(狀態(tài))的定義和使用往往會(huì)分散到各個(gè)地方乍桂,不方便管理和維護(hù)
react框架的思想
- 以組件的方式去劃分一個(gè)個(gè)功能模塊
- 組件內(nèi)以jsx來(lái)描述UI的樣子袜瞬,以state來(lái)存儲(chǔ)組件內(nèi)的狀態(tài)
- 當(dāng)應(yīng)用的狀態(tài)發(fā)生改變時(shí)怜俐,通過(guò)setState來(lái)修改狀態(tài),狀態(tài)發(fā)生變化時(shí)邓尤,UI會(huì)自動(dòng)發(fā)生更新
特點(diǎn)
聲明式編程
聲明式編程只需要維護(hù)自己的狀態(tài)拍鲤,當(dāng)狀態(tài)改變時(shí),React可以根據(jù)最新的狀態(tài)去渲染我們的UI界面
在React中汞扎,表示界面的JSX是依賴于state的季稳,
而當(dāng)state發(fā)生改變的時(shí)候,可以使用setState去重新調(diào)用render函數(shù)澈魄,從而重新渲染界面
在react中景鼠,上述的f函數(shù)就是render函數(shù)
組件化開發(fā)
網(wǎng)站的整體看成是一個(gè)組件,也就是根組件
隨后網(wǎng)頁(yè)根據(jù)功能點(diǎn)進(jìn)行劃分成一個(gè)個(gè)小的組件
最后再通過(guò)打包工具,將這一個(gè)個(gè)小的組件整合起來(lái)
跨平臺(tái)開發(fā)
- 2013年铛漓,React發(fā)布之初主要是開發(fā)Web頁(yè)面
- 2015年溯香,F(xiàn)acebook推出了ReactNative,用于開發(fā)移動(dòng)端跨平臺(tái)
- 2017年浓恶,F(xiàn)acebook推出ReactVR玫坛,用于開發(fā)虛擬現(xiàn)實(shí)Web應(yīng)用程序
React依賴的庫(kù)
庫(kù) | 說(shuō)明 |
---|---|
react | 包含了react和react-native所共同擁有的核心代碼 |
react-dom | react渲染在不同平臺(tái)所需要的核心代碼 |
babel | 編譯器 |
react-dom會(huì)根據(jù)需要渲染的平臺(tái),將對(duì)于的vdom渲染成對(duì)應(yīng)的dom或組件
平臺(tái) | 說(shuō)明 |
---|---|
web端 | react-dom會(huì)講jsx最終渲染成真實(shí)的DOM包晰,顯示在瀏覽器中 |
native端 | react-dom會(huì)講jsx最終渲染成原生的控件(比如Android中的Button湿镀,iOS中的UIButton) |
babel是目前前端使用非常廣泛的編輯器,
雖然ES6很方便,但是某些老版本瀏覽器不支持ES6伐憾,只支持ES5
babel讓我們可以直接在編寫代碼的時(shí)候使用ES6
在部署的時(shí)候勉痴,會(huì)將我們ES6的代碼轉(zhuǎn)換為ES5的代碼,從而適配我們所有需要適配的瀏覽器
默認(rèn)情況下開發(fā)React其實(shí)可以不使用babel
但是React默認(rèn)情況下需要使用React.createElement來(lái)編寫頁(yè)面結(jié)構(gòu)树肃,
而React.createElement本身編寫是非常繁瑣的和可讀性差
所以React提供了React.createElement的語(yǔ)法糖JSX
默認(rèn)情況下jsx(JavaScript XML)蚀腿,React是無(wú)法直接解析的
所以需要babel將jsx轉(zhuǎn)換為React.createElement形式的代碼后在交給React來(lái)進(jìn)行解析
Hello World
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 全局掛載React對(duì)象 -->
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<!-- 全局掛載ReactDOM對(duì)象 -->
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<!-- 掛載點(diǎn) -->
<div id="app"></div>
<!-- type="text/babel" -> 告訴瀏覽器這些腳本要交給babel進(jìn)行解析 -->
<script type="text/babel">
// 將參數(shù)1的內(nèi)容掛載到參數(shù)2上,并覆蓋原本掛載點(diǎn)中的內(nèi)容
// 參數(shù)1 -- JSX對(duì)象 需要渲染的內(nèi)容 經(jīng)過(guò)React.createElement處理后返回ReactRenderObject對(duì)象 也就是VDom對(duì)象
// 參數(shù)2 --- 掛載點(diǎn)
ReactDOM.render(<h2>Hello World</h2>, document.getElementById('app'))
</script>
</body>
</html>
命令式編程 vs 聲明式編程
示例
- 在界面顯示一個(gè)文本: Hello World
- 點(diǎn)擊下方的一個(gè)按鈕扫外,點(diǎn)擊后文本改變?yōu)镠ello React
命令式編程
<!--
這種即需要關(guān)心頁(yè)面狀態(tài)又需要關(guān)心模板
還需要在界面發(fā)生改變的時(shí)候,手動(dòng)去操作DOM來(lái)更新界面的編程范式就被稱之為響應(yīng)式編程
響應(yīng)式編程的本質(zhì)就是每一步都是向?yàn)g覽器發(fā)送一條條的指令廓脆,凡事都是親力親為的
-->
<div id="app">
<h2 id="msg"></h2>
<button id="btn">change</button>
</div>
<script type="text/babel">
let msg = 'Hello World'
const msgEl = document.getElementById('msg')
const btnEl = document.getElementById('btn')
msgEl.innerHTML = msg
btnEl.addEventListener('click', () => {
msg = 'Hello React'
msgEl.innerHTML = msg
})
</script>
聲明式編程
let msg = 'Hello World'
function render() {
// 使用小括號(hào)是為了表示成一個(gè)整體筛谚,同時(shí)可以進(jìn)行換行顯示
const jsx = (
// JSX有且只能有一個(gè)根元素
<div>
{/* 使用大括號(hào)語(yǔ)法來(lái)插入變量 */}
<h2>{msg}</h2>
{/* 在react中原生事件使用小駝峰,因?yàn)閞eact對(duì)原生事件進(jìn)行了二次封裝 */}
<button onClick={ handleChange }>change</button>
</div>
)
ReactDOM.render(jsx, document.getElementById('app'))
}
function handleChange() {
msg = 'Hello React'
render()
}
// 初始化渲染
render()
但是上邊的代碼看起來(lái)有點(diǎn)凌亂停忿,且整個(gè)邏輯其實(shí)可以看做一個(gè)整體驾讲,也就是整個(gè)邏輯本身就可以看成是一個(gè)功能點(diǎn),即整個(gè)邏輯其實(shí)可以封裝為一個(gè)組件席赂,也就是根組件
// 這種只要定義模板和狀態(tài)即可吮铭,不需要我們頻繁的去操作DOM的編程范式就是響應(yīng)式編程
// 一個(gè)類繼承自React.Component后就成為了React組件
class App extends React.Component {
constructor() {
// 繼承類必須在構(gòu)造器的第一行調(diào)用super方法
// 以初始化父類實(shí)例
super()
// 如果定義的對(duì)象是需要響應(yīng)式的對(duì)象
// 也就是那些當(dāng)值發(fā)生改變后,界面也需要相應(yīng)發(fā)生刷新的那些對(duì)象
// 在定義這些響應(yīng)式對(duì)象的時(shí)候颅停,必須定義在state對(duì)象中
this.state = {
msg: 'Hello World'
}
}
// 組件必須實(shí)現(xiàn)一個(gè)render函數(shù)
// 該函數(shù)需要返回對(duì)應(yīng)的jsx對(duì)象
// 用于告訴React需要顯示的template
render() {
// 如果return后邊的div需要和return分兩行來(lái)進(jìn)行編寫
// 就需要給div外包裹小括號(hào)谓晌,以表示這是個(gè)整體
return (
<div>
{/* render不是react中的原生函數(shù),所以其內(nèi)部的this指向是正確的 */}
<h2>{this.state.msg}</h2>
<button onClick={ this.handleChange }>change</button>
</div>
)
}
// 默認(rèn)情況下癞揉,React內(nèi)置的原生事件在調(diào)用的時(shí)候纸肉,會(huì)將原生事件中的this置為undefined
// 所以我們需要使用箭頭函數(shù)進(jìn)行重置
handleChange = () => {
// render函數(shù)本質(zhì)是返回JSX對(duì)象
// 但是單獨(dú)調(diào)用render函數(shù)并不會(huì)將新的JSX交給ReactDOM來(lái)重新渲染
// 所以在更新響應(yīng)式數(shù)據(jù)的時(shí)候,必須調(diào)用setState函數(shù)
// 在實(shí)際開發(fā)中喊熟,我們一般不會(huì)手動(dòng)去調(diào)用render函數(shù)
// this.render()
// 響應(yīng)式數(shù)據(jù)發(fā)生改變的時(shí)候
// 調(diào)用setState方法進(jìn)行界面刷新
this.setState({
msg: 'Hello React'
})
}
}
ReactDOM.render(<App />, document.getElementById('app'))
簡(jiǎn)單案例
電影列表
class App extends React.Component {
constructor() {
super()
this.state = {
movies: ['電影1', '電影2', '電影3', '電影4', '電影5']
}
}
render() {
return (
<div>
{/*
react直接顯示數(shù)組的時(shí)候柏肪,會(huì)主動(dòng)調(diào)用數(shù)組的join方法,分隔符為空字符串
所以這里實(shí)際展示的是this.state.moives.join('')
*/}
{ this.state.movies }
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
既然react在顯示的時(shí)候芥牌,會(huì)主動(dòng)調(diào)用數(shù)組的join方法烦味,那么我們所需要做的就是為數(shù)組中的每一項(xiàng)包裹li標(biāo)簽即可
class App extends React.Component {
constructor() {
super()
this.state = {
movies: ['電影1', '電影2', '電影3', '電影4', '電影5']
}
}
render() {
return (
<ul>
{
this.state.movies.map(moive => <li>{ moive }</li>)
}
</ul>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
計(jì)數(shù)器
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
render() {
return (
<div>
<h2>{ this.state.count }</h2>
<button onClick={() => this.change(1)}>+1</button>
<button onClick={() => this.change(-1)}>-1</button>
</div>
)
}
change = step => {
this.setState({
count: this.state.count + step
})
}
}
ReactDOM.render(<App />, document.getElementById('app'))