• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

React项目优化记录

武飞扬头像
baby_baby
帮助2

前言

记录在项目中使用到的一些优化。

性能优化

1.Gzip压缩

借助CompressionWebpackPlugin插件来提前对文件进行Gzip压缩,服务器查找到有与源文件同名的.gz文件就会直接读取,不会主动压缩,降低cpu负载,优化了服务器性能。 安装

npm install --save-dev compression-webpack-plugin@1.1.12

webpackconfig.plugin中增加如下配置:

  new CompressionWebpackPlugin({
    filename: '[path].gz[query]',
    algorithm: 'gzip',
    test: new RegExp('\\.(js|css)$'),
    threshold: 10240,
    minRatio: 0.8, //压缩比例
    deleteOriginalAssets: false //是否删除源文件
  })

nginx中增加gzip相关配置:

注:gzip_types中不要配置image相关

    gzip on;
    gzip_static on;    
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml rss text/javascript;
    gzip_proxied  any;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_buffers 16 8k;

开启Gzip后: 学新通 学新通 文件体积减小至原来的1/4,测试平均整体加载时间减少20%。 文件体积越大效果越明显。

2.Code Split

  • 第三库一般比较稳定,不会轻易改变,单独提取出来,做长期缓存。
  • 可以拆分代码块,避免代码块体积过大,加载时间长

在webpack config文件的optimization中增加配置

splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/, //用于控制哪些模块被这个缓存组匹配到。原封不动传递出去的话,它默                                           认会选择所有的模块。可以传递的值类型:RegExp、String和                                                   Function;
          chunks: 'initial',  //同步(initial)、异步(async)、所有模块有效(all)
          minSize: 30000, //最小尺寸
          name: 'vendors', // 打包的chunks的名字
          priority: -10, //优先级,表示抽取权重,数字越大表示优先级越高
          enforce: true,
          reuseExistingChunk: true   // 可设置是否重用已用chunk 不再创建新的chunk
        },
        styles: {
          name: 'styles',
          test: /\.(css|less)$/,
          chunks: 'all',
          enforce: true,
        } 
      }
    }

3.React.lazy、React.Suspense按需动态加载

React.lazy 可以让我们像渲染常规组件一样处理动态引入的组件,结合 Suspense 可以更优雅地展现组件懒加载的过渡动画以及处理加载异常的场景。

import React, { lazy } from 'react' const SmsManager = lazy(() => import(/* webpackChunkName: "manager" */ 'manager'))

动态加载遇到的问题:

  • 因为网络问题导致加载失败,报错Loading chunk failed
  • 用户使用过程中,如果重新发布,因为浏览器缓存为旧的文件,和服务器文件名不一致,会导致文件下载失败报错Loading chunk failed

解决办法: 针对网络问题,可以通过webpack-retry-chunk-load-plugin重新加载

let {RetryChunkLoadPlugin} = require('webpack-retry-chunk-load-plugin')
//plugins中新增配置
  plugins: [
    new RetryChunkLoadPlugin({
      cacheBust: `function() {
        return Date.now();
      }`,
      maxRetries: 2,//加载失败最多尝试次数
    })
  ]

针对使用过程中发布导致下载失败的问题,可以通过重新加载的方式解决(这种方式不算特别好,如果有更好的方法我再补充)

import React from 'react'
import { observer, inject } from 'mobx-react'
import Component from 'edt-components/Component'
import { sessionStorage } from 'utils/storage'
import FullLoading from 'edt-components/FullLoading'
import _ from 'lodash'
@observer
class ErrorBoundary extends Component {
  constructor(props) {
    super(props)
    this.data = {
      hasError: false,
      errorType: null
    }
    this.clearLoadingChunk()
  }
  componentDidCatch(error, errorInfo) {
    this.handleError(error, errorInfo)
  }
  clearLoadingChunk = () => {
    window.clearTimeout(this.clearTimer)
    this.clearTimer = window.setTimeout(() => {
      sessionStorage.removeItem('loadingChunk')
    }, 60000)
  }
  handleError = (error, errorInfo) => {
    this.setData({hasError: true})
    this.handleLoadingChunkError(error)
  }
  handleLoadingChunkError = (error, errorInfo) => {
    //页面浏览期间重新发布,会出现浏览器缓存文件名和服务器文件名不一致,导致下载js失败白屏的问题
    const loadFaild = new RegExp(/Loading chunk (\d)  failed/g)
    if (error && error.message && error.message.match(loadFaild)) {
      this.setData({errorType: ERROR_ENUM.RE_LOADING})
      let loadingChunk = sessionStorage.getItem('loadingChunk')
      loadingChunk = _.isNumber(loadingChunk) ? loadingChunk   1 : 1
      if (loadingChunk > 2) {
        this.setData({errorType: ERROR_ENUM.LOAD_FAIL})
        //当前流程结束,重置loadingChunk
        sessionStorage.setItem('loadingChunk', 0)
      } else {
        sessionStorage.setItem('loadingChunk', loadingChunk)
        window.location.reload()
      }
    }
  }
  renderErrorContent = () => {
    const {errorType} = this.data
    if (errorType === ERROR_ENUM.LOAD_FAIL) {
      return (
        <h3>页面加载失败, 请刷新页面重新加载...</h3>
      )
    }
    if (errorType === ERROR_ENUM.RE_LOADING) {
      return (
        <FullLoading/>
      )
    }
  }
  render() {
    return this.data.hasError ? this.renderErrorContent() : this.props.children
  }
}

