使用dva腳手架(dva-cli)快速構建React項目


安裝 dva-cli

你應該會更希望關注邏輯本身,而不是手動敲入一行行代碼來構建初始的項目結構,以及配置開發環境。

那么,首先需要安裝的是 dva-cli 。dva-cli 是 dva 的命令行工具,包含 init、new、generate 等功能,目前最重要的功能是可以快速生成項目以及你所需要的代碼片段。

$ npm install -g dva-cli

安裝完成后,可以通過 dva -v 查看版本,以及 dva -h 查看幫助信息。

創建新應用

安裝完 dva-cli 后,我們用他來創建一個新應用,取名 myApp

$ dva new myApp --demo

注意:--demo 用於創建簡單的 demo 級項目,正常項目初始化不加要這個參數。

然后進入項目目錄,並啟動。

$ cd myApp
$ npm start

幾秒之后,會看到這樣的輸出:

          proxy: listened on 8000
     livereload: listening on 35729
📦  173/173 build modules
webpack: bundle build is now finished.

(如需關閉 server,請按 Ctrl-C.)

在瀏覽器里打開 http://localhost:8000/ ,正常情況下,你會看到一個 "Hello Dva" 頁面。

如果你想在此基礎上完成更為完整的應用實例,請直接跳轉react實例:理解dva構建項目的原理

定義 model

接到需求之后推薦的做法不是立刻編碼,而是先以上帝模式做整體設計。

  1. 先設計 model
  2. 再設計 component
  3. 最后連接 model 和 component

這個需求里,我們定義 model 如下:

app.model({ namespace: 'count', state: { record : 0, current: 0, }, });

namespace 是 model state 在全局 state 所用的 key,state 是默認數據。然后 state 里的 record 表示 highest recordcurrent 表示當前速度。

完成 component

完成 Model 之后,我們來編寫 Component 。推薦盡量通過 stateless functions 的方式組織 Component,在 dva 的架構里我們基本上不需要用到 state 。

import styles from './index.less'; const CountApp = ({count, dispatch}) => { return ( <div className={styles.normal}> <div className={styles.record}>Highest Record: {count.record}</div> <div className={styles.current}>{count.current}</div> <div className={styles.button}> <button onClick={() => { dispatch({type: 'count/add'}); }}>+</button> </div> </div> ); };

注意:

  1. 這里先 import styles from './index.less';,再通過 styles.xxx 的方式聲明 css classname 是基於 css-modules 的方式,后面的樣式部分會用上
  2. 通過 props 傳入兩個值,count 和 dispatchcount 對應 model 上的 state,在后面 connect 的時候綁定,dispatch用於分發 action
  3. dispatch({type: 'count/add'}) 表示分發了一個 {type: 'count/add'} 的 action,至於什么是 action,詳見:Actions@redux.js.org

更新 state

更新 state 是通過 reducers 處理的,詳見 Reducers@redux.js.org

reducer 是唯一可以更新 state 的地方,這個唯一性讓我們的 App 更具可預測性,所有的數據修改都有據可查。reducer 是 pure function,他接收參數 state 和 action,返回新的 state,通過語句表達即 (state, action) => newState

這個需求里,我們需要定義兩個 reducer,add 和 minus,分別用於計數的增和減。值得注意的是 add 時 record 的邏輯,他只在有更高的記錄時才會被記錄。

請注意,這里的 add 和 minus 兩個action,在 count model 的定義中是不需要加 namespace 前綴的,但是在自身模型以外是需要加 model 的 namespace

app.model({
  namespace: 'count',
  state: {
    record: 0,
    current: 0,
  },
+ reducers: { + add(state) { + const newCurrent = state.current + 1; + return { ...state, + record: newCurrent > state.record ? newCurrent : state.record, + current: newCurrent, + }; + }, + minus(state) { + return { ...state, current: state.current - 1}; + }, + }, });

注意:

  1. { ...state } 里的 ... 是對象擴展運算符,類似 Object.extend,詳見:對象的擴展運算符
  2. add(state) {} 等同於 add: function(state) {}

綁定數據

還記得之前的 Component 里用到的 count 和 dispatch 嗎? 會不會有疑問他們來自哪里?

