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类型, 不需要再另外下载类型包, 部分组件使用方式也进行了修改, 使用时需注意
  • 颜色值大小写规范

从零搭建一个react门户网站

前言

开始使用react开发, 做了一个门户网站, 记录一下相关的内容

技术栈

  • react
  • react-router
  • typescript
  • less
  • babel
  • webpack

使用组件库

  • 多语言: react-intl
  • 浏览器兼容性: babel-polyfill
  • modal组件: react-modal
  • 图片放大: react-zmage

编码规范设计

结构

总体结构
src/
├── best/
├── com/
├── entries/
├── images/
├── scripts/
├── styles/
├── types/
├── dev.html
└── dist.html
  • best与com 与后台接口交互有关, 自动生成

  • entries: 页面入口

  • images: 图片资源

  • scripts: js内容

  • styles: 未使用css module, css 代码在该文件夹中

  • types: TS相关类型定义

js代码结构
scripts/
├── commons/
├── components/
├── consts/
├── locale/
├── pages/
├── remotes/
├── router/
└── utils
  • commons: 与业务有关的公共方法
  • components: 组件模块
  • consts: 常量
  • locale: 语言文件
  • pages: 页面
  • remotes: ajax方法
  • router: 路由
  • utils: 与业务无关的公共方法: 时间转换等

命名

  • 文件夹命名: 模块\页面等使用使用 Pascal 命名法,其他使用小写

  • 文件命名:使用 Pascal 命名法

  • 类命名: 使用 Pascal 命名法

  • 变量命名:使用 Camel 命名法。

  • 常量命名: 使用全部字母大写,单词间下划线分隔的命名方式

  • 语言字符串变量命名

    • 语言包命名规则: 模块_内容_类型
    • 模块分类包括:公共、menu、submenu 以及单个页面等
    • 内容即表示文字具体的含义
    • 类型包括:title、text等, 如果一项内容中存在多个单词,用Camel进行命名,如:menu_aboutUs_title

注释

  • 模块: 多行注释
/**
* 页面的tab组件,使用如下:
* let tabs: ITab[] = [{
    text: 'tab_boardOfDirectors_text',
    component: ()=> {
        return (
        <Directors></Directors>
        )
    }
    }]
<Tabs tabs={tabs}></Tabs>
*/
  • 模块定义
  • 使用范例
  • 注意事项( 可选 )
  • 函数(模块及公共方法): 多行注释
/**
* 渲染表格中的扩展行或者行
*/
  • 函数含义或者模块含义
  • 变量注释(因为用ts, 大部分类型不用注明, 变量含义不明确时备注)
  • 变量或逻辑解释: 单行注释, 简单明确, 复杂算法类可用多行详细说明

  • 备注: TS的使用可以让大部分函数参数和变量不用注释, 不提倡过分注释

函数

  • 注释: 见上
  • 命名:
    • 与语义有关, 区分获取数据\事件操作等
    • 静态函数加_表示
  • 行数不超过50行
  • 一个函数最好执行一个功能,不要多个功能代码混合在一起

其他

  • 使用了tslint配置, 但未统一团队内部使用插件, 导致部分格式化不一致(如tab和空格的使用)

模块设计

路由

  • 思路:

    • 使用react-router组件
    • 未使用服务端渲染, 在App组件内加入页面路由
    • 单独实现router组件, 将路由与对应的组件映射到router下
    • 在menu中填充路由地址, link到正确的页面
  • 实现

    • 在app下加入页面根路由