//页面错误类型枚举
const ERROR_ENUM = {
  LOAD_FAIL: 'LOAD_FAIL',
  RE_LOADING: 'RE_LOADING'
}

export default ErrorBoundary

4.Js压缩UglifyJsPlugin

  optimization: _.assignIn({}, commonConfig.optimization, {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      })
    ],
  })

5.Css压缩 MiniCssExtractPlugin

  new MiniCssExtractPlugin({
    filename: '[id]_'   config.version   '_[hash].css',
    chunkFilename: '[id]_'   config.version   '_[hash].css'
  }),

6.HTML压缩

  plugins: [
    new HtmlWebpackPlugin({
      title: config.title,
      template: config.template,
      inject: true
    })
  ]

7.长列表虚拟加载

使用react-virtualized 源代码结合列表代码实现虚拟加载

代码优化

1.Css自动化重构

项目中原来没有依据配色规范等,有一个公共的样式文件来对项目样式文件进行管理,如果需要支持主题更换,则需要重新提取整合项目中的css样式。 可以使用Css自动化重构工具lemonj,自动提取整合,不需要人工收集和整理,开发人员只需要修改变量名就行了。

安装

npm install lemonj -g

分析项目css代码,components为需要分析的文件名

lemonj analysis components

得到整体分析结果(坏味道代码),和两个文件mappings.less和result.json 坏味道代码:

Code Smell:  {
  colors: 24,
  importants: 4,
  issues: 8,
  mediaQueries: 1,
  absolute: 0,
  oddWidth: 1
}

mappings.less:

@color_8: rgba(0, 0, 0, 0.1);
// components\TransferPicker\style.less:4
@color_9: #fff;
// components\ATable\style.less:171
// components\ATable\style.less:177
@color_10: #e6f7ff;
// components\ATable\style.less:284
// components\ATable\style.less:295
@color_11: #fbfdff;
// components\NavigationBar\style.less:30
@color_12: #929598;
// components\Card\style.less:45
@color_13: #F8F9FA;

修改mappings.less中的变量名,如color_8改为primary_color,保存,执行命令:

lemonj refactor componnets

项目中的所有对应样式则会改成mappings.less中对应的样式变量名

2. 复杂列表逻辑分层

原来的复杂列表代码,所有的数据查询,渲染,事件全部放在一个js中,随着功能扩展,代码量越来越大, 不易维护。 因此将列表使用高阶组件设计分层,分为数据层,事件层,渲染层。

import Data from './Data'
import Event from './Event'
import Render from './Render'
...

@Data
@Event
@Render
class List {
...
}

项目工程化

1.组件库

由于刚开始只有一个系统,后续拓展新增项目后,时间紧急就直接把组件代码复制到新项目中,但是多个系统维护一套组件很麻烦,时间长了导致组件不统一,而且迭代功能有时候需要多个系统重复修改。 因此抽离公共组件库,多个系统共用一套组件库。

2.组件库展示网站

抽取组件库之后,遇到了以下问题

  • 多个系统共用一个组件库,策划方的组件UI设计没有对应示例可以查看,无法与组件库的样式和交互保持统一
  • 组件库目前难以管理,组件的改动会同时影响多个系统中组件的展示和交互,开发人员对组件改动带来的影响 难以把控
  • 项目中组件库使用频繁,但是查阅组件API繁琐,需要翻阅组件代码
  • 无法直观全面地了解每个组件具备的功能,对新人学习成本较高,组件新增功能也无法快速让组内其他人了解
  • 组件测试

因此参考bisheng ,打造一个组件库文档网站,以解决上述问题。

使用框架:bisheng

参考链接:文档工具bisheng的使用

3.单元测试

抽离组件库后,必须增加单元测试。

jest是facebook推出的一个前端测框架,搭配enzyme可以轻松实现对React应用的单元测试

4.前端监控

使用Sentry进行前端监控

参考链接:Sentry部署实践

5.自定义CLI工具

  • 多个项目分别管理自己的webpack配置,webpack进行bug修复或升级更迭时需要多个项目分别修改,重复劳动,难以维护,易导致项目构建版本和配置差异。创建webpack-cli统一构建流程及构建产物,减少项目更新迭代的重复劳动。
  • 每次新建项目,都需要复制粘贴,删除多余文件,修改项目个性化配置及通用交互等,操作重复麻烦,易遗漏部分相关修改。创建create-cli自定义脚手架支持项目初始化使用命令行自动生成项目模板

参考链接:NodeCli工具实践

6.css模块化插件

平时写代码的过程中偶尔会遇到样式被其他模块样式覆盖的问题,react方面目前市面上一直没有比较好的解决办法。参考vue对css scoped的处理方法,创建了css作用域插件scoped-css-plugin。支持对class进行转换,在样式名后增加hash值来达到css作用域的目的。

  • 转换规则
  1. 以组件文件夹名称__dirname为准,检查文件夹内index.js和style.less
  2. 根据文件夹路径生成相应的hash
  3. index.js中发现匹配__dirname的className,则在该className后新增className-css-hash-[hash]的类名
  4. style.less中发现匹配__dirname的className,则将该className转换为className-css-hash-[hash]

进行转换之后,js中保留了原className供外部定制css,组件内样式将依据hash划定到作用域内(没有增加css选择器优先级)

待优化

  • 公共组件优化(表单配置化,表单拓展,部分组件功能拓展)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaikhc
系列文章
更多 icon
同类精品
更多 icon
继续加载