react与ts项目2-项目总结20191205

内容纲要

前言

上一次写项目总结在8月底, 距离现在大约也已经过去了三个月了, 这三个月应该处理了两个项目: ir移动端内容及dhl项目, 总结了下时间, 两个之间重合的时间比较多, 竟然没有觉得太紧张, 应该主要归功于ir移动端一直没有设计稿, dhl原型及内容较迟才出来吧, 哈哈, 刚刚还觉得时间真长, 看了下也就不觉得了, 还蛮欣慰, 真的…

技术栈

  • react
  • less
  • typescript
  • css module
  • react-router
  • react-intl
  • react-vuex初实践

组件篇

  • 按钮(单行大按钮)

  • 卡片

  • checkbox

  • CurrendLocale 非组件内容获取locale对应的value值 — 后续会着重提一下

  • 对话框组件-dialog

  • 底部固定外框-fixed

  • 底部按钮组件

  • 商品列表组件

  • 后台头部组件

  • 前台底部链接组件

  • 前台头部内容组件

  • 前台头部menu组件(三横杠动画效果)

  • input输入框组件(包含searchInput)

  • menu组件(与NavLink相关内容进行了在原版基础上的修改)

  • 加载更多组件

  • 无数据组件

  • select组件

  • tabs组件

  • tip组件( 算是败笔吧, 可以从dialog组件中进行包容适配的)

  • Toast组件

值得后续借鉴组件及内容
CurrendLocale
  • 用处

    • 为功能函数提供获取多语言文件中key值对应的value,
    • 这里与上一篇遇到的问题又不太相同, 上一篇主要是在切换语言时, 无法二次获取页面数据, 这里是公共函数不能使用injectIntl进行包裹, 无法将多语言props数据传入
  • 实现

    • 创建一个返回空内容组件, 并用injectIntl 注入intl相关, 并将该prop使用方法暴露出去
    • 该组件需要先行渲染, 所以此次采用的是在myRouter中渲染, 然后便可以在这个代码中使用暴露的方法
  • 代码

    import { injectIntl, InjectedIntlProps, FormattedMessage, MessageValue } from 'react-intl'
    import * as React from 'react'
    /**
    * 用于页面获取多语言文件中key值对应的value,主要在函数式组件中使用
    * 组件需要先渲染,才能使用,目前该组件已经在myRouter中渲染,可以在各组件中使用selfIntl和formatMsg方法
    */
    class CurrentLocale extends React.Component {
      static instance: CurrentLocale = null
      componentWillMount() {
          if (!CurrentLocale.instance) {
              CurrentLocale.instance = this
          }
      }
      render() {
          return false
      }
    }
    export default injectIntl(CurrentLocale)
    export function selfIntl() {
      return CurrentLocale.instance.props.intl
    }
    export function formatMsg(
        messageDescriptor: FormattedMessage.MessageDescriptor, 
      values?: { [key: string]: MessageValue }
    ) {
      return selfIntl().formatMessage(messageDescriptor, values)
    }
    
Fixed组件
  • 用处: 为页面底部提供一个fixed外框, 固定内容在页面底部

  • 主要问题:

    • fixed主要是css处理, 但是不会在html中占据高度, 如果页面内容高度超出整个html的高度, 在fixed下部的部分会被隐藏
    • 想要完整显示页面, 而且不想在每个单独页面中进行
    • 进行了多种尝试
    • 对页面最小高度加上fixed的高度,使用margin等 — 没有使用fixed组件的页面会凭空多出一部分页面内容
    • 对fixed增加高度, 因为脱离文档流而无效
  • 解决方法: 在fixed组件中, 增加一个与fixed高度一致的div作为占位符

    /**
    * 页面的Fixed组件,使用如下:
    * 
    *      
    * 
    */
    import classnames from 'classnames'
    import * as React from 'react'
    import { pxToVw } from 'scripts/utils'
    const styles = require('./styles.less')
    interface IFixedProps {
      height?: number,
      className?: string
    }
    export default class Fixed extends React.Component{
      render() {
          const { height, className, children } = this.props
          const classNameFixed = classnames(styles.fixed, className)
          let vwHeight = pxToVw(height)
          let clsFixed = classnames(styles.replace, className)
          return (
              
    {children}
    {/* 增加一个占位符,代替fixed部分增加页面的文本高度, 保证页面高度超过最小高度时可以下滑看见所有的文本内容*/}
    ) } }
