本文基于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),从而实现下三角样式。
- 使用&:after表示在当前元素内容的最后追加一个内容为空的元素,content设为空用于占位;该元素设定为absolute故在父级添加
@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; } } } }