日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

dva 中的响应编程

發布時間:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dva 中的响应编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

思維盲區

我最開始學習使用 dva 是從《12 步 30 分鐘,完成用戶管理的 CURD 應用》開始的,這同時也是 dva 的官方教程。然而因為領悟能力太差,最開始完全沒理解。前 4 步還跟得上,第 5 步創建 model 和改造 service 就懵逼了。硬著頭皮照抄代碼,抄到最后數據沒出來,我還不知道自己哪兒錯了。

大寫的尷尬。

現在再看這篇教程,發現從第 5 步的 model 開始,dva 的作者就試圖推廣一種最近流行的理念:響應型編程

就目前來說,渲染數據流有至少兩種方式。由外界改變組件內部狀態的主動型,以及由組件監聽外界改變而渲染自身的響應型。很多人 —— 尤其是 oop 重度患者 —— 的慣性思維是第一種。無論把負責業務的 service 類扔的多遠,service 和 controller 都是直接連接的

以請求數據為例,我還停留在拿取數據推送到組件中進行渲染的階段。有那么一段時間,我對 container 和 component 之間的值交換還局限在屬性傳值回調函數層面上。雖然回調函數實現了一定程度上的事件響應,但組件之間仍舊脫離不開互相直連的主動型編碼的怪圈。

吃火鍋的正確姿勢

舉一個吃火鍋的例子來解釋主動型編程響應型編程的差別:

一般情況下,吃火鍋的時候都是點了菜和肉擺在自己臉跟前,想吃什么自己夾了往鍋里扔,看看快熟了就撈出來吃掉。

這是主動型。

但是這么吃火鍋有三個問題:

第一,一切都要親力親為。想吃肉就要親自把肉放到鍋里去,想吃菜也要親自把菜放進去。如果還想吃豆腐、蘑菇、粉條、羊尾魚丸…想吃的東西越多,操作就越復雜。

第二,既然是親自放東西,就得把東西擺在自己面前。桌子一共就那么點兒地方,想吃的東西越多占用的空間就越大。既不容易留出足夠的空間吃燙熟的茼蒿,也不容易把想吃的牛肉片從眼前一大堆的蔬菜里挑出來。萬一要換桌,還得的把這一大堆吃的一起打包帶走,漏掉一樣就吃不到。

第三,如果我想吃撒尿牛丸和蝦滑、魷魚,旁邊的哥們海鮮過敏。是應該我負責往鍋里放然后燙熟撈出來,他從我這里撈他能吃的;還是我倆各放各的,自己撈自己想吃的東西?前者雖然一個人做了共同的事情,但是別人一起吃的時候難免會撈錯;后者雖然看起來互不干擾,但是兩個人都在燙牛丸,多少是浪費。萬一是個鴛鴦鍋我還不吃辣怎么辦?燙到最后全亂套了。

用編程的術語說,便是:低內聚,高耦合

或許正統的 oop 語言(比如 Java)可以用封裝、繼承、多態來某種程度的緩解這個問題(僅僅是某種程度上),但是 JavaScript 想從語言的角度實現就會無比操蛋(JavaScript 用 prototype 模擬 oop 實現,而 es6 里的 class 和 Java 里的 class 又完全不是一個東西)。

現在我們換種方式吃火鍋:分出一個人來啥也不吃,把所有吃的都放在他面前。想吃蘑菇就對他說一聲,讓他替你把蘑菇放進火鍋燙熟,替你把熟蘑菇放進蘸料碟里。

你唯一要做的事情就是吼一嗓子,然后從自己的蘸料碟里夾蘑菇,吃。

哎呀,這個就太爽了。

想吃豬腦,“來盤豬腦”;想吃鴨血,“來盤鴨血”;想吃 10 盤地瓜片就大喊“來10盤地瓜片”,用不著自己費事一盤一盤的往鍋里倒。

而且既然食材都堆在另一個地方,自己面前留一個吃東西的蘸料碟就夠用,十干凈整潔。桌子隨便換,揮揮衣袖帶雙筷子走就可以了。

一群人組團吃,大家各點各的吃互不干擾。燙火鍋的也始終只有一個人,既不會造成資源浪費,又不必讓其他人關心額外的東西。

這就是所謂的響應型編碼。

被分出去的那個人,在 React 體系里就是 Redux(或者相同功能的庫);具體到 dva 框架中,就是 model。

