结构-行为-样式 - React + Redux + Webpack + Antd 小Demo

最近在学习React,从小白到入门,谈不上精通。每一个新的框架都是一样,主要是自己有自己的学习方法,再多的框架也是手到擒来。React主要的思想就是组件开发,官网上的Demo也说的很详细,就是让你在做一个页面之前,先想想,这个东西能拆分成什么模块,子模块,怎样嵌套。设计的思路理清楚之后才是动手写代码,同时,作为一个框架,它采取的是一种包容的态度。一个Demo下来,除了官方的那几个Js之外,其他大部分都是第三方的插件。Redux就是Flux的改编版本。不费话了,来看看我的这个Demo吧:

首先,咱们来看下整体目录结构:

一、先看看最终的样子:

  其实就是套用了Antd的一个布局架子,加上表格和自定义的搜索栏,基本上实现了增删改查的功能。这个页面包括搜索模块与表格模块,这里咱们只说表格模块,搜索模块类似。

二、第一层,也就是整个页面的外层,调用 的是Antd的布局代码(布局 Layout - Ant Design):

import React from 'react';
import NavLink from './NavLink';
import { Layout, Menu, Breadcrumb, Icon } from 'antd';

const { SubMenu } = Menu;
const { Header, Content, Sider } = Layout;


export default React.createClass({
    
    render(){
        return(
          <Layout>
            <Header className="header">
              <div className="logo" >
                    <img src={require("../images/logo.png")} alt="" width="112" height="35" />
              </div>
              <Menu
                theme="dark"
                mode="horizontal"
                defaultSelectedKeys={['3']}
                style={{ lineHeight: '64px' }}
                className="headerMenu"
              >
                <Menu.Item key="1"><NavLink to="/" >首页</NavLink></Menu.Item>
                <Menu.Item key="2"><NavLink to="/about">报表</NavLink></Menu.Item>
                <Menu.Item key="3"><NavLink to="/repos/pageb" onlyActiveOnIndex>配置</NavLink></Menu.Item>
              </Menu>
            </Header>
            <Layout>
              <Sider width={200} style={{ background: '#BD2626' }}>
                <Menu
                  mode="inline"
                    theme="dark"
                  defaultSelectedKeys={['1']}
                  defaultOpenKeys={['sub1']}
                  style={{ height: '100%' }} className="diy-dark"
                >
                  <SubMenu key="sub1" title={<span><Icon type="user" />权限管理</span>}>
                    <Menu.Item key="1"><NavLink to="/repos/pagea">组织架构</NavLink></Menu.Item>
                    <Menu.Item key="2"><NavLink to="/repos/pageb">用户管理</NavLink></Menu.Item>
                    <Menu.Item key="3">角色管理</Menu.Item>
                  </SubMenu>
                  <SubMenu key="sub2" title={<span><Icon type="laptop" />参数配置</span>}>
                    <Menu.Item key="5">功能配置</Menu.Item>
                    <Menu.Item key="6">业态配置</Menu.Item>
                    <Menu.Item key="6">特征配置</Menu.Item>
                    <Menu.Item key="6">码表配置</Menu.Item>
                  </SubMenu>
                  <SubMenu key="sub3" title={<span><Icon type="notification" />日志管理</span>}>
                    <Menu.Item key="9">接口日志</Menu.Item>
                    <Menu.Item key="10">操作日志</Menu.Item>
                  </SubMenu>
                </Menu>
              </Sider>
              <Layout style={{ padding: '0 24px 24px' }}>
                <Breadcrumb style={{ margin: '12px 0' }}>
                  <Breadcrumb.Item>配置</Breadcrumb.Item>
                  <Breadcrumb.Item>权限管理</Breadcrumb.Item>
                  <Breadcrumb.Item>组织架构</Breadcrumb.Item>
                </Breadcrumb>
                <Content style={{ background: '#fff', padding: 15, margin: 0, minHeight: 515 }}>
                    {this.props.children}
                </Content>
              </Layout>
            </Layout>
          </Layout>
        )}
   });

  注:整个Js看作是一个组件,子组件中Content下面。

三、第2-1层,表格组件:

  3.1、首先,定义两个自定义事件,删除与修改:

      

 //删除一条记录
  handleDelete(record){
     this.props.dispatch(action.doDelete({record,data}));
  }
    //修改一条记录
  handleModify (record) {
      record.title = "修改记录";
      record.modalType = "modify";
      let isVisible = true;
     this.props.dispatch(action.setVisible(isVisible));
      let current = record;
     this.props.dispatch(action.showModal({current,data}));
  }

  3.2、然后在构造函数中绑定我们的自定义事件,得到当前对象:

       

  //构造函数
  constructor(props) {
      super(props);
      this.handleDelete = this.handleDelete.bind(this);
      this.handleModify = this.handleModify.bind(this);
  }

  3.3、调用Antd的表格组件,进行表格的组装,事件在columns时就可以先设定好,具体如下:

 const columns = [{
          title: '姓名',
          dataIndex: 'name',
          key: 'name',
          render: text => <a href="javascript:;">{text}</a>,
        }, {
          title: '邮箱',
          dataIndex: 'email',
          key: 'email',
        },  {
          title: '手机',
          dataIndex: 'phone',
          key: 'phone',
        },{
          title: '地址',
          dataIndex: 'address',
          key: 'address',
        }, {
          title: '操作',
          key: 'action',
          render: (text, record) => (
            <span>
              <a href="javascript:;" onClick={()=>this.handleModify(record)} >修改</a>
              <span className="ant-divider" />
              <a href="javascript:;" onClick={()=>this.handleDelete(record)} >删除</a>
            </span>
          ),
        }];

  3.4、最后Render出来的虚拟Dom就一行:

