React VS Vue渲染100-10000个Swiper性能比较

#1

从入行前端起,我就开始使用react开发,直至今年,因为新公司使用vue,所以我也切换到vue开发。

开发过程中做了一个需求:实现几百个轮播,并且可以切换不同个数的轮播。

最终这个项目我使用了vue+swiper,期间遇到了比较严重的卡顿,也就是性能问题,后面我也做了一些优化。

由此,我很好奇使用react和vue分别实现swiper在性能方面有什么不同。

为了避开开发环境的影响,测试的数据分别在打包后的 生产状态下进行统计!

1、使用create-react-app创建一个react项目,使用vue-cli创建一个vue项目。2个项目我都没有加vuex或redux,仅仅是一个简单的demo,确保受其他因素的影响小一些。

2、跳过添加代码的步骤,我们看一下测试的demo图。

3、生成测试数据
new Array(1000).fill({a: 1000})

第一回合 比较1000个轮播的性能

初始化的时候,2个项目都是1000个轮播,我们对比一下2者目前状态下的性能。

React:

Vue:

有2个指标需要关注一下,Scripting和Rendering,渲染方面,2者时间差不多,但js执行上,React不如Vue快。

接着,对比一下内存占用情况。

React:

Vue:

React内存占用高一点,Vue相对低一些。

总体来说,该回合React完败。

第二回合 比较10000个轮播性能

初始化的时候,2个项目都是10000个轮播,我们对比一下2者目前状态下的性能。

这一次,基数暴增!!!
React:

Vue:

渲染方面和js执行上,React都比Vue慢。

接着,对比一下内存占用情况。

React:

Vue:

React内存比Vue高。

总体来说,该回合React完败。

第三回合 比较切换不同轮播个数时候的性能变化

之前2个回合React都输了,我不服!!来个究极对比,this.setState和this.data更新那个更快??

React:初始化的时候是1000,然后我点击按钮切换到2000, 5000,10000,100,监测性能变动。

Vue:初始化的时候是1000,然后我点击按钮切换到2000, 5000,10000,100,监测性能变动。

结果分析:在切换过程中,2者的内存都是在增长,整体js的执行时间,React比Vue长一点,在点击切换的时候,我也能感受到React卡顿比较明显。在切换结束后,双方都变成了100个轮播的状态,而此时React的内存还是很高,Vue已经降到很低了。

结论

我还做了其他个数的性能比较,发现React始终不如Vue,数据量越大,React就越卡,而Vue的体验上始终比较流畅。
当然,这个结果不能作为最终的结论,因为更新数据的时候,该数据的对象层次越深,在虚拟DOM的js执行上就可能影响不同。
我自己做的那个需求里面,就是一个几十KB的JavaScript对象,在Vue中进行Swiper渲染和切换,最后的效果也是很卡。

优化方案

那么既然React和Vue在渲染数据量比较大的Swiper上都表现的不那么好,有没有什么优化方案?

答案是有的!!

Swiper提供了一种 “virtual slide”的渲染方式,这种渲染可以100%避免性能瓶颈,但是也有缺点,就是无法做到一些特殊的交互效果,所以最终我没有采用这种方案。

最后附上核心源码:

React:App.js文件

    import React, { Component } from 'react'
    import './App.css'
    import 'swiper/dist/css/swiper.min.css'
    import Swiper from 'swiper'
    import logo from './logo.svg'

    class App extends Component {
      state = {
        arrList: new Array(1000).fill({a: 1000}),
        options: {
          spaceBetween: 10,
          loop:true,
          freeMode: true,
          loopedSlides: 5, //looped slides should be the same
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
          }
        }
      }
      initSwiper = () => {
        this.swiper = new Swiper('.gallery-top', this.state.options)
      }
      componentDidMount() {
        this.initSwiper()
      }
      componentWillUpdate(nextProps, nextState, nextContext) {
        this.start = new Date().getTime()
        if (this.swiper) {
          this.swiper.destroy()
        }
      }

      componentDidUpdate(prevProps, prevState, snapshot) {
        this.initSwiper()
        this.end = new Date().getTime()
        console.log('time: ', this.end - this.start)
      }
      switchData = (val) => {
        this.setState(() => ({arrList: new Array(val).fill({a: val})}))
      }

      render() {
        const { arrList } = this.state
        return (
          <div className="App">
            <div className="switch-data">
              <span onClick={() => this.switchData(10000)}>10000</span>
              <span onClick={() => this.switchData(5000)}>5000</span>
              <span onClick={() => this.switchData(2000)}>2000</span>
              <span onClick={() => this.switchData(1000)}>1000</span>
              <span onClick={() => this.switchData(100)}>100</span>
            </div>
            <div className="swiper-container gallery-top">
              <div className="swiper-wrapper">
                {
                  arrList.length > 0 && arrList.map((item, key) => {
                    return (
                      <div key={key} className="swiper-slide">
                        <p>key: {key}</p>
                        <p>value: {item.a}</p>
                        <img src={logo} alt=""/>
                      </div>
                    )
                  })
                }
              </div>
              <div className="swiper-button-next swiper-button-white"></div>
              <div className="swiper-button-prev swiper-button-white"></div>
            </div>
          </div>
        )
      }
    }

    export default App

