美文网首页react
一步一步创建dva项目,完成用户管理的 CURD 应用 (rea

一步一步创建dva项目,完成用户管理的 CURD 应用 (rea

作者: 静夜秋雨 | 来源:发表于2018-01-16 16:28 被阅读3911次

    前言

    dva 官方项目里面有些用法不是最新的,本文针对性的解决并以正确的用法告知刚开始接触dva 项目的码友们 ,帮助大家避免入坑。
    12 步 30 分钟,完成用户管理的 CURD 应用 优化版
    git地址:https://github.com/Sawyer-china/react-user-dashboard
    现在我们就开始一步一步的构建 如遇见问题可 添加 qq群:218618405 进行提问

    如果你觉得该文章对你有帮助加个喜欢,github 加个 start 谢谢

    1. 安装全局 dva-cli

    $ npm install dva-cli -g
    

    2. 创建项目

    选好目录然后创建项目

    $ dva new user-dashboard
    

    3. 根据提示进入目录并运行项目

    $ cd user-dashboard
    $ npm start
    

    如果看到以下页面,那么恭喜你,我们往下进行了


    image.png

    4. 引入antd组件库 (andt: 官方地址 https://ant.design/index-cn)

    $ npm install antd --save
    

    安装完成后打开 src/routes/IndexPage.js 引入一个antd组件试试 在文件头部引入

    import Button from 'antd/lib/button'
    import 'antd/lib/button/style/css';  
    

    在function IndexPage 函数中使用组件

    <Button type="primary">Primary</Button>
    

    如果页面出现一个Button 则代表成功了

    5. 按需加载

    在项目中往往我们需要进行组件的按需加载以免去不必要的组件被打包到实际的项目中,以减少js的体积大小
    安装以下插件

    $ cnpm install babel-plugin-import --save-dev
    

    并找到根目录下面的.webpackrc文件,并在文件中添加插件配置

    "extraBabelPlugins": [
        ["import", { "libraryName": "antd", "style": "css" }]
    ]
    

    配置更多玩法参考:https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md
    修改以下代码

    // import Button from 'antd/lib/button'
    // import 'antd/lib/button/style/css'; 
    import { Button } from 'antd'
    

    6. 做 webpack 反向代理

    在配置文件中添加以下代码

    "proxy": {
            "/api": {
                "target": "http://jsonplaceholder.typicode.com/",
                "changeOrigin": true,
                "pathRewrite": { "^/api": "" }
            }
        }
    

    访问 http://localhost:8000/api/users 如果你看见一串json数据代表代理成功,就可以进行下一步开发了(该json数据是dva官方提供的测试数据,使用Mockjs开发)
    完成以上准备工作我们就开始正式的demo开发了

    7. 创建 Users.js Router

    在routes目录下创建Users.js

    import React, { Component } from 'react'
    import { connect } from 'dva'
    
    
    import styles from './Users.css'
    
    class Users extends Component {
        render() {
            return (
                <div className={styles.normal}>
                    Users.js
                </div>
            )
        }
    }
    
    Users.propsTypes = {}
    
    export default connect()(Users)
    

    8. 配置路由 打开根目录router.js

    import React from 'react'
    import { Router, Route, Switch, Redirect, routerRedux } from 'dva/router'
    import IndexPage from './routes/IndexPage'
    
    import dynamic from 'dva/dynamic' // 路由按需加载
    
    const { ConnectedRouter } = routerRedux
    
    function RouterConfig({ history, app }) {
        const IndexPage = dynamic({
            app,
            component: () => import('./routes/IndexPage')
        })
        const Users = dynamic({
            app,
            component: () => import('./routes/Users')
        })
        return (
            <ConnectedRouter history={history}>
                    <Switch>
                        <Route path="/" exact component={IndexPage} />
                        <Route path="/users" exact component={Users} />
                    </Switch>
            </ConnectedRouter>
        )
    }
    
    export default RouterConfig
    

    浏览地址 输入 http://localhost:8000/#/users 将会看到 users 路由页面

    9. 在components 文件夹下 新建 MainLayout/Header.js

    import React, { Component } from 'react'
    import { Menu, Icon } from 'antd'
    import { connect } from 'dva'
    
    import { Link, routerRedux } from 'dva/router'
    
    class Header extends Component {
        render() {
            const { location } = this.props
            return (
                <Menu
                    selectedKeys={[location.pathname]}
                    mode="horizontal"
                    theme="dark"
                >
                    <Menu.Item key="/users">
                        <Link to="/users">
                            <Icon type="bars" />Users
                        </Link>
                    </Menu.Item>
                    <Menu.Item key="/">
                        <Link to="/">
                            <Icon type="home" />Home
                        </Link>
                    </Menu.Item>
                    <Menu.Item key="/404">
                        <Link to="/page-you-dont-know">
                            <Icon type="frown-circle" />404
                        </Link>
                    </Menu.Item>
                    <Menu.Item key="/antd">
                        <a href="https://github.com/dvajs/dva">dva</a>
                    </Menu.Item>
                </Menu>
            )
        }
    }
    
    export default connect()(Header)
    
    

    10. 在 components/MainLayout 新建MainLayout.js

    import React, { Component } from 'react'
    import styles from './MainLayout.css'
    import Header from './Header'
    
    class MainLayout extends Component {
        render() {
            const { children, location } = this.props
            return (
                <div className={styles.normal}>
                    <Header />
                    <div className={styles.content}>
                        <div className={styles.main}>
                            {children}
                        </div>
                    </div>
                </div>
            )
        }
    }
    
    export default MainLayout
    
    

    11. 在 routes 中添加 App.js

    import React, { Component } from 'react'
    import { connect } from 'dva'
    import { withRouter } from 'dva/router'
    
    import MainLayout from '../components/MainLayout/MainLayout'
    
    
    class App extends Component {
        render() {
            let { children, location } = this.props
            return (
                <MainLayout location={location}>
                    {children}
                </MainLayout>
            )
        }
    }
    
    App.propTypes = {}
    
    export default withRouter(
        connect(({ app, loading }) => ({
            app,
            loading
        }))(App)
    )
    

    添加完成之后修改 router.js 页面
    在头部引入 App.js

    import App from './routes/App'
    

    然后修改return 中的代码

    return (
            <ConnectedRouter history={history}>
                <App>
                    <Switch>
                        <Route path="/" exact component={IndexPage} />
                        <Route path="/users" exact component={Users} />
                        <Route path="*" render={() => <Redirect to="users" />} />
                    </Switch>
                </App>
            </ConnectedRouter>
        )
    

    现在就可以切换路由了如下图示:


    222.gif

    接下来着重users页面的开发

    12. 创建 users model 和 service

    新建 src/models/users.js:

    // user api
    import * as usersService from '../services/users'
    // 引入 node 模块
    // import url from 'url'
    // import qs from 'qs'
    
    export default {
       namespace: 'users',
       state: {
           list: [],
           total: 0,
           page: 0
       },
       reducers: {
           /**
            * test
            * @param {*} state 
            * @param {*} param1 
            */
           save(state, { payload: { data: list, total, page } }) {
               return { ...state, list, total, page }
           },
           search(state) {
               return { ...state }
           }
       },
       effects: {
           *fetch({ payload: { page } }, { call, put }) {
               const { data, headers } = yield call(usersService.fetch, { page })
               yield put({
                   type: 'save',
                   payload: {
                       data,
                       total: headers['x-total-count'],
                       page: parseInt(page, 10)
                   }
               })
           },
           *create({ payload: values }, { call, put }) {
               yield call(usersService.create, values)
           },
           *patch({ payload: { id, values } }, { call, put }) {
               yield call(usersService.patch, { id, values })
               yield put({ type: 'reload' })
           },
           *remove({ payload: { id } }, { call, put }) {
               yield call(usersService.remove, { id })
               yield put({ type: 'reload' })
           },
           *reload(action, { put, select }) {
               const page = yield select(state => state.users.page)
               yield put({ type: 'fetch', payload: { page } })
           }
       },
       subscriptions: {
           // setup({ dispatch }, done) {
           //     done('错了错了')
           // throw new Error('Whoops!')
           // }
           setup({ dispatch, history }) {
               return history.listen(({ pathname, search }) => {
                   // const { query } = url.parse(search)
                   // const oPath = qs.parse(query)
                   // if (pathname === '/users') {
                   //     console.log('/users')
                   //     console.log(oPath)
                   //     dispatch({ type: 'fetch', payload: oPath })
                   // }
               })
           }
       }
    }
    
    

    新建 src/services/users.js

    import request from '../utils/request'
    
    export function queryUsers() {
        return request('/api/users')
    }
    
    export function fetch({ page = 1 }) {
        return request(`/api/users?_page=${page}&_limit=5`)
    }
    
    export function create(values) {
        return request('/api/users', {
            methods: 'POST',
            data: JSON.stringify(values)
        })
    }
    
    export function patch({ id, values }) {
        return request(`/api/users/${id}`, {
            methods: 'PATCH',
            data: JSON.stringify(values)
        })
    }
    
    export function remove({ id }) {
        return request(`/api/users/${id}`, {
            methods: 'DELETE'
        })
    }
    

    由于我们需要从 response headers 中获取 total users 数量,所以需要改造下 src/utils/request.js:

    import fetch from 'dva/fetch'
    
    // function parseJSON(response) {
    //     return response.json()
    // }
    
    function checkStatus(response) {
        if (response.status >= 200 && response.status < 300) {
            return response
        }
    
        const error = new Error(response.statusText)
        error.response = response
        throw error
    }
    
    /**
     * Requests a URL, returning a promise.
     *
     * @param  {string} url       The URL we want to request
     * @param  {object} [options] The options we want to pass to "fetch"
     * @return {object}           An object containing either "data" or "err"
     */
    // export default function request(url, options) {
    //   return fetch(url, options)
    //     .then(checkStatus)
    //     .then(parseJSON)
    //     .then(data => ({ data }))
    //     .catch(err => ({ err }));
    // }
    
    async function request(url, options) {
        const response = await fetch(url, options)
        checkStatus(response)
        const data = await response.json()
        const ret = {
            data,
            headers: {}
        }   
    
        if (response.headers.get('x-total-count')) {
            ret.headers['x-total-count'] = response.headers.get('x-total-count')
        }
        return ret
    }
    
    export default request
    

    剩余部分请参考
    https://github.com/sorrycc/blog/issues/18 (该代码可能有部分内容会导致错误)
    请以一下链接为准
    https://github.com/Sawyer-china/react-user-dashboard
    最终完成效果如下图所示:

    222.gif

    如遇问题欢迎加群讨论
    QQ群: 218618405
    github:https://github.com/Sawyer-china

    相关文章

      网友评论

      • Jess_ce:我按步骤来,为什么我的是.webpackrc这样,而你的源代码是这个文件.webpackrc.js; 反代理代码应该写哪个文件里面?
      • 多多__指教:请问这里的删除、添加、修改功能都没有真正改变数据,是因为连接的代理服务器http://jsonplaceholder.typicode.com/的吗?那怎么连接到本地的MongoDB上呢?
        静夜秋雨:@多多__指教 :smiley:
        多多__指教:@静夜秋雨 谢谢
        静夜秋雨:这个只是模拟操作,真实的项目后台会做相应的处理的,删除成功或者失败后后端会返回一个接口,告知的
      • Chopin406:function RouterConfig({ history, app }) 这个函数的参数history,app是从哪里来的,我看你index.js的调用`app.router(require('./router').default)`,看不出来怎么执行的
        Chopin406:多谢了
        静夜秋雨:app.router(require('./router').default) 这一块可以看下
        app.router(({history, app}) =>
        <Router history={history}>
        <Route path="/" component={HomePage} />
        </Router>
        );
        静夜秋雨:这是一个回调函数,可以把这个打印出来看看是什么
      • 北海lewis:为什么一直访问不到api/users,一直报错说index.js中有语法错误
        静夜秋雨:可能是没有做反向代理,直接访问这个api可以访问到json数据吗?
      • 我和詹皇55开:安裝到第8部import dynamic from 'dva/dynamic' 的時候會顯示
        Module not found: Can't resolve 'dva/dynamic' 是什麽情況?
        深海潜鱼:我也出现了这个问题,是怎么解决的呢?
        静夜秋雨:你的项目使用dva 创建的吗?这个提示是没有找到这个包

      本文标题:一步一步创建dva项目,完成用户管理的 CURD 应用 (rea

      本文链接:https://www.haomeiwen.com/subject/xgjsoxtx.html