05-组件设计实践
准备
建议对照完整代码一起看 user-dashboard。
按照之前快速上手的内容,我们可以使用 dva-cli 工具快速生成规范的目录,在命令行中输入:
$ mkdir myApp && cd myApp
$ dva init
现在,规范的样例模板我们已经有了,接下来我们一步一步添加自己的东西,看看如何完成我们的组件设计。
设置路由
在准备好了 dva 的基本框架以后,需要为我们的项目配置一下路由,这里首先设置 Users Router Container 的访问路径,并且在 /routes/
下创建我们的组件文件 Users.jsx
。
// .src/router.js
import React, { PropTypes } from 'react';
import { Router, Route } from 'dva/router';
import Users from './routes/Users';
export default function({ history }) {
return (
<Router history={history}>
<Route path="/users" component={Users} />
</Router>
);
};
// .src/routes/Users.jsx
import React, { PropTypes } from 'react';
function Users() {
return (
<div>User Router Component</div>
);
}
Users.propTypes = {
};
export default Users;
其它路由可以自行添加,关于路由更多信息,可以查看 react-router 获取更多内容。
Users Container Component 的设计
基础工作都准备好了,接下来就开始设计 Users Container Component。在本项目中 Users Container 的表现为 Route Components(这也是 dva 推荐的结构划分),可以理解页面维度的容器,所以我们在 /routes/
下加入 Users.jsx
。
我们采用自顶向下
的设计方法,修改 ./src/routes/Users.jsx
如下:
// ./src/routes/Users.jsx
import React, { Component, PropTypes } from 'react';
// Users 的 Presentational Component
// 暂时都没实现
import UserList from '../components/Users/UserList';
import UserSearch from '../components/Users/UserSearch';
import UserModal from '../components/Users/UserModal';
// 引入对应的样式
// 可以暂时新建一个空的
import styles from './Users.less';
function Users() {
const userSearchProps = {};
const userListProps = {};
const userModalProps = {};
return (
<div className={styles.normal}>
{/* 用户筛选搜索框 */}
<UserSearch {...userSearchProps} />
{/* 用户信息展示列表 */}
<UserList {...userListProps} />
{/* 添加用户 & 修改用户弹出的浮层 */}
<UserModal {...userModalProps} />
</div>
);
}
export default Users;
其中,UserSearch
,UserList
,UserModal
我们还未实现,不过我们可以暂时让他们输出一段话,表示占位,基本的结构表现的很清楚,Users Router Container
由这三个 Presentational Components
组成。(其中{...x}的用法可以参看es6)
// ./src/components/Users/UserSearch.jsx
import React, { PropTypes } from 'react';
export default ()=><div>user search</div>;
// ./src/components/Users/UserList.jsx
import React, { PropTypes } from 'react';
export default ()=><div>user list</div>;
// ./src/components/Users/UserModal.jsx
import React, { PropTypes } from 'react';
export default ()=><div>user modal</div>;
现在如果你的本地环境是成功的,访问 http://127.0.0.1:8989/ 浏览器中看到:
需要注意的是,定义我们的组件一般有三种方式:
// 1. 传统写法
const App = React.createClass({});
// 2. es6 的写法
class App extends React.Component({});
// 3. stateless 的写法(我们推荐的写法)
const App = (props) => ({});
其中第1种是我们不推荐的写法,第2种是在你的组件涉及 react 的生命周期方法的时候采用这种写法,而第3种则是我们一般推荐的写法。详细内容可以参看Stateless Functions。
在确定了最简陋的结构以后,接下来需要做的事情,就是完善 Users Container 中的组件,在这里我们优先实现 UserList
组件。
Userlist 组件
暂时放下<UserSearch />
和<UserModal />
,先来看看<UserList />
的实现,这是一个用户的展示列表,我们期望只需要把数据传入进去,修改 ./src/components/Users/UserList.jsx
:
// ./src/components/Users/UserList.jsx
import React, { Component, PropTypes } from 'react';
// 采用antd的UI组件
import { Table, message, Popconfirm } from 'antd';
// 采用 stateless 的写法
const UserList = ({
total,
current,
loading,
dataSource,
}) => {
const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
render: (text) => <a href="#">{text}</a>,
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '住址',
dataIndex: 'address',
key: 'address',
}, {
title: '操作',
key: 'operation',
render: (text, record) => (
<p>
<a onClick={()=>{}}>编辑</a>
<Popconfirm title="确定要删除吗?" onConfirm={()=>{}}>
<a>删除</a>
</Popconfirm>
</p>
),
}];
// 定义分页对象
const pagination = {
total,
current,
pageSize: 10,
onChange: ()=>{},
};
return (
<div>
<Table
columns={columns}
dataSource={dataSource}
loading={loading}
rowKey={record => record.id}
pagination={pagination}
/>
</div>
);
}
export default UserList;
为了方便起见,我们这里使用一个优秀的UI组件库 antd。 antd
提供了 table 组件,可以让我们方便的展示相关数据,具体使用方式可以参看其文档。
需要注意的是,由于我们采用了 antd,所以我们需要在我们的代码中添加样式,可以在 ./src/index.jsx
中添加一行:
import 'antd/dist/antd.css';
这样我们使用的的 antd 组件就可以展示出样子了:
其中我们发现,在我们设计 UserList
的时候,需要将分页信息 total、current
以及加载状态信息 loading
也传入进来,所以现在使用 UserList
就需要像这样:
<UserList
current={current}
total={total}
dataSource={list}
loading={loading}
/>
接下来,我们回到 Users Router Container
模拟一些静态数据,传入 UserList ,让其展现数据。
给 UserList 添加静态数据
// ./src/routes/Users.jsx
import React, { Component, PropTypes } from 'react';
// Users 的 Presentational Component
// 暂时都没实现
import UserList from '../components/Users/UserList';
import UserSearch from '../components/Users/UserSearch';
import UserModal from '../components/Users/UserModal';
// 引入对应的样式
// 可以暂时新建一个空的
import styles from './Users.less';
function Users() {
const userSearchProps={};
const userListProps={
total: 3,
current: 1,
loading: false,
dataSource: [
{
name: '张三',
age: 23,
address: '成都',
},
{
name: '李四',
age: 24,
address: '杭州',
},
{
name: '王五',
age: 25,
address: '上海',
},
],
};
const userModalProps={};
return (
<div className={styles.normal}>
{/* 用户筛选搜索框 */}
<UserSearch {...userSearchProps} />
{/* 用户信息展示列表 */}
<UserList {...userListProps} />
{/* 添加用户 & 修改用户弹出的浮层 */}
<UserModal {...userModalProps} />
</div>
);
}
Users.propTypes = {
users: PropTypes.object,
};
export default Users;
传入了静态数据以后,组件的表现如下:
组件设计小结
虽然我们上面实现的代码很简单,但是已经包含了组件设计的主要思路,可以看到 UserList
组件是一个很纯粹的 Presentation Component
,所需要的数据以及状态是通过 Users Router Component
传递的,我们现在还是用的静态数据,接下来我们来看看如何在 model 创建 reducer 来将我们的数据抽象出来。
下一步,进入添加Reducers。