首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React 折腾记 - (10) UmiJS 2.x + antd 重写后台管理系统记录的问题及解决姿势

React 折腾记 - (10) UmiJS 2.x + antd 重写后台管理系统记录的问题及解决姿势

作者头像
CRPER
发布2018-12-28 16:14:41
3.2K0
发布2018-12-28 16:14:41
举报

前言

用的是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这种,

日期可以直接用momentadd方法往后推导,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来改善性能

可以缓存同样参数的结果集,非常适用于递归这类的函数处理,大大减少计算的压力;

memoize-one;

也能用于React这类,是否有必要重新setState, 第二个参数支持比较,官方推荐用lodash去深度比较


函数式组件内返回一个HOC的组件

最简单粗暴的方法就是用变量缓存,然后直接返回组件,比如我这边文章就用了;

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;

若是要开启umimodel动态引入, page级别不允许调用其他pagemodel,不然会报错,初始化找不到的!!!

所以全局性放在全局更为合适,当然你不需要动态引入的话,页面间跨调是允许的..我目前是这么做;


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这个模板(太臃肿),自己写比较实在


总结

有新的且觉得有点意义的问题我会陆续更新...继续写小程序的需求去..

有不对之处请留言,会及时修正..谢谢阅读

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年12月21日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 问题汇总及解决姿势
    • moment的一些用法及antd 日期组件的细节
      • props.children的改造,添加样式亦或者事件!
        • 用memoize-one来改善性能
          • 函数式组件内返回一个HOC的组件
            • umi 约定式基础鉴权
              • model的规划
                • pages目录下的文件或者目录不自动生成对应可访问的page
                  • umi配置开发的反向代理及目录的alias
                    • 如何在umi这种加入preloading
                      • 标题如何自动随着路由表信息改变
                        • antd 菜单栏随着宽度自适应及风格变化
                        • 总结
                        相关产品与服务
                        云开发 CloudBase
                        云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档