dva 框架介紹

dva 是基于 redux 最佳實(shí)踐 實(shí)現(xiàn)的 framework颊亮,簡(jiǎn)化使用 redux 和 redux-saga 時(shí)很多繁雜的操作

數(shù)據(jù)流向

數(shù)據(jù)的改變發(fā)生通常是通過(guò)用戶交互行為或者瀏覽器行為(如路由跳轉(zhuǎn)等)觸發(fā)的,當(dāng)此類行為會(huì)改變數(shù)據(jù)的時(shí)候可以通過(guò) dispatch 發(fā)起一個(gè) action,如果是同步行為會(huì)直接通過(guò) Reducers 改變 State ,如果是異步行為(副作用)會(huì)先觸發(fā) Effects 然后流向 Reducers 最終改變 State,所以在 dva 中狗唉,數(shù)據(jù)流向非常清晰簡(jiǎn)明,并且思路基本跟開源社區(qū)保持一致

image.png

Modul

Subscription

Subscriptions 是一種從 源 獲取數(shù)據(jù)的方法涡真,它來(lái)自于 elm分俯。
Subscription 語(yǔ)義是訂閱,用于訂閱一個(gè)數(shù)據(jù)源哆料,然后根據(jù)條件 dispatch 需要的 action缸剪。數(shù)據(jù)源可以是當(dāng)前的時(shí)間、服務(wù)器的 websocket 連接东亦、keyboard 輸入杏节、geolocation 變化、history 路由變化等等典阵。

subscriptions: {
  setup({ dispatch, history }) {
    history.listen(async ({ pathname }, action) => {
      const re =
        pathToRegexp('/group-member/list/:groupId').exec(pathname)
        ||
        pathToRegexp('/group-member/del/:groupId').exec(pathname)
      if (action !== 'POP' && re && re[1]) {
        const groupId = +re[1]
        dispatch({ type: 'initList' })
        dispatch({ type: 'fetchGroupMemberList', groupId })
      }
    })
  },
},

Effect

Effect 被稱為副作用奋渔,在我們的應(yīng)用中,最常見的就是異步操作壮啊。它來(lái)自于函數(shù)編程的概念嫉鲸,之所以叫副作用是因?yàn)樗沟梦覀兊暮瘮?shù)變得不純,同樣的輸入不一定獲得同樣的輸出歹啼。
dva 為了控制副作用的操作玄渗,底層引入了redux-sagas做異步流程控制,由于采用了generator的相關(guān)概念狸眼,所以將異步轉(zhuǎn)成同步寫法捻爷,從而將effects轉(zhuǎn)為純函數(shù)。

effects: {
  * fetchGroupMemberList({ groupId }, { call, put }) {
    try {
      const { succeed, data: { role, member: { list: briefs } } } =
        yield call(fetch.get, `${GROUP_MEMBER_URL}/${groupId}/1/${GROUP_MEMBER_PAGE_SIZE}`)
      if (succeed) {
        yield put({ type: 'nextList', briefs, page: 1 })
        yield put({ type: 'setIdAndRole', role })
      }
    } catch (err) {
      console.log('Error when fetch group member list', err.stack)
      yield put({ type: 'app/showToast', title: '獲取群組成員列表錯(cuò)誤' })
    }
  },
  ...
},

Reducer

在 dva 中份企,reducers 聚合積累的結(jié)果是當(dāng)前 model 的 state 對(duì)象也榄。通過(guò) actions 中傳入的值,與當(dāng)前 reducers 中的值進(jìn)行運(yùn)算獲得新的值(也就是新的 state)司志。需要注意的是 Reducer 必須是純函數(shù)甜紫,所以同樣的輸入必然得到同樣的輸出,它們不應(yīng)該產(chǎn)生任何副作用骂远。并且囚霸,每一次的計(jì)算都應(yīng)該使用immutable data,這種特性簡(jiǎn)單理解就是每次操作都是返回一個(gè)全新的數(shù)據(jù)(獨(dú)立激才,純凈)拓型,所以熱重載和時(shí)間旅行這些功能才能夠使用额嘿。

reducers: {
  initList(state) {
    console.log('initLists')
    return {
      ...state,
      list: [],
    }
  },
  ...
},

State

State 表示 Model 的狀態(tài)數(shù)據(jù),通常表現(xiàn)為一個(gè) javascript 對(duì)象(當(dāng)然它可以是任何值)劣挫;操作的時(shí)候每次都要當(dāng)作不可變數(shù)據(jù)(immutable data)來(lái)對(duì)待册养,保證每次都是全新對(duì)象,沒(méi)有引用關(guān)系压固,這樣才能保證 State 的獨(dú)立性球拦,便于測(cè)試和追蹤變化

state: {
  id: 0,
  title: '全部成員',
  list: [],
  briefs: {},
  itemCount: 1,
  isManager: false,
},

Action