return (
       <Table columns={columns} dataSource={data} />
    );

四、事件处理与状态传递(Redux相关):

  4.1、Redux工作原理(盗图一张):

    

 

    那些很专业的话就不说了,这里就说实际Demo中的对应关系。实际开发中,应该要定义Action、Reducer、Store三个文件,它们工作流程就是上面图中所示。

  4.2、表格相关 Action:

//修改记录
export const doModify = ({current,data})=>{
    for(let i=0;i<data.length;i++){
        if(current.key == data[i].key){
            data[i] = current;
            data[i].address = current.residence.join(" - ");
            break;
        }
    }
    const list = data;
    return {
        type:'DO_MODIFY',
        list
    }
}
//删除记录
export const doDelete = ({record,data})=>{
    for(let i=0;i<data.length;i++){
        if(data[i].key == record.key){
            data.splice(i,1);
            break;
        }
    }
    let list = data;
    return {
        type:'DO_DELETE',
        list 
    }
}

注:应该把当前动作所包含的逻辑写在Action中,不要写在Reducer中。触发Action的地方应该是在组件的文件中,通过dispatch方法来触发 :this.props.dispatch(action.showModal({current,data})); 类似这个。

  4.3、增删改查的 Reducer:

const doHeader = (state='',action)=>{
    switch(action.type){
        case 'DO_SUBMIT':
            return {"list":action.list};
        case 'DO_ADD':
            return {"list":action.list};
        case 'DO_MODIFY':
            return {"list":action.list};
        case 'DO_DELETE':
            return {"list":action.list};
        default:
            return state;
    }
}

注:建议把一个组件的Reducer放在一起,最终Store调用 的就是一个总的Reducer。

  4.4、只有一个Store:

import {createStore} from 'redux';
import todoApp from '../reducers';


let store = createStore(
    todoApp
);

export default store;

注:这里的todoApp就是Reducers的总入口。

所以,最终的思路就是,在组件的文件中,有一个事件,通过 dispatch 分发到Action,处理完之后通过Reducer反映到全局的State中,直接的就是另一个组件监听到它需要 的State改变了,反映到页面。整个过程都是单向的,一个组件中的Props值是不可以改变的。

五、webpack配置:

  5.1、开发模式和发布模式:

    5.1.1、开发模式会定义devserver和热更新,具体如下:

 devServer: {//webpack-dev-server 配置
        contentBase: "./server",//本地服务器所加载的页面所在的目录
        port: 8888,
        colors: true,//终端中输出结果为彩色
        historyApiFallback: true,//不跳转
        inline: true,
        hot:true//热更新
    },
    postcss:[
        autoprefixer({browsers:['last 10 versions']})//postcss 插件
    ],
    plugins:[
        new webpack.BannerPlugin('Copyright Chvin'),//添加 js头
        new webpack.HotModuleReplacementPlugin()//热更新
    ]

    5.1.2、发布模式则不用那么麻烦,定义一下文件导出格式与入口文件:

plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/server/index.tmpl.html"
        }),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("[name]-[hash].css"),
        new webpack.optimize.DedupePlugin(),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV':'"production"'
        })
    ],

  注:这里省略了其他配置,只是写出了要注意的地方

  5.2、打包与调试命令配置(在Package.json中),start是本地开发命令,build就是发布文件生成:

 "scripts": {
    "start": "webpack-dev-server --progress && echo sddss",
    "build": "webpack --config ./webpack.production.config.js --progress"
  },

六、路由配置:

  6.1、引用路由:

import { Router, Route, hashHistory, IndexRoute } from 'react-router';

  6.2、首页、嵌套。Router中包含Route,首页的路由可以通过IndexRoute来定义,同时,要定义路由中对应的组件。具体如下:

render(
    <Provider store={store} >
        <Router history={hashHistory}>
        <Route path="/" component={App}>
          <IndexRoute component={Home}/>
          <Route path="/repos" >
            <Route path="/repos/pagea" component={About}/>
            <Route path="/repos/pageb" component={Counter}/>
          </Route>
          <Route path="/about" component={About}/>
        </Route>
      </Router>
    </Provider>
,document.getElementById('root'));

完整Demo下载:https://github.com/chickentang/GIT_TANG

有写的不对的地方欢迎留言。。。

猜你喜欢

转载自blog.csdn.net/kingbox000/article/details/79938550