dva-cli+ant-design的使用記錄

ha_wolf發表於2018-09-19

這一套很多時候會拿來當做專案的後臺管理系統,ant-design提供了豐富的元件,到目前為止使用的體感都很不錯,在此記錄一些搭建,以及使用時的歷程,個人理解難免出現偏差,歡迎糾正。

0.初識DVA

dva 首先是一個基於 redux 和 redux-saga 的資料流方案,然後為了簡化開發體驗,dva 還額外內建了 react-router 和 fetch,所以也可以理解為一個輕量級的應用框架 相比於create-react-app 構建的專案我認為dva更易於理解和學習。
那麼dva裡有什麼呢?
過去曾寫過react-native,理解過一些關於redux的東西,相較而言redux和dva概念很像,都是在View與資料之間增加了一層用於更新資料的Reducer。更新資料只能通過dispatch一個action,更新的資料也會直觀的反應在View上。
dva應該是redux的超集,他提供了更方便的語法糖@connect,loading狀態,提供了Effect來用於呼叫請求,發出action,此外還包含了react-router,fetch。可以說是一個完整的react資料流解決方案。
至於更深層次的理解,那就需要dva的原始碼解析

1.如何搭建?

搭建框架這東西還遠為熟絡,很多時候都是照著文件自己嘗試的,很幸運的是ant-design的文件寫的很好。
專案實戰這裡對如何搭dva的框架有了較為詳細描述,可以酌情參考。

優化:
上述的簡單配置還遠未達到開發的要求,我們還需要:
1.動態載入models。
2.將HashRouter轉化為BrowserRouter,去掉難看的‘#’。
3.線上配置router(使用資料庫的資料)用於在應用中配置不同賬號的許可權。
4.封裝請求,統一做錯誤判斷。
5.配置代理,解決開發中的跨域問題,以及BrowserRouter帶來的重新整理問題。
6.其他......

2.各類元件使用心得

2.1 一切的開始:路由與Menu元件,路由是後臺非常核心的一部分。

2.2 使用頻率最高:Table元件
ant-design的表格自帶有篩選,分頁等功能,配合dva使用起來體感還不錯。注意,table需要設定key,不然會報紅,如果key相同則會出現難以預見的bug,具體可以嘗試下就知道了。
此外和別的表格元件類似,他的顯示控制可以在colums中做控制,render中同樣可以做一些簡單的三目判斷。具體如下:

   const paginationProps = {
      showSizeChanger: true,
      showQuickJumper: true,
      ...ChannelDetail.managerLogs.pagination,
    };
      <Table
        loading={loading}
        dataSource={ChannelDetail.managerLogs.list}
        pagination={paginationProps}
        onChange={this.handleChange}
        columns={columns}
        // className="small-table"
      />
    const columns = [
      {
        title: '時間',
        dataIndex: 'createTime',
        key: 'createTime',
        render: text => {
          return moment(text).format('YYYY-MM-DD hh:mm:ss');
        },
      },
      //也可以簡寫為
      {
        title: '時間',
        dataIndex: 'createTime',
        key: 'createTime',
        render: text =>(moment(text).format('YYYY-MM-DD hh:mm:ss');) 
      },
    ];
複製程式碼

分頁時,需要傳入pagination,並再資料中有同名的分頁欄位,並再change方法中傳入paginationProps中的分頁欄位名作為引數,loading用於loading狀態的控制。
此外,antd自帶的表格間距(padding)有些大,可以通過width來調整,而我則是寫了個類名來統一縮小padding了。

2.3 非常強大的表單元件:Form
自帶表單驗證,資料提交等功能,雖然react的表單資料不能雙向繫結,但有了他後資料就不需要走state了,他自帶強大的set功能,並且自帶表單驗證,可以用正則,注意使用了FormItem後就不需要使用setState,以及value來做資料繫結了,this.props.form.setFieldsValue更適用,且不會報warning...,另外formItemLayout可以通過與antd的柵格外掛來控制label與輸入框的長度。示例:

    <FormItem label="分成比例" {...formItemLayout}>
      {this.props.form.getFieldDecorator('guanli', {
        rules: [
          { required: true, message: '管理員分成不能為空' },
          { pattern: /^100$|^(\d|[1-9]\d)(\.\d+)*$/, message: '分成比例應在0~100之間' },
        ],
        initialValue: 0,
      })(
        <Input
          placeholder="請輸入管理員分成"
          onChange={e => {
            e.target.value != 0
              ? this.props.form.setFieldsValue({
                  guanli: e.target.value,
                  yuangong: 100 - e.target.value,
                })
              : this.props.form.setFieldsValue({ guanli: 0, yuangong: 0 });
          }}
          type="number"
        />
      )}
    </FormItem>
