React簡介
React 起源于Facebook的內(nèi)部項目,因為該公司對市場上所有JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設(shè) Instagram 的網(wǎng)站。做出來以后,發(fā)現(xiàn)這套東西很好用,就在2013年5月開源了。
- 由于 React 的設(shè)計思想極其獨特,屬于革命性創(chuàng)新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關(guān)注和使用,認為它可能是將來 Web 開發(fā)的主流工具。
React 核心的概念
虛擬DOM(Virtual Document Object Model)
- DOM的本質(zhì):就是用JS表示的UI元素
- 虛擬DOM:并不是由瀏覽器提供的,而是我們程序員手動模擬實現(xiàn)的,類似于瀏覽器中的DOM,有著本質(zhì)的區(qū)別
// 導(dǎo)入模塊
import React from 'react'
import ReactDOM from 'react-dom'
// 創(chuàng)建虛擬Dom元素
var myDiv = React.createElement('div', {
title: 'this is a title',
id: 'mydiv'
}, '我是一個 div')
ReactDOM.render(myDiv, document.getElementById('app'))
Diff算法
- tree diff:新舊DOM樹,逐層對比的方式,就叫做 tree diff,每當我們從前到后,把所有層的節(jié)點對比完后,必然能夠找到那些 需要被更新的元素;
- component diff:在對比每一層的時候,組件之間的對比,叫做 component diff;當對比組件的時候,如果兩個組件的類型相同,則暫時認為這個組件不需要被更新,如果組件的類型不同,則立即將舊組件移除,新建一個組件,替換到被移除的位置;
- element diff:在組件中,每個元素之間也要進行對比,那么,元素級別的對比,叫做 element diff;
- key:key這個屬性,可以把 頁面上的 DOM節(jié)點 和 虛擬DOM中的對象,做一層關(guān)聯(lián)關(guān)系;