Vue: App.vue文件

    <template>
      <div id="app">
        <div class="switch-data">
          <span @click="switchData(10000)">10000</span>
          <span @click="switchData(5000)">5000</span>
          <span @click="switchData(2000)">2000</span>
          <span @click="switchData(1000)">1000</span>
          <span @click="switchData(100)">100</span>
        </div>
        <div class="swiper-container gallery-top">
          <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(item, key) in arrList" :key="key">
              <p>key: {{key}}</p>
              <p>value: {{item.a}}</p>
              <img src="./assets/logo.png" alt="">
            </div>
          </div>
          <!-- Add Arrows -->
          <div class="swiper-button-next swiper-button-white"></div>
          <div class="swiper-button-prev swiper-button-white"></div>
        </div>
      </div>
    </template>

    <script>
    // import HelloWorld from './components/HelloWorld.vue'
    import Swiper from 'swiper'

    export default {
      name: 'app',
      data () {
        return {
          arrList: new Array(1000).fill({a: 1000}),
          options: {
            spaceBetween: 10,
            loop:true,
            freeMode: true,
            loopedSlides: 5, //looped slides should be the same
            navigation: {
              nextEl: '.swiper-button-next',
              prevEl: '.swiper-button-prev',
            }
          }
        }
      },
      mounted() {
        this.initSwiper()
      },
      beforeUpdate () {
        this.start = new Date().getTime()
        if (this.swiper) {
          this.swiper.destroy()
        }
      },
      updated() {
        this.initSwiper()
        this.end = new Date().getTime()
        console.log('end: ', this.end - this.start)
      },
      methods: {
        initSwiper () {
          this.swiper = new Swiper('.gallery-top', this.options)
        },
        switchData (val) {
          this.arrList = new Array(val).fill({a: val})
        }
      }
    }
    </script>

    <style>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    html, body {
      position: relative;
      height: 100%;
    }
    body {
      font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
      font-size: 48px;
      color:#000;
      margin: 0;
      padding: 0;
    }
    .swiper-container {
      width: 100%;
      margin-left: auto;
      margin-right: auto;
      height: 100vh;
    }
    .swiper-slide {
      background-size: cover;
      background-position: center;
      border: 1px solid #000;
      height: 300px;
      text-align: center;
    }
    .gallery-top {
      width: 100%;
    }
    .gallery-thumbs {
      box-sizing: border-box;
      padding: 10px 0;
    }
    .gallery-thumbs .swiper-slide {
      opacity: 0.4;
    }
    .gallery-thumbs .swiper-slide-thumb-active {
      opacity: 1;
    }

    .App-link {
      color: #61dafb;
    }

    @keyframes App-logo-spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    .switch-data {
      position: fixed;
      left: 100px;
      top: 100px;
      z-index: 10;
    }
    .switch-data span {
      display: flex;
      justify-content: center;
      align-items: center;
      border: 1px solid #000000;
      background: #000000;
      color: #fff;
      padding: 10px 30px;
      margin-bottom: 20px;
    }
    </style>
2 Likes
#2

这看起来 像是 安利 vue 的文章了

#3

哦,你对技术有偏见吗? @ZeroJsus

#4

并没有 两者我都在用 看到这篇文章 我觉得vue确实在性能方面 是要强一些

#5

性能分析主要用在技术选型上,不同业务场景下,预先测试框架的性能,找到该场景下性能最好的框架。也不一定在所有场景中Vue都比React快。当然,我还没见过React比Vue快的时候。

#6

是通过性能分析来进行技术选型么 那么 这个步骤一般是在一个项目的评审期进行实验么

#7

没有严格的时间限制,敏锐的 前端会在恰当的时机先调研好。