在定義了 Model 和 Component 之后,我們需要把他們連接起來。這樣 Component 里就能使用 Model 里定義的數據,而 Model 中也能接收到 Component 里 dispatch 的 action 。

這個需求里只要用到 count

function mapStateToProps(state) { return { count: state.count }; } const HomePage = connect(mapStateToProps)(CountApp);

這里的 connect 來自 react-redux

定義路由

接收到 url 之后決定渲染哪些 Component,這是由路由決定的。

這個需求只有一個頁面,路由的部分不需要修改。

app.router(({history}) => <Router history={history}> <Route path="/" component={HomePage} /> </Router> );

注意:history 默認是 hashHistory 並且帶有 _k 參數,可以換成 browserHistory,也可以通過配置去掉 _k 參數。

添加樣式

默認是通過 css modules 的方式來定義樣式,這和普通的樣式寫法並沒有太大區別,由於之前已經在 Component 里 hook 了 className,這里只需要在 index.less 里填入以下內容:

.normal {
  width: 200px; margin: 100px auto; padding: 20px; border: 1px solid #ccc; box-shadow: 0 0 20px #ccc; } .record { border-bottom: 1px solid #ccc; padding-bottom: 8px; color: #ccc; } .current { text-align: center; font-size: 40px; padding: 40px 0; } .button { text-align: center; button { width: 100px; height: 40px; background: #aaa; color: #fff; } }

異步處理

在此之前,我們所有的操作處理都是同步的,用戶點擊 + 按鈕,數值加 1。

現在我們要開始處理異步任務,dva 通過對 model 增加 effects 屬性來處理 side effect(異步任務),這是基於 redux-saga 實現的,語法為 generator。(但是,這里不需要我們理解 generator,知道用法就可以了)

在這個需求里,當用戶點 + 按鈕,數值加 1 之后,會額外觸發一個 side effect,即延遲 1 秒之后數值 1 。

app.model({
  namespace: 'count',
+ effects: { + *add(action, { call, put }) { + yield call(delay, 1000); + yield put({ type: 'minus' }); + }, + }, ... +function delay(timeout){ + return new Promise(resolve => { + setTimeout(resolve, timeout); + }); +}

注意:

  1. *add() {} 等同於 add: function*(){}
  2. call 和 put 都是 redux-saga 的 effects,call 表示調用異步函數,put 表示 dispatch action,其他的還有 select, take, fork, cancel 等,詳見 redux-saga 文檔
  3. 默認的 effect 觸發規則是每次都觸發(takeEvery),還可以選擇 takeLatest,或者完全自定義 take 規則

刷新瀏覽器,正常的話,就應該已經實現了最開始需求圖里的所有要求。

訂閱鍵盤事件

在實現了鼠標測速之后,怎么實現鍵盤測速呢?

在 dva 里有個叫 subscriptions 的概念,他來自於 elm

Subscription 語義是訂閱,用於訂閱一個數據源,然后根據條件 dispatch 需要的 action。數據源可以是當前的時間、服務器的 websocket 連接、keyboard 輸入、geolocation 變化、history 路由變化等等。

dva 中的 subscriptions 是和 model 綁定的。

+import key from 'keymaster'; ... app.model({ namespace: 'count', + subscriptions: { + keyboardWatcher({ dispatch }) { + key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) }); + }, + }, });

這里我們不需要手動安裝 keymaster 依賴,在我們敲入 import key from 'keymaster'; 並保存的時候,dva-cli 會為我們安裝 keymaster 依賴並保存到 package.json 中。輸出如下:

use npm: tnpm
Installing `keymaster`... [keymaster@*] installed at node_modules/.npminstall/keymaster/1.6.2/keymaster (1 packages, use 745ms, speed 24.06kB/s, json 2.98kB, tarball 15.08kB) All packages installed (1 packages installed from npm registry, use 755ms, speed 23.93kB/s, json 1(2.98kB), tarball 15.08kB) 📦 2/2 build modules webpack: bundle build is now finished.

最終我們得到的效果是這樣的:


詳情請參考:https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/getting-started.md


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM