05月19, 2019

一些做UI组件的分享

最近配合同事一起完善了一下UI组件,以下算是一些记录吧。

我有点佩服我那个同事,他之前把antd的最终产出的es目录给拿过来,然后放在lib目录下,供component目录里面的组件使用。

让我更佩服的是:他有些代码直接在那个lib下面改,也是蛮厉害的。

alt

当然这一步确实省了不少的时间。但是还是那一步,出来混的,迟早是要还的。在未来,还是需要砍掉那些lib下的引用。

组件部分

其实我个人觉得组件部分是没啥可以分享的,因为写UI组件要比写框架容易太多太多了。然而,这里面也有很多讲究的点,比如说抽象和复用这一块。

不知道大家有没有看过antddatePicker组件,它将日期组件、月组件、range组件整合在一起,这个代码真的写的很赞,里面用了大量的高阶组件。

最近我也基于它的这个,实现了一个季组件:

alt

alt

唯一有些难受的是:

alt

这个里面没法接收一个函数,它的格式只是依旧moment的格式来走,所以上面没法显示成2019年三季度

当然强行要实现,也是可以的,但这样一来,会破坏代码的美感。

噢对,上周还实现了一个倒计时的按钮,有两种形态:

alt

alt

里面也参考着用了一些高阶组件:

<CountDown startCount={count}>
  {(count) => {
    // 这里返回jsx内容
  }}
</CountDown>

CountDown组件,就是用来倒计时的:

import { Component } from "react";
import PropTypes from 'prop-types';

class CountDown extends Component {
  state = {
    count: this.props.startCount
  }

  componentDidMount() {
    const { endCount } = this.props;
    this.intervalHandle = setInterval(() => { 
      const newCount = this.state.count - 1; 
      if (newCount >= endCount) { 
        this.setState({count: newCount}); 
      } else { 
        window.clearInterval(this.intervalHandle); 
      } 
    }, 1000);
  }

  componentWillUnmount() {
    if (this.intervalHandle) { 
      window.clearInterval(this.intervalHandle); 
    }
  }

  render() {
    const { children } = this.props;
    const { count } = this.state;
    return children(count);
  }
}

CountDown.defaultProps = {
  startCount: 10,
  endCount: 0
}

CountDown.propTypes = {
  startCount: PropTypes.number,
  endCount: PropTypes.number
}

export default CountDown;

它接收两个参数:startend

一些总结

其实说这么多,想表达的是:写UI组件容易,但多思考一些扩展性,会更好。

测试部分

结构

alt

components目录下有一个__test__文件夹,用来放全局设置文件。

每一个组件下面都会有__test__目录,用来跑该组件的测试用例。

jest.config.js配置

const { resolve } = require('path');

module.exports = {
  rootDir: resolve(__dirname, 'src', 'components'),
  // 指定需要进行单元测试的文件匹配规则
  testMatch: [
      '<rootDir>/**/__test__/*.test.js'
  ],
  // 全局设置文件
  "setupFilesAfterEnv": [
    '<rootDir>/__test__/setupTests.js'
  ],
  "moduleNameMapper": {
    "\\.(css|less)$": "<rootDir>/__test__/styleMock.js"
  }
};

这里大家记着点最后一个,我会在下面提及。

setupTests.js

从 Enzyme 3 开始,你需要安装 Enzyme 以及与你正在使用的 React 版本相对应的适配器。 (上面的例子使用 React 16 的适配器。)

还需要在 全局设置文件 中配置适配器:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

styleMock.js

在之前写的组件代码里面,有大量的import "xx.less"这种代码。

那么在跑测试用例时,会挂!!

所以必须给它转换掉,转成空对象即可:

// styleMock
module.exports = {};

当然,我觉得这样写是有问题的。拿按钮来说,测试props的不同size,难道只测试class对应上?

我的理解是还是得要测试实际渲染到页面后,它的button高度或者容器高度来验证这个属性传的是否是对的,因为css是可以随意写的。

当然具体这一块怎么搞,我还没有深入研究过,也许要用styledCompnent。等我真正做这一块内容后,再来做分享吧!

babel配置

这是为了能在test的代码里面使用es6,配置还是比较容易的:

{
  "env": {
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
  },
  "presets": [
    "@babel/env",
    "@babel/react"
  ]
}

类型声明

有同事提及,希望在用dpl-react的时候,能有提示。所以参考着其他的库的用法,给每个组件都加了index.d.ts文件。

一些无奈

加了index.d.ts文件之后,在跑这个UI项目时,会报warning,说不识别的类型后缀。OK,我就加了ts-loader,然后痛苦的就来了,报了下面这个错误:

alt

真是噩梦的开始,查了很多资料,按照github issue各种去玩,就差代码给我玩坏了。

解决及typings配置

好在耐着性子,继续查阅各种资料,换了个思路,终于解决:

{
    test: /(?<!\.d)\.tsx?$/,
    loader: 'ts-loader',
    exclude: /node_modules/
},
{
    test: /\.d\.ts$/,
    loader: 'ignore-loader'
},

然后除了给各个组件加上index.d.ts之后,还得给导出一个大的index.d.ts文件:

alt

该文件的路径要写到package.json里面的typings属性中:

{
    "typings": "./src/@types/index.d.ts"
}

其他的一些补充

在抄antd的声明时,对泛型里面的东西有一些困惑:

import React from 'react';
export interface ButtonProps {
}
export default class Button extends React.Component<ButtonProps, any> {

}

@types/react/index.d.ts的397行有定义:

alt

大家可以深入去看一下,P是指props类型,S则是指state类型。

本文链接:www.my-fe.pub/post/some-ui-component-share.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。