<IntlProvider locale={this.state.lang} messages={fields} defaultLocale={this.state.lang}>
<Router>
<HeadComp changeLanguage={this.changeLanguage} />
<MyRouter />
<FootComp />
</Router>
</IntlProvider>
  • 单独实现myRouter组件,赋值HashRouter

    • 定义router对象接口
    // router对象
    interface IRouterObj {
        path: string
        component: React.ReactNode
        isExact?: boolean
    }
    • router对象接口, 以path\component\isExact为属性, 对router组件进行赋值
    const layout = (
        <Switch>
            {routerData.map((item, i) => {
                return <Route key={i} path={item.path} 
                exact={item.isExact ? true : false} 
                component={item.component} />
            })}
        </Switch>
    )
    • 组装至router中

      {layout}
  • 实现menu组件, 在menu组件中处理路由跳转

    
           ${this.state.showMenuItem ? 'menu_hover' : ''} ${props.cls} }   activeClassName='menu_hover'>
          
           
    
  • 问题

    • 新闻类问题:

    • fb分享问题

      • fb分享出来的链接会去掉路由中的#, 本项目为单页面应用, 如此, 只能手动设定一个特别的路由去匹配,然后再跳转到正确的页面
      /**
       * 新闻页匹配字符串,自动添加#,避免因其他网站跳转删去#引发问题
       */
      export const newsDetailUrlType = '?newsDetail='
      /**
       * 处理facebook跳转时因为facebook网站删去#而引发的问题
       */
      export function setNewsDetailUrl(history: History) {
        let url = document.location.search
        if (url.indexOf(newsDetailUrlType) !== -1) {
      
          url = decodeURIComponent(url)
          let newsUrl = url.replace(newsDetailUrlType, '')
          //facebook 会增加该查询条件,影响单页面路由解析,去除该部分内容
          newsUrl = newsUrl.split('&fbclid')[0]
          document.location.search = ''
          //直接使用localtion.href替换的话,在谷歌下面会先渲染首页,再跳转到新闻页,看起来有两次渲染的效果,优化后去除
          history.push(newsUrl)
        }
      }
      • &fbclid 是fb分享之后加上的, 域名下才存在该问题, 只能再修改下
    • 关键字与描述类型只能动态更改

      • 之前对fb\tw之类实现分享, 但是必须要描述与关键字, 动态更改也无法用, 只能后续更改为服务端渲染再看
    • 路由字符串更改问题

      • menu的需求,当然涉及刷新页面, 点中的menu是需要hover颜色的, 本次是根据路由的路径来匹配对应的菜单
      render(){
        let { data: menuItems, location: { pathname } } = this.props
        setNewsDetailUrl(this.props.history)
        let baseUrl = pathname.split('/')[1]
        setPageTitle(baseUrl)
        return (
          
        ${this.props.cls} menu_ul}> { menuItems.map((item, i) => { //利用基准路由与当前url匹配,设置正确的activeUrl let selfBaseUrl = item.url.split('/')[1] return ( ) }) }
      ) }
    • script加载问题

    • 问题原因: 单页面应用切换路由时, 页面内容实际没有重新加载,

    • 处理方式: 第一次加载时页面内容保存加载出来的dom内容,后续直接使用该内容

    • 后续问题: 如果第一次js没有加载完, 在页面内部一直切换也不会再次加载, 除非刷新页面

    • link参数问题

双语

  • 思路

    • 使用统一字符串来作为页面文字的key值,对应的value作为各个语言的具体文字
    • 使用react-intl
    • 利用cookie, 设置当前页面语言类型及方法请求头参数, 获取对应的语言类型
    • 一个是不需要从远程获取的, 如menu文字等, 一个需要从远程获取对应内容的文字, 如history内容等
  • 实现

    • 在app 组件, 初始化语言包, 并在根组件使用intl的IntlProvider组件, 将语言包配置在整个应用程序中

    • 在app增加切换语言方法, 并用传prop形式传入切换组件

    /**
     * 切换语言类型
     */
    changeLanguage = (lang: keyof ILocales) => {
      let current = this.state.lang;
      if (current !== lang) {
        storage.setCookie('ir_lang', lang)
        this.setState({
          lang
        });
        //切换语言时重新刷新页面
        location.reload()
      }
    }
    render() {
      app.language.currentLang = this.state.lang
      const fields = app.language[this.state.lang]
      return (
        
          
            {/* 传入切换方法 */}
            
            
            
          
        
      );
    }
    • 用app.language 保存所有语言包内容及当前语言类型, 在不可以使用react-intl组件的情况下使用该值代替, 如下, 获取时间格式时, 无法使用react-intl的方法, 自己添加便需要获取一下
    /**
     * 返回形如:Jan 01, 2019 这样的数据,后续如果中文有特殊要求的话就再针对中英文进行修改
     * @param occureTime 
     */
    export function getDateString(occureTime: I__dateTimeTrans) {
        let lang = app.language.currentLang
        // 英文下:如Jan 01, 2019
        if (lang === 'en') {
            let date = new Date(occureTime).toDateString().split(' ')
            let year = date[date.length - 1]
            date.shift()
            date.pop()
            return date.join(' ') + ', ' + year
        } else {
            //中文:2019.01.01
            return getCalendarDate(occureTime, 'yyyy.MM.dd')
        }
    }
    • 页面使用

    • 直接显示文字时, 使用FormattedMessage组件即可, 还有其他的用法, 可以参考文档

    • 无法使用组件, 想直接获取到当前字符串对应的文字时, 如下, option的text 必须是纯文本, 无法用FormattedMessage来显示

      //因为页面部分文字不能直接用FormattedMessage组件,需要将语言包注入组件prop中
      export default injectIntl(EmailAlertsPage)
      /**
      * 渲染下拉框的option内容
      */
      renderOptions = (data: IoptionsItem[]) => {
      const { intl } = this.props
      let pleaseSel = intl.formatMessage({ id: 'email_pleaseSelect_text' })
      let res = [];
      res.push()
      res.push(
        data.map((item, i) => {
          let text = intl.formatMessage({ id: ${item.name}, defaultMessage: ${item.name} })
          return (
            // 订阅邮件下发key值错误
            
          )
        })
      )
      return res
      }
  • 问题

    • 更改语言时, 页面需重新加载(部分不能使用 react-intl )

    • 针对页面的语言包可以获取的内容, 切换语言时, react-intl 会帮助直接改变, 但是页面需要服务端数据时, 必须触发页面刷新, 才可以重新渲染页面整体

    • 目前使用的方法是强制刷新页面, 但感觉不是特别合适

      //切换语言时重新刷新页面
      location.reload()
    • 不需要组件, 需要单纯获取语言的内容

    • 如上面的获取当前时间显示的方法, 因为react-intl 获取页面数据, 需要将intl 作为参数注入的react 类中

      import { FormattedMessage, injectIntl, InjectedIntlProps } from 'react-intl'
      class EmailAlertsPage extends React.Component{}
    • 目前方法如上, 使用全局变量, 感觉也不是特别好

组件设计

tab切换

需求

20190908-react-tabs

20190908-react-tabs_active

  • 一个是固定的tab, 切换显示内容即可

  • 一个是数据填充的tab, 根据切换显示正确的tab页内容

实现
  • 简单版本实现

    render() {
    let { tabs } = this.props
    let currentComp = tabs[this.state.activeCompInd].component
    return (
      
      {tabs.map((item, i) => { return })}
    {currentComp()}
    ) }
    • tab数据与component一起从外部传入, 内容渲染tabs内容与被点击的当前tab内容即可
  • 数据切换, 其实数据填充与tab类似, 主要是tabs的实现有所改变

    /**
    * 获取tabs的第一个和最后一个tab的索引值,根据这两个进行渲染
    */
    getFirstLastInd = () => {
    let { tabs } = this.props
    let { activeCompInd } = this.state
    let showItemLen = this.showItemLen;
    let len = tabs.length - showItemLen
    let firstInd = 0;
    let lastInd = 0;
    if (len <= 0) {
      lastInd = tabs.length - 1
    } else {
      //不在最前面
      if (activeCompInd !== 0) {
        //是否在最后,不在最后-1 ,在最后减去showItemLen -1;
        firstInd = activeCompInd === tabs.length - 1 ? activeCompInd - (showItemLen - 1) : activeCompInd - 1
      }
      lastInd = firstInd + showItemLen - 1;
    }
    return {
      firstInd,
      lastInd
    }
    }
    
    /**
    * 渲染tabs内容
    */
    renderTabs = () => {
    let { tabs } = this.props
    let { activeCompInd } = this.state
    let { firstInd, lastInd } = this.getFirstLastInd();
    
    let getArrows = (index: number) => {
      return (
        
      )
    }
    let getArrowTab = (item: T, index: number) => {
      let { renderText } = this.props
      let text = renderText(item);
      return (
        
  • { this.changaActiveComp(index) }}> {index === firstInd ? getArrows(index) : ''} ${text}} defaultMessage={${text}} > {index === lastInd ? getArrows(index) : ''}
  • ) } let getTab = () => { var res = []; //当数据量小于2时,页面不展示tab页 if (tabs.length < 2) { return null; } for (let i = firstInd; i <= lastInd; i++) { res.push(getArrowTab(tabs[i], i)) } return res } return (
      {getTab()}
    ) }

