umiJS
- 插件化
- 開箱即用
- 約定式路由
- https://umijs.org/zh-CN
- npm i -g umi //全局安裝一下就可以umi dev 開發(fā)使用了
umiJS 有強(qiáng)的約束力
- src/pages,其實(shí)也可以直接pages特愿,習(xí)慣上還是建議寫上src
- pages 強(qiáng)約定了放頁面蟆盐,pages/page1历筝,pages/page2脓鹃,這樣命名的組建奋姿,路由就直接是這樣了
- 首頁的話就直接是 pages/index锄开,強(qiáng)約束。
- umijs 里面還配置了很多內(nèi)置的腳本称诗,可以直接開發(fā) umi dev 就可以開發(fā)了萍悴,什么都不用配置
- 包括一些熱拔插,熱啟動(dòng)的功能
- umi dev 編譯出.umi 文件夾寓免,可以讓上面的pages/page1癣诱,pages/page2,直接打路由可以訪問
打包 umi build
- 像 umi dev 一樣爽
兩種路由使用方法
- umijs有兩種路由使用方法
- 約定式路由:約定好的文件夾和文件袜香,來代表頁面撕予,umi會(huì)根據(jù)開發(fā)者書寫好的頁面,生成路由配置困鸥;最終會(huì)變成配置式路由嗅蔬。
- 配置式路由:直接書寫路由配置文件
約定式路由
- umi約定,工程中的pages文件夾中存放的是頁面疾就。如果工程包含src目錄澜术,則src/pages是頁面文件夾唉
- umi約定,頁面的文件名字猬腰,和頁面的路徑鸟废,就是頁面的路由
- 舉例:pages/a -> 路由就是/a,pages/son/a -> 路由就是/son/a姑荷,非常簡單盒延,非常爽
- umi約定缩擂,如果的頁面文件名字是index.js,pages/index.js -> 首頁路由 /
- 子頁面的首頁也是 pages/son/index.js
- 注意避免文件名和當(dāng)前目錄文件夾名字一樣添寺。否則沖突無法匹配胯盯。
- umi約定,如果src/layouts目錄存在计露,則該目錄中的index.js表示全局通用的布局博脑,布局中的props.children則表示添加具體的頁面。切換別的路由都會(huì)有這個(gè)通用的布局票罐。
- umi約定叉趣,如果pages文件夾中,包含_layout.js该押,則它所在目錄以及其所有的子目錄中的頁面疗杉,公用該布局。
- 404約定蚕礼,pages/404.js中烟具,表示404頁面,匹配不到路由跳到該頁面闻牡;開發(fā)模式中無效
路由跳轉(zhuǎn)
- umi/Link净赴,使用和NavLink 完全一樣,其實(shí)就是react-router-dom中的Link
- NavLink罩润, 其實(shí)就是react-router-dom 中的 NavLink
- 跳轉(zhuǎn)鏈接:導(dǎo)入 umi/Link玖翅、umi/NavLink
- 代碼、組件中跳轉(zhuǎn)割以,導(dǎo)入umi/router金度,它是個(gè)對象,就是history的一些方法
- @表示src目錄
獲取路由信息
- 所有的頁面严沥、布局組件猜极,都會(huì)通過屬性props,收到下面的屬性
- match -> react-router的match
- history -> react-router的match
- match
- location
- route 路由對應(yīng)的配置
- 如果要使用pathname之類的消玄,請使用withRouter
- $id.js 文件名約定參數(shù)跟伏,會(huì)傳到組件的props.match.params.id, 就react的動(dòng)態(tài)路由/:id
-
.js 參數(shù)就可選了
-
id$.js -> /:name/:id
配置式路由
- 當(dāng)使用配置式路由的時(shí)候,約定式路由會(huì)失效翩瓜,有兩種真實(shí)用方式
- 項(xiàng)目根目錄下的文件.umirc.js受扳,他是umijs的配置,里面有個(gè)routes:[]
export default {
routes:[
{
title:"首頁"
path:"/"
component: "./index" //相對于pages路徑配置的兔跌,pages/index
exact:false
routes:[
//包子路由 {...}
".src/routes/Private.js", ".src/routes/b.js" //這種可以實(shí)現(xiàn)路由權(quán)限
]
}
]
}
- 每個(gè)路由配置都可以添加任何屬性
- 項(xiàng)目根目錄下的config/config.js
- 看官網(wǎng)
使用dva
- dva模型勘高,作為一個(gè)umi的一個(gè)插件
- dva 是管理redux的插件
- 官方插件集 umi-piugin-react
- 使用umi插件在.umirc.js
- dva插件和umi整合后,將模型分為兩種:
- 全局模型:所有頁面通用,工程一開始啟動(dòng)后华望,模型就會(huì)掛載到倉庫
- 定義全局模型蕊蝗,約定在src/models 目錄下,模型的命名空間和文件名一致
// dva 寫法赖舟,組件connect 之后蓬戚,dispatch({ type:"counter/delete" })
export default {
state:0, // counter.js
reducers:{
delete(state){ // type:"counter/delete"
return state - 1
},
add(state){
return state + 1
}
}
effects: {
*update(action, sageEffects){
// 這里是一些邏輯
// 副作用處理用 sageEffects 指令
}
}
}
- 局部模型:只能被某些頁面使用,訪問具體的頁面時(shí)才會(huì)掛載到倉庫
- 定義局部模型建蹄,約定是pages文件夾下碌更,在哪里定義,哪里的頁面就會(huì)動(dòng)態(tài)加載該模型
- 開發(fā)模式全都能看見的洞慎,局部模型有點(diǎn)像多組件的state的上提,但是嘿棘,它是單鏈?zhǔn)降木⑼龋芟蛏险?/li>
- 單model.js,
樣式
- 保證類樣式名稱的唯一性:css-module / BEM
- umi 使用了webpack鸟妙,的css-loader加載器焦人,內(nèi)部包含了css-module
- css文件 -> css-module -> 對象,導(dǎo)入的時(shí)候直接用導(dǎo)入的對象的key
- import styles from "./a.css"
- 不共享樣式通常放同一個(gè)目錄即可
- 共享放assets/css文件夾中重父,盡量以button.css 這樣子放會(huì)好點(diǎn)花椭,維護(hù)性好
- 樣式代碼的重復(fù) less
- less 天生支持,less文件 -> less-loader -> css代碼 -> css-loader(開啟了module) -> 對象
代理和數(shù)據(jù)模擬
- 代理
- 一般用于解決development模式的跨域
- .umirc.js 配置proxy
- 數(shù)據(jù)模擬
- 解決前后端協(xié)同開發(fā)的問題房午,umi是一個(gè)企業(yè)級別的腳手架矿辽,什么都做好了,開箱即用
- mock模塊郭厌,模擬數(shù)據(jù)袋倔,無需管后端
umi 約定的mock
- mock文件夾中的文件
- src/pages文件夾中的_mock.js文件
- 以上兩種js文件,均會(huì)被umijs讀取折柠,并作為數(shù)據(jù)模擬的配置
- 開發(fā)完把文件名字一改宾娜,就可以了。
- 甚至可以自行發(fā)揮扇售,添加模擬數(shù)據(jù)前塔,mockjs庫
- 建議使用mockjs庫,方便快捷; http://mockjs.com/
dva
- https://dvajs.com/guide/
- 寫業(yè)務(wù)代碼的時(shí)候action 和 reducers 模版代碼太重了承冰,dva就很好的把這兩個(gè)東西激進(jìn)的整合起來了华弓。
- dva不僅僅是一個(gè)第三方庫,更是一個(gè)框架巷懈。它主要整合了redux的相關(guān)內(nèi)容该抒,讓我們方便處理數(shù)據(jù)
- dva 依賴 react、react-router、redux凑保、redux-saga冈爹、react-redux、connected-react-router
- dva 把這些東西都整合起來欧引,使用起來非常舒服
- https://dvajs.com/guide/concepts.html#數(shù)據(jù)流向
初始化dva
- model是dva的最核心的東西频伤。
- dva 配置文件內(nèi)容東西也很多,https://dvajs.com/api/#輸出文件
import React from "react"
import dva from "dva"
const App = (props) => {
return(
<div>
網(wǎng)站根App
</div>)
}
// 得到一個(gè)dva對象
const app = dva()
// 啟動(dòng)之前定義一個(gè)模型芝此,redux-saga/action/reducer 整合在一起
// 必須在啟動(dòng)之前
app.model({
namespace:"student", //不能缺省憋肖,倉庫名。
state:{ //默認(rèn)值
total:0,
datas:[]
},
reducers:{ //reducer婚苹,dva會(huì)自動(dòng)合并
// dva 約定岸更,方法的名字,就是具體action type
// 組件使用dva下的connect鏈接膊升,跟以前一樣的用法
delete(state, action){
//以前同步的邏輯
return {...state} //注意 immer 數(shù)據(jù)寫法
},
},
effects:{ //副作用怎炊,底層redux-saga,方法的名字就是觸發(fā)的action
/**
* @param {*} action
* @param {*} sagaEffect redux-saga 對象
* {
* put({type:'delete'}) //重新觸發(fā)自己的action廓译,不用加前綴
* ...還有很多方法评肆,查文檔
* }
*/
*asyncAdd(action, sagaEffect){
// 這里推薦ES6 開發(fā),阮老師的ES6
// https://es6.ruanyifeng.com/#docs/generator
}
},
/**
* 每個(gè)對象是一個(gè)函數(shù)非区,訂閱者瓜挽。
* 加入store的時(shí)候執(zhí)行一次
*/
subscriptions:{
func1(obj){
//處理一些訂閱事件,因?yàn)樗贿\(yùn)行一次征绸,非常適合訂閱一些事件
//比如注冊一些特別的事件
//obj.dispatch({type:'delete'})
}
}
})
// 設(shè)置啟動(dòng)后的根路由,
app.router(() => <App/>)
// 該函數(shù)傳入一個(gè)選擇器久橙,相當(dāng)于document.getElementById("root")
app.start("#root")
組件使用dva
import React from "react"
import { connect } from "dva"
const Test = (props) => {
return(
<div>
網(wǎng)站根App
</div>)
}
const mapStateToProps = (state) => ({
total:state.student.total
});
const mapDispatchToProps = (dispatch) => ({
// 傳入的props方法以on開頭。注意加命名空間
onDelete:() => dispatch({type:"student/delete", playload:1})
})
export default connect(mapStateToProps, mapDispatchToProps)(Test)
Generator 函數(shù)的語法
- https://es6.ruanyifeng.com/#docs/generator
- Generator 生成器
- 由構(gòu)造函數(shù) Generator 創(chuàng)建的對象歹垫,該對象即是一個(gè)迭代器剥汤,同時(shí),又是一個(gè)可迭代對象
// 偽代碼排惨,Generator 是js引擎的用的吭敢,外部不能用
// 注意不能箭頭函數(shù),箭頭函數(shù)沒有this
var g = new Generator()
// 迭代器
g.next()
// 它也是一個(gè)可迭代對象
// for of
var iterator = g[Symbol.iterator];
- 生成器函數(shù)暮芭,該函數(shù)用于創(chuàng)建一個(gè)生成器
//function *func1
function* create(){
}
//生成器函數(shù)create調(diào)用后鹿驼,返回的一定是一個(gè)生成器,而函數(shù)體里面的代碼不會(huì)執(zhí)行
//函數(shù)體的代碼是生成器控制執(zhí)行的
var g = create()
- 每當(dāng)調(diào)用一次生成器的next方法辕宏,生成器的函數(shù)體會(huì)從上次的yield的位置(或開始位置)運(yùn)行到下一個(gè)yield畜晰, yield只能在生成器函數(shù)里面使用。
- yield 表達(dá)式返回的數(shù)據(jù)瑞筐,會(huì)當(dāng)做當(dāng)前迭代的數(shù)據(jù)凄鼻,提前return 會(huì)提前done:true
- 生成器的返回值,是結(jié)束后的返回值,一旦結(jié)束后繼續(xù)next()块蚌,
- 都是{ value:undefined, done:false }
function* create(){
console.log("kaishi")
yield; //g.next() -> { value:undefined, done:false }
yield 1; //g.next() -> { value:1, done:false }
return 2; //g.next() -> { value:2, done:true }
}
var g = create()
- 生成器調(diào)用next的時(shí)候闰非,可以傳遞參數(shù),該參數(shù)會(huì)作為生成器函數(shù)體上一次yield表達(dá)式的值
- 注意 yield getPromise()峭范;不管getPromise邏輯怎么樣财松,返回的啥,yield得到的就是啥
function* create(){
console.log("開始")
//將 1 作為這次的迭代結(jié)果纱控,等待第二次next辆毡,result才是1
//每次會(huì)卡在yield等待你 next() 走
//yield 受外面控制的,next() 才往下走甜害,參數(shù)是yield的返回值
let result = yield 1; // 等待舶掖,我的返回值是上次next(b)中的b
console.log("yield 1", result) // yield 1, b
result = yield 2; // 等待,我的返回值是上次next(c)中的c
console.log("yield 2", result) // yield 2, c
result = yield 3; // 等待唾那,我的返回值是上次next(d)中的d
console.log("yield 3", result) // yield 3, d
//yield + 1 = 4 次就完成了访锻,后面再next都是 { value:undefined, done:true }
}
var g = create()
g.next("a") // 第一次傳參無任何意義 { value:1, done:false }
g.next("b") // { value:2, done:false }
g.next("c") // { value:3, done:false }
g.next("d") // { value:undefined, done:true }
- 如果生成器函數(shù)嵌套生成器函數(shù),如果單單只是寫執(zhí)行闹获,沒有任何作用
function* g2(){}
function* create(){
console.log("開始")
let result = yield 1; // 等待,我的返回值是上次next(b)中的b
console.log("yield 1", result) // yield 1, b
result = yield 2; // 等待河哑,我的返回值是上次next(c)中的c
g2(); //沒有任何反映避诽,想下為什么?
result = yield* g2() //這樣才會(huì)進(jìn)入這個(gè)生成器璃谨。result是g2的返回值
console.log("yield 2", result) // yield 2, c
result = yield 3; // 等待沙庐,我的返回值是上次next(d)中的d
console.log("yield 3", result) // yield 3, d
//yield + 1 = 4 次就完成了,后面再next都是 { value:undefined, done:true }
}
var g = create()
g.next("a")
g.next("b")
g.next("c")
g.next("d")
redux-saga
- redux-saga 就是基于上面的生成器做的
- 純凈佳吞,強(qiáng)大拱雏,靈活
- https://redux-saga-in-chinese.js.org/
- 最開始的時(shí)候會(huì)啟動(dòng)一個(gè)saga任務(wù),其實(shí)就是生成器函數(shù)底扳,因?yàn)橥獠靠煽刂郑挥玫鹊絘ction
- saga任務(wù)提供了很多任務(wù)指令,可以通過指令控制它的運(yùn)行衷模,說白了就是通過指令來控制redux流程
- sage任務(wù)就是一個(gè)生成器函數(shù)
function* task(){ console.log('啟動(dòng)了') }
const sagaMid = createSagaMiddleware()
// store 后面運(yùn)行
sagaMid.run(task) //啟動(dòng)了生成器鹊汛,生成器就受外面控制了
- 一般會(huì)把生成器函數(shù)放在saga文件夾
function* task(){
console.log('啟動(dòng)了')
yield 3; // yield 普通數(shù)字沒什么意義,會(huì)立即調(diào)用阱冶,同步代碼一樣
yield `saga effects`; //放的是saga指令刁憋,會(huì)特殊處理,控制整個(gè)流程
}
- saga effects
- 再次強(qiáng)調(diào)木蹬,saga中至耻,yield 后面如果是個(gè)Promise,saga會(huì)等待完成把值返回到下次next
- 如果Promise reject 失敗了,會(huì) throw 出錯(cuò)誤尘颓,要try catch 一下
- 指令前面必須使用yield走触,才會(huì)被saga控制
- 每個(gè)指令本質(zhì)上就是一個(gè)函數(shù),該函數(shù)調(diào)用后泥耀,會(huì)返回一個(gè)指令對象饺汹,saga 會(huì)接受指令特殊處理
- {value:'saga effects', done:false},內(nèi)部是這樣處理的
- take指令:監(jiān)聽某個(gè)action痰催,只監(jiān)聽一次兜辞。yield 得到的是完整的action對象,take(actionTypes.a)
- all:等待多個(gè)生成器夸溶,完成才會(huì)下一步 all([task1(), task1()])
- takeEvery:死循環(huán) take指令逸吵;takeEvery(actionTypes.a, task1())
- delay: 阻塞action,指定毫秒數(shù)
- put:重新觸發(fā)action缝裁,相當(dāng)于一個(gè)dispatch扫皱;put(action())
- call:用于副作用函數(shù)調(diào)用,通常是異步的
- apply:和call作用完全一樣
- select:用于得到當(dāng)期倉庫的數(shù)據(jù)
- cps: 用于傳統(tǒng)的回調(diào)寫法捷绑,node.js標(biāo)準(zhǔn)風(fēng)格的異步回調(diào)
- 更多的指令還是得看文檔
function* task(){
console.log('啟動(dòng)了')
yield 3; // yield 普通數(shù)字沒什么意義韩脑,會(huì)立即調(diào)用,同步代碼一樣
yield takeEvery(actionTypes.a, task()); //放的是saga指令粹污,會(huì)特殊處理段多,控制整個(gè)流程
}
react hook
- 原理:
- hook只能使用在函數(shù)組件
- 一個(gè)函數(shù)組件可以有很多hook,包括useState的hook壮吩,這樣非常有利于橫切關(guān)注點(diǎn)进苍。
useState:
- 運(yùn)行函數(shù)組件時(shí),調(diào)用useState(第一次運(yùn)行)
- 檢查一個(gè)狀態(tài)表格
- 狀態(tài)表格無內(nèi)容
- 使用默認(rèn)值在表格創(chuàng)建一個(gè)狀態(tài)
- 將狀態(tài)加入數(shù)組(多個(gè)時(shí)候就加多個(gè))
- 重新渲染界面又調(diào)用一次
- 狀態(tài)表格有內(nèi)容(不需要重新創(chuàng)建了)
- 忽略默認(rèn)值鸭叙,直接得到狀態(tài)
- 表格附著在函數(shù)組件上的觉啊,所以不會(huì)共享狀態(tài)(各有各的狀態(tài)表格)
- 注意細(xì)節(jié):
- useState最好寫在函數(shù)起始位置,方便維護(hù)
- useState不能出現(xiàn)在判斷條件if里面沈贝,這樣對維護(hù)表格不利(react根本不讓這么寫)杠人。
- useState返回的第二項(xiàng),引用不變缀程,節(jié)省內(nèi)存
- 如果使用函數(shù)改變數(shù)據(jù)搜吧,若數(shù)據(jù)和之前的數(shù)據(jù)完全相等(使用Object.is),不會(huì)導(dǎo)致重新渲染杨凑。
- 使用函數(shù)改變數(shù)據(jù)滤奈,傳入的值不會(huì)和原來的合并,而是直接替換撩满。(setState是混合的蜒程,可以局部修改)
- 也不能直接修改數(shù)據(jù)绅你,和之前類組件一樣。
- 如果要強(qiáng)制刷新組件
- 強(qiáng)制刷新組件昭躺,類組件用forceUpdate()
- useState直接set一個(gè)空對象就行了,因?yàn)樗歉采w
- 和類組件一樣忌锯,組件中改變狀態(tài)可能是異步的(在dom事件中),多個(gè)狀態(tài)會(huì)合并提高效率领炫,此時(shí)偶垮,不能 信任之前的狀態(tài),應(yīng)該使用回調(diào)函數(shù)的方式帝洪。
如果某些狀態(tài)之間沒有必然的聯(lián)系似舵,應(yīng)該切分為不同的狀態(tài),而不要合并成一個(gè)狀態(tài) 因?yàn)榇a耦合度越低葱峡,越好維護(hù)
Effect Hook:用于函數(shù)組件中處理副作用
- useEffect:接受一個(gè)參數(shù)砚哗,是函數(shù)
- 副作用:ajax,計(jì)時(shí)器砰奕,更改dom對象蛛芥,以及其它會(huì)對外部產(chǎn)生影響的部分。類組件的時(shí)候军援,涉及服務(wù)端渲染仅淑,除了DidMount,DidUpdate,willUnMount以外其它都會(huì)渲染兩次,盡管react反復(fù)強(qiáng)調(diào)只能在三個(gè)生命周期使用胸哥,但是很多開發(fā)者不遵守漓糙,直接出Effect Hook了。
細(xì)節(jié)
- useEffect運(yùn)行的時(shí)間點(diǎn)是頁面真實(shí)dom渲染完成后烘嘱。因此它是異步執(zhí)行的,并且不會(huì)阻塞瀏覽器
- 相當(dāng)于類組件的生命鉤子DidMount和DidUpdate蝗蛙,掛載完和更新完
- 但是又有區(qū)別的蝇庭,類組件的兩個(gè)生命鉤子更改了真實(shí)的dom,但是用戶沒有看到UI界面捡硅,同步的
- useEffect中的副作用函數(shù)哮内,更改了真實(shí)的dom,并且用戶已經(jīng)看到了UI更新壮韭,異步的北发。不會(huì)阻塞界面
- 每個(gè)函數(shù)組件中可以多次使用,但是也不能放在判斷或循環(huán)等代碼塊中喷屋,和useState一樣琳拨,后面的hook基本一致
- useEffect是有返回值的,返回的是一個(gè)清理函數(shù)屯曹,函數(shù)的運(yùn)行時(shí)間點(diǎn)是每次運(yùn)行副作用的函數(shù)之前狱庇,首次渲染組件不會(huì)運(yùn)行惊畏。組件銷毀時(shí),一定會(huì)運(yùn)行密任。
- 重新渲染組件的時(shí)候颜启,不想重新運(yùn)行副作用函數(shù),可以使用第二個(gè)參數(shù)浪讳,這個(gè)參數(shù)是數(shù)組缰盏,然后副作用函數(shù)就依賴這個(gè)數(shù)組,只有依賴的數(shù)據(jù)和上一次不一樣時(shí)淹遵,才會(huì)運(yùn)行執(zhí)行第一個(gè)副作用函數(shù)參數(shù)口猜。
- 所以當(dāng)傳遞了依賴數(shù)據(jù)之后,如果數(shù)據(jù)沒有變化 8.1 副作用函數(shù)僅在第一次渲染后運(yùn)行 8.2 清理函數(shù)僅在卸載組件后運(yùn)行
- 所以合呐,就可以利用useEffect來實(shí)現(xiàn)之前類組件的掛載運(yùn)行一次暮的,卸載時(shí)運(yùn)行一次。(特別是監(jiān)聽移除dom事件)
- 如果依賴項(xiàng)使用的是空數(shù)組淌实,清理函數(shù)就沒有作用了冻辩,但是卸載還是會(huì)運(yùn)行一次
- 副作用函數(shù)中,如果使用了函數(shù)組件上下文的變量拆祈,由于閉包的影響重贺,會(huì)導(dǎo)致副作用函數(shù)中變量不會(huì)實(shí)時(shí)變化。js的知識(shí)點(diǎn)伏恐。
- 如果副作用函數(shù)在每次注冊時(shí)亿傅,會(huì)覆蓋之前的副作用函數(shù),因此淤年,盡量保持副作用函數(shù)穩(wěn)定钧敞,否則控制起來會(huì)比較復(fù)雜。(把副作用函數(shù)抽出去寫麸粮,動(dòng)態(tài)改變)
自定義hook:將一些常用的溉苛、跨組件的hook功能,抽離出去形成一個(gè)函數(shù)弄诲,該函數(shù)就是自定義hook
- 例如1:很多組件都需要在第一次加載完成之后愚战,獲取所有XXX的數(shù)據(jù)。
- 放在以前類組件齐遵,只能在掛載完成DidMount請求數(shù)據(jù)寂玲,或者redux。
- 以前的一些做法:
- render props 數(shù)據(jù)一樣梗摇,界面不一樣拓哟,其實(shí)就是傳遞一個(gè)render函數(shù)下去運(yùn)行。
- withComponent 高階組件就是把相同的邏輯抽出來留美。
- 自定義hook細(xì)節(jié):
- 函數(shù)名必須以use開頭
- 調(diào)用自定義hook函數(shù)時(shí)彰檬,應(yīng)該放到頂層
- 自定義hook其實(shí)就是把hook抽成一個(gè)函數(shù)出去伸刃,多個(gè)hook就可以抽多個(gè),橫切關(guān)注點(diǎn)逢倍,用組合compose編程捧颅。
- render props 數(shù)據(jù)一樣,界面不一樣较雕,其實(shí)就是傳遞一個(gè)render函數(shù)下去運(yùn)行碉哑。withComponet 高階組件就是把相同的邏輯抽出來。
reducer hook
- 該函數(shù)接受兩個(gè)參數(shù)亮蒋,一個(gè)是state扣典,和action,和redux那一套一樣慎玖。調(diào)用store.dispatch分發(fā)action返回變化后的數(shù)據(jù)贮尖。
// redux:action -> store.dispatch(action.type) -> reducer -> newState -> store
// 通用的reducer hook, 其實(shí)就是一個(gè)store
export default function useReducer(reducer, initState, initFunc) {
// initFunc是一個(gè)對第二個(gè)參數(shù)進(jìn)行計(jì)算的一個(gè)回調(diào)函數(shù)
const [state, setState] = useState(initFunc ? initFunc(initState) : initState)
// 相當(dāng)與store.dispatch
function dispatch(action){
const newState = reducer(state, action)
setState(newState)
}
return [state, dispatch]
}
// reducer hook 未來可能會(huì)結(jié)合redux使用
Context hook
- 獲取上下文數(shù)據(jù),之前的React.createContext()消費(fèi)者也要套一層趁怔。用這個(gè)hook就可以直接返回ctx的值
callback hook
- 用于得到一個(gè)固定引用值的函數(shù)湿硝,通常用它來優(yōu)化性能
- 該函數(shù)有兩個(gè)參數(shù):
- 函數(shù),useCallback會(huì)固定該函數(shù)的引用润努,只要依賴項(xiàng)沒發(fā)生變化則會(huì)保持之前函數(shù)的地址
- 依賴項(xiàng)关斜,也是個(gè)數(shù)組
- 該函數(shù)返回值:引用相對固定的函數(shù)地址
Memo hook
- 用于保持一些比較穩(wěn)定的數(shù)據(jù),通常用于性能優(yōu)化铺浇,用法和callback一樣痢畜,只是callback只能固話一個(gè)引用返回。Memo功能強(qiáng)大些鳍侣,可以固化更多東西丁稀。
- 可以讓一些穩(wěn)定的,高開銷的渲染數(shù)據(jù)避免掉沒有必要的渲染倚聚。
- 比如說list數(shù)據(jù)不想受點(diǎn)擊事件避免不了的重新渲染二驰,就可以用Memo
- 為什么會(huì)出Memo callback這樣的API,因?yàn)楹瘮?shù)組件是普通函數(shù)秉沼,它總是被重新渲染,不像類組件那樣有些生命鉤子只渲染一次矿酵。
Ref hook 一個(gè)參數(shù):默認(rèn)值唬复,返回一個(gè)固定的對象。{current:值}
imperativeHandle Hook
useImperativeHandle(ref, () => {
//如果不給依賴項(xiàng)全肮,則每次運(yùn)行函數(shù)組件都會(huì)調(diào)用該函數(shù)
//如果使用了依賴項(xiàng)敞咧,則第一次調(diào)用后,則進(jìn)行緩存辜腺,依賴項(xiàng)變化才重新運(yùn)行
<!-- ref.current = 1 -->
return 1
}, [])
// 這個(gè)hook休建,主要是用來使用ref轉(zhuǎn)發(fā)的
LayoutEffect Hook
- useEffect 是在瀏覽器渲染之后運(yùn)行的乍恐,如果用它操作真實(shí)的dom,渲染頁面有時(shí)候會(huì)閃爍测砂,卡屏
- 真實(shí)渲染之前做一些改動(dòng)茵烈,useLayoutEffect: 完成dom改動(dòng),還沒有呈現(xiàn)給用戶砌些。用法和useEffect一樣呜投。
細(xì)節(jié)
- 但是應(yīng)該盡量的使用useEffect,因?yàn)樗粫?huì)阻塞頁面渲染存璃,如果出現(xiàn)了問題仑荐,再考慮使用useLayoutEffect
antd pro
- 整合了很多前端的技術(shù)
- 阿里的最佳實(shí)踐,基于umi再次整合
- React/umi/dva/antd