1. 程式人生 > >React共享單車後臺管理系統開發(記錄筆記2)——主頁面架構設計

React共享單車後臺管理系統開發(記錄筆記2)——主頁面架構設計

本文基於React共享單車後臺管理系統的開發進行學習記錄,內含React生態系統的整體介紹及相關內容,最終成果物程式碼請參見最後一章。

一.基礎外掛安裝,Less檔案載入配置

  • 安裝React-Router:yarn add react-router-dom、Axios:yarn add axios

  • 安裝AntD(是基於less開發的)故需要讓webpack支援less或import antd/dist/entd.css直接引入css檔案

  • 安裝less:yarn add less

  • 安裝less-loader:yarn add less-loader

  • 暴露webpack配置檔案:執行yarn eject

    暴露webpack配置檔案scripts和config(webpack.config.dev.js是開發環境;webpack.config.prod.js是生產環境;webpackDevServer.config.js是本地server開發)及資訊

  • 修改less-loader

    在webpack.config.dev.js和webpack.config.prod.js中新增內容如下

    {
      test: /\.less$/,
          use: [
              require.resolve('style-loader'),
              {
                  loader: require
    .resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { // Necessary for external CSS imports to work // https://github.com/facebookincubator/create-react-app/issues/2677
    ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009', }), ], }, }, { loader:require.resolve('less-loader') } ], },

    由於use陣列的執行順序是從後到前,故在最下方新增less-loader載入則會被最先執行。修改完檔案內容後需要重啟專案yarn start生效

  • 使用AntD

    • 使用AntD中的Button

    • 在antd中引入Button元件

      import {Button} from 'antd'
    • 引入antd的css樣式

      import 'antd/dist/antd.css'
    • 頁面中使用元件

      <Button>AntD按鈕</Button>
    • 按需載入css檔案內容【只會打包所需元件,減少請求量】

    • 安裝babel-plugin-import外掛:yarn add babel-plugin-import此外掛用於實現按需載入所需的css,而不是每次都引入所有的css

    • 把檔案暴露出來的方式修改配置檔案(prod和dev檔案),在options中新增plugin陣列資訊,libraryName表示匯出的庫名,style為true表示將css引入專案行內樣式。相當於直接把antd.less檔案引入到專案js中,不用再引入import 'antd/dist/antd.css'

      {
        test: /\.(js|jsx|mjs)$/,
        include: paths.appSrc,
        loader: require.resolve('babel-loader'),
        options: {
            plugins:[
                ['import',[{
                    libraryName:'antd',
                    style:true
                }]]
            ],
            compact: true,
        },
      },
    • 配置樣式主題,在載入loader時新增options並設定主題顏色,可以統一修改Antd主題顏色

    {
        test: /\.less$/,
            use: [
                require.resolve('style-loader'),
                {
                    loader: require.resolve('css-loader'),
                    options: {
                        importLoaders: 1,
                    },
                },
                {
                    loader: require.resolve('postcss-loader'),
                    options: {
                        // Necessary for external CSS imports to work
                        // https://github.com/facebookincubator/create-react-app/issues/2677
                        ident: 'postcss',
                        plugins: () => [
                            require('postcss-flexbugs-fixes'),
                            autoprefixer({
                                browsers: [
                                    '>1%',
                                    'last 4 versions',
                                    'Firefox ESR',
                                    'not ie < 9', // React doesn't support IE8 anyway
                                ],
                                flexbox: 'no-2009',
                            }),
                        ],
                    },
                },
                {
                    loader:require.resolve('less-loader'),
                    options:{
                        modules:false,
                        modifyVars:{
                            "@primary-color":"#f9c700"
                        }
                    }
                }
            ],
    },

    primary-color是antd的less變數,修改時可以修改專案的主色調。

二.專案主頁結構開發

1.頁面結構定義

  • 左側導航欄,右側顯示內容
  • 右側顯示內容分別分為上Header、中Content和下Footer部分

2.目錄結構定義

  • 定義components包,分別加入Footer、Header、NavLeft包,內部加入index.js檔案,匯入時可以匯入到包的級別,系統會自動尋找包下的index.js檔案
  • 定義style包,內部加入common.less檔案定義全域性樣式

3.柵格系統使用

  • 柵格系統一共24列
  • Antd中行用Row元件,列使用Col元件,列存在span屬性(span={長度值}的方式寫入span屬性),同一Row下的Col span總和為24

4.calc計算方法使用

  • calc()是less中動態計算長度值

  • 任何長度值都可以用calc()函式進行計算

  • 例子使用:

    width:calc(100%-50px)//表示寬度屬性是整個佈局的100%減去50px的長度
  • calc(100vh):vh的含義相當於1%,100vh即是100%

5.關於less

  • less是預編譯器
  • eg1.在div下有a標籤,想設定a的樣式和div的樣式,在less中可以在定義時巢狀
//css中
div{...}
div a{...}

//less中
div{
    ...
    a:{
      ...  
    }
}
  • eg2.less可以使用定義的變數
@colorA:'red'
div{
    color:@colorA
    a:{
        color:black  
    }
}

6.例項程式碼

  • admin.js

    import React from 'react';
    import { Row, Col } from 'antd';
    import Header from './components/Header';
    import Footer from './components/Footer';
    import NavLeft from './components/NavLeft';
    import './style/common.less'
    class Admin extends React.Component{
      render(){
          return(
              <Row className="container">
                  <Col span={4} className="nav-left">
                      <NavLeft/>
                  </Col>
                  <Col span={20} className="main">
                      <Header/>
                      <Row className="content">
                          content
                      </Row>
                      <Footer/>
                  </Col>
              </Row>
          );
      }
    }
    export default Admin;
  • style/common.less

    .container{
      display: flex;
      .nav-left{
          width: 15%;
          min-width: 180px;
          height: calc(100vh);
          background-color: red;
      }
      .main{
          flex: 1;
          height: calc(100vh);
      }
      .content{
          position: relative;
          padding: 20px;
      }
    }
  • components/NavLeft/index.js

    import React from 'react';
    class NavLeft extends React.Component{
      render(){
          return(
              <div>
                  This is NavLeft
              </div>
          );
      }
    }  
    export default NavLeft;
  • components/Header/index.js

    import React from 'react';
    class Header extends React.Component{
      render(){
          return(
              <div>Header</div>
          )
      }
    }
    export default Header;
  • components/Footer/index.js

    import React from 'react';
    class Footer extends React.Component{
      render(){
          return(
              <div>Footer</div>
          )
      }
    }
    export default Footer;

7.導航欄內容

  • 在config下編寫manuConfig.js返回導航欄內容title和key

    const menuList = [
      {
          title:'首頁',
          key:'/admin/home'
      },
      {
          title:'UI',
          key:'/admin/ui',
          children:[
              {
                  title:'按鈕',
                  key:'/admin/ui/buttons',
              },
              {
                  title:'彈框',
                  key:'/admin/ui/modals',
              },
              {
                  title:'Loading',
                  key:'/admin/ui/loadings',
              },
              {
                  title:'通知提醒',
                  key:'/admin/ui/notification',
              },
              {
                  title:'全域性Message',
                  key:'/admin/ui/messages',
              },
              {
                  title:'Tab頁籤',
                  key:'/admin/ui/tabs',
              },
              {
                  title:'圖片畫廊',
                  key:'/admin/ui/gallery',
              },
              {
                  title:'輪播圖',
                  key:'/admin/ui/carousel',
              }
          ]
      },
      {
          title:'表單',
          key:'/admin/form',
          children:[
              {
                  title:'登入',
                  key:'/admin/form/login',
              },
              {
                  title:'註冊',
                  key:'/admin/form/reg',
              }
          ]
      },
      {
          title:'表格',
          key:'/admin/table',
          children:[
              {
                  title:'基礎表格',
                  key:'/admin/table/basic',
              },
              {
                  title:'高階表格',
                  key:'/admin/table/high',
              }
          ]
      },
      {
          title:'富文字',
          key:'/admin/rich'
      },
      {
          title:'城市管理',
          key:'/admin/city'
      },
      {
          title:'訂單管理',
          key:'/admin/order',
          btnList:[
              {
                  title:'訂單詳情',
                  key:'/admin/order/detail'
              },
              {
                  title:'結束訂單',
                  key:'/admin/order/finish'
              }
          ]
      },
      {
          title:'員工管理',
          key:'/admin/user'
      },
      {
          title:'車輛地圖',
          key:'/admin/bikeMap'
      },
      {
          title:'圖示',
          key:'/admin/charts',
          children:[
              {
                  title:'柱形圖',
                  key:'/admin/charts/bar'
              },
              {
                  title:'餅圖',
                  key:'/admin/charts/pie'
              },
              {
                  title:'折線圖',
                  key:'/admin/charts/line'
              },
          ]
      },
      {
          title:'許可權設定',
          key:'/admin/permission'
      },
    ];
    export default menuList;
  • 在public資料夾下新增assets資料夾,放置logo-ant.svg圖片。(注意:public檔案是build之後儲存內容的檔案,通常內部放置靜態資源,public下的檔案內容在訪問時全是通過根目錄直接訪問檔案地址)

  • 編寫Navleft元件內容/src/Navleft/index.js,使用antd中的Menu和Icon元件,Menu元件的theme屬性可以設定選單樣式。對應的SubMenu元件需要設定title和key屬性值,Menu.Item元件需要設定title和key屬性值和元件內容。

    import React from 'react';
    import {Menu,Icon} from 'antd';
    import MenuConfig from './../../config/menuConfig';
    import './index.less';
    import MenuItem from 'antd/lib/menu/MenuItem';
    const SubMenu = Menu.SubMenu;
    class NavLeft extends React.Component{
      componentWillMount(){
          const menuTreeNode = this.renderMenu(MenuConfig);
          this.setState({
              menuTreeNode
          })
      }
      //選單渲染
      renderMenu=(data)=>{
          return data.map((item,index)=>{
              if(item.children){
                  return(
                      <SubMenu title={item.title} key={item.key}>
                          {this.renderMenu(item.children)}
                      </SubMenu>
                  )
              }
              return <Menu.Item title={item.title} key={item.key}>{item.title}</Menu.Item>
          })
      }
      render(){
          return(
              <div>
                  <div className="logo">
                      <img src="/assets/logo-ant.svg"  alt=""></img>
                      <h1>Imooc MS</h1>
                  </div>
                  <Menu theme="dark">
                      {this.state.menuTreeNode}
                  </Menu>
              </div>
          );
      }
    }  
    export default NavLeft;
  • 編寫Navleft元件樣式/src/Navleft/index.less

    .logo{
      line-height: 90px;
      padding-left: 20px;
      background-color:#001529;
      img{
          height:35px;
          vertical-align: middle;
      }
      h1{
          color: #ffffff;
          font-size: 20px;
          display: inline-block;
          vertical-align: middle;
          margin: 0 0 0 10px;
      }
    }

8.編寫首頁頭部內容

  • 利用jsonp可以解決跨域問題(所謂跨域就是跨域名,跨埠,跨協議),web頁面上呼叫js檔案時則不受跨域影響(且凡是有src屬性的標籤都具有跨域能力,如<\script> <\img> <\iframe>等)

  • Promise最大好處:將回調函式的非同步呼叫變成鏈式呼叫。函式返回的Promise物件引數接受一個函式,引數1為成功的resolve回撥函式,引數2是失敗的reject回撥函式,函式體中返回對應函式呼叫。呼叫該返回Promise物件的函式的then方法引數為resolve函式傳入的屬性值為引數的函式,在方法體內寫入回撥成功返回的內容。

  • 首頁頭部程式碼內容:

    • /src/components/Header/index.js利用setInterval函式實時重新整理時間資訊,並呼叫axios包中的jsonp方法傳送請求並根據promise進行回撥。
    import React from 'react';
    import { Row,Col } from 'antd';
    import './index.less';
    import Util from '../../utils/utils';
    import axios from '../../axios';
    class Header extends React.Component{
        componentWillMount(){
            this.setState({
                userName:'河畔一角'
            })
            setInterval(()=>{
                let sysTime = Util.formateDate(new Date().getTime());
                this.setState({
                    sysTime
                })
            },1000)
            //this.getWeatherAPIDate();由於百度API不提供服務,故該功能暫時不使用
        }
    
        getWeatherAPIDate(){
            let city = encodeURIComponent('大連');
            axios.jsonp({
                url:'http://api.map.baidu.com/telematics/v3/weather?location='+city+'&output=json&ak=kjb3EeFgETSvZuI2zeakeQ42hVtyDZdG'
            }).then((res)=>{
                if(res.status == 'success'){
                    let data = res.result[0].weather_data[0];
                    this.setState({
                        dayPictureUrl:data.dayPictureUrl,
                        weather:data.weather
                    })
                }
            })
        }
    
        render(){
            return(
                <div className="header">
                <Row className="header-top">
                    <Col span={24}>
                        <span>歡迎,{this.state.userName}</span>
                        <a href="#">退出</a>
                    </Col>
                </Row>
                <Row className="breadcrumb">
                    <Col span={4} className="breadcrumb-title">
                        首頁
                    </Col>
                    <Col span={20} className="weather">
                        <span className="date">{this.state.sysTime}</span>
                        <span className="weather-detail">晴轉多雲</span>
                    </Col>
                </Row>
                </div>
            )
        }
    }
    export default Header;
    • /src/components/Header/index.less
    .header{
        .header-top{
            height: 60px;
            line-height: 60px;
            padding: 0 20px;
            text-align: right;
            a{
                margin-left:40px;
            }
        }
        .breadcrumb{
            height: 40px;
            line-height: 40px;
            padding: 0 20px;
            border-top: 1px solid #f9c700;
            .breadcrumb-title{
                text-align: center;
            }
            .weather{
                text-align: right;
                .date{
                    margin-right: 10px;
                }
            }
        }
    }
    • /src/axios/index.js利用JsonP模組傳送請求獲得jsonp資料,解決跨域問題:
    import JsonP from 'jsonp';
    export default class Axios{
        static jsonp(options){
            return new Promise((resolve,reject)=>{
                JsonP(options.url,{
                    param:'callback'
                },function(err,response){
                    if(response.status == 'success'){
                        resolve(response);
                    }else{
                        reject(response.message);
                    }
                })
            })
        }
    }

9.底部元件開發

  • 底部元件佈局

    • 編寫src/components/Footer/index.js檔案用於實現Footer元件
    import React from 'react';
    import './index.less';
    class Footer extends React.Component{
        render(){
            return(
                <div className="footer">
                    版權所有:汪喆_Jack
                </div>
            )
        }
    }
    export default Footer;
    • 編寫src/components/Footer/index.less檔案用於實現Footer元件樣式

    • 相關知識點:less匯入到其他less中@import "檔案路徑";[注意用@import引入,用分號分割],less使用變數方式:@變數名

    @import "./../../style/default.less";
    .footer{
        height: 100px;
        padding: 40px 0;
        text-align: center;
        color: @colorJ;
    }
  • Home頁面實現

    • 編寫src/pages/home/index.js實現主頁內容
    import React from 'react';
    import './index.less';
    class Home extends React.Component{
        render(){
            return(
                <div className="home-wrap">
                    歡迎學習慕課後臺管理系統課程
                </div>
            )
        }
    }
    export default Home;
    • 編寫src/pages/home/index.less實現主頁樣式,利用height:calc(62vh)代表內容高度佔右側的62%
    @import './../../style/default.less';
    .home-wrap{
        background-color: @colorM;
        height: calc(62vh);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
    }

    注意:實現水平和垂直居中的方式,給標籤設定為display:flex,設定align-items:center為垂直居中;設定justify-content:center為水平居中

    display: flex;
    align-items: center;//前提佈局是flex,align-items表示垂直居中
    justify-content: center;//前提佈局是flex,align-items表示水平居中
  • 使用CSS實現箭頭圖示(原因:css實現只會佔幾字節)

    • 注意:當內部元素使用絕對定位,父級需要使用相對定位,否則內部元素則會相對整個文件進行絕對定位

    • 利用after偽類新增內容實現箭頭圖示(即:after選擇器,在對應元素內容之後插入新內容)

    • 例項程式碼:

      • 使用&:after表示在當前元素內容的最後追加一個內容為空的元素,content設為空用於佔位;該元素設定為absolute故在父級新增position:relative;利用設定border-top為指定顏色,左右border為透明色(transparent),從而實現下三角樣式。
    @import './../../style/default.less';
    .header{
        background-color: @colorM;
        .header-top{
            height: 60px;
            line-height: 60px;
            padding: 0 20px;
            text-align: right;
            a{
                margin-left:40px;
            }
        }
        .breadcrumb{
            height: 40px;
            line-height: 40px;
            padding: 0 20px;
            border-top: 1px solid #f9c700;
            position: relative;
            .breadcrumb-title{
                font-size: @fontC;
                text-align: center;
                &:after{
                    position: absolute;
                    content: '';
                    left: 40px;
                    top: 39px;
                    border-top: 9px solid @colorM;
                    border-left: 12px solid transparent;
                    border-right: 12px solid transparent;
                }
            }
            .weather{
                text-align: right;
                .date{
                    margin-right: 10px;
                }
            }
        }
    }