簡(jiǎn)介:
當(dāng)2014年Facebook推出React時(shí),給整個(gè)業(yè)界帶來全新的看待網(wǎng)頁應(yīng)用開發(fā)的方式,和React一同問世的Flux,也克服傳統(tǒng)MVC框架的很多弊病嘱朽。技術(shù)在不斷發(fā)展软吐,在2015年瘩将,F(xiàn)lux的一個(gè)變體Redux出現(xiàn),進(jìn)一步優(yōu)化了Flux的功能。
優(yōu)秀之處:
? 專注視圖層:(不處理路由姿现,數(shù)據(jù)肠仪,以及邏輯的處理)
? VirtualDom:只對(duì)虛擬dom更新,不對(duì)真實(shí)dom更新备典;只對(duì)dom進(jìn)行必要的更新异旧,實(shí)現(xiàn)的重繪的最小化;
? jsx語法:
? 高內(nèi)聚提佣,低耦合
? onClick 不是原生dom中的onclick事件泽艘,這里是onClict是通過react的事件委托方式,觸發(fā)掛在頂層dom節(jié)點(diǎn)上的一個(gè)事件處理函數(shù)(提高了性能)镐依;
? 可以將樣式一起寫入組件文件中匹涮;
? 關(guān)于jsx語法的說法:
? 在jsx語法中可以直接寫入組件,注意組件為了和普通標(biāo)簽區(qū)別槐壳,必須第一個(gè)字母大寫然低;
? jsx語法是歷史的進(jìn)步還是倒退;(樣式务唐,結(jié)構(gòu)雳攘,邏輯于一體)
? 活躍的生態(tài)圈:(開源項(xiàng)目)
? 組件式開發(fā)
? 手動(dòng)搭建項(xiàng)目:
? npm init
? 安裝項(xiàng)目依賴
cnpm install --save-dev babel-core babel-loader babel-preset-stage-2 babel-plugin-transform-react-jsx babel-plugin-transform-runtime babel-preset-es2015 babel-preset-react babel-preset-stage-0 css-loader html-loader html-webpack-plugin react react-dom style-loader url-loader webpack webpack-dev-server
? 配置babelrc文件:
?{
????"presets": [
????????"es2015",
????????"stage-0",
????????"react"
????],
????"plugins": ["transform-runtime", "transform-react-jsx"]
}
? webpack.config.js的配置文件:
除了如下配置有更改,其他可以完全遵照vue的配置
{
????????????????test:?/\.js|.jsx$/,
????????????????use: [
????????????????????"babel-loader",
????????????????],
????????????????exclude:?/node_modules/
????????????},
? 在vsCode中開發(fā)react的配置:
安裝jsx插件配置如下:
安裝React/Redux/react-router Snippets插件枫笛,可以幫助我們快速生成一定的語句吨灭;
所有的react組件使用jsx后綴來標(biāo)識(shí);
? 自動(dòng)化構(gòu)建項(xiàng)目:
? 初始化:
全局安裝:
npm install –-global create- react- app
初始化項(xiàng)目:
create- react- app myReactApp
啟動(dòng)服務(wù):
cd myReactApp
npm start
? 組件寫法:
import?React,{Component}?from "react"
import?ClickCount?from "./ClickCount.jsx"
class?Main extends?Component {
??constructor(props){
????super(props)
??}
??render(){
????return?(
??????<ul>
????????<li><ClickCount?name="first"?num={1}></ClickCount></li>
????????<li><ClickCount?name="second"?num={10}></ClickCount></li>
????????<li><ClickCount?name="third"?num={7}></ClickCount></li>
??????</ul>
????)
??}
}
export default?Main
注:對(duì)于jsx語法的解析必須引入React刑巧,雖然這里不用喧兄,但是如果不引入會(huì)因無法解析jsx而報(bào)錯(cuò);組件的定義必須遵循首字母大寫
? jsx語法使用:
屬性:
1啊楚、屬性值直接為字符串
2吠冤、動(dòng)態(tài)解析屬性值
const?element =?(<img src={user.avatarUrl}></img>);
react的jsx:
const?element =?(
??<h1 className="greeting">
????Hello, world!
??</h1>
);
用React.createElement()同樣可以達(dá)到同樣的效果;
const?element =?React.createElement(
??'h1',
??{className:?'greeting'},
??'Hello, world!'
);
? 項(xiàng)目的分解:
npm run eject 進(jìn)行項(xiàng)目配置文件的彈射恭理;
? react的工作方式:
? 從繁瑣的dom處理中解脫出來拯辙,響應(yīng)式,函數(shù)式編程思維颜价;輸入相同的數(shù)據(jù)涯保,將產(chǎn)出相同的效果;數(shù)據(jù)更新周伦,dom同時(shí)做出更新夕春;
? Virtual Dom
Dom:結(jié)構(gòu)化文本的抽象表達(dá)式;
虛擬dom:對(duì)dom樹的抽象横辆,不會(huì)觸及瀏覽器撇他,只存在于js內(nèi)存的空間樹形結(jié)構(gòu),每次的數(shù)據(jù)刷新狈蚤,render函數(shù)的執(zhí)行都會(huì)觸發(fā)一個(gè)新舊虛擬dom 的對(duì)比困肩,如果無改變,則不更新脆侮,有改變锌畸,只更新相應(yīng)dom結(jié)構(gòu);
? 元素的渲染
? jsx語法將元素渲染到dom中
? 更新元素渲染:
React 元素都是immutable 不可變的靖避。當(dāng)元素被創(chuàng)建之后潭枣,你是無法改變其內(nèi)容或?qū)傩缘模桓陆缑娴奈ㄒ环椒ň褪侵匦聮燧d節(jié)點(diǎn)
function?tick() {
??const element = (
??????<h2>{new?Date().toLocaleTimeString()}.</h2>
??);
??ReactDOM.render(
????element,
????document.getElementById('root')
??);
}
setInterval(tick, 1000);
? 只會(huì)更新必要的部分(可以在谷歌開發(fā)者工具中進(jìn)行dom更新的查看)
? 在react的jsx語法中幻捏,如果標(biāo)簽沒有子元素盆犁,可以直接使用閉合標(biāo)簽;
? 在react的組件必須首字母進(jìn)行大寫篡九,否則React.createEelement則無法查找到組件谐岁,進(jìn)行編譯;
? jsx支持標(biāo)簽名為表達(dá)式榛臼;伊佃,如果使用動(dòng)態(tài)標(biāo)簽名,則需先將表達(dá)式解析賦值給一個(gè)變量沛善;
? React組件以及基本語法:
? 組件的設(shè)計(jì)要素:
高內(nèi)聚:把邏輯緊密相關(guān)的內(nèi)容放在一個(gè)組件中航揉;
低耦合:不同組件之間的依賴關(guān)系要盡量弱化;盡量保持每個(gè)組件的獨(dú)立性金刁;
? 組件的生命周期:
? 裝載過程:
? Constructor:
初始化state帅涂;
綁定成員函數(shù)的this環(huán)境(bind、::?)防止以解構(gòu)的方式進(jìn)行全局作用域進(jìn)行調(diào)用尤蛮;
例:this.fn=this.fn.bind(this)或this.fn=::this.fn
? GetInitialState:初始化state值,必須結(jié)合React.createClass()使用漠秋,使用es6的方式,此函數(shù)無效果抵屿;
const Test=React.createClass({
??getInitialState:function(){
????return?{
??????name:"zhangsan"
????}
??},
??getDefaultProps:function(){
????return?{
??????name:"zhangsan"?//這里的zhangsan相當(dāng)于默認(rèn)值
????}
??}
})
? GetDefaultProps:初始化props值,必須結(jié)合React.createClass()使用庆锦,使用es6的方式茉继,此函數(shù)無效果携御,用屬性defaultProps代替;
class?Test extends?Component {
??constructor(props){
super(props)
this.state={data:new Date()}
??}
??render(){
????return?(
??????<ul>
????????<li><ClickCount?name="third"?num={7}></ClickCount></li>
??????</ul>
????)
??}
}
Test.defaultProps={
????name:"moren"
}
? ComponentWillMount:在render函數(shù)調(diào)用之前進(jìn)行調(diào)用臀蛛;
? Render:并不做實(shí)際的渲染動(dòng)作尿扯,只返回一個(gè)jsx描述結(jié)構(gòu)求晶,最后渲染與否由react來決定;必須是個(gè)純函數(shù)衷笋,不能設(shè)計(jì)到數(shù)據(jù)的更變(this.setState)芳杏;
render(){
????return?(
??????<ul>
????????<li><ClickCount?name="third"?num={7}></ClickCount></li>
??????</ul>
????)
??}
? ComponentDidMount :在render函數(shù)調(diào)用之后進(jìn)行調(diào)用;但不是在render函數(shù)調(diào)用之后立即調(diào)用,而是當(dāng)所有的dom樹掛載并渲染完成后才會(huì)調(diào)用爵赵,這是因?yàn)閞ender函數(shù)并不進(jìn)行渲染吝秕,而只是返回一個(gè)jsx對(duì)象,渲染的工作由react庫進(jìn)行空幻,只有當(dāng)所有的組件的jsx對(duì)象組合完畢之后烁峭,react通過對(duì)比后才會(huì)渲染,所有此鉤子函數(shù)是發(fā)生的所有組件的render函數(shù)都執(zhí)行后才會(huì)執(zhí)行秕铛;(只能在瀏覽器端觸發(fā))
注:與angular中的link或者post函數(shù)有點(diǎn)相似约郁,但是這里不僅指子組件,而是當(dāng)前組件中的所有組件但两,包括兄弟組件鬓梅;提供了dom操作的接口
? 更新過程:
? ComponentWillReceiveProps:傳入的props的改變或者組件進(jìn)行刷新(forceUpdate函數(shù)觸發(fā)組件的刷新)都會(huì)觸發(fā)此函數(shù),但是通過this.setState改變的數(shù)據(jù)則不會(huì)觸發(fā)此函數(shù)谨湘,
? ShouldComponentUpdate:react組件的鉤子函數(shù)兩個(gè)需要有返回值的鉤子函數(shù)之一己肮,另一個(gè)為render,此鉤子函數(shù)的返回值為一個(gè)bolen值悲关,如果為true時(shí)谎僻,則prop的改變以及state的改變都會(huì)引起組件的刷新,如果為false時(shí)寓辱,則不再進(jìn)行渲染艘绍;此鉤子函數(shù)接受兩個(gè)參數(shù),一個(gè)是nextProps秫筏,一個(gè)是nextState诱鞠,可以將將要更新的值和此時(shí)的做對(duì)比,然后返回true和false來進(jìn)行性能的校優(yōu)这敬;
? ComponentWillUpdate:跟componentWillMount相似
? Render
? ComponentDidUpdate:跟componentDidMount相似
? 卸載過程:
ComponentWillUnmount:此鉤子函數(shù)可以在組件卸載前執(zhí)行航夺,可以進(jìn)行手動(dòng)添加dom元素的刪除,以及計(jì)時(shí)器和事件監(jiān)聽的移除崔涂;
? React組件的數(shù)據(jù)
組件內(nèi)部數(shù)據(jù)類型:
兩種數(shù)據(jù)結(jié)構(gòu)阳掐,prop和state,這兩種數(shù)據(jù)的改變都會(huì)引起組件的重新渲染冷蚂;
? Prop:組件的外部接口缭保,接受外部數(shù)據(jù);跟html的屬性的書寫類似蝙茶,但是除了接受字符串之外艺骂,還可以接受js對(duì)象,數(shù)字等的隆夯;如果組件進(jìn)行數(shù)據(jù)反饋給外界钳恕,可以通過prop傳入組件一個(gè)函數(shù)别伏;
? Prop的讀取:
由class類的constructor函數(shù)接受忧额,以及super()方法調(diào)用時(shí)的傳入厘肮;
最后的props為一個(gè)對(duì)象,鍵值分別為傳入時(shí)的屬性名宙址;這里比較方便的是使用es6的解構(gòu)賦值轴脐;
? PropTypes的檢查:
可以通過增加類的propTypes屬性來定義prop規(guī)格调卑,在constructor函數(shù)中進(jìn)行如下定義:
Index.propTypes={ ????//組件全局進(jìn)行配置
caption:PropTypes.string.isRequired,
initValue:PropTypes.number
}
注意:此屬性不會(huì)影響組件的渲染抡砂,只是做到在開發(fā)過程中輔助開發(fā);
? State:由于組件不能改變出入的prop恬涧,所以當(dāng)組件要進(jìn)行自己的狀態(tài)紀(jì)錄時(shí)就需要用到state注益;
? 初始化:state的初始化可以在constructor中,通過this.state進(jìn)行設(shè)置溯捆,值必須為一個(gè)js對(duì)象的格式丑搔,通常將prop傳入的外部值賦給state,以便后續(xù)操作提揍;設(shè)置默認(rèn)值時(shí)啤月,可以用?| | 操作符進(jìn)行;
? 讀取和更新state
更新使用this.setState({count:this.state.count++})方法劳跃;
讀取為:this.state.count這種方式
? Prop和state的對(duì)比:
Prop:定義外部接口谎仲;賦值在外部環(huán)境使用組件時(shí);組件內(nèi)部不能更改
State:紀(jì)錄內(nèi)部狀態(tài)刨仑;賦值在組件內(nèi)部郑诺;在組件內(nèi)部可以進(jìn)行更改;
? react中css的樣式書寫
? 全局引入css文件
此種方式會(huì)將css文件在全局引入杉武,也就是說當(dāng)前樣式文件中書寫的所有樣式辙诞,在任何一個(gè)組件都可以引用,此種引用方式有可能會(huì)造成全局的污染轻抱,變量名沖突飞涂,又或者說不符合組件式開發(fā)的思想;此種方式一般是引入全局的css文件祈搜;
import “app.css”
? 局部作用域(css modules的方式)
此種方式采用的是css modules的做法封拧,通過此做法,最后會(huì)將css文件中類名和dom中類名形成唯一的hash值夭问,這樣就不會(huì)造成類名的沖突了泽西,使用場(chǎng)景,一般為組件內(nèi)部的樣式防止全局書寫造成的類名污染缰趋;
比如如下寫法:
app.css:
.title {
??color:?red;
}
jsx:
import style from './App.css';
export default ()?=>?{
??return?(
????<h1 className={style.title}>
??????Hello World
????</h1>
??);
};
最后會(huì)編譯成如下代碼:
<h1 class="_3zyde4l1yATCOkgn-DBWEL">
??Hello World
</h1>
._3zyde4l1yATCOkgn-DBWEL {
??color:?red;
}
注:在css Modules中還提供了一種css全局作用域的書寫方式捧杉,也就是說如果通過此種方式注冊(cè)的css類名不會(huì)被替換為hash值陕见,書寫方式如下:
css:
:global(.title){
color:red;
}
? 行內(nèi)樣式:
在jsx的dom上直接使用style屬性進(jìn)行樣式的設(shè)置,樣式的值為一個(gè)對(duì)象味抖,這種方式的書寫评甜,可以支持css樣式以js對(duì)象的方式進(jìn)行書寫,如果不想將組件的內(nèi)部樣式進(jìn)行作用域隔離使用css modules的方式進(jìn)行書寫仔涩,則可以此種方式進(jìn)行書寫忍坷,可以直接在render函數(shù)中定義樣式對(duì)象,也可以通過專門外部樣式js文件配置樣式對(duì)象熔脂,然后引入到dom進(jìn)行使用佩研,書寫方式如下:
var style={
color:red;
}
<div style={style}>天氣真好</div>
注:在書寫css屬性時(shí),一定要采用駝峰式寫法霞揉;
? 事件處理
? react中的事件綁定屬性采用的是駝峰式寫法:
<button onClick={this.clickfn}>點(diǎn)擊</button>
? 處理react使用class類定義方法時(shí)旬薯,無法自動(dòng)綁定this的弊端:
? 在constructor中使用bind為事件函數(shù)綁定this
this.clickfn=this.clickfn.bind(this)
? 在dom中調(diào)用函數(shù)時(shí),先使用箭頭函數(shù)綁定this适秩,箭頭函數(shù)的this是在定義時(shí)形成的
<button onClick={()=>this.clickfn()}>點(diǎn)擊</button>
? jsx的onClick和html的onclick不同之處
? html的onclick弊端:
注冊(cè)的事件處理函數(shù)都是全局環(huán)境绊序,污染了全局環(huán)境;
使用onclick的dom元素秽荞,如果在dom樹中刪除時(shí)骤公,必須手動(dòng)的注銷事件處理器,否則會(huì)造成內(nèi)容泄露扬跋;
? jsx的onClick:
掛載的每一個(gè)函數(shù)都是在組件內(nèi)部阶捆,而不是全局;
無論多少個(gè)onClick都是采用的事件委托的方式胁住,在dom樹頂層添加一個(gè)事件監(jiān)聽函數(shù)趁猴,此函數(shù)會(huì)根據(jù)具體組件分配具體的函數(shù);
? 條件判斷:
在react的jsx語法中彪见,如果遇到js語句使用{}進(jìn)行包裹儡司;
? if語句的使用:
render() {
????????var?element=null
????????????if(this.state.stateT){
???????????????????element= <div > stateT為true時(shí)?< /div>
????????????????}else{
?????????????????????element= <div > stateT為false時(shí)?< /div>
????????????????}
????????return?(
????????????<div>
????????????????<button onClick={this.changeFn}>切換</button>
????????????????{element}
????????????</div>
????????)
????}
? jsx與運(yùn)算符&&的結(jié)合:
render(){
????????return?(
?????????????<div>
????????????????<button onClick={this.changeFn}>切換</button>
????????????????{this.state.stateT&& <div > stateT為false時(shí)?< /div>}
????????????</div>
????????)
????}
? 三目運(yùn)算符
render(){
????????return?(
?????????????<div>
????????????????<button onClick={this.changeFn}>切換</button>
????????????????{this.state.stateT?<div>stateT為true時(shí)</div>:<div> stateT為false時(shí)</div>}
????????????</div>
????????)
}
? 在jsx語法中,render可以返回null余指,表示不進(jìn)行任何dom的渲染捕犬,但是鉤子函數(shù)會(huì)執(zhí)行;
? 列表和keys
? 基礎(chǔ)用法:(key一般采用數(shù)據(jù)id酵镜,或者數(shù)據(jù)在數(shù)組中的索引)
render() {
????????var?element=this.state.list.map((i,index)=><li key={index}>{i}</li>)
????????return?(
????????????<div>
????????????????<button onClick={this.changeFn}>切換</button>
????????????????{element}
????????????</div>
????????)
}
注:如果后續(xù)進(jìn)行數(shù)據(jù)的重新排序碉碉,則最好不要使用索引當(dāng)作key值,否則淮韭,由于key值和dom的內(nèi)容同時(shí)改變垢粮,則會(huì)引起全部dom節(jié)點(diǎn)的重繪;如果使用的是id靠粪,則只會(huì)調(diào)整dom節(jié)點(diǎn)的順序蜡吧,不會(huì)引起重繪dom樹毫蚓;(可以用上面例子演示,推薦使用id作為標(biāo)識(shí)昔善,而不是index)
? key值只需要在兄弟元素之間唯一就可以元潘,不需要是在全局唯一的
render() {
????????var?element=this.state.list.map((i,index)=><li key={i}>{i}</li>)
????????var?element2=this.state.list.map((i,index)=><li key={i}>{i+1}</li>)
????????return?(
????????????<div>
????????????????<button onClick={this.changeFn}>切換</button>
????????????????{element}
????????????????{element2}
????????????</div>
????????)
??? ?}
? 在jsx中使用map進(jìn)行遍歷
render() {
????????return?(
????????????<ul>
????????????????<button onClick={this.changeFn}>切換</button>
???????????????{this.state.list.map((i)=><li key={i}>{i}</li>)}
????????????</ul>
????????)
????}
? 表單
在vue中,我們知道由于vue的mvvm架構(gòu)君仆,v與m之間是響應(yīng)式的翩概,可以實(shí)時(shí)同步,但是在react中不存在數(shù)據(jù)的雙向數(shù)據(jù)綁定返咱,只是負(fù)責(zé)view層的渲染钥庇,所以我們?cè)?/b>react中取表單中的值時(shí)需要一定的方式
? 受控組件(采用react的單向數(shù)據(jù)流方式實(shí)現(xiàn)vue一樣的雙向數(shù)據(jù)綁定)
changeFn(event){
????????this.setState({
????????????name:event.target.value
????????})
????}
????render() {
????????return?(
????????????<div>
????????????????<p>表單</p>
????????????????<label htmlFor="name">name:</label>
????????????????<input type="text"?id="name"?value={this.state.name} onChange={this.changeFn}/>
????????????????<p>{this.state.name}</p>
????????????</div>
????????)
????}
如果想對(duì)從input中獲取到的值進(jìn)行格式化時(shí),則如下操作就可以實(shí)現(xiàn):
this.setState({
????????????name:event.target.value.toUpperCase()
????????})
? textarea標(biāo)簽在react中設(shè)置和讀取值都采用value的方式
? 在react中的使用select洛姑,并實(shí)時(shí)獲取其選擇的值:
render() {
????????return?(
????????????<div>
????????????????<p>表單</p>
????????????????<label htmlFor="name">name:</label>
????????????????<select value={this.state.name} onChange={this.changeFn}>
????????????????????<option value="one">one</option>
????????????????????<option value="two">two</option>
????????????????????<option value="three">three</option>
????????????????</select>
????????????????<p>{this.state.name}</p>
????????????</div>
????????)
}
? 在react中實(shí)現(xiàn)多表單元素值得獲取
changeFnAll(event){
????????let target=event.target
????????let name=target.name
????????let value=target.type==="checkbox"?target.checked:target.value
????????this.setState({
????????????[name]:value
????????})
????}
????// 實(shí)現(xiàn)多表單元素值的獲取
????render() {
????????return?(
????????????<div>
????????????????<p>表單</p> ???
????????????????<input type="checkbox"?value={this.state.checkbox} onChange={this.changeFnAll} name="checkbox"/>
????????????????<input type="text"?value={this.state.text} onChange={this.changeFnAll} ?name="text"/>
????????????????<p>{this.state.checkbox.toString()}</p>
????????????????<p>{this.state.text}</p>
????????????</div>
????????)
????}
? 組件間通訊
? 父組件向子組件傳遞數(shù)據(jù):
? 字符串常量的傳遞:
<MyComponent message="hello world"?/>
<MyComponent message={'hello world'} />
? 沒有給屬性傳值上沐,它默認(rèn)為?true
<MyTextBox autocomplete />
不建議如下寫法皮服,容易與es6的對(duì)象簡(jiǎn)潔表示法沖突
<MyTextBox autocomplete={true} />
? 使用擴(kuò)展運(yùn)算符進(jìn)行屬性的設(shè)置
??const?props =?{firstName:?'Ben', lastName:?'Hector'};
<Greeting {...props} />;
? 子組件向父組件傳遞數(shù)據(jù)
只能通過將父組件的一個(gè)方法用prop的方式傳入子組件楞艾,在子組件觸發(fā)更變時(shí),調(diào)用這個(gè)傳入的方法龄广,然后把子組件更變后的值以參數(shù)的形式傳入父組件硫眯;從而達(dá)到父組件和子組件的通信的效果;
? 兄弟組件間的通訊
? 狀態(tài)提升
對(duì)于沒有直接關(guān)聯(lián)關(guān)系的兩個(gè)節(jié)點(diǎn)择同,就如?Child_1 與?Child_2 之間的關(guān)系两入,他們唯一的關(guān)聯(lián)點(diǎn),就是擁有相同的父組件敲才。參考之前介紹的兩種關(guān)系的通訊方式裹纳,如果我們向由?Child_1 向?Child_2 進(jìn)行通訊,我們可以先通過?Child_1 向?Parent 組件進(jìn)行通訊紧武,再由?Parent 向?Child_2 組件進(jìn)行通訊
? 觀察者模式
也就是像angular和vue的事件派發(fā)機(jī)制剃氧,在這里react沒有內(nèi)置的事件派發(fā)模式,所以需要手動(dòng)封裝一個(gè)事件派發(fā)機(jī)制阻星;
如下代碼:
var?watchObj = {
????arrFn: {},
????on(name, fn) {
????????if?(this.arrFn[name] === undefined) {
????????????this.arrFn[name] = []
????????}
????????this.arrFn[name].push(fn)
????},
????emit() {
????????let?name, args;
????????if?(arguments.length == 0) {
????????????return?false;
????????}
????????name = arguments[0]
????????args = [].concat(Array.prototype.slice.call(arguments, 1));
????????if?(this.arrFn[name] !== undefined?&& this.arrFn[name].length > 0) {
????????????this.arrFn[name].forEach(function(i) {
????????????????i(...args)
????????????}, this);
????????}
????}
}
export?default?watchObj
? 組合與繼承
在vue中的有一種組件特性叫內(nèi)容分發(fā)朋鞍,可以在父組件中自定義子組件的dom結(jié)構(gòu),在react中也提供了這么一種機(jī)制
? 包含關(guān)系:(在子組件通過this.props.children獲取父組件中寫在子組件開閉標(biāo)簽間的dom節(jié)點(diǎn))
父組件:
render() {
????????return?(
????????????<div>
???????????????<Children>
????????????????<p>相當(dāng)于vue的內(nèi)容分發(fā)</p>
???????????????</Children>
????????????</div>
????????)
????}
子組件:
render() {
????????return?(
????????????<div>
????????????????<p>這里是子組件</p>
???????????????{this.props.children}
????????????</div>
????????)
????}
? 子組件中動(dòng)態(tài)渲染其他組件:(在父組件中將其他組件動(dòng)態(tài)綁定到prop屬性上妥箕,在子組件中通過相應(yīng)的屬性來獲取)
父組件:
render() {
????????return?(
?????????var?one= <span>這里為true</span>
????????var?two=<span>這里為false</span>
????????return?(
????????????<div>
????????????????<Slot?one={one} two={two}></Slot>
????????????</div>
????????)
????????)
????}
子組件:
render() {
???????return?<div> ??
????? <p> ?這里是子組件</p>
????????{this.state.state?this.props.one:this.props.two}
????</div>
}
? react-router
?2.0 完全向后兼容?1.0滥酥,所有在?1.0 被棄用的?API 都會(huì)在控制臺(tái)以?warn 的形式打印出來,在?3.0 中將會(huì)完全移除?1.0 所棄用的東西畦幢,現(xiàn)在最新版本是4.2.0版本坎吻;但是4.0版本后,有很多方法不再向前兼容(這里講解的為2.8.1版本)
? 基本用法:
? 首先我們需要導(dǎo)入一些組件...
import { Router, Route, Link } from 'react-router'
? 配置路由信息:
? 使用jsx語法
React.render((
??<Router>
????<Route path="/"?component={App}>
??????<Route path="about"?component={About}?/>
??????<Route path="inbox"?component={Inbox}?/>
????</Route>
??</Router>
),?document.body)
? 使用js對(duì)象進(jìn)行配置
const?routes =?{
??path:?'/',
??component:?App,
??childRoutes:?[
????{?path:?'about',?component:?About },
????{?path:?'inbox',?component:?Inbox },
??]
}
React.render(<Router routes={routes}?/>,?document.body)
? 獲取參數(shù)
? 通過動(dòng)態(tài)路由片段
<Route path="messages/:id"?component={Message}?/>
?可以通過如下方式進(jìn)行獲取id:
this.props.params.id
? 通過query字符串
/foo?bar=baz宇葱;
this.props.location.query.bar
? 路由的配置規(guī)則(Router)
? 路徑語法:
? :paramName?– 匹配一段位于?/瘦真、??或?#?之后的?URL返奉。?命中的部分將被作為一個(gè)參數(shù)
? ()?– 在它內(nèi)部的內(nèi)容被認(rèn)為是可選的
? *?– 匹配任意字符(非貪婪的)直到命中下一個(gè)字符或者整個(gè)?URL 的末尾,并創(chuàng)建一個(gè)?splat?參數(shù)
如下解釋:
// 匹配?/hello/michael 和?/hello/ryan
<Route path="/hello/:name"> ????????
// 匹配?/hello, /hello/michael 和?/hello/ryan
<Route path="/hello(/:name)"> ??
// 匹配?/files/hello.jpg 和?/files/path/to/hello.jpg ?
<Route path="/files/*.*"> ?????????
? 常用路由類型:
browserHistory:react推薦使用(采用的是html5的瀏覽器路由紀(jì)錄)
hashHistory:默認(rèn)的(采用的是hash的瀏覽器路由紀(jì)錄)
createMemoryHistory
使用browserHistory如下:
import?{?browserHistory } from 'react-router'
<Router?history={browserHistory}>
? 路由的跳轉(zhuǎn)配置(Link):
需要?jiǎng)討B(tài)的引入組件:
import { Link} from “react-router“
<Link></Link>
屬性:
to: 值為字符串吗氏,例:”/list” (路由跳轉(zhuǎn)的路徑)芽偏;
query:值為對(duì)象,例:{name:1} (這里相當(dāng)于/list?name=1,這種路由的書寫方式)弦讽;
state:值為對(duì)象污尉,可以通過this.location.state獲取到當(dāng)前的值;
activeClassName:當(dāng)某個(gè)?route 是激活狀態(tài)時(shí)往产,<Link>?可以接收傳入的?className被碗。失活狀態(tài)下是默認(rèn)的?class。寫法如下:activeClassName="current"
activeStyle:當(dāng)某個(gè)?route 是激活狀態(tài)時(shí)仿村,可以將樣式添加到鏈接元素上锐朴;寫法如下:activeStyle={{color: 'red'}}
onClick(e):自定義點(diǎn)擊事件的處理方法。如處理?<a>?標(biāo)簽一樣?- 調(diào)用?e.preventDefault()?來防止過度的點(diǎn)擊蔼囊,同時(shí)?e.stopPropagation()?可以阻止冒泡的事件
? 默認(rèn)路由的設(shè)置:
jsx:
<IndexRoute component={?Index?}/>
js對(duì)象:
const routes = {
??path:?'/',
??component:?App,
??indexRoute:{
????component:Index
??},
??childRoutes:?[
????{ path:?'list', component:?List?},
??]
}
在嵌套路由中使用{this.props.children}來渲染動(dòng)態(tài)路由焚志,這里相當(dāng)于vue-router中的router-view
? 路由對(duì)應(yīng)組件的渲染
? 單一組件的渲染:(this.props.children)
路由:
????????????<Route?path='center'?component={Center}>
????????????????<Route?path="left"?component={Left}></Route>
????????????</Route>
組件:(Center)
render(){
????????return?(
??????????<div>
????????????<p>這里是中心也</p>
????????????{this.props.children}
??????????</div>
????????)
???}
? 多組件的渲染
路由:
????????????<Route?path='center'?component={Center}>
???????????????<Route?path="left"?components={{left:Left,right:Right}}></Route>
????????????</Route>
組件:
render(){
const { left, right } = this.props
????????return?(
??????????<div>
????????????<p>這里是中心也</p>
????????????{left}-{right}
??????????</div>
????????)
???}
? 異步組件(組件的懶加載)
? 單組件路由的定義:(getComponent()方法使用)
注:這里采用的是webpack的require.ensure()進(jìn)行的組件懶加載,接受三個(gè)參數(shù)畏鼓,第一個(gè)參數(shù)為加載當(dāng)前模塊時(shí)的依賴酱酬,第二個(gè)參數(shù)為加載的當(dāng)前的模塊的回掉函數(shù),第三個(gè)參數(shù)為加載完畢后模塊的名稱云矫;
這里的getComponent函數(shù)第一個(gè)參數(shù)為當(dāng)前路由的信息膳沽,第二個(gè)參數(shù)為一個(gè)回調(diào)函數(shù),這里的回掉函數(shù)遵循node.js的回掉函數(shù)風(fēng)格(回調(diào)函數(shù)的第一個(gè)參數(shù)為錯(cuò)誤的捕捉让禀,一般無錯(cuò)誤挑社,所以一般默認(rèn)填入null,第二個(gè)參數(shù)為異步裝載成功的組件巡揍;)
<Route?path="list"?getComponent={(location, cb) => {
????????????require.ensure([],(require)=>{
???????????????cb(null, require('./components/routerPage/list.jsx').default)
????????????},'List')
??????????}} />
? 多組件路由的定義:(getComponents方法)
<Route?path="center"?getComponents={(location, cb) => {
????????????require.ensure([],function(require){
???????????????cb(null, {center:require('./components/routerPage/center.jsx').default,list:require('./components/routerPage/center.jsx').default})
????????????},'Center')
? 用js對(duì)象的方式配置路由組件的懶加載
const routes = {
??path:?'/',
??getComponent(nextState, cb) {
????require.ensure([], (require) => {
??????cb(null, require('./components/app.jsx').default)
????}, 'App')
??},
??indexRoute:{
????getComponent(nextState, cb) {
??????require.ensure([], (require) => {
????????cb(null, require('./components/index.jsx').default)
??????}, 'Index')
????},
??},
??childRoutes:?[
????// require("./routes/listR.js").default
?????{
????????path:?'list',
????????getComponent(nextState, cb) {
????????????require.ensure([], (require) => {
????????????????cb(null, require('./components/routerPage/list.jsx').default)
????????????}, 'List')
????????}
????}
??]
}
? Route的鉤子函數(shù):(Route組件上書寫)
onEnter(nextState,?replaceState,?callback?)
當(dāng)?route 即將進(jìn)入時(shí)調(diào)用痛阻。它提供了下一個(gè)路由的?state,一個(gè)函數(shù)重定向到另一個(gè)路徑吼肥。this?會(huì)觸發(fā)鉤子去創(chuàng)建?route 實(shí)例录平。
當(dāng)?callback?作為函數(shù)的第三個(gè)參數(shù)傳入時(shí),這個(gè)鉤子將是異步執(zhí)行的缀皱,并且跳轉(zhuǎn)會(huì)阻塞直到?callback?被調(diào)用斗这。
onLeave()
當(dāng)?route 即將離開時(shí)調(diào)用
? 路由注入到組件的props上的屬性和參數(shù):
? history:路由的前端歷史紀(jì)錄和路由跳轉(zhuǎn)方法
go():
goBack():
goForward():
push():
replace()
? location:當(dāng)前路由的參數(shù)信息
state:通過在link上的state屬性設(shè)置的對(duì)象值
action:當(dāng)前路由的紀(jì)錄形式
pathname:當(dāng)前hash路由
query:通過路由的傳參,路徑啤斗?后的路徑表箭,或者是query屬性傳遞的
? params:通過動(dòng)態(tài)路由傳遞的值
? routeParams:(小心有坑)
this.props.params?是直接在組件中指定?route 的一個(gè)子集。例如钮莲,如果?route 的路徑是?users/:userId?而?URL 是?/users/123/portfolios/345免钻,那么?this.props.routeParams?會(huì)是?{userId:?'123'}彼水,并且?this.props.params?會(huì)是?{userId:?'123',?portfolioId:?345}。
? route:當(dāng)前路由的配置信息
? routes:當(dāng)前路由所包含的所有路由片段
? 在路由跳轉(zhuǎn)前阻止路由的跳轉(zhuǎn):
在組件內(nèi)部如下引入:
import?{?withRouter } from 'react-router'
組件拋出時(shí):
export default?withRouter(List)
組件回調(diào)函數(shù)的注冊(cè):
componentDidMount(){
?????????this.props.router.setRouteLeaveHook(
?????????????this.props.route,
?????????????this.routerWillLeave)
????}
????routerWillLeave(nextLocation) {
??????// 返回?false 會(huì)繼續(xù)停留當(dāng)前頁面极舔,
??????// 否則凤覆,返回一個(gè)字符串,會(huì)顯示給用戶拆魏,讓其自己決定
????????return?'確認(rèn)要離開盯桦?';
????}
? 數(shù)據(jù)的請(qǐng)求
在constructor和componentDidUpdate中(在進(jìn)入路由或者進(jìn)入路由的相應(yīng)組件內(nèi)部進(jìn)行數(shù)據(jù)請(qǐng)求);
withRouter進(jìn)入目標(biāo)路由或組件前渤刃,進(jìn)行數(shù)據(jù)的請(qǐng)求拥峦;
? PropTypes類型檢查
在react的15.5版本后,React.PropTypes已經(jīng)廢棄了卖子,需要單獨(dú)引入prop-types庫進(jìn)行驗(yàn)證
import?PropTypes?from 'prop-types'
class?Children extends?Component {
????constructor(props) {
????????super(props)
????}
???render() {
????????return?(
????????????<div>
????????????????<p>這里是子組件</p>
???????????????{this.props.name}
????????????</div>
????????)
????}
}
Children.propTypes={
????name:PropTypes.string
}
prop-types庫可以校驗(yàn)的類型如下:
import?PropTypes?from 'prop-types';
MyComponent.propTypes = {
??// 你可以將屬性聲明為以下?JS 原生類型
??optionalArray:?PropTypes.array,
??optionalBool:?PropTypes.bool,
??optionalFunc:?PropTypes.func,
??optionalNumber:?PropTypes.number,
??optionalObject:?PropTypes.object,
??optionalString:?PropTypes.string,
??optionalSymbol:?PropTypes.symbol,
??// 任何可被渲染的元素(包括數(shù)字略号、字符串、子元素或數(shù)組)洋闽。
??optionalNode:?PropTypes.node,
??// 一個(gè)?React 元素
??optionalElement:?PropTypes.element,
??// 你也可以聲明屬性為某個(gè)類的實(shí)例玄柠,這里使用?JS 的
??// instanceof 操作符實(shí)現(xiàn)。
??optionalMessage:?PropTypes.instanceOf(Message),
??// 你也可以限制你的屬性值是某個(gè)特定值之一
??optionalEnum:?PropTypes.oneOf(['News', 'Photos']),
??// 限制它為列舉類型之一的對(duì)象
??optionalUnion:?PropTypes.oneOfType([
????PropTypes.string,
????PropTypes.number,
????PropTypes.instanceOf(Message)
??]),
??// 一個(gè)指定元素類型的數(shù)組
??optionalArrayOf:?PropTypes.arrayOf(PropTypes.number),
??// 一個(gè)指定類型的對(duì)象
??optionalObjectOf:?PropTypes.objectOf(PropTypes.number),
??// 一個(gè)指定屬性及其類型的對(duì)象
??optionalObjectWithShape:?PropTypes.shape({
????color:?PropTypes.string,
????fontSize:?PropTypes.number
??}),
??// 你也可以在任何?PropTypes 屬性后面加上?`isRequired`
??// 后綴喊递,這樣如果這個(gè)屬性父組件沒有提供時(shí)随闪,會(huì)打印警告信息
??requiredFunc:?PropTypes.func.isRequired,
??// 任意類型的數(shù)據(jù)
??requiredAny:?PropTypes.any.isRequired,
??// 你也可以指定一個(gè)自定義驗(yàn)證器阳似。它應(yīng)該在驗(yàn)證失敗時(shí)返回
??// 一個(gè)?Error 對(duì)象而不是?`console.warn` 或拋出異常骚勘。
??// 不過在?`oneOfType` 中它不起作用。
??customProp:?function(props, propName, componentName) {
????if?(!/matchme/.test(props[propName])) {
??????return?new?Error(
????????'Invalid prop `'?+ propName + '` supplied to'?+
????????' `'?+ componentName + '`. Validation failed.'
??????);
????}
??},
??// 不過你可以提供一個(gè)自定義的?`arrayOf` 或?`objectOf`
??// 驗(yàn)證器撮奏,它應(yīng)該在驗(yàn)證失敗時(shí)返回一個(gè)?Error 對(duì)象俏讹。?它被用
??// 于驗(yàn)證數(shù)組或?qū)ο蟮拿總€(gè)值。驗(yàn)證器前兩個(gè)參數(shù)的第一個(gè)是數(shù)組
??// 或?qū)ο蟊旧硇蟮酰诙€(gè)是它們對(duì)應(yīng)的鍵泽疆。
??customArrayProp:?PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
????if?(!/matchme/.test(propValue[key])) {
??????return?new?Error(
????????'Invalid prop `'?+ propFullName + '` supplied to'?+
????????' `'?+ componentName + '`. Validation failed.'
??????);
????}
??})
};