遇到的奇奇怪怪问题

iframe加载

  • 目的: 在react中使用iframe加载

    render() {
    let { showIframe } = this.state
    return (
      
    {this.getLoading()}
    ${showIframe ? 'block' : 'none'} }} >
    ${showIframe ? 'block' : 'none'} }} >
    ) }
  • 实现: 固定宽高, 直接使用, onload 可以用来判断当前iframe是否加载上, 可以用来填充加载图标

script加载问题

  • 目的: 在react中,加载js内容, 并通过该js加载其他内容

  • 实现

    /**
     * 刷新页面时获取PressReleases的元素内容
     */
    getPressDiv = () => {
    let el = document.createElement('div')
    let wd_widget = document.createElement('div')
    wd_widget.className = 'wd_widget'
    wd_widget.dataset.wd_widgetId = 'JGD4fX6HgrCA'
    wd_widget.dataset.wd_widgetHost = '//bestinc.investorroom.com'
    let loadingDiv = document.createElement('div')
    loadingDiv.className = 'page_loading'
    wd_widget.appendChild(loadingDiv)
    el.appendChild(wd_widget)
    
    let scr = document.createElement('script')
    scr.src = '//bestinc.investorroom.com/js/wd_widgets.js'
    scr.async = true
    el.appendChild(scr)
    return el
    }
    componentDidMount() {
    //pressDiv不存在时,会将数据内容暂存pressDiv上
    if (!pressDiv) {
      pressDiv = this.getPressDiv()
    }
    this.selfElement.appendChild(pressDiv)
    }
    render() {
    return (
      
    (this.selfElement = el)}>
    ) }
    • 生成script标签
    • 利用常量, 如果当前div没有内容时, 生成script加载, 如果有, 直接显示div内容
  • 问题

    • html加载script标签, 只有第一次有效, 在单页面应用中, 第二次加载都无效
    • 目前改过的方法可以保证大部分问题, 但是如果第一次数据未加载完, 后面切换页面都不会有展示, 只能刷新页面重新加载