menu组件三横杠与X动画效果
  • 实现效果

    Kapture 2019-12-06 at 11.05.06

  • 问题:

    • 不用图片时: 三横杠在其他移动端设备下会出现某一个横线高度与其他两个横线不一致的情况(怀疑是不同分辨率导致的, 但是不明白内部原理导致无法解决)
    • 不用css绘制: 直接使用图片, 之间Ir就是使用的该方法, 但是动画效果没有了, 用户体验上会差很多, 本人也不太想接受
  • 处理: 三横杠单纯显示使用图片, 动画效果进行处理(点击后先显示css绘制的三横杠, 过一段时间在显示X, 保证css动画的实现)

  • 代码

    {this.state.showMenu ? : }
    /**
    * 切换menu显示的函数,为了避免部分移动端下横杠大小不一致问题和保留动画效果
    * 切换时选择适当的state进行处理,随后留一定的动画时间来正确渲染
    */
    handleToggleMenu() {
    let { showMenu } = this.state
    if (showMenu) {
      this.setState({
        showClose: !showMenu
      }, () => {
        setTimeout(() => {
          //为了维持动画效果
          this.setState({
            showMenu: !this.state.showMenu
          })
        }, 100)
      })
    } else {
      this.setState({
        showMenu: !showMenu
      }, () => {
        setTimeout(() => {
          //为了维持动画效果
          this.setState({
            showClose: this.state.showMenu
          })
        }, 50)
      })
    }
    }

    ps: handleToggleMenu 本来可以更精简一些, 但是为了让其他人看的更清楚, 采用了if语句

input输入框组件
  • 简介: 第一次使用react实现输入框组件, 发现自己想要的很多, 但实现起来总有很多的问题, 也学到了不少, 下一次应该会更好啦

  • 问题:

    • props定义, input 本身的, 组件扩展的, 尤其是input本身props内容,经常出现遗漏, 然后使用ts检查就报错, 并且还不能传递到input组件本身
    • input输入报错问题: 检查空值, 异常输入检测等(以及与外层Form之间的交互等)
    • 可控组件与不可控组件问题
  • 处理方式

    • props定义: 继承原生元素的接口, 在底层, 进行剔除本次组件需要的参数, 赋值到input自身
    export interface IInputItemProps extends React.InputHTMLAttributes
    getSingleInput() {
            let { placeholder, intl, value: propVal } = this.props
            let { value } = this.state
            value = typeof propVal === 'undefined' ? value : propVal
            let inputProps = omit(this.props, [
                'cls',
                'label',
                'isRequired',
                'needNullLabel',
                'afterItem',
                'beforeIcon',
                'afterIcon',
                'isShowError',
                'onIconClickFunc',
                'onChangeFunc',
                'onEnterFunc',
                'onErrorFunc',
                'intl',
                'defaultValue',
                'placeholder',
                'value'
            ])
            if (placeholder) {
                placeholder = intl.formatMessage({
                    id: placeholder,
                    defaultMessage: placeholder
                })
            }
            return (
                
            )
        }
    /**
     * 用来剔除对象中不需要的字段及值
     * @param obj 
     * @param uselessKeys 
     */
    export default function omit(obj: T, uselessKeys: string[]) {
        return Object.keys(obj).reduce((acc, key) => {
            return uselessKeys.includes(key) ? acc : { ...acc, [key]: obj[key as keyof T] }
        }, {})
    }
    • 输入报错处理: 本次只使用了外部传入prop来控制检查报错时机, 感觉时机不太对, 而且没有留其他方式报错的情况( 比如不允许输入某些字符, 当然本次项目没有要求这部分), 与form的交互有点冗余, 下次项目可以试试antd, 然后去扒内核, 错误内容提示也没有, 这部分真的可以研究下, 现在这样, 太简陋了
    isShowError: 是否需要展示error样式
    • 可控与不可控组件

    • 根据外部需要, 变成可控 or 不可控组件, 不应该写死组件是否可控

      value = typeof propVal === 'undefined' ? value : propVal
    • 可控组件为由外界prop可以控制, 而不可控组件, 不需要要外部传参, 也可以正常运作

  • 待解决问题

    • 输入报错显示, 如何显示对应的错误提示以及匹配错误( 提供正则匹配等 )
    • 如何与form进行交互, 目前感觉每一次使用该组件, 页面代码存在冗余内容
    • 数字组件未深入了解, 最大值最小值等功能都没有提供
