最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼)

來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 15:24:28
文檔

前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼)

前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼):本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助?,F(xiàn)實(shí)世界有很多是以響應(yīng)式的方式運(yùn)作的,例如我們會(huì)在收到他人的提問(wèn),然后做出響應(yīng),給出相應(yīng)的回答。在
推薦度:
導(dǎo)讀前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼):本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助?,F(xiàn)實(shí)世界有很多是以響應(yīng)式的方式運(yùn)作的,例如我們會(huì)在收到他人的提問(wèn),然后做出響應(yīng),給出相應(yīng)的回答。在

本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助。

現(xiàn)實(shí)世界有很多是以響應(yīng)式的方式運(yùn)作的,例如我們會(huì)在收到他人的提問(wèn),然后做出響應(yīng),給出相應(yīng)的回答。在開(kāi)發(fā)過(guò)程中我也應(yīng)用了大量的響應(yīng)式設(shè)計(jì),積累了一些經(jīng)驗(yàn),希望能拋磚引玉。

響應(yīng)式編程(Reactive Programming)和普通的編程思路的主要區(qū)別在于,響應(yīng)式以推(push)的方式運(yùn)作,而非響應(yīng)式的編程思路以拉(pull)的方式運(yùn)作。例如,事件就是一個(gè)很常見(jiàn)的響應(yīng)式編程,我們通常會(huì)這么做:

button.on('click', () => { 
 // ...})

而非響應(yīng)式方式下,就會(huì)變成這樣:

while (true) { 
 if (button.clicked) { // ...
 }
}

顯然,無(wú)論在是代碼的優(yōu)雅度還是執(zhí)行效率上,非響應(yīng)式的方式都不如響應(yīng)式的設(shè)計(jì)。

Event Emitter

Event Emitter是大多數(shù)人都很熟悉的事件實(shí)現(xiàn),它很簡(jiǎn)單也很實(shí)用,我們可以利用Event Emitter實(shí)現(xiàn)簡(jiǎn)單的響應(yīng)式設(shè)計(jì),例如下面這個(gè)異步搜索:

class Input extends Component { 
 state = { value: ''
 }
 onChange = e => { this.props.events.emit('onChange', e.target.value)
 }
 afterChange = value => { this.setState({
 value
 })
 }
 componentDidMount() { this.props.events.on('onChange', this.afterChange)
 }
 componentWillUnmount() { this.props.events.off('onChange', this.afterChange)
 }
 render() { 
 const { value } = this.state 
 return ( <input value={value} onChange={this.onChange} />
 )
 }
}
class Search extends Component { 
 doSearch = (value) => {
 ajax(/* ... */).then(list => this.setState({
 list
 }))
 }
 componentDidMount() {
 this.props.events.on('onChange', this.doSearch)
 }
 componentWillUnmount() {
 this.props.events.off('onChange', this.doSearch)
 }
 render() {
 const { list } = this.state
 return ( <ul>
 {list.map(item => <li key={item.id}>{item.value}</li>)} </ul>
 )
 }
}

這里我們會(huì)發(fā)現(xiàn)用Event Emitter的實(shí)現(xiàn)有很多缺點(diǎn),需要我們手動(dòng)在componentWillUnmount里進(jìn)行資源的釋放。它的表達(dá)能力不足,例如我們?cè)谒阉鞯臅r(shí)候需要聚合多個(gè)數(shù)據(jù)源的時(shí)候:

class Search extends Component { 
 foo = ''
 bar = ''
 doSearch = () => {
 ajax({
 foo,
 bar
 }).then(list => this.setState({
 list
 }))
 }
 fooChange = value => { this.foo = value this.doSearch()
 }
 barChange = value => { this.bar = value this.doSearch()
 }
 componentDidMount() { this.props.events.on('fooChange', this.fooChange) this.props.events.on('barChange', this.barChange)
 }
 componentWillUnmount() { this.props.events.off('fooChange', this.fooChange) this.props.events.off('barChange', this.barChange)
 }
 render() { // ...
 }
}

顯然開(kāi)發(fā)效率很低。

Redux

Redux采用了一個(gè)事件流的方式實(shí)現(xiàn)響應(yīng)式,在Redux中由于reducer必須是純函數(shù),因此要實(shí)現(xiàn)響應(yīng)式的方式只有訂閱中或者是在中間件中。

如果通過(guò)訂閱store的方式,由于Redux不能準(zhǔn)確拿到哪一個(gè)數(shù)據(jù)放生了變化,因此只能通過(guò)臟檢查的方式。例如:

function createWatcher(mapState, callback) { 
 let previousValue = null
 return (store) => {
 store.subscribe(() => { const value = mapState(store.getState()) if (value !== previousValue) {
 callback(value)
 }
 previousValue = value
 })
 }
}const watcher = createWatcher(state => { 
 // ...}, () => { // ...})

watcher(store)

這個(gè)方法有兩個(gè)缺點(diǎn),一是在數(shù)據(jù)很復(fù)雜且數(shù)據(jù)量比較大的時(shí)候會(huì)有效率上的問(wèn)題;二是,如果mapState函數(shù)依賴上下文的話,就很難辦了。在react-redux中,connect函數(shù)中mapStateToProps的第二個(gè)參數(shù)是props,可以通過(guò)上層組件傳入props來(lái)獲得需要的上下文,但是這樣監(jiān)聽(tīng)者就變成了React的組件,會(huì)隨著組件的掛載和卸載被創(chuàng)建和銷毀,如果我們希望這個(gè)響應(yīng)式和組件無(wú)關(guān)的話就有問(wèn)題了。

另一種方式就是在中間件中監(jiān)聽(tīng)數(shù)據(jù)變化。得益于Redux的設(shè)計(jì),我們通過(guò)監(jiān)聽(tīng)特定的事件(Action)就可以得到對(duì)應(yīng)的數(shù)據(jù)變化。

const search = () => (dispatch, getState) => { 
 // ...}const middleware = ({ dispatch }) => next => action => { 
 switch action.type { case 'FOO_CHANGE': case 'BAR_CHANGE': { const nextState = next(action) // 在本次dispatch完成以后再去進(jìn)行新的dispatch
 setTimeout(() => dispatch(search()), 0) return nextState
 } default: return next(action)
 }
}

這個(gè)方法能解決大多數(shù)的問(wèn)題,但是在Redux中,中間件和reducer實(shí)際上隱式訂閱了所有的事件(Action),這顯然是有些不合理的,雖然在沒(méi)有性能問(wèn)題的前提下是完全可以接受的。

面向?qū)ο蟮捻憫?yīng)式

ECMASCRIPT 5.1引入了getter和setter,我們可以通過(guò)getter和setter實(shí)現(xiàn)一種響應(yīng)式。

class Model { 
 _foo = ''
 get foo() { return this._foo
 }
 set foo(value) { this._foo = value this.search()
 }
 search() { // ...
 }
}// 當(dāng)然如果沒(méi)有g(shù)etter和setter的話也可以通過(guò)這種方式實(shí)現(xiàn)class Model { 
 foo = ''
 getFoo() { return this.foo
 }
 setFoo(value) { this.foo = value this.search()
 }
 search() { // ...
 }
}

