隨著React的大火,flux架構也跟著火了起來,Redux是實現flux架構的庫中的佼佼者,使用react+redux來開發應用的開發人員也越來越多
確實,React+Redux+webpack+babel+es6,這是一個極具生產力的組合
源自flux架構的單向流動使得應用的邏輯和資料流動變得可控,當應用邏輯變得複雜的時候,其優勢越加明顯,開發越加高效
但是在開發的時候出現了一些問題,比如說概念太多,檔案太鬆散等等問題
以及為瞭解決非同步互動問題,會引用一些中介軟體來處理store,比較常見的是用thunk中介軟體+asysc或者用saga等等
圖為一個中介軟體處理store的代碼
這不是Dva解決的最大的問題,Dva的重點是引進了model這個概念,將以前一堆鬆散的概念和檔案組織到一起了,這才是關鍵
React+redux+redux-saga中,每新增1個組件或者說新增1個頁面,都要新增3個檔案,sagas,reducers,action各自對應1個檔案,編寫的時候不停的切換,極其的麻煩,而且檔案多了之後,檔案的管理不是很方便
DVA正是解決這些問題的
首先,我想說一個問題,那就是……Dva這個名字……
咳,好吧,最初的最初我關注到這個架構,就是因為它的名字,然後就好奇的點進去了,然後就被它的優雅給吸引,再也不可自拔……
然後它的設定檔,叫roadhog
如果你是一名OWer,那麼你已經想迫不及待的看下去了
為什麼要用DVA呢。
以下是官方文檔的說明:
易學易用:僅有 6 個 api,對 redux 使用者尤其友好
elm 概念:通過 reducers, effects 和 subscriptions 組織 model
支援 mobile 和 react-native:跨平台
支援 HMR:目前基於 babel-plugin-dva-hmr 支援 components、routes 和 models 的 HMR
動態載入 Model 和路由:按需載入加快訪問速度
外掛程式機制:比如 dva-loading 可以自動處理 loading 狀態,不用一遍遍地寫 showLoading 和 hideLoading
完善的文法分析庫 dva-ast:dva-cli 基於此實現了智能建立 model, router 等
支援 TypeScript:通過 d.ts
Let’s start!
首先奉上Dva官方文檔地址,是發布在github上的:dva
首先是安裝:
使用npm安裝即可: npm install dva-cli@0.7 -g
需要注意的是:1.node版本必須在6.5以上 2.dva-cli必須版本在0.7.x(可以安裝完dva-cli之後用dva -v查看一下版本號碼)
以上兩點都是DVA官方的要求
然後使用new命令建立一個新的dva應用: dva new dva-demo
耐心等待建立完成
這是應用建立成功的提示
好的,現在我們來看建立好的工程~
可以先運行一下試試: npm run start
運行結果如圖
url為:http://localhost:8000/#/?_k=gebc0m
嗯 一個奇怪的url
不過如果你對前端路由,或者說對react-router有所瞭解的話,就知道這是hashHistory
如果不習慣可以修改為browserHistory
在根目錄下的index.js檔案中
修改代碼
再運行看看效果
url正常了
不過路由改成browserHistory的話,還需要後端代碼的配合
現在我們來觀察這個建立好的工程的目錄
可以很明顯的看到代碼都放在src檔案夾了
另外在public檔案夾下放了一個index.html檔案
我們開啟它來看看
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Dva Demo</title> <link rel="stylesheet" href="/index.css" /></head><body><div id="root"></div><script src="/index.js"></script></body></html>
很明顯,這是一個React典型應用的html模版檔案,和用create-react-app腳手架建立的應用中產生的index.html內容差不多
我們幾乎不對這個html進行代碼改動,也不在裡面加入html標籤,css檔案等,整個檔案的重點只是body裡的兩行代碼
<div id="root"></div>
這是確定了id的div標籤,我們編寫的React組件最後都是展示在這個div塊裡
<script src="/index.js"></script>
最後所有js檔案都會打包到這個Js檔案裡統一載入
再來看js檔案
整個程式的入口檔案是根目錄下的index.js檔案
import dva from 'dva';import { browserHistory } from 'dva/router';import './index.css';// 1. Initializeconst app = dva({ history: browserHistory,});// 2. Plugins// app.use({});// 3. Modelapp.model(require('./models/example'));// 4. Routerapp.router(require('./router'));// 5. Startapp.start('#root');
dva-cli產生的檔案給我們寫的注釋已經寫的很清楚了
先初始化,再載入外掛程式,再載入Model,再載入路由,最後啟動程式
我們就根據這個過程來理清楚檔案關係
先從第4步路由來分析
載入路由的語句為: app.router(require('./router'));
很明顯能看出,載入的是同在根目錄下的router.js檔案
我們開啟router.js檔案
import React from 'react';import { Router, Route } from 'dva/router';import IndexPage from './routes/IndexPage';function RouterConfig({ history }) { return ( <Router history={history}> <Route path="/" component={IndexPage} /> </Router> );}export default RouterConfig;
可以很明顯的看出,主體結構是配置react-router,攔截”/”,然後渲染IndexPage
根據import語句,找到routes目錄下面的IndexPage.js
import React from 'react';import { connect } from 'dva';import styles from './IndexPage.css';function IndexPage() { return ( <div className={styles.normal}> <h1 className={styles.title}>Yay! Welcome to dva!</h1> <div className={styles.welcome} /> <ul className={styles.list}> <li>To get started, edit <code>src/index.js</code> and save to reload.</li> <li><a href="https://github.com/dvajs/dva-docs/blob/master/v1/en-us/getting-started.md">Getting Started</a></li> </ul> </div> );}IndexPage.propTypes = {};export default connect()(IndexPage);
顯然,這是一個React組件,而且是Redux中所說的容器型的組件,因為在匯出的時候通過connect這個高階函數來裝飾這個組件,以便能夠從Redux狀態樹中擷取資料
因為這是一個很簡單的頁面,並沒有匯入在Components目錄下封裝好的組件,如果是在實際開發,頁面足夠複雜的時候,會在Component中封裝組件,然後在Routes目錄下的中的組件匯入,然後再匯入路由檔案中
components目錄下的example.js
import React from 'react';const Example = () => { return ( <div> Example </div> );};Example.propTypes = {};export default Example;
只是1個樣本檔案,沒有使用它,它本身也沒什麼意義
我們再回到根目錄下的index.js中,第4步路由部分載入的檔案我們已經回溯到底了,我們來看第3步model
app.model(require('./models/example'));
可以看出,載入的是models目錄下的example.js檔案
開啟查看其代碼
export default { namespace: 'example', state: {}, subscriptions: { setup({ dispatch, history }) { // eslint-disable-line }, }, effects: { *fetch({ payload }, { call, put }) { // eslint-disable-line yield put({ type: 'save' }); }, }, reducers: { save(state, action) { return { ...state, ...action.payload }; }, },};
model這個概念,是dva的核心,可以看到上面代碼中主要分為幾個欄位:namespace,state,subscriptions,effects,reducers
這幾個欄位,官方文檔中給出的說明是這樣的:
namespace - 對應 reducer 在 combine 到 rootReducer 時的 key 值
state - 對應 reducer 的 initialState
subscription - elm@0.17 的新概念,在 dom ready 後執行
effects - 對應 saga,並簡化了使用
reducers- 還是對應原來的reducer,概念沒有變化
熟悉react,redux, redux-saga 這一套應用架構的,無縫切換
可以看到,關於redux中action,reducer的那一堆,都集中在model裡面了,編寫代碼再也不用切換過去切換過來,而且在一個檔案中,組織代碼非常方便,再也不用code everywhere
services目錄下的檔案是為models目錄下的檔案服務的
更多的,可以去看官方給出的一個CURD使用者管理的demo
12 步 30 分鐘,完成使用者管理的 CURD 應用