1. 程式人生 > >React + Redux + Webpack + Antd 小Demo

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

有寫的不對的地方歡迎留言。。。