menu组件
  • router对比问题处理: 利用NavLink提供的函数及参数处理, 比渲染时需要接入路由参数及强行比较要好

    /*现在*/
    ${this.state.showMenuItem ? 'menu_expand_hover' : ''} ${props.cls} menu_div }
    activeClassName="menu_hover"
    isActive={(_, location) => {
      let matchUrl = location.pathname.split('/')[1]
      let baseUrl = props.url.split('/')[1]
      return matchUrl === baseUrl
    }}
    >
    {props.children ? props.children :
      }
    
    /*之前*/
    
      ${this.props.cls} menu_ul}> { menuItems.map((item, i) => { //利用基准路由与当前url匹配,设置正确的activeUrl let selfBaseUrl = item.url.split('/')[1] return ( ) }) }
Toast组件
  • 不是自己写的, 只是比较有借鉴意义

  • 函数与react组件相集合

  • 创建dom元素后在callback中调用内容, 保证不会出现无dom问题

  • close 单个\整体都很清楚

  • 将remove作为提示函数返回, 并有提供promise操作, 操作很方便

其他
  • 无数据与加载更多样式 — 任务开始时应要求设计提供
  • 提示样式及形式 — 任务开始时应要求设计提供

样式篇

变量提取
  • 本次是在开发之前对页面颜色等公共变量进行了一轮提取, 部分有问题内容也及时联系设计进行修改, 没有如ir中一样, 中途发现问题再一个个修改, 避免了很多重复的工作, 并且及时发现设计的是否合理, 避免后续返工
  • 使用也很方便, 不需要一边加一边改, 两边之间重合的提交与修改就比较少
  • 命名有一点分歧, 下一次可以再明确一些
字体相关
  • 设计使用的字体中的bold\medium\normal等来区分加粗\加黑字体等, 因为web端需要下载字体, 这样是一种不必要的行为, 以后遇到可以让设计修改

  • 本次因为思源黑体下载内容过大, 使用了Verdana字体进行替代, 无须下载, 英文字体使用dhl本身的delivery

    @font-face {
      font-family: Delivery;
      src: url("./fonts/Delivery.woff") format('woff');
    }
css module相关
  • 本次使用了与前一个项目不同的css引入方式
  • 优点
    • 取名简单
    • 紧跟自家组件, 查找方便
    • 不涉及公共变量, 其他组件几乎不受影响
  • 缺点
    • global 方式有时不是那么方便, 在里面也只对标签有用(li\div), class等无用
    • 好像会影响svg的打包加载, 不太明白为啥…
pxToVw相关
  • 前一次, 使用rem来实现移动端内容, 本次尝试了vw在移动端的使用( 而且上一次是web在前, 所以直接用了rem, 省事), 这次直接做的移动端, 现在看来也比较省事, 不知道下次补web端内容的时候效果怎么样

  • 内容

    • 增加postcss-loader
    "postcss-loader"
    • 增加postcss配置
    module.exports = {
        plugins: [
            require('postcss-px-to-viewport')({
                viewportWidth: 375,
                exclude: /(\/|\\)(node_modules)(\/|\\)/,
                landscape: false,
                landscapeUnit: 'vw',
                landscapeWidth: 667,
                unitPrecision: 3,
                selectorBlackList: ['igvw'] //忽略该class下面的px, 如: .row-igvw{}
            })
        ]
    }
    • 安装postcss-loader\postcss-px-to-viewport 包

    • js内部使用转换方法

    // 设计稿宽度,可参考宽度
    const REFERENCE_WIDTH = 375
    /**
     * 将长度由px转为vw进行使用
     * @param num 
     */
    export function pxToVw(num: number) {
        let singleWidth = REFERENCE_WIDTH / 100
        return (num / singleWidth).toFixed(3) + 'vw'
    }

产品相关

tab页切换
  • 目前我的运单等都是点击进行切换tab页, 其实可以做成滑动的, 手感会更加好点
动画处理
  • 三横杠与X切换

    transition: all ease 0.2s;
    transform: rotate(-45deg);
  • 上下箭头转动方向

    .down {
    transform: rotate(0);
    }
    .up {
    transform: rotate(-180deg);
    }
  • 加载中

    .loading img {
    animation: load 2s infinite linear;
    }
提示相关
  • 部分提示应该有mask, 阻止用户动作, 部分应该取消mask, 允许用户进行操作
  • 提交\ 加载等需要增加提示
  • 统一处理错误消息, 不需要各个页面都必须挨个处理错误消息
客服消息
  • 点击进入消息详情, 直接滚动到最后一条显示
  • 客服消息显示顺序应该是最新的在最后面, 按对话时间进行排列
输入框
  • 区分数字输入框与text输入框
  • 搜索输入框, 需要类型增加type = search

其他

  • 三横杠多设备无法实现高度统一及动画效果保留
  • vuex使用 — 简单的使用, 大胆照着例子实现就好
  • react-intl使用注意事项
    • 新版react-intl 不支持某些移动端(小米亲测), 后续使用新版可以先进行测试再处理
    • 新版react-intl 自身携带ts类型, 不需要再另外下载类型包, 部分组件使用方式也进行了修改, 使用时需注意
  • 颜色值大小写规范

发表评论

邮箱地址不会被公开。 必填项已用*标注