内容简介:用的是为什么说另类..就是原生
用的是 umi 2.x
,写起来挺舒服;顺带完善了上一版本后台的一些细节问题,功能等
umijs
类似 create-react-app
, 也是一套方案的集合体,亮点很多.可以具体官网去看
- 声明式的路由(
nuxtjs
既视感) -
dva(基于redux+redux-saga的封装方案)
:写起来有vuex
的感觉;
主要记录我在过程中遇到的问题及解决的姿势,技术栈 antd 3.11.x
+ umi 2.x
+ react 16.7
问题汇总及解决姿势
moment的一些用法及antd 日期组件的细节
关于moment
为什么说另类..就是原生 日期API
结合 moment
,因为我们接口需要传递时间戳,而是不带毫秒级的;
而且时间必须为当天的凌晨 00:00:00
开始,结束时间到操作的此刻(直接 new Date().getTime()
就是此刻);
// 会直接返回你设置时间的时间戳 new Date().setHours(0, 0, 0, 0) // 凌晨`00:00:00` moment(new Date().setHours(0, 0, 0, 0)) // 近七天 moment(new Date().setHours(0, 0, 0, 0) - 7 * 24 * 3600000) // 月初 moment().startOf('month') 复制代码
转成 unix stamp(服务器常用的时间戳规格)
,调用 moment().unix()
即可;
若是不控制到凌晨 00:00:00
这种,
日期可以直接用 moment
的 add
方法往后推导, subtract
往前推导,支持日/周/月/年
antd
的日期组件
置空用 null
是允许的,其他的话需要转成 moment
对象,控件获取的值默认就是 moment
对象
props.children
的改造,添加样式亦或者事件!
在封装一些组件的过程,我用了 React.Fragment(<></>: 简写)
来保证组件同级并列
有些必须需要 props.children
带上一些属性或者样式来保证我想要的效果.
一开始无解, 因为 Fragement简写的姿势
没法 props
,那也就是说没做写成高阶;
找了下官方文档,发现有这么两个 API
:
- React.Children : 提供了几个遍历子元素(
React Element
)的方法,与常规数组用法类似,只是参数不一样 - React.cloneElement: 如名字所示,克隆子元素
这是上篇文章用到的部分内容,需要改造传递进来的按钮,给添加样式
// 构建 // 克隆子组件并且添加自己要添加的特性 const PropsBtn = React.Children.map(this.props.children, child => React.cloneElement(child, { style: { marginLeft: 8, }, }) ); // 渲染 {PropsBtn ? <>{PropsBtn}</> : null} 复制代码
用 memoize-one
来改善性能
可以缓存同样参数的结果集,非常适用于递归这类的函数处理,大大减少计算的压力;
也能用于 React
这类,是否有必要重新 setState
, 第二个参数支持比较,官方推荐用 lodash
去深度比较
返回一个递归包裹的组件
最简单粗暴的方法就是用变量缓存,然后直接返回组件,比如我这边文章就用了;
React 折腾记 - (9) 基于Antd+react-router-breadcrumbs-hoc封装一个小巧的面包屑组件
umi 约定式基础鉴权
在 layouts
里面分别写对应的布局,然后由一个鉴权组件去判定是否允许进入,比如
/src/layout/index.js
import React from 'react'; import withRouter from 'umi/withRouter'; // 鉴权组件, 我写了webpack alias import Authorized from 'components/Authorized'; // 布局组件 import EnranceLayout from './EntranceLayout'; import AdminLayout from './AdminLayout'; // 中文地区时间转换引入 import moment from 'moment'; import 'moment/locale/zh-cn'; // 路由动效 import { TransitionGroup, CSSTransition } from 'react-transition-group'; // 页面标题 import { Helmet } from 'react-helmet'; import { getDocumentTitle } from 'components/Sidebar/RouterTree'; moment.locale('zh-cn'); export default withRouter(props => { const { location: { pathname }, location, } = props; // 根据路由寻址,再结合鉴权来判定是否允许进入,根据您自身的业务进行调整 if (pathname.indexOf('/entrance') === -1) { if (pathname.indexOf('/editor') !== -1) { return ( <Authorized> <Helmet> <title>{getDocumentTitle(pathname)}</title> </Helmet> <TransitionGroup> <CSSTransition key={location.key} classNames="spread" timeout={1000}> {props.children} </CSSTransition> </TransitionGroup> </Authorized> ); } return ( <AdminLayout> <Helmet> <title>{getDocumentTitle(pathname)}</title> </Helmet> <TransitionGroup> <CSSTransition key={location.key} classNames="spread" timeout={1000}> {props.children} </CSSTransition> </TransitionGroup> </AdminLayout> ); } return ( <EnranceLayout> <TransitionGroup> <CSSTransition key={location.key} classNames="spread" timeout={1000}> {props.children} </CSSTransition> </TransitionGroup> </EnranceLayout> ); }); 复制代码
model的规划
全局的放在 src/models
目录,其他的 page
级别推荐直接 model.js
,官方说会自下往上寻找;
是根据 namespace
来区分的..不允许存在同名的 namespace
;
若是要开启 umi
的 model
动态引入, page
级别不允许调用其他 page
的 model
,不然会报错,初始化找不到的!!!
所以全局性放在全局更为合适,当然你不需要动态引入的话,页面间跨调是允许的..我目前是这么做;
pages
目录下的文件或者目录不自动生成对应可访问的 page
默认在 page
目录下,除了部分特殊的文件(比如官方自己过滤的 models
),都会自动产生可访问的页面,
也就是说文件会被当做路由组件;
屏蔽的话, 打开项目的配置文件 .umirc.js
const path = require('path'); // ref: https://umijs.org/config/ export default { plugins: [ // ref: https://umijs.org/plugin/umi-plugin-react.html [ 'umi-plugin-react', { antd: true, // 默认引入antd dva: { // 启用引入dva immer: true, dynamicImport: false, // models 动态引入关闭 hmr: true, }, dynamicImport: false, // 组件切割动态引入 title: '声兮后台管理系统', dll: true, routes: { // 此处用正则忽略不想产生路径的文件或者目录!!! exclude: [/model\.js/, /models\//, /services(\/|\.js)?/, /components\//], }, hardSource: true, locale: {}, }, ], ], }; 复制代码
umi配置开发的反向代理及目录的alias
const path = require('path'); // ref: https://umijs.org/config/ export default { plugins: [ alias: { '@': path.resolve(__dirname, './src'), models: path.resolve(__dirname, './src/models'), components: path.resolve(__dirname, './src/components'), utils: path.resolve(__dirname, './src/utils'), services: path.resolve(__dirname, './src/services'), assets: path.resolve(__dirname, './src/assets'), }, proxy: { '/api/web': { target: 'http://stagapi.xxxx.com', changeOrigin: true, secure: false, // pathRewrite: { '^/api': '/' }, }, }, }; 复制代码
如何在umi这种加入 preloading
就是react代码没加载之前,显示的区域块,
目前的做法就是自定义模板文件,放在react渲染块内部,在解析代码渲染完毕会被替换掉
效果如下
src/pages/document.ejs
<!doctype html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>xx管理后台</title> <style> .preloadLoading{ position:fixed; left:0; top:0; width:100%; height:100%; display:flex; justify-content:center; align-items:center; } @-webkit-keyframes square-animation { 0% { left: 0; top: 0; } 10.5% { left: 0; top: 0; } 12.5% { left: 32px; top: 0; } 23% { left: 32px; top: 0; } 25% { left: 64px; top: 0; } 35.5% { left: 64px; top: 0; } 37.5% { left: 64px; top: 32px; } 48% { left: 64px; top: 32px; } 50% { left: 32px; top: 32px; } 60.5% { left: 32px; top: 32px; } 62.5% { left: 32px; top: 64px; } 73% { left: 32px; top: 64px; } 75% { left: 0; top: 64px; } 85.5% { left: 0; top: 64px; } 87.5% { left: 0; top: 32px; } 98% { left: 0; top: 32px; } 100% { left: 0; top: 0; } } @keyframes square-animation { 0% { left: 0; top: 0; } 10.5% { left: 0; top: 0; } 12.5% { left: 32px; top: 0; } 23% { left: 32px; top: 0; } 25% { left: 64px; top: 0; } 35.5% { left: 64px; top: 0; } 37.5% { left: 64px; top: 32px; } 48% { left: 64px; top: 32px; } 50% { left: 32px; top: 32px; } 60.5% { left: 32px; top: 32px; } 62.5% { left: 32px; top: 64px; } 73% { left: 32px; top: 64px; } 75% { left: 0; top: 64px; } 85.5% { left: 0; top: 64px; } 87.5% { left: 0; top: 32px; } 98% { left: 0; top: 32px; } 100% { left: 0; top: 0; } } @-webkit-keyframes hue-rotate { 0% { -webkit-filter: hue-rotate(0deg); filter: hue-rotate(0deg); } 100% { -webkit-filter: hue-rotate(360deg); filter: hue-rotate(360deg); } } @keyframes hue-rotate { 0% { -webkit-filter: hue-rotate(0deg); filter: hue-rotate(0deg); } 100% { -webkit-filter: hue-rotate(360deg); filter: hue-rotate(360deg); } } .loading { position: relative; width: 96px; height: 96px; -webkit-transform: rotate(45deg); transform: rotate(45deg); -webkit-animation: hue-rotate 10s linear infinite both; animation: hue-rotate 10s linear infinite both; } .loading__square { position: absolute; top: 0; left: 0; width: 28px; height: 28px; margin: 2px; border-radius: 2px; background: #07a; background-image: -webkit-linear-gradient(45deg, #fa0 40%, #0c9 60%); background-image: linear-gradient(45deg, #fa0 40%, #0c9 60%); background-image: -moz-linear-gradient(#fa0, #fa0); background-size: cover; background-position: center; background-attachment: fixed; -webkit-animation: square-animation 10s ease-in-out infinite both; animation: square-animation 10s ease-in-out infinite both; } .loading__square:nth-of-type(0) { -webkit-animation-delay: 0s; animation-delay: 0s; } .loading__square:nth-of-type(1) { -webkit-animation-delay: -1.42857s; animation-delay: -1.42857s; } .loading__square:nth-of-type(2) { -webkit-animation-delay: -2.85714s; animation-delay: -2.85714s; } .loading__square:nth-of-type(3) { -webkit-animation-delay: -4.28571s; animation-delay: -4.28571s; } .loading__square:nth-of-type(4) { -webkit-animation-delay: -5.71429s; animation-delay: -5.71429s; } .loading__square:nth-of-type(5) { -webkit-animation-delay: -7.14286s; animation-delay: -7.14286s; } .loading__square:nth-of-type(6) { -webkit-animation-delay: -8.57143s; animation-delay: -8.57143s; } .loading__square:nth-of-type(7) { -webkit-animation-delay: -10s; animation-delay: -10s; } </style> </head> <body> <div id="root"> <div class="preloadLoading"> <div class='loading'> <div class='loading__square'></div> <div class='loading__square'></div> <div class='loading__square'></div> <div class='loading__square'></div> <div class='loading__square'></div> <div class='loading__square'></div> <div class='loading__square'></div> </div> </div> </div> </body> </html> 复制代码
标题如何自动随着路由表信息改变
首先得自己维护一份静态路由表,类似 vue
或者 react-router@3
那种,
结合 @withRouter
拿到 pathname
传入到静态路由表遍历
(这里就可以用到上面说的memoize-one来提高性能),
效果如下
姿势如下
用 react-helmet
来实现 title
的替换,这货不仅仅可以替换 title
还能替换 meta
这些
参考上面的问题 ==> umi 约定式基础鉴权 ,这里就有用到
antd 菜单栏随着宽度自适应及风格变化
就是缩小的时候隐藏部分子菜单,这个问题在我做侧边栏变水平的时候遇到.我缩小到 ipad
的尺寸
会溢出,用了常规的法子,就正常了,就是 style
那里设置一个最大宽度或者宽度
至于风格变化是因为 antd
内置了两套风格
<Menu style={{ maxWidth: '100%', flex: 1 }} subMenuOpenDelay={0.3} theme={theme ? 'dark' : 'light'} mode={mode ? 'horizontal' : 'inline'} openKeys={openKeys} selectedKeys={selectedKeys} onOpenChange={this.onOpenChange} > 复制代码
当然 Logo
组件这些肯定是你自己拿了状态去变化的,还有包裹的父级区域的样式
目前不做配置保存,想做保存的,写在 localStorage
不失为一个好法子,没必要写到数据库,都是自己人用
效果如下
项目没有用到 antd pro
这个模板(太臃肿),自己写比较实在
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
TCP/IP详解 卷1:协议
W.Richard Stevens / 范建华 / 机械工业出版社 / 2000-4-1 / 45.00元
《TCP/IP详解卷1:协议》是一本完整而详细的TCP/IP协议指南。描述了属于每一层的各个协议以及它们如何在不同操作系统中运行。作者W.Richard Stevens用Lawrence Berkeley实验室的tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输的不同分组。对tcpdump输出的研究可以帮助理解不同协议如何工作。 《TCP/IP详解卷1:协议》适合作为计算机专业学......一起来看看 《TCP/IP详解 卷1:协议》 这本书的介绍吧!