dva 是一個基於 redux 和 redux-saga的數據流方案,而且爲了簡化開發體驗,dva 還額外內置了 react-router 和 fetch,因此也能夠理解爲dva是一個輕量級的應用框架。 -- 摘自官網
本練習使用dva+antd作了個購物車小練習,目的是爲了更快上手dva,因此練習的複雜度不高,更可能是基礎運用開發。 項目參考來源
npm install dva-cli -g
dva new shopping-cart-dva
cd shopping-cart-dva
在dva項目裏使用antd
css
yarn add antd babel-plugin-import
編輯.webpackrc
,使babel-plugin-import
插件生效前端
{ "extraBabelPlugins": [ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
編輯.webpackrc
,配置proxy
屬性,代理請求java
"proxy": { "/api": { "target": "http://localhost:3000", // 後端接口 "changeOrigin": true, // "pathRewrite": { "^/api" : "" } } }
本次練習主要爲了熟悉dva作狀態管理,對後臺要求不高,只是簡單地返回商品數據
yarn add cors express
新建server
文件夾,用express
搭建一個簡單的後臺node
新建data/products.json
,存放json
格式的商品數據react
{ "products": [ { "id": 12, "bigImage":"http://localhost:3000/images/12064273040195392_1.jpg", "smallImage": "http://localhost:3000/images/12064273040195392_2.jpg", "sku": 12064273040195392, "title": "Cat Tee Black T-Shirt", "description": "4 MSL", "availableSizes": ["S", "XS"], "style": "Black with custom print", "price": 10.9, "installments": 9, "currencyId": "USD", "currencyFormat": "$", "isFreeShipping": true } ] }
新建app.js
,並編輯webpack
const path = require('path'); const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors()); const port = 3000; app.get('/api/products', (req, res) => { res.sendFile(path.join(__dirname, 'data', 'products.json')); }); app.listen(port, () => { console.log(`[products] API listening on port ${port}.`); });
本次練習使用concurrently
並行命令git
yarn add concurrently
編輯package.json
github
"scripts": { "start": "roadhog server", "server": "nodemon server/app", "build": "roadhog build", "lint": "eslint --ext .js src test", "precommit": "yarn run lint", "dev": "concurrently \"yarn run server\" \"yarn run start\"" },
yarn run dev
命令同時啓動先後端,在http://localhost:8000
訪問建立商品列表路由web
新建src\routes\products\index.js
和index.css
(dva默認支持CSS Module模塊化, 若是須要使用connect()
方法,須要從dva中引入)
import React, {Component} from 'react'; import { connect } from 'dva'; // 引入connect() // import { Badge, Icon } from 'antd'; 在組件中使用antd import styles from './index.css'; // 按需引入css class Products extends Component { constructor(){ super(); } render(){ return ( <div className={styles.container}> soooo many products!!! </div> ); } }; // 這裏的products是namespace爲products的model層 const mapStateToProps = ({ products })=>{ return { counts: products.counts } }; // 跟react-redux的connect使用方法類似 export default connect(mapStateToProps)(Products);
編輯src/router.js
import React from 'react'; import { Router, Route, Switch } from 'dva/router'; import IndexPage from './routes/IndexPage'; import Products from './routes/products'; // 使用hash前端路由模式 function RouterConfig({ history }) { return ( <Router history={history}> <Switch> <Route path="/" exact component={IndexPage} /> <Route path="/products" exact component={Products} /> </Switch> </Router> ); } export default RouterConfig;
dva已經在src/utils/request.js
中爲咱們封裝了fetch請求。因此咱們能夠新建src/services/products.js
來封裝一個向後臺請求商品數據的方法
import request from '../utils/request'; export function fetch() { return request('/api/products'); }
dva 經過 model 的概念把一個領域的模型管理起來,包含同步更新 state 的 reducers,處理異步邏輯的 effects,訂閱數據源的 subscriptions
新建src/models/products.js
import * as productsService from '../services/products'; export default { namespace: 'products', state: { list: [], // 商品列表 }, reducers: { save(state, { payload: { data: list } }) { return { ...state, list }; } }, effects: { *fetch({}, { call, put }) { const { data } = yield call(productsService.fetch); yield put({ type: 'save', payload: { data: data.products }, }); }, }, subscriptions: { setup({ dispatch, history }) { return history.listen(({ pathname }) => { if (pathname === '/products') { dispatch({ type: 'fetch' }); } }); }, }, };
到此,model層和service層咱們已經簡單搭建好,能夠在組件中拿到商品數據
拓展:model
中的subscription
訂閱
model
中的subscription
至關於一個監聽器,能夠監聽路由變化,鼠標,鍵盤變化,服務器鏈接變化,狀態變化等,這樣在其中就能夠根據不一樣的變化作出相應的處理,在這個subsription
中的方法名是隨意定的,每次變化都會一次去調用裏面的全部方法,因此一邊會加相應的判斷dva-loading
插件
在實際開發中,因爲存在向後端請求商品數據,可能不能第一時間拿到商品數據,因此每每在數據加載的過程當中,咱們會給用戶一個「正在加載中...」的提示。而dva-loading
插件能夠幫咱們簡化這一過程。
yarn add dva-loading
修改src/index.js
import createLoading from 'dva-loading'; app.use(createLoading());
接着咱們就能夠在組件中使用
const ProductsList = ({ list, loading }) => { ... // loading的值即爲true,false // 插件會自動設置數據裏的 loading 狀態爲 true 或 false // 咱們能夠經過loading的值判斷是否正處於加載數據的狀態,從而給用戶一些提示 } export default connect(({ products, loading }) => ({ list: products.list, // 注意:products爲所須要的model層的namespace loading: loading.models.products }))(ProductsList);