Action 是一個(gè)普通 javascript 對(duì)象,它是改變 State 的唯一途徑帐我。無(wú)論是從 UI 事件坎炼、網(wǎng)絡(luò)回調(diào),還是 WebSocket 等數(shù)據(jù)源所獲得的數(shù)據(jù)拦键,最終都會(huì)通過(guò) dispatch 函數(shù)調(diào)用一個(gè) action谣光,從而改變對(duì)應(yīng)的數(shù)據(jù)。action 必須帶有 type 屬性指明具體的行為芬为,其它字段可以自定義萄金,如果要發(fā)起一個(gè) action 需要使用 dispatch 函數(shù);需要注意的是 dispatch 是在組件 connect Models以后碳柱,通過(guò) props 傳入的

dispatch({ type: 'initList' })

dispatching function 是一個(gè)用于觸發(fā) action 的函數(shù),action 是改變 State 的唯一途徑熬芜,但是它只描述了一個(gè)行為莲镣,而 dipatch 可以看作是觸發(fā)這個(gè)行為的方式,而 Reducer 則是描述如何改變數(shù)據(jù)的涎拉。
在 dva 中瑞侮,connect Model 的組件通過(guò) props 可以訪問(wèn)到 dispatch,可以調(diào)用 Model 中的 Reducer 或者 Effects

Route Components

在 dva 中我們通常將其約束為 Route Components鼓拧,因?yàn)樵?dva 中我們通常以頁(yè)面維度來(lái)設(shè)計(jì) Container Components半火。
所以在 dva 中,通常需要 connect Model的組件都是 Route Components季俩,組織在/routes/目錄下钮糖,而/components/目錄下則是純組件(Presentational Components)

class GroupMember extends PureComponent {
  static propTypes = {
    groupId: PropTypes.number.isRequired,
    title: PropTypes.string,
    list: PropTypes.arrayOf(PropTypes.number),
    fetchNext: PropTypes.func,
    briefs: PropTypes.instanceOf(Object),
    removeMember: PropTypes.func,
    showConfirm: PropTypes.func,
    url: PropTypes.string.isRequired,
  }

  static defaultProps = {
    title: '',
    list: [],
    fetchNext: () => {},
    briefs: {},
    isManager: false,
    removeMember: () => {},
    showConfirm: () => {},
  }

  submitRemoveMember = id => () => {
    const { removeMember, groupId } = this.props
    removeMember(groupId, id)
  }

    ...

  render() {
    const { title, list, fetchNext, groupId } = this.props
    return (
      <Container>
        <Navigator
          title={title}
        />
        <MemberListView
          ref={(listView) => { this.listView = listView }}
          dataSource={ds.cloneWithRows(list)}
          renderRow={this.renderMember}
          enableEmptySections
          onEndReached={() => fetchNext(groupId)}
          onEndReachedThreshold={100}
        />
      </Container>
    )
  }
}

connect

通過(guò)connect將modul中的元素作為props的方式傳遞給component

export default connect(({ groupMember, app }, { location }) => {
  const url = location.pathname
  const groupId = +location.params.groupId
  const userId = app.userInfo.userId
  let list = [...groupMember.list]
  let briefs = { ...groupMember.briefs }
  if (url.indexOf('del') !== -1) {
    list = list.filter(each => (each !== userId))
    briefs = R.dissoc(userId, briefs)
  }
  return { ...groupMember, groupId, url, list, briefs }
}, { ...actions, ...appActions })(GroupMember)

參考文檔:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酌住,隨后出現(xiàn)的幾起案子店归,更是在濱河造成了極大的恐慌,老刑警劉巖酪我,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件消痛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡都哭,警方通過(guò)查閱死者的電腦和手機(jī)秩伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門逞带,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人纱新,你說(shuō)我怎么就攤上這事展氓。” “怎么了怒炸?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵带饱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我阅羹,道長(zhǎng)勺疼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任捏鱼,我火速辦了婚禮执庐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘导梆。我一直安慰自己轨淌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布看尼。 她就那樣靜靜地躺著递鹉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藏斩。 梳的紋絲不亂的頭發(fā)上躏结,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音狰域,去河邊找鬼媳拴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兆览,可吹牛的內(nèi)容都是我干的屈溉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抬探,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼子巾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起小压,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤砰左,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后场航,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缠导,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年溉痢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了僻造。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憋他。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖髓削,靈堂內(nèi)的尸體忽然破棺而出竹挡,到底是詐尸還是另有隱情,我是刑警寧澤立膛,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布揪罕,位于F島的核電站,受9級(jí)特大地震影響宝泵,放射性物質(zhì)發(fā)生泄漏好啰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一儿奶、第九天 我趴在偏房一處隱蔽的房頂上張望框往。 院中可真熱鬧,春花似錦闯捎、人聲如沸椰弊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)秉版。三九已至,卻和暖如春茬祷,著一層夾襖步出監(jiān)牢的瞬間清焕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工牲迫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耐朴,地道東北人借卧。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓盹憎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铐刘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陪每,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容