JSX語法
- 如要要使用 JSX 語法,必須先運行
npm i babel-preset-react -D,然后再.babelrc中添加 語法配置; - JSX語法的本質(zhì):還是以 React.createElement 的形式來實現(xiàn)的,并沒有直接把 用戶寫的 HTML代碼,渲染到頁面上;
- 如果要在 JSX 語法內(nèi)部,書寫 JS 代碼了,那么,所有的JS代碼,必須寫到 {} 內(nèi)部;
- 當 編譯引擎,在編譯JSX代碼的時候,如果遇到了
<那么就把它當作 HTML代碼去編譯,如果遇到了{}就把 花括號內(nèi)部的代碼當作 普通JS代碼去編譯; - 在{}內(nèi)部,可以寫任何符合JS規(guī)范的代碼;
- 在JSX中,如果要為元素添加
class屬性了,那么,必須寫成className,因為class在ES6中是一個關(guān)鍵字;和class類似,label標簽的for屬性需要替換為htmlFor. - 在JSX創(chuàng)建DOM的時候,所有的節(jié)點,必須有唯一的根元素進行包裹;
- 如果要寫注釋了,注釋必須放到 {} 內(nèi)部
// JSX語法創(chuàng)建
var mytitle = 'this is a title'
var name = 'Alex'
var myDiv = <div>
<h1>我是一個標題,title 屬性: {mytitle} </h1>
<p>我是 P 標簽</p>
<p>我的名字是 {name}</p>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
React 項目搭建
運行 npm i react react-dom -S 安裝包
-
在項目中導(dǎo)入兩個相關(guān)的包
- 在 React 學(xué)習(xí)中,需要安裝 兩個包 react react-dom
- react 這個包,是專門用來創(chuàng)建React組件、組件生命周期等這些東西的;
- react-dom 里面主要封裝了和 DOM 操作相關(guān)的包,比如,要把 組件渲染到頁面上
import React from 'react' import ReactDOM from 'react-dom' 使用JS的創(chuàng)建虛擬DOM節(jié)點
// 2. 在 react 中,如要要創(chuàng)建 DOM 元素了,只能使用 React 提供的 JS API 來創(chuàng)建,不能【直接】像 Vue 中那樣,手寫 HTML 元素
// React.createElement() 方法,用于創(chuàng)建 虛擬DOM 對象,它接收 3個及以上的參數(shù)
// 參數(shù)1: 是個字符串類型的參數(shù),表示要創(chuàng)建的元素類型
// 參數(shù)2: 是一個屬性對象,表示 創(chuàng)建的這個元素上,有哪些屬性
// 參數(shù)3: 從第三個參數(shù)的位置開始,后面可以放好多的虛擬DOM對象,這寫參數(shù),表示當前元素的子節(jié)點
var myH1 = React.createElement('h1', null, '這是一個大大的H1')
var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '這是一個div', myH1)
- 使用 ReactDOM 把元素渲染到頁面指定的容器中:
// ReactDOM.render('要渲染的虛擬DOM元素', '要渲染到頁面上的哪個位置中')
// 注意: ReactDOM.render() 方法的第二個參數(shù),和vue不一樣,不接受 "#app" 這樣的字符串,而是需要傳遞一個 原生的 DOM 對象
ReactDOM.render(myDiv, document.getElementById('app'))
React-組件
創(chuàng)建方式一:
函數(shù)組件
// 組件創(chuàng)建一
var person = {name: 'alex',age: 20}
function Hello(props) {
return <div>
<h1>我是一個組件,我通過function 構(gòu)造器來創(chuàng)建</h1>
<p>我的姓名是 {props.name}, 我的年齡: {props.age}</p>
</div>
}
// 擴散傳值
ReactDOM.render(<Hello {...person}></Hello>, document.getElementById('app'))
通過props 傳值
function Welcome (props) {
return <div>Hello, {props.name}</div>
}
ReactDOM.render(<Welcome name = "alex"></Welcome>, document.getElementById('app'))
創(chuàng)建方式二:
class 組件
var person = { name: 'alex', age: 20 }
class Hello extends React.Component{
render() {
return <div>
<h1>我是一個組件,我通過function 構(gòu)造器來創(chuàng)建</h1>
<p>我的姓名是 {this.props.name}, 我的年齡: {this.props.age}</p>
</div>
}
}
var myDiv = <div>
<Hello {...person}></Hello>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
組合組件
// 組件組合
function Welcome(props) {
return <div>Hello, {props.name}</div>
}
function App() {
return (
<div>
<Welcome name='alex'></Welcome>
<Welcome name='demo'></Welcome>
<Welcome name='ground'></Welcome>
<div className='box'></div>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
兩種創(chuàng)建組件方式的對比
- 用構(gòu)造函數(shù)創(chuàng)建出來的組件:專業(yè)的名字叫做“無狀態(tài)組件”
- 用class關(guān)鍵字創(chuàng)建出來的組件:專業(yè)的名字叫做“有狀態(tài)組件”
用構(gòu)造函數(shù)創(chuàng)建出來的組件,和用class創(chuàng)建出來的組件,這兩種不同的組件之間的本質(zhì)區(qū)別就是:有無state屬性!??!
有狀態(tài)組件和無狀態(tài)組件之間的本質(zhì)區(qū)別就是:有無state屬性!
組件生命周期&State
在組件創(chuàng)建、到加載到頁面上運行、以及組件被銷毀的過程中,總是伴隨著各種各樣的事件,這些在組件特定時期,觸發(fā)的事件,統(tǒng)稱為 組件的生命周期;
組件生命周期分為三部分:
1.組件創(chuàng)建階段
組件創(chuàng)建階段的生命周期函數(shù),有一個顯著的特點:創(chuàng)建階段的生命周期函數(shù),在組件的一輩子中,只執(zhí)行一次;
-
componentWillMount:組件將要被掛載,此時還沒有開始渲染虛擬DOM -
render:第一次開始渲染真正的虛擬DOM,當render執(zhí)行完,內(nèi)存中就有了完整的虛擬DOM了 -
componentDidMount:組件完成了掛載,此時,組件已經(jīng)顯示到了頁面上,當這個方法執(zhí)行完,組件就進入都了 運行中 的狀態(tài)
2.組件運行狀態(tài)
組件運行階段:也有一個顯著的特點,根據(jù)組件的state和props的改變,有選擇性的觸發(fā)0次或多次;
-
componentWillReceiveProps:組件將要接收新屬性,此時,只要這個方法被觸發(fā),就證明父組件為當前子組件傳遞了新的屬性值; -
shouldComponentUpdate:組件是否需要被更新,此時,組件尚未被更新,但是,state 和 props 肯定是最新的 -
componentWillUpdate:組件將要被更新,此時,尚未開始更新,內(nèi)存中的虛擬DOM樹還是舊的 -
render:此時,又要重新根據(jù)最新的 state 和 props 重新渲染一棵內(nèi)存中的 虛擬DOM樹,當 render 調(diào)用完畢,內(nèi)存中的舊DOM樹,已經(jīng)被新DOM樹替換了!此時頁面還是舊的 -
componentDidUpdate:此時,頁面又被重新渲染了,state 和 虛擬DOM 和 頁面已經(jīng)完全保持同步
3.組件銷毀階段
也有一個顯著的特點,一輩子只執(zhí)行一次;
-
componentWillUnmount:組件將要被卸載,此時組件還可以正常使用;

defaultProps
在組件創(chuàng)建之前,會先初始化默認的props屬性,這是全局調(diào)用一次,嚴格地來說,這不是組件的生命周期的一部分。在組件被創(chuàng)建并加載候,首先調(diào)用 constructor 構(gòu)造器中的 this.state = {},來初始化組件的狀態(tài)。
React生命周期的回調(diào)函數(shù)總結(jié)成表格如下:

組件生命周期的執(zhí)行順序:
- Mounting:
- constructor()
- componentWillMount()
- render()
- componentDidMount()
- Updating:
- componentWillReceiveProps(nextProps)
- shouldComponentUpdate(nextProps, nextState)
- componentWillUpdate(nextProps, nextState)
- render()
- componentDidUpdate(prevProps, prevState)
- Unmounting:
- componentWillUnmount()
綁定this并傳參的三種方式
1.在事件中綁定this并傳參:
<input type="button" value="--綁定傳參數(shù)--" onClick={this.handleMsg.bind(this, 10, 20)} />
handleMsg (num, num2) {
console.log(num, num2);
this.setState({
msg: num
})
}
2.在構(gòu)造函數(shù)中綁定this并傳參:
// 修改構(gòu)造函數(shù)中的代碼:
this.handleMsg2 = this.handleMsg2.bind(this, '', '');
<input type="button" value="在構(gòu)造函數(shù)中綁定this并傳參" onClick={this.handleMsg2} />
// 在構(gòu)造函數(shù)中綁定this并傳參
handleMsg2(arg1, arg2) {
this.setState({
msg: '在構(gòu)造函數(shù)中綁定this并傳參:' + arg1 + arg2
});
}
3.用箭頭函數(shù)綁定this并傳參
<input type="button" value="用箭頭函數(shù)綁定this并傳參" onClick={() => { this.handleMsg3('', '') }} />
// 用箭頭函數(shù)綁定this并傳參
handleMsg3(arg1, arg2) {
this.setState({
msg: '用箭頭函數(shù)綁定this并傳參:' + arg1 + arg2
});
}
綁定文本框與state中的值
在React.js中,默認沒有提供雙向數(shù)據(jù)綁定這一功能,默認的,只能把state之上的數(shù)據(jù)同步到界面的控件上,但是不能默認實現(xiàn)把界面上數(shù)據(jù)的改變,同步到state之上,需要程序員手動調(diào)用相關(guān)的事件,來進行逆向的數(shù)據(jù)傳輸!
綁定文本框和state的值:
{/*只要將value屬性,和state上的狀態(tài)進行綁定,那么,這個表單元素就變成了受控表單元素,這時候,如果沒有調(diào)用相關(guān)的事件,是無法手動修改表單元素中的值的*/}
<input style={{ width: '100%' }} ref="txt" type="text" value={this.state.msg} onChange={this.handleTextChange} />
// 這是文本框內(nèi)容改變時候的處理函數(shù)
handleTextChange = () => {
this.setState({
msg: this.refs.txt.value
});
}
注意setState的一個問題:
// 保存最新的state狀態(tài)值,在保存的時候,是異步地進行保存的,所以,如果想要獲取最新的,剛剛保存的那個狀態(tài),需要通過回掉函數(shù)的形式去獲取最新state
this.setState({
msg: this.refs.txt.value
// msg: e.target.value
}, function () {
// 獲取最新的state狀態(tài)值
console.log(this.state.msg);
});
context特性
context 可以將父組件的值傳遞給子組件來進行使用
// getChildContextTypes
// 1. 在 父組件中,定義一個 function,這個function 有個固定的名稱,叫做 getChildContext ,內(nèi)部,必須 返回一個 對象,這個對象,就是要共享給 所有子孫自建的 數(shù)據(jù)
getChildContext() {
return {
color: this.state.color
}
}
// 2. 使用 屬性校驗,規(guī)定一下傳遞給子組件的 數(shù)據(jù)類型, 需要定義 一個 靜態(tài)的(static) childContextTypes(固定名稱,不要改)
static childContextTypes = {
color: ReactTypes.string // 規(guī)定了 傳遞給子組件的 數(shù)據(jù)類型
}
// 3. 上來之后,先來個屬性校驗,去校驗一下父組件傳遞過來的 參數(shù)類型
static contextTypes = {
color: ReactTypes.string // // 這里,如果子組件,想要使用 父組件通過 context 共享的數(shù)據(jù),那么在使用之前,一定要先 做一下數(shù)據(jù)類型校驗
}
記住一串單詞組合getChildContextTypes
前3個、后3個、后兩個
一個方法、兩個靜態(tài)屬性
Ant Design
antd 是基于 Ant Design 設(shè)計體系的 React UI 組件庫,主要用于研發(fā)企業(yè)級中后臺產(chǎn)品。
路由
1.安裝: npm add react-router-dom
2.導(dǎo)入: import { HashRouter, Route, Link } from 'react-router-dom'
- HashRouter 表示一個路由的根容器,將來,所有的路由相關(guān)的東西,都要包裹在 HashRouter 里面,而且,一個網(wǎng)站中,只需要使用一次 HashRouter 就好了;
- Route 表示一個路由規(guī)則, 在 Route 上,有兩個比較重要的屬性, path component
- Link 表示一個路由的鏈接 ,就好比 vue 中的 <router-link to=""></router-link>
3.設(shè)置HashRouter根路由
4.使用Linke創(chuàng)建路由連接
5.Route創(chuàng)建路由規(guī)則
// 路由學(xué)習(xí)
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
// 當 使用 HashRouter 把 App 根組件的元素包裹起來之后,網(wǎng)站就已經(jīng)啟用路由了
return <HashRouter>
<div>
<h1>這是網(wǎng)站的APP根組件</h1>
<hr />
<Link to="/home">首頁</Link>
<Link to="/movie">電影</Link>
<Link to="/about">關(guān)于</Link>
<hr />
{/* Route 創(chuàng)建的標簽,就是路由規(guī)則,其中 path 表示要匹配的路由,component 表示要展示的組件 */}
{/* Route 具有兩種身份:1. 它是一個路由匹配規(guī)則; 2. 它是 一個占位符,表示將來匹配到的組件都放到這個位置, 如果想讓路由規(guī)則,進行精確匹配,可以為 Route,添加 exact 屬性,表示啟用精確匹配模式 */}
<Route path="/home" component={Home}></Route>
<Route path="/movie" component={Movie}></Route>
<Route path="/about" component={About}></Route>
</div>
</HashRouter>
}
}