(理想情況下)所有的組件只和 model 連接,互相之間完全沒有直接交集,這便是響應型編碼思想在 dva 框架中的體現。

dva 中的響應型編碼

有了響應型編碼的理論以后,我很容易的就理解第 5 步的操作。

此時我的情況是:

  • 可以通過 http://localhost:8000/user 訪問到 user

?

?

  • 可以通過 http://localhost:8000/api/users 訪問到 user 數據

?

?

通過 dva g model user 可以很方便的創建 model/user.js 并注冊進 index.js 中(命令行萬歲!) ,雖然目前還什么都沒有:

?

?

我需要做的事情就是把數據從 api/user接口拉下來,渲染進 route/user 里(component 可以等等再談)。

把大象...我是說數據渲染進 route/user 需要三步:

  • 編寫請求接口的方法
  • 使用 1 的方法獲得數據
  • 將 2 數據渲染進頁面
  • 編寫請求接口的方法

    dva 的新手大禮包里已經提供了基礎的網絡請求函數 utils/resquest.js ,雖然大多數情況下都會對其進行一些擴展才能滿足現實項目的需求,但是就目前來說暫且是夠用的。

    ?

    ?

    以 oop 觀點來看,utils/resquest.js 相當于項目所有請求函數的基類(base class)。如果需要進行具體業務的編寫,應該新建一個繼承 utils/resquest.js 的子類。但 JavaScript 不算是純種 oop 的語言,所以慣例都是新建一個具體的業務類 services/user.js,通過在 services/user.js 中 import 的方式調用 utils/resquest.js。

    // 在 services 目錄下新建 services/user.js,負責具體的 user 業務import request from '../utils/request';export function getUserData() { // 偷懶,暫時把 example.js 的代碼拷貝過來return request('api/users'); // 這里是一個 promise 對象 }

    實際上這個時候如果直接把請求函數寫在 route/user.js 里已經可以渲染頁面了。

    // 這是一個錯誤的示范import React, { Component, PropTypes } from 'react'; import * as userService from '../services/user';class User extends Component {static propTypes = {className: PropTypes.string,};constructor(props) {super(props);this.state = {list : []}}componentDidMount() {this.getData();}getData = () => {userService.getUserData().then((res) => {this.setState({list: res.data});})}buildContent = () => {const {list} = this.state;return list.map( (itm, index) => {return <div key={index}>{itm.name}</div>})}render() {return (<div>{this.buildContent()}</div>);} }export default User;

    這明顯是主動型編程寫法,和 dva 的響應型理念背道而馳。也許簡單或者低交互度的界面這么寫起來會很省事,但是可擴展性接近于零。一旦復雜度和交互度提升,組件的會變得越來越復雜,最后變成一個巨大的坑。

    在 model 中使用 services 函數并獲得數據

    有了 services/user.js 函數,可以進行具體的請求動作,在 model/user.js 請求數據了。

    應該寫在 model/user.js 哪里呢?

    ?

    ?

    這里可能又要多說一點所謂純函數的概念,即對于給定的輸入有唯一不變的輸出并不含任何明顯可見的副作用(side effects)的函數(可參考這篇英文文章或者中文版)。

    請求網絡數據自帶副作用屬性(異步操作),而副作用(side effect)看起來確實和 model/user.js 里的某個屬性有點相似...

    dva 的官方說法是:

    真實情況最常見的副作用就是異步操作,所以 dva 提供了 effects 來專門放置副作用,不過可以發現的是,由于 effects 使用了 Generator Creator,所以將異步操作同步化,也是純函數。

    dva 負責處理異步的是封裝后的 redux-saga 模塊。也就是說,需要使用 call 方法。所以 dva 的請求數據套路是這樣的:

    effects: {*getData(action, { call, put }) { // 第一個參數是 dispatch 所發出的 action,第二個參數是 dva 封裝的 saga 函數。可以直接用 es 6 的解構風格拿取所需要的具體函數。const temp = yield call(userService.getUserData, {}); // 因為現在沒有參數console.log('temp', temp); // 純函數中不應有副作用(把數據轉移到控制臺也算副作用),這里只是方便在 chrome 里查看,}},

    寫完了?并沒有。

    贊美太陽...呸!dispatch!

    我眼中 dva 里 dispatch-atcion 與 model/effect 的原理有點像 Android 四大組件之一的廣播:

  • 通過 dispatch 函數發出一個包含 type 屬性(此為必須)的 action。
  • dva 監聽到 action 后會根據 action 的 type 值尋找對應 model 的 effect 的方法(action.type 的值就是 effects 里的方法名,在 model 外 dispatch 需要使用 modelName/effectsMethodName 的格式)
  • 找到方法后就調用方法,把 action 作為參數放在第一個位置。
  • 使用 dispatch 的好處是顯而易見的:切分業務模塊

    組件不必再負責具體的業務操作(自己動手涮肉),只需要 dispatch action (大喊一聲) 到對應的 model 里(給那個負責上菜的人)。

    需要用戶列表數據的組件未必只有 route/user.js,其他需要數據的組件可以在自己里面 dispatch action。

    同時 model/user.js 的 getData 方法是獨一份,你 dispatch 多少 type 為
    user/getData (如果在 model 內 dispatch 可以省略前綴)的 action 都得歸到我這來處理。

    高內聚(業務處理集中),低耦合( 隨時隨地隨便哪個組件隨意姿勢 dispatch)。

    官方教程中給出的做法是在 model 里的訂閱部分 subscriptions寫一個監聽,根據監聽到具體的事件(進入 /user 頁面)進行特定操作(dispatch action)。

    subscriptions: {setup({ dispatch, history }) { // eslint-disable-linereturn history.listen( ({pathname, query}) => {if(pathname === '/user') {dispatch({type: 'getData',payload: {txt: 'hello, dva'}})}})},},

    這么做同樣也是進一步切離業務,不必把 dispatch 寫在具體組件的生命周期中,減少組件的復雜程度(其實關鍵還是 dispatch ,訂閱說到底也是為 dispatch 服務的)。

    現在應該可以看到輸出后的數據了。

    ?

    ?

    渲染數據

    雖然現在拿到了數據,但是數據還憋在 model/effects 里和 route/user.js 沒什么關系,總的想個辦法把數據和組件關聯起來。

    是時候讓 dva 的 state 出場了。

    我理解的 dva 中 model 內的 state 屬性,實際上是封裝后的 Redux 全局 store 的一部分。通過不重復的 namespace(桌號) 確定 store(餐館) 中唯一的 model(餐桌),把 model/effects 請求到的原始數據(生食)放進 model/reducer (特定的火鍋)里進行必要的處理(燙熟),再放進 model/state (蘸料碟)里,route/user.js 只需要從這里拿取所需要的數據(吃的)就可以了。

    從 effects 里往 reducer 里傳遞數據使用的是 saga 的put 方法,參數同樣也是一個 action 對象,action 中必須包含的 type 屬性的值就是 reducer 屬性里的方法名:

    import * as userService from '../services/user';export default {namespace: 'user',state: {},reducers: {dealData(state, action) {// 理論上 reducer 里的函數應該是純函數,此處只是為了方便在控制臺里看參數console.log('state==>', state);console.log('action==>', action);return { ...state }}},effects: {*getData(action, { call, put }) {const temp = yield call(userService.getUserData, {});yield put({type: 'dealData',payload: {temp}});}},subscriptions: {setup({ dispatch, history }) { // eslint-disable-linereturn history.listen( ({pathname, query}) => {if(pathname === '/user') {dispatch({type: 'getData',payload: {txt: 'hello, dva'}})}})},}, };

    ?

    ?

    剩下的做法就是在 model/user.js 的 state 屬性里定義一個屬性并賦值了。

    state: {dataList: []},reducers: {dealData(state,{ payload: { temp: { data: dataList } } }// action// { payload: { temp: { data: dataList } }} // 是 es 6 的解構做法,等同于// const {payload} = action;// const {temp} = payload;// const {data} = temp;// const dataList = data;) {return { ...state, dataList }; // 必須有返回值(純函數必須有返回值),否則會報錯// 經評論提醒 修改 // 等同于 // let tmp = Object.assign([], this.state) // tmp.dataList = dataList}},

    現在需要的數據已經掛在 model/user.js 的 state 屬性里了,最后一步便是在 route/user.js 里使用 connect 和 mapStateToProps 讓組件監聽數據源,實現響應型編碼了。

    import React from 'react'; import { connect } from 'dva'; // 0.關鍵的 connect import styles from './User.css'; import * as userService from '../services/user'; function User({ dataList }) { // 5. 這里的屬性就是 3 里的返回值return (<div className={styles.normal}>{!!dataList.length && dataList.map((data, index) => {return <div key={index}>{data.name}</div>})}</div>); }function mapStateToProps(store) { // 1關鍵的 mapStateToPropsconst { dataList } = store.user; // 2.從 model/user.js 拿取需要的數據return { dataList }; // 3.將數據作為屬性返回 }export default connect(mapStateToProps)(User); // 4.連接組件

    ?

    ?

    碎碎念

    其實往后的代碼還有蠻多,分頁、封裝、引入 antd 調整樣式。不過都是一些需要花時間慢慢雕琢、順便發發 dispatch 的細節(其實細節也很重要 >_<),至少理解起來比較容易了。

    理解第 5 步思路的順序是基于數據流向的,而實際開發中的編寫順序剛好是倒過來:先確定頁面需要的數據,再編寫 model 中的業務,最后把網絡接口掛進來。不過現在這么干已經心里有譜,知道怎么回事了。

    可喜可賀。


    鏈接:https://juejin.im/post/59946bbcf265da246f37e6d9
    來源:掘金
    著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

    總結

    以上是生活随笔為你收集整理的dva 中的响应编程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 久热中文字幕在线 | 九九资源网 | 亚洲成人播放器 | 中文字幕一区久久 | 国产wwwxxx | 国产乱码精品一区二三赶尸艳谈 | 国产视频一二三区 | 国产无套精品一区二区三区 | 成人日b视频 | 韩国一级淫片免费看 | 国内自拍2020 | 麻豆乱码国产一区二区三区 | 精彩毛片 | 99视频一区二区 | 香蕉视频ap | 激情视频网址 | 一区二区三区日韩视频 | 亚洲av无码成人精品区 | 国产人妻大战黑人20p | 713电影免费播放国语 | 国产字幕在线观看 | 日韩一区二区三区精品 | 成人尹人| 久久成人一区二区 | 国产欧美一级片 | 免费看片91 | 国产毛片一区二区三区 | av黄在线 | 日韩综合区 | 夜夜cao| 夫妻啪啪呻吟x一88av | 亚洲中文字幕第一区 | 日批黄色| 日韩精品免费一区二区三区 | 国产精品专区在线观看 | 欧美aaa级片| 国产中文在线观看 | 亚洲乱码国产乱码精品精的特点 | 久久9966| 亚洲精品日韩在线观看 | 太久av| 国产精品有限公司 | 操人视频在线观看 | 国产精品第一页在线观看 | 欧美日韩视频网站 | 亚洲欧美精品一区二区三区 | 国产av电影一区二区三区 | 亚洲成人三区 | 久久精品系列 | 天天爽夜夜爽视频 | 91视频你懂的| 黑料视频在线 | 欧美一区中文字幕 | 欧美裸体网站 | 国产制服av| 成人亚洲在线 | 狠狠操免费视频 | 国语粗话呻吟对白对白 | 国产拍拍视频 | 日本大尺度做爰呻吟 | 国产精品亚洲一区二区 | 国产一级视频在线观看 | 国产精品日韩专区 | 婷婷在线网 | 国产精品xxxxxx| 青青草成人在线观看 | 久久久久人妻精品色欧美 | 小小姑娘电影大全免费播放 | www久久99 | 色综合av综合无码综合网站 | 国产又粗又大又爽视频 | 性开放网站 | a∨鲁丝一区鲁丝二区鲁丝三区 | 亚洲美女在线视频 | 蝌蚪自拍网站 | 91精品91久久久中77777 | 欧美日韩一区二区中文字幕 | 国产三级短视频 | 手机看片1024日韩 | 私人网站 | 国产日本在线 | 99成人免费视频 | 91精品久久久久久 | 成人午夜网站 | 精品久久久久久久久久久久久久 | 国产一区午夜 | 狠狠插av | 国产又大又粗又硬 | 丰满雪白极品少妇流白浆 | 成人福利网址 | 欧美亚洲第一区 | 国产情侣激情自拍 | 麻豆传谋在线观看免费mv | 成人午夜电影网站 | 日本国产在线 | 久久蜜桃精品 | 2021久久| 中文字幕日韩亚洲 | 超碰人人擦 |