Mobx和Vue就使用了這樣的方式實(shí)現(xiàn)響應(yīng)式。當(dāng)然,如果不考慮兼容性的話我們還可以使用Proxy。

當(dāng)我們需要響應(yīng)若干個(gè)值然后得到一個(gè)新值的話,在Mobx中我們可以這么做:

class Model { 
 @observable hour = '00'
 @observable minute = '00'
 @computed get time() { return `${this.hour}:${this.minute}`
 }
}

Mobx會(huì)在運(yùn)行時(shí)收集time依賴了哪些值,并在這些值發(fā)生改變(觸發(fā)setter)的時(shí)候重新計(jì)算time的值,顯然要比EventEmitter的做法方便高效得多,相對(duì)Redux的middleware更直觀。

但是這里也有一個(gè)缺點(diǎn),基于getter的computed屬性只能描述y = f(x)的情形,但是現(xiàn)實(shí)中很多情況f是一個(gè)異步函數(shù),那么就會(huì)變成y = await f(x),對(duì)于這種情形getter就無(wú)法描述了。

對(duì)于這種情形,我們可以通過(guò)Mobx提供的autorun來(lái)實(shí)現(xiàn):

class Model { 
 @observable keyword = ''
 @observable searchResult = [] constructor() {
 autorun(() => { // ajax ...
 })
 }
}

由于運(yùn)行時(shí)的依賴收集過(guò)程完全是隱式的,這里經(jīng)常會(huì)遇到一個(gè)問(wèn)題就是收集到意外的依賴:

class Model { 
 @observable loading = false
 @observable keyword = ''
 @observable searchResult = [] constructor() {
 autorun(() => { if (this.loading) { return
 } // ajax ...
 })
 }
}

顯然這里loading不應(yīng)該被搜索的autorun收集到,為了處理這個(gè)問(wèn)題就會(huì)多出一些額外的代碼,而多余的代碼容易帶來(lái)犯錯(cuò)的機(jī)會(huì)。 或者,我們也可以手動(dòng)指定需要的字段,但是這種方式就不得不多出一些額外的操作:

class Model { 
 @observable loading = false
 @observable keyword = ''
 @observable searchResult = []
 disposers = []
 fetch = () => { // ...
 }
 dispose() { this.disposers.forEach(disposer => disposer())
 } constructor() { this.disposers.push(
 observe(this, 'loading', this.fetch),
 observe(this, 'keyword', this.fetch)
 )
 }
}class FooComponent extends Component { 
 this.mode = new Model()
 componentWillUnmount() { this.state.model.dispose()
 } // ...}

而當(dāng)我們需要對(duì)時(shí)間軸做一些描述時(shí),Mobx就有些力不從心了,例如需要延遲5秒再進(jìn)行搜索。

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

本文如未解决您的问题请添加抖音号:51dongshi(抖音搜索懂视),直接咨询即可。

文檔

前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼)

前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼):本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于前端響應(yīng)式編程的方案及其缺點(diǎn)的詳細(xì)介紹(附代碼),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助?,F(xiàn)實(shí)世界有很多是以響應(yīng)式的方式運(yùn)作的,例如我們會(huì)在收到他人的提問(wèn),然后做出響應(yīng),給出相應(yīng)的回答。在
推薦度:
標(biāo)簽: 代碼 具體的 的代碼
  • 熱門焦點(diǎn)
專題
Top

抖音扫码关注

手机端二维码

每天分享百科知识!