成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

把react什么的用起來——我不是雙向綁定

移動(dòng)開發(fā)
react強(qiáng)調(diào)的是單向數(shù)據(jù)流,但即便是單向數(shù)據(jù)流也總要有個(gè)數(shù)據(jù)的來源,如果數(shù)據(jù)來源于頁面自身上的用戶輸入,那效果也就等同于雙向綁定了。

[[163973]]

先弄個(gè)什么例子呢?如果是現(xiàn)代的MVVM框架,可能會(huì)用雙向綁定來吸引你。那react有雙向綁定嗎?

沒有。

也算是有吧,有插件。不過雙向綁定跟react不是一個(gè)路子的。react強(qiáng)調(diào)的是單向數(shù)據(jù)流。 當(dāng)然,即便是單向數(shù)據(jù)流也總要有個(gè)數(shù)據(jù)的來源,如果數(shù)據(jù)來源于頁面自身上的用戶輸入,那效果也就等同于雙向綁定了。

下面就展示一下如何達(dá)到這個(gè)效果。我們來設(shè)計(jì)一個(gè)登錄的場(chǎng)景,用戶輸入用戶名后,會(huì)在問候語的位置展示用戶名,像下圖這樣:

預(yù)警一下先,我要用這個(gè)小東西展示react+redux的數(shù)據(jù)流工作方式,所以代碼看起來比較多, 肯定比一些MVVM框架雙向綁定一對(duì)雙大括號(hào)代碼要多得多。但正如我前面說的,它倆不是一個(gè)路子, react這種模式的好處后面你一定會(huì)看出來,這里先耐著性子把這幾段貌似很羅嗦的代碼看完。 react和redux很多重要的思想在這就開始提現(xiàn)出來了。

先把組件寫出來。為了簡(jiǎn)便,我們把整個(gè)登錄頁面作為一個(gè)組件,放在containers目錄下。 還記得前面說過containers和components目錄嗎?把組件放在containers目錄下,意味著這個(gè)組件要跟外界打交道。 不過一開始,我們先別管打交道的事兒,就寫一個(gè)簡(jiǎn)單的,普通的組件:

  1. import React from 'react' 
  2.  
  3. class Login extends React.Component{ 
  4.   render(){ 
  5.     return ( 
  6.       <div> 
  7.         <div>早上好,{this.props.username}</div> 
  8.         <div>用戶名:<input/></div> 
  9.         <div>密 碼:<input type="papssword"/></div> 
  10.         <button>登錄</button> 
  11.       </div> 
  12.     ) 
  13.   } 
  14.  
  15. export default Login 

為了能讓我們寫的東西顯示出來,得改點(diǎn)模板代碼,現(xiàn)在來修改一下src/index.js,里面原來的代碼都不需要了,改成:

  1. import React from 'react'
  2. import { render } from 'react-dom'
  3. import { Provider } from 'react-redux'
  4. import configureStore from '../stores'
  5. import Login from '../containers/Login'
  6.  
  7. const store = configureStore(); 
  8.  
  9. render( 
  10.   <Provider store={store}> 
  11.     <Login /> 
  12.   </Provider>, 
  13.   document.getElementById('app'
  14. ); 

搭建環(huán)境時(shí)自動(dòng)打開的瀏覽器頁面還沒關(guān)吧?保存代碼后少等片刻就可以看到我們做的登陸頁面了。

目前這個(gè)登錄組件里問候語里顯示的用戶名和用戶輸入的用戶名毫無關(guān)系,如何將它們聯(lián)系起來呢? 既然看到了{(lán)this.props.username}你肯定會(huì)想到有一個(gè)數(shù)據(jù)模型。的確是有這么個(gè)東西,不過在redux里, 這個(gè)數(shù)據(jù)模型很壯觀,整個(gè)應(yīng)用只有一個(gè)數(shù)據(jù)模型,所以更應(yīng)該管它叫數(shù)據(jù)倉(cāng)庫(kù)。這個(gè)倉(cāng)庫(kù)的代碼在stores/index.js里面。 代碼很簡(jiǎn)單,就是用reducers和initialState兩個(gè)參數(shù)來創(chuàng)建一個(gè)倉(cāng)庫(kù)。看剛才run.js里面的代碼, 有個(gè)叫Provider的組件使用了倉(cāng)庫(kù),意思很明顯:在provider這個(gè)組件內(nèi)部,已經(jīng)給我們提供好了倉(cāng)庫(kù)的訪問條件, 也就是說我們的Login組件已經(jīng)可以訪問倉(cāng)庫(kù)了。怎么訪問呢?需要把我們的組件跟倉(cāng)庫(kù)連接起來。 登錄組件代碼***一行“export default Login”要改成這樣:

  1. function mapStateToProps(state) { 
  2.   return {} 
  3. export default connect(mapStateToProps)(Login); 

connect是react-redux這個(gè)庫(kù)提供的函數(shù),功能就是把組件連接到rudux的倉(cāng)庫(kù)。注意在文件頂部加上一句“import { connect } from 'react-redux'”。 這里有個(gè)函數(shù)mapStateToProps,它返回的對(duì)象就是從倉(cāng)庫(kù)取出的數(shù)據(jù),具體的數(shù)據(jù)等我們寫完reducer再補(bǔ)充。

那么reducer是什么呢?

我們考慮一下倉(cāng)庫(kù)的數(shù)據(jù)是要變化的,怎么讓它變化呢?我們得給個(gè)規(guī)則,這個(gè)規(guī)則描述起來就是: “在發(fā)生某一動(dòng)作(action)時(shí),倉(cāng)庫(kù)中的一部分?jǐn)?shù)據(jù)要進(jìn)行相應(yīng)的變化”。我們管會(huì)因動(dòng)作而變化的這一部分?jǐn)?shù)據(jù)叫做狀態(tài), 許許多多瑣碎的狀態(tài)組成了倉(cāng)庫(kù)數(shù)據(jù),所以整個(gè)倉(cāng)庫(kù)其實(shí)就是一個(gè)大的狀態(tài)。在程序運(yùn)行過程中,我們主要關(guān)心的就是這個(gè)倉(cāng)庫(kù)的狀態(tài)如何變化。 如何變化?那就要靠reducer。針對(duì)一個(gè)動(dòng)作,倉(cāng)庫(kù)里會(huì)有一個(gè)或多個(gè)狀態(tài)發(fā)生變化,reducer就是要指導(dǎo)狀態(tài)如何變化。