分享相关

  • url 无法展示

  • # 被自动删去

  • 生成的url 被增加后缀

    /**
    * 新闻页匹配字符串,自动添加#,避免因其他网站跳转删去#引发问题
    */
    export const newsDetailUrlType = '?newsDetail='
    /**
    * 处理facebook跳转时因为facebook网站删去#而引发的问题
    */
    export function setNewsDetailUrl(history: History) {
      let url = document.location.search
      if (url.indexOf(newsDetailUrlType) !== -1) {
    
          url = decodeURIComponent(url)
          let newsUrl = url.replace(newsDetailUrlType, '')
          //facebook 会增加该查询条件,影响单页面路由解析,去除该部分内容
          newsUrl = newsUrl.split('&fbclid')[0]
          document.location.search = ''
          //直接使用localtion.href替换的话,在谷歌下面会先渲染首页,再跳转到新闻页,看起来有两次渲染的效果,优化后去除
          history.push(newsUrl)
      }
    
    }

字体模糊问题

  • 3D动画
/**
 * 获取移动动画样式
 */
getTransitionStyle(ms: number, x: number) {
  return {
    transition: `transform ${this.props.edgeEasing} ${ms / 1000}s`,
    'WebkitTransition': `transform ${this.props.edgeEasing} ${ms / 1000}s`,
    transform: `translateX(-${x}px)`,
    'WebkitTransform': `translateX(-${x}px)`,
  }
}