複製程式碼

FormItem的表單驗證:

  subAdd = e => {
    const { dispatch } = this.props;
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        values.divideRatio = `${values.guanli}:${values.yuangong}`; //對引數格式做最後的控制
        dispatch({
          type: 'channel/addChannel',
          payload: {
            ...values,
          },
          callback: res => {
            if (res.code === 0) {
              message.success('新增成功!');
              this.props.form.resetFields();
              this.searchBtn();
              this.setState({ channelId: res.data, addModal: false, configModal: true });
            } else {
              message.error(res.msg);
            }
          },
        });
      }
    });
  };
複製程式碼

2.4 彈框Modal。antd的彈框自定義程度還是比較高的,其中包括了正常的彈框和確認框,不過彈框的位置應該不是按照居中來的,彈框較小的時候可能存在位置偏上的情況,如果設計要求較高,那就需要通過global來做矯正咯。
彈出框同樣可以用來做成元件,配合上面的formItem使用也是可以的。如下,staffOk中兩個引數分別為子元件傳遞的通過表單校驗後的輸入引數值,和一個重置表單的方法,這樣就能在父元件中呼叫介面,並且重置表單。而addModal則用來控制子元件的顯示與隱藏。

      <AddStaff
          handleOk={this.staffOk}
          ChannelList={dataall}
          handleCancel={this.staffCancel}
          addModal={this.state.staffModal}
          channelId={this.state.channelId}
          type={1}
        />
        
    staffOk = (e, func) => {
        this.setState({ addModal: false });
        const { dispatch } = this.props;
        dispatch({
          type: 'staff/addChannelStaff',
          payload: {
            ...e,
          },
          callback: res => {
            if (res.code === 0) {
              message.success('新增成功!');
              this.searchBtn();
              func(); // 子元件傳遞的重置表單方法
              this.setState({ staffModal: false });
            } else {
              message.error(res.msg);
            }
          },
        });
  };
複製程式碼

2.5 柵格元件,antd的柵格元件與bootstrap的類似,分為了24分,支援offset,push之類的位置調控,gutter來控制間距,提供響應式佈局,此外還包含了flex的佈局控制。

2.6 其他...

3.dva帶來的方便與繁雜

3.1 dva-loading dva很強大一個功能來自於他的loding是自帶的,能通過監聽請求來設定loading的狀態,而antd的loading元件很齊全,無論是表格的loading還是Spin元件都能很好的適配。但是dva的loading存在一個問題,當ajax請求出現伺服器錯誤時,他是不會將loading狀態取消的,而antd自帶的處理是當介面報錯時會自動跳轉去相應的報錯頁面,而當你返回時,這個loading狀態依然還在,並且只能通過重新載入瀏覽器才能取消,倘若後臺介面處理不周,那儘量還是自己寫loading的為好,此外,可能存在一些配置修改dva的loading來對介面報錯做出反應,但是暫時不知如何處理......

3.2 redux的vm層中往往會在state中定義初始值,在定義的過程中儘量還是講資料結構中會出現的層級都寫進去,這樣在首屏載入時就不會報一些空值的錯誤,也在後續的處理中方便很多。

3.3 非同步請求。很多時候首屏的資料往往只需要一張表格,而後續的一些篩選條件,地區,以及選擇列表可以後續載入,這時用非同步請求就會大大提升使用者體驗。

4.eslint的愛與恨

4.1 為了程式碼規範化,專案中引入了eslint,為了適應其規範,同時引入了prettier來格式化程式碼,用的是windows的系統,可能會出現crlf 和lf 的報錯,於是在.eslintrc檔案 rules 裡面 配置 "linebreak-style": [0 ,"error", "windows"]。

5.那些年我們踩過的坑

5.1 BrowserRouter帶來的重新整理瀏覽器報404的問題。單頁應用在不做處理時重新整理頁面會去尋找路徑下的資源而不是從index.js的入口進入,就會導致上述的問題。在開發過程中往往通過proxy做代理來解決這個問題。而在伺服器上則需要對伺服器做一些相應的配置。這裡找到一個比較齊全的解決方案:解決方案

5.2 使用cnpm安裝依賴會導致build無法使用的問題。

6.一些記錄

6.1 為何對元件的樣式修改中必須用:global()才能生效?
react引入了CSS Modules它自動為每一個類生成一個雜湊值,可以惟一標誌這個類,它讓我們可以像使用js模組那樣,想用哪部分樣式,就引入哪部分樣式。但是對元件賦予className時這是通過元件封裝將傳入值賦予這個元件的class,不會有雜湊值,所以這時候就需要:global(),:global()代表的全域性變數是不會附加雜湊值的,這時樣式才能對元件的class生效。
CSS Modules