等等,那動(dòng)作是哪來的?從具體上說,動(dòng)作一般是來源于用戶的操作或者網(wǎng)絡(luò)請(qǐng)求的回應(yīng)。在代碼里需要對(duì)動(dòng)作規(guī)范一下, 其實(shí)也就是跟reducer進(jìn)行一個(gè)約定,讓它知道有動(dòng)作來了。其實(shí)怎樣表示動(dòng)作都可以,只要具有唯一性就行。 一般我們就用字符串就行了,即容易制造唯一,又能夠表義,在使用中小心點(diǎn)別重了就行。下面就來定義一個(gè)用戶輸入用戶名的動(dòng)作:

  1. const INPUT_USERNAME = 'INPUT_USERNAME' 

咋不直接用字符串呢?為了避免低級(jí)錯(cuò)誤,定義了這個(gè)常量以后,發(fā)起動(dòng)作時(shí)用這個(gè)常量,reducer也根據(jù)這個(gè)常量辨別動(dòng)作類型。

我們光告訴reducer發(fā)生了“用戶輸入”這個(gè)動(dòng)作還不夠,還要告訴reducer用戶輸入了什么內(nèi)容。所以完整的動(dòng)作得是一個(gè)具有豐富信息的對(duì)象。 為了方便,我們寫一個(gè)動(dòng)作生成器,也就是個(gè)函數(shù):

  1. function inputUsername (value) { 
  2.   return { 
  3.     type: INPUT_USERNAME, 
  4.     value: value 
  5.   } 

現(xiàn)在reducer就能得到足夠的信息來指導(dǎo)狀態(tài)的變化了。reducer要做的就是把倉(cāng)庫(kù)里一個(gè)叫做“username”的狀態(tài)的值修改一下。 由于狀態(tài)可以是一層套一層的,所以reducer也被設(shè)計(jì)成可以一層套一層。單個(gè)reducer就是它上級(jí)reducer的一分子。 其實(shí)reducer本身也就是個(gè)函數(shù):

  1. function username (state='', action) { 
  2.   switch(action.type){ 
  3.     case INPUT_USERNAME: 
  4.       return action.value 
  5.     defalut: 
  6.       return state 
  7.   } 

reducer的函數(shù)名對(duì)應(yīng)著狀態(tài)名稱,函數(shù)接受兩個(gè)參數(shù):***個(gè)是當(dāng)前狀態(tài),如果是程序開始運(yùn)行的時(shí)候, 很可能沒有當(dāng)前狀態(tài),就給個(gè)默認(rèn)值,這里是空字符串;第二個(gè)是前面動(dòng)作生成器生成的action對(duì)象。 一個(gè)reducer可以處理多種動(dòng)作,目前我們只有一個(gè),以后有別的就直接加case分支。對(duì)于每種動(dòng)作, reducer都要返回一個(gè)新的狀態(tài)值,這個(gè)值就可以根據(jù)action傳來的信息按照業(yè)務(wù)要求生成了。 ***一定要加一個(gè)默認(rèn)情況返回當(dāng)前狀態(tài)。在redux里,任何一個(gè)action都會(huì)在所有的reducer里過一遍, 所以對(duì)于一個(gè)reducer來說實(shí)際上絕大多數(shù)情況action都不是它能處理的,***還是返回當(dāng)前狀態(tài)值。 覺得很低效嗎?😉別怕,只是空走了一遍分支,這對(duì)諸如修改DOM這樣的重頭戲來說根本不算什么。

reducer是一層又一層的樹狀結(jié)構(gòu),怎么把它們組合到一起呢?rudex提供了一個(gè)組合工具combineReducers。 加入我們已經(jīng)寫好了另一個(gè)名為password的reducer,組合它們就是這個(gè)樣子:

  1. combineReducers({username, password}) 

注意,combineReducers接收的參數(shù)是一個(gè)對(duì)象,而不是多個(gè)函數(shù),上面的代碼用的是es6的簡(jiǎn)寫方式。

很容易發(fā)現(xiàn),上面的reducer和action生成器都是非常死板的代碼,今后我們會(huì)寫大量的這樣的代碼, 那會(huì)出現(xiàn)滿篇樣板代碼的情形,那可有點(diǎn)蠢笨了。所以我們把重復(fù)的東西盡可能的抽取出來,寫個(gè)reucer生成器以及action生成器的生成器:

  1. // reducer生成器,為了以后使用方便,起名為create reducer的簡(jiǎn)寫 
  2. function cr (initialState, handlers) { 
  3.   return function reducer(state = initialState, action) { 
  4.     if (handlers.hasOwnProperty(action.type)) { 
  5.       return handlers[action.type](state, action); 
  6.     } else { 
  7.       return state; 
  8.     } 
  9.   } 
  10.  
  11. // actiong生成器的生成器,同樣原因,起名為create action creator的簡(jiǎn)寫 
  12. return function(...args) { 
  13.   let action = { type } 
  14.   argNames.forEach((arg, index) => { 
  15.     action[argNames[index]] = args[index] 
  16.   }) 
  17.   return action 

這倆函數(shù)完成的事情跟我們寫樣板代碼做的事情完全相同。具體說明一下:

cr的兩個(gè)參數(shù):initialState是初始狀態(tài);handlers是由一堆函數(shù)組成的對(duì)象,每個(gè)函數(shù)的名稱對(duì)應(yīng)著一個(gè)action的類型, 每個(gè)函數(shù)接受的參數(shù)與reducer一樣,是action和當(dāng)前狀態(tài),返回值會(huì)被當(dāng)做新狀態(tài)。默認(rèn)情況就不用我們處理了。

cac接受的***個(gè)參數(shù)是action的類型名稱,后面參數(shù)是所有附帶數(shù)據(jù)的屬性名稱。

好了,把代碼規(guī)整一下。對(duì)現(xiàn)在小小的模擬雙向綁定的功能來說,我們還不需要記錄密碼的狀態(tài),不過我們也先寫上,后面會(huì)用到。

***先寫action。因?yàn)橐话銇碚f,只要你想好了你得應(yīng)用有什么功能,action就可以寫了,而且action不依賴其它東西。

src/actions/login.js:

  1. import {cac} from '../utils' 
  2.  
  3. export const INPUT_USERNAME = 'INPUT_USERNAME' 
  4. export const INPUT_PASSWORD = 'INPUT_PASSWORD' 
  5.  
  6. export const inputUsername = cac(INPUT_USERNAME, 'value'
  7. export const inputPassword = cac(INPUT_PASSWORD, 'value'

action類型名稱的常量現(xiàn)在都寫到了action文件里,不過也許把所有這些常量放到一個(gè)單獨(dú)的文件里比較好, 這樣在es6語法的幫助下就可以避免重復(fù)了。

這里我們把所有的東西都導(dǎo)出了,action類型名稱reducer會(huì)用到,action生成器組件會(huì)用到。

然后寫reducer。當(dāng)你想好應(yīng)用的功能后,接下來就是要考慮背后的數(shù)據(jù)結(jié)構(gòu)了。而reducer一寫出來,數(shù)據(jù)結(jié)構(gòu)就確定了。

src/reucers/login.js:

  1. import {combineReducers} from 'redux'
  2. import {cr} from '../utils' 
  3. import {INPUT_USERNAME, INPUT_PASSWORD} from 'actions/login' 
  4.  
  5. export default combineReducers({ 
  6.   username: cr('', { 
  7.     [INPUT_USERNAME](state, {value}){return value} 
  8.   }), 
  9.   password: cr('', { 
  10.     [INPUT_PASSWORD](state, {value}){return value} 
  11.   }) 
  12. }) 

對(duì)action文件的引用,路徑里沒有用../,這樣寫是因?yàn)閍ctions是一個(gè)別名,它代表actions目錄的絕對(duì)路徑,這是webpack幫我們做的。 當(dāng)然你也可以定義自己的別名,修改cfg/base.js就行,比如在resolve.alias對(duì)象里加一個(gè)自己的工具集:“utils:srcPath + '/utils.js'”。

rducer最終是要注冊(cè)到store那里的,這個(gè)過程在src/storces/index.js里面已經(jīng)寫了, 可以看到里面的代碼用的是../reducers這個(gè)文件(這是個(gè)目錄,實(shí)際的文件是里面index.js), 所以我們也需要把新寫的reducer注冊(cè)到這里面去。修改src/reducers/index.js:

  1. import { combineReducers } from 'redux'
  2. import login from './login' 
  3. const reducers = { 
  4.   login 
  5. }; 
  6. module.exports = combineReducers(reducers); 

在reducers/index里,所有的reducer也是通過combineReducers組合到一起的,只不過現(xiàn)在我們只有一個(gè)孤零零的子reducer:login。

終于,是時(shí)候回到組件上來了。src/containers/Login.js現(xiàn)在要修改成這樣:

  1. import React from 'react' 
  2. import { connect } from 'react-redux' 
  3. import {inputUsername, inputPassword} from 'actions/login' 
  4.  
  5. class Login extends React.Component{ 
  6.   inputUsernameHandler(evt){ 
  7.     this.props.dispatch(inputUsername(evt.target.value)) 
  8.   } 
  9.   inputPasswordHandler(evt){ 
  10.     this.props.dispatch(inputPassword(evt.target.value)) 
  11.   } 
  12.   render(){ 
  13.     return ( 
  14.       <div> 
  15.         <div>早上好,{this.props.username}</div> 
  16.         <div>用戶名:<input onChange={this.inputUsernameHandler.bind(this)}/></div> 
  17.         <div>密 碼:<input type="papssword" onChange={this.inputPasswordHandler.bind(this)}/></div> 
  18.         <button>登錄</button> 
  19.       </div> 
  20.     ) 
  21.   } 
  22.  
  23. function mapStateToProps(state) { 
  24.   return { 
  25.     username: state.login.username, 
  26.     password: state.login.password 
  27.   } 
  28. export default connect(mapStateToProps)(Login); 

有幾處變化:

首先,前面已經(jīng)說過,要把組件連接到倉(cāng)庫(kù),就要用connect。并且現(xiàn)在我們已經(jīng)確定了倉(cāng)庫(kù)里login對(duì)應(yīng)狀態(tài)的數(shù)據(jù)接口, 那么mapStateToProps返回的內(nèi)容也就確定了。login狀態(tài)里的兩個(gè)屬性映射成了組件的屬性, 所以用this.props.username就可以訪問到倉(cāng)庫(kù)里的login.username。

然后兩個(gè)input上都加上了change事件處理。當(dāng)change事件被觸發(fā)時(shí),通過this.props.dispatch函數(shù)就可以通知倉(cāng)庫(kù)有動(dòng)作發(fā)生了, 倉(cāng)庫(kù)此時(shí)就會(huì)調(diào)用所有的reducer來應(yīng)對(duì)這個(gè)事件。

好了,到這里小小的雙向綁定功能實(shí)現(xiàn)了😓試試吧。

在MVVM框架里只需要建立一個(gè)視圖模型,用一對(duì)雙大括號(hào)就能完成的事情,到react加redux里面為何如此大費(fèi)周折?

其實(shí)我是專門在展示完整的redux+react開發(fā)流程。如果只是要單個(gè)頁面上的這點(diǎn)功能,用事件處理來改變組件的state就行了。 那么redux為什么要引入這么個(gè)流程?我在開發(fā)中覺得有這么幾個(gè)特點(diǎn):從直觀上看在視野不一樣。還是跟MVVM比吧, MVVM框架的視野在于局部,而redux的視野在于全局。MVVM對(duì)一個(gè)controller對(duì)應(yīng)一個(gè)模型,模型里的數(shù)據(jù)只能自己用, 模型之間通信需要其它的數(shù)據(jù)傳遞方式。redux(或者說是flux的模式)管理著一個(gè)大數(shù)據(jù)倉(cāng)庫(kù), 任何時(shí)候都可以從這個(gè)倉(cāng)庫(kù)中取到一切細(xì)節(jié)的狀態(tài)(有沒有云的感覺?),當(dāng)開發(fā)單頁應(yīng)用的時(shí)候,這一優(yōu)勢(shì)會(huì)特別明顯。 從編程語言角度上看,redux+react方式充分利用了函數(shù)式編程的優(yōu)勢(shì)。redux(flux)強(qiáng)調(diào)單向數(shù)據(jù)流, 單向數(shù)據(jù)流就像生產(chǎn)流水線,原料被各個(gè)工序依次加工,最終成為產(chǎn)品,而在這個(gè)過程中要避免外界因素對(duì)各個(gè)階段的原料產(chǎn)生影響, 否則就會(huì)出現(xiàn)非預(yù)期的產(chǎn)品(次品)。純函數(shù)就像這個(gè)流水線中的工序,讓數(shù)據(jù)處理的過程簡(jiǎn)單明了。 發(fā)現(xiàn)了嗎?前面的代碼中純函數(shù)是主力。reducer很明顯是純函數(shù)。組件也是純函數(shù),注意,我們的組件并沒有直接被狀態(tài)控制, 而是有個(gè)connect的過程,狀態(tài)是被映射成組件的屬性的,對(duì)于組件來說,根本不知道狀態(tài)為何物。 這樣我們的組件、reducer都非常獨(dú)立,非常容易測(cè)試,意義也非常直白。

吹噓了這么多,靠目前這點(diǎn)簡(jiǎn)單的代碼也不容易看出來。畢竟這些代碼還沒啥實(shí)際意義,作為一個(gè)現(xiàn)代的前端應(yīng)用,連異步都沒有。。。

那么下一節(jié),我們就加點(diǎn)異步進(jìn)來。

責(zé)任編輯:倪明 來源: 博客園
相關(guān)推薦

2020-01-06 15:00:43

Linux電腦發(fā)行版

2021-06-30 09:20:18

NuShell工具Linux

2019-04-19 11:56:48

框架AI開發(fā)

2016-11-18 13:15:02

廣電電視推流網(wǎng)

2015-12-14 15:30:45

銳捷網(wǎng)絡(luò)

2018-10-23 09:00:00

Linux日志

2021-03-10 09:54:43

RustNuShell系統(tǒng)

2024-05-21 10:28:51

API設(shè)計(jì)架構(gòu)

2012-07-11 09:34:39

微軟云計(jì)算

2022-05-22 21:16:46

TypeScriptOmit 工具

2020-07-06 15:13:16

安卓AirDrop無線傳輸

2024-07-04 11:33:33

2024-06-11 09:02:30

2022-09-20 07:46:15

重試組件retrying

2022-11-07 09:25:02

Kafka存儲(chǔ)架構(gòu)

2019-05-17 11:05:54

中國(guó)移動(dòng)運(yùn)營(yíng)商SPN

2021-09-18 08:52:45

人工智能

2023-11-27 07:47:14

2015-05-28 10:35:07

前端gulpdemo

2020-11-27 14:28:13

數(shù)據(jù)分析工具數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产欧美久久一区二区三区 | 国产成人99久久亚洲综合精品 | 国产97在线看| 久久精品在线 | 美女天堂| 一级免费看片 | 免费观看a级毛片在线播放 黄网站免费入口 | 成人在线精品视频 | 日韩中文字幕在线免费 | 在线亚洲一区二区 | 人干人操| www.v888av.com| 玖玖国产精品视频 | 亚洲精品久久久久久久久久久久久 | 国产精品中文字幕在线播放 | 亚洲va欧美va人人爽午夜 | 国产精品美女久久久久久免费 | 99久久免费精品国产男女高不卡 | 亚洲精品电影在线观看 | 福利片在线看 | 日本亚洲精品成人欧美一区 | 国产午夜在线 | 在线视频 亚洲 | 毛片一级片| 成人欧美一区二区三区黑人孕妇 | 日韩色视频 | 亚洲综合久久久 | av首页在线| 久久91精品久久久久久9鸭 | 乳色吐息在线观看 | 黄色在线网站 | 午夜激情影院 | 中文字幕国产 | 最新中文字幕在线播放 | 91资源在线 | 国产精品久久久久久久久污网站 | 日韩三区在线 | 国产成人综合在线 | 天天操狠狠操 | 天天操夜夜操免费视频 | 中文字幕一区二区三区在线观看 |