平行四边形(实现与问题)

需求

20190908-react-menu1

  • hover区域为平行四边形
  • 文字显示正常
实现
//外部使用transform(定义沿着 X 轴的 2D 倾斜转换), 对内部span进行反方向旋转, 调整旋转角度
.nav_externaLink {
        width:180px;
        box-sizing: border-box;
        text-align: center;
        line-height: 40px;
        background-color: @menu_navFrightSelect_bg;
        transform: skewX(-@menu_navFrightAngle_size);
        float: left;
        border: none;
        cursor: pointer;
        span {
            display: inline-block;
            transform: skewX(@menu_navFrightAngle_size);
            color: @menu_navFrightSelect_color;
                }
}
问题

20190908-react-menu2

  • 因为是直接旋转的x轴, 看见的区域已经不在范围内, hover进入该区域, 会出现下方区域无法保持显示, 给用户造成困扰
解决方案
  • 给绿色图框区域添加填充物

  • ${this.state.showMenuItem ? 'menu_hover' : ''} ${props.cls} } activeClassName='menu_hover' > {/* 下拉倾斜角与原menu之间会有空隙,需要填充物 */}
    ${this.state.showMenuItem ? 'submenu_show' : 'submenu_hidden'} submenu_over} > {this.getCoverMenu(props)}
    ${this.state.showMenuItem ? 'submenu_show' : 'submenu_hidden'} submenu_block} > {submenu(props)}
  • .submenu_over {
    position: absolute;
    background: transparent;
    width: @menu_block_width;
    margin-left: -20px;
    div {
      height: 40px;
    }
    }

    20190908-react-menu3

  • 然后也要注意, div个数不可过多, 导致右下角点击也显示hover区域是有问题的, 所以增加了限制, 最多为五个

    /**
    * 获取用于填充的内容,大于5个div的部分不用填充
    */
    getCoverMenu = (props: IMenuLevelProps) => {
    if (props.subMenus) {
      return props.subMenus.map((item, i) => {
        if (i > 5) {
          return null
        } else {
          return (
            
    ) } }) } }

富文本加载阻塞问题

问题

在集成后的后台管理的新闻页, 加载富文本之前刷新页面列表, 切换到新增新闻页, 富文本一直加载不出来, 而本地富文本加载很快

原因

观察原因: 富文本需要的图片加载时间过长, 导致加载很慢, 浏览器对图片的并行加载机制一般是6个, 但刷新新闻页时, 图片列表要多很多,而且由于服务器在美国, 我们本地打开加载要慢很多 , 导致富文本图片加载被阻塞

处理

关闭了新闻列表的图片显示功能

视频加载问题

问题

谷歌浏览器升级到76之后, oss 路径的视频无法加载, 播放报错

20190908-react-video1

不正常的地址, 获取的数据

20190908-react-video2

正常的视频数据获取

20190908-react-video3

原因

oss传过来的content-type不对, 谷歌在升级之后对这种情况不进行兼容, 在开发中, 后续要注意content-type 可能导致的问题