精华内容
下载资源
问答
  • vue长列表按需渲染, 使用 提供npm版本和umd版本 npm 安装 // install npm i vue-better-list-view --save-dev // in your project import listView from 'vue-better-list-view' 或 umd 引入 [removed][removed] ...
  • vue 长列表性能优化

    千次阅读 2020-08-11 10:12:21
    vue 长列表性能优化 因为每次 DOM 修改,浏览器往往需要重新计算元素布局,再重新渲染。也就是所谓的重排(reflow)和重绘(repaint)。尤其是在页面包含大量元素和复杂布局的情况下,性能会受到影响。 一个常见的...

    vue 长列表性能优化

    因为每次 DOM 修改,浏览器往往需要重新计算元素布局,再重新渲染。也就是所谓的重排(reflow)和重绘(repaint)。尤其是在页面包含大量元素和复杂布局的情况下,性能会受到影响。

    一个常见的场景是大数据量的列表渲染。通常表现为可无限滚动的无序列表或者表格,当数据很多时,页面会出现明显的滚动卡顿

    方案1:
    做分页处理,首页默认展示第一页数据,滚动加载,这也是一种方案,如果列表做不了分页,那该如何处理。

    方案2:
    虚拟列表的实现原理:只渲染可视区的 dom 节点,其余不可见的数据卷起来,只会渲染可视区域的 dom 节点,提高渲染性能及流畅性,优点是支持海量数据的渲染;当然也会有缺点:滚动效果相对略差(海量数据与滚动效果的取舍问题就看自己的需求喽
    使用vue-virtual-scroller插件

    安装这个插件:
    $ npm install -D vue-virtual-scroller
    
    main.js 引入这个插件:
    import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
    import Vue from "vue";
    import VueVirtualScroller from "vue-virtual-scroller";
    
    Vue.use(VueVirtualScroller);
    

    方案3:
    滚动节流减少服务器压力。

    方案4:
    vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

    exportdefault{ data: =>({ users: {} }), asynccreated { constusers = awaitaxios.get("/api/users"); this.users = Object.freeze(users); } };
    
    展开全文
  • 本资源是对浏览器中的Event Loop深入理解,requestAnimationFrame性能优化,DocumentFragment性能优化,Vue.js中的固定高度虚拟列表优化,面向未来的无限列表优化方案的实践,是对vue原理的一次深入理解。
  • 记一次vue长列表的内存性能分析和优化

    原理:这种方案的原理是使用一个大容器作为滚动区域,里面有一个内容区域,JS通过数据数量和每条数据的高度计算出内容区的高度,内容区用padding或绝对定位撑开滚动区域,让容器可滚动,另外就是数据项了,滚动的时候,计算当前滚动位置scrollTop,再从数据项中找出各项的高度,从头到尾计算出此时容器中放什么数据。
    总结一下,主要的五个优化

    1. 将表格实现改为其他元素标签实现

    2. 仅渲染视窗可见的数据

    3. 进行函数节流

    4. 减少驻留的VNode和Vue组件,不使用显示的子组件slot方式,改为手动创建虚拟DOM来切断对象引用

    5. 减少操作期间增加的对象,操作时组件必然会更新创建,可以减少组件中子组件的数量

    记一次vue长列表的内存性能分析和优化

    展开全文
  • vue长列表性能优化

    千次阅读 2019-06-20 17:39:51
    我们应该都知道 vue会通过 object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 vue来劫持我们的数据,在大量数据展示的情况...

    我们应该都知道 vue会通过 object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 vue来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 vue劫持我们的数据呢?可以通过 object.freeze方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

    export default {
      data: () => ({
        users: {}
      }),
      async created() {
        const users = await axios.get("/api/users");
    
        this.users = Object.freeze(users);
      }
    };

    另外需要说明的是,这里只是冻结了 users的值,引用不会被冻结,当我们需要 reactive数据的时候,我们可以重新给 users赋值。

    methods: {
        handelClick() {
          const newArr = [7, 8, 9, 10];
          this.users = newArr;
        }
      }

     

    展开全文
  • 记一次vue长列表的内存性能分析和优化 好久没写东西,博客又长草了,这段时间身心放松了好久,都没什么主题可以写了 上周接到一个需求,优化vue的一个长列表页面,忙活了很久也到尾声了,内存使用和卡顿都做了一...

    记一次vue长列表的内存性能分析和优化

     

    好久没写东西,博客又长草了,这段时间身心放松了好久,都没什么主题可以写了
    上周接到一个需求,优化vue的一个长列表页面,忙活了很久也到尾声了,内存使用和卡顿都做了一点点优化,还算有点收获
    写的有点啰嗦,可以看一下我是怎么进行这个优化的,也许有点帮助呢

    这个长列表页面,其实是一个实时日志上报的页面,随着页面打开时间的增加,日志数量也会增多,常规的页面布局和渲染免不了会遇到性能问题。

    使用了vue框架,框架内部的虚拟DOM和组件缓存已经做了一些优化,比起原生实现是有了一些优化处理。

    但这个页面是用到element-ui的el-table组件,渲染出来的是表格数据列表,众所周知,表格在渲染的时候需要绘制整个表格区,所以,

    第一步就是将表格实现改为其他元素标签实现

    这一步操作之后,其实没什么大的变化的,几千条日志(每条日志还有很多信息)左右,滚动页面明显卡顿严重

    而需求又改不了,日志可以展开查看详情或收起,已经看过的日志在下次看的时候不需要加载,新的日志会实时添加进来

    以前在做大表格数据鼠标滑过行着色的时候,也有严重的卡顿,当时主要的优化手段是不对所有数据进行处理,仅处理视窗可见区域,也可以在这里试试,所以

    第二步就是仅渲染视窗可见的数据

    这种方案的原理是使用一个大容器作为滚动区域,里面有一个内容区域,JS通过数据数量和每条数据的高度计算出内容区的高度,内容区用padding或绝对定位撑开滚动区域,让容器可滚动,另外就是数据项了,滚动的时候,计算当前滚动位置scrollTop,再从数据项中找出各项的高度,从头到尾计算出此时容器中放什么数据

    哈哈哈 ... 这文字描述简直了,看不懂就不看了吧,可以去看下别人的解说

    知道原理之后,实现起来也不难,不过代码就写的比较凌乱了,还是使用现成的比较成熟的vue插件吧,比较方便

    复制粘贴一顿猛操作之后,页面重新展现出来,想着应该可以收工了吧

    然鹅,测试的时候发现,页面内存使用可以达到一两G,看来不仅要优化卡顿,还要优化内存使用

     

     

    还能遇到这种少见的页面崩溃,也算是开了眼了

     

     

    这个方案是把原先页面应该渲染的所有DOM拆分出来,动态地渲染该渲染的部分,

    所以就会有一个问题,动态计算需要时间,当滚动非常快的时候会有明显的卡顿现象,所以

    第三步就是进行函数节流,即控制scroll事件的处理,在规定的时间内仅触发一次

    代码参考

    // 函数节流,频繁操作中间隔 delay 的时间才处理一次
    function throttle(fn, delay) {
        delay = delay || 200;
        
        var timer = null;
        // 每次滚动初始的标识
        var timestamp = 0;
    
        return function() {
            var arg = arguments;
            var now = Date.now();
            
            // 设置开始时间
            if (timestamp === 0) {
                timestamp = now;
            }
            
            clearTimeout(timer);
            timer = null;
            
            // 已经到了delay的一段时间,进行处理
            if (now - timestamp >= delay) {
                fn.apply(this, arg);
                timestamp = now;
            }
            // 添加定时器,确保最后一次的操作也能处理
            else {
                timer = setTimeout(function() {
                    fn.apply(this, arg);
                    // 恢复标识
                    timestamp = 0;
                }, delay);
            }
        }
    };
    
    var count = 0;
    
    window.onscroll = throttle(function(e) {
        console.log(e.type, ++count); // scroll
    }, 500);
    

    虽然改善不是很大,但好歹也是一种方案

    接下来是针对这个磨人的内存占用了,也花了蛮多时间去分析去定位,头发又少了几根..

    现象是这样的:

    刚进入页面的时候,最初100条数据,仅渲染30条数据,内存就占用了100+M

    滚动的时候内存蹭蹭蹭往上涨,峰值能到几个G,一段时间后又下降一部分

    随着数据总量的增多,内存最初的占用和最后的占用也不同

    在常规滚动和快速滚动的时候,内存占用也不同

    最后发现在数据总量一定的时候,内存最大占用量是固定的(垃圾回收之后)

    嗯挺奇怪的,实际项目比较复杂,有其他组件干扰,不好排除法分析

    所以就从插件给的Demo 开刀,发现它的表现是一致的

     

     

    分析要有数据,实验和方案选取要有对比测试

    所以使用Chrome DevTool 自带的 Memory工具,另外为了避免Chrome插件的影响,在隐身窗口中进行调试

     

     

    上面有个强制垃圾回收的按钮,JS垃圾回收机制是什么这里就不说了,可以去搜一下

    目前垃圾回收方案主要都是标记清除法了,而实现主要是根据GC根往下一层层遍历,遍历不到的对象会被垃圾回收掉,当某些对象本应该被回收,但还是能从GC根访问的时候,就产生了内存泄漏,主要需要考虑两类内存泄漏:普通JS的对象,游离的DOM节点(本该被回收,却还有对象引用它)

    垃圾回收的时间点是不固定的,随机的,我们在代码中没法控制

    点击左边的第一个小圆圈就可以开始分析了,一般来说分析之前都会自动进行垃圾回收,不过为了更准确,可以再强制点按钮回收一次

    常用的主要就是两种分析方式:

    第一种是进行堆快照(JS的对象一般放在堆中),查看当前的内存分布情况

    第二种是进行内存时间线分析,查看一顿操作之后的内存增长情况,主要针对这个操作过程(这个时候可以结合Performance标签功能中来分析)

    上图中左侧是两个快照的结果,64.5M是进入页面之后的内存快照,149M是各种操作之后的内存快照

    <VirtualList :size="50" :remain="6" :bench="44" class="list" :start="startIndex" :debounce="10">
                <Item v-for="(udf, index) of items" :index="index" :key="index"></Item>
            </VirtualList>

    这个长列表总共10w条数据,仅仅渲染了50条(6 + 44)数据,每条数据仅仅是短短的字符串,不该占用这么多内存

    去看下内存具体占用情况

     

     

    内容有点多,因为用的是vue,所以我们只需要关注比较重要的虚拟DOM对象 VNode和渲染的组件就行了

    VNode基本就是所有的数据了,VueComponent是当前渲染的,所以,这里的VNode是不是有很多内存浪费了,与之关联的很多东西也占坑了

     

     

    看看字符串内容,每条仅仅占用了32字节,所以这里想到的一个点是要缩减Item项的数量

    然后,想想为什么所有虚拟DOM都留在了内存中呢,展开一个来看对象的引用关系,有一个$slot.default

     

     

    然后回去看看插件的实现,插件是将所有子项目都放到了子元素中,以slot的方式插入,然后在内部抽出进行再创建

     

     

     

     

    容器组件在重新渲染的时候,确实能触发了组件的销毁函数 destroy,而这个也将对象间的关系清的干干净净的了

    具体可以看vue中组件是怎么销毁的

    Vue.prototype.$destroy = function () {
          var vm = this;
          if (vm._isBeingDestroyed) {
            return
          }
          callHook(vm, 'beforeDestroy');
          vm._isBeingDestroyed = true;
          // remove self from parent
          var parent = vm.$parent;
          if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
            remove(parent.$children, vm);
          }
          // teardown watchers
          if (vm._watcher) {
            vm._watcher.teardown();
          }
          var i = vm._watchers.length;
          while (i--) {
            vm._watchers[i].teardown();
          }
          // remove reference from data ob
          // frozen object may not have observer.
          if (vm._data.__ob__) {
            vm._data.__ob__.vmCount--;
          }
          // call the last hook...
          vm._isDestroyed = true;
          // invoke destroy hooks on current rendered tree
          vm.__patch__(vm._vnode, null);
          // fire destroyed hook
          callHook(vm, 'destroyed');
          // turn off all instance listeners.
          vm.$off();
          // remove __vue__ reference
          if (vm.$el) {
            vm.$el.__vue__ = null;
          }
          // release circular reference (#6759)
          if (vm.$vnode) {
            vm.$vnode.parent = null;
          }
        };
    

    把$vnode的对象关系都切的差不多了,但slot方式的使用下是处理不了的,所以在垃圾回收之后,内存中的vnode对象非常多

    再来看看内存占用的最大值

     

     

    可以发现VNode增长了一部分,而最为瞩目的是VueComponent数量竟然有那么多,按道理应该只有渲染的几个组件的

    为了做对比,我们一般使用comparison对比两个快照,看看相差的地方

    相关使用可以去看文档

    有兴趣的也可以导入我这两个快照自行分析 default maximum

     

     

    这段时间里创建的vue对象基本没能被清理掉,说明有很多不应该出现的对象引用关系,其中detached HTMLDivElement是指游离的DOM对象,一般用于分析DOM相关的内存泄漏,可以猜测出这里的主角应该是vue的组件

    挑一个组件来看看,可以发现它还是和slot有关的,所以滚动期间创建的组件,属于VNode节点的componentInstance属性,而VNode节点没法被回收,所以组件驻留在内存中

    接下来的问题是,既然一开始VNode是所有的数据了,为何在滚动期间,还会有那么多VNode会创建出来

    挑一个这期间增加的VNode来看看引用关系,可以发现VNode中有两种,增加的是不同的_vnode

    @后面带的是对象的id,另外我们也可以在调试的时候,console打印出它们是不同的对象

     

     

    经过上面各种分析,有两个问题需要去解决

    减少驻留的VNode和Vue组件

    减少操作期间增加的对象

    减少驻留,即不用slot的方式,那只能改插件了

    插件中vm.$slots.default 获取到的是vnode节点,然后再使用render函数传递vnode进行创建组件并渲染

    由此想来,我们也可以自己创建vnode节点,

    不直接写成子组件,而是将纯粹的数据项和组件单元传递给插件,让插件来创建vnode节点

    <VirtualList :size="50" :remain="6" :bench="44" class="list" :start="startIndex"
                :items="items" :item-component="itemComponent" :item-binding="itemBinding">
            </VirtualList>

    items 是数据项,itemComponent是 import 进来的一个组件单元,itemBinding是一个函数,返回类似渲染函数的data对象,用以传递属性

    itemBinding(item, idx) {
                    return {
                        key: item,
                        props: {
                            index: item
                        }
                    };
    
                    // return {
                    //     key: item.id,
                    //     props: {
                    //         index: item.num,
                    //     },
                    //     nativeOn: {
                    //         dblclick: (...args) => {
                    //             console.log(idx, 'dblclick');
                    //         }
                    //     }
                    // }
                }
    

    在插件内部,接收传递进来的items和itemComponent,构造出相应的vnodes,当然slots方式也可以支持

    for (var i = delta.start; i <= Math.ceil(delta.end); i++) {
                        targets.push(!this.itemComponent ? slots[i]
                            // create vnode, using custom attrs binder
                            : this.$createElement(this.itemComponent, this.itemBinding(this.items[i], i) || {})
                        )
                    }
    
                    return targets
    

    完整的代码实例可以看这里

    解决办法挺简单的,虽然这一步创建会耗费一些时间,不过测试发现,跟原先的做法差不多的,原先的也需要创建

    来看看优化之后的内存占用情况

     

     

     

     

    同样的数据,最初进入页面占用5M,各种操作之后也差不多,操作之中创建的vue对象基本被清理掉了,且对象数量还算符合预期

    在当前10万条简单数据下,内存使用初始减小成1/13,最大减小成1/26,而且随着总数量的增加,优化比率也更高

    在实际项目组件复杂的情况下使用,400条日志,内存使用大概由400M到80M,优化率达到了1/5,也挺可观

    接下来考虑一下如何减少操作期间增加的对象

    这就需要收集一些操作过程中的数据了

    分析过程,我比较喜欢用Performance面板,这里有非常详细的函数调用栈,

    另外还要使用调试大法,由最开始的onScroll事件入口开始,一步一步地理解组件创建更新销毁过程,看看哪些地方合不合理,能不能在上游在外部间接地改进

     

     

    点击左侧小圆圈开始记录,然后滚动一段时间,然后结束记录,查看收集的信息

    勾选了右上角的memory选项框知乎,这个面板也可以查看内存的使用,不过记得手动进行一次垃圾回收(那个按钮),因为它一般在记录之前不会自动调用

    可以发现还是比较规律的,挑这段略为明显的进行分析

    有兴趣的也可以自己导入我这份数据进行分析

     

     

    可以发现这里发生了组件的更新,$mount和$destroy的调用,是发生在插件重新渲染可视区域组件的时候

    找到关键的地方,调试分析发现每次都会创建新的VNode对象

     

     

     

     

     

     

    这样看来,操作期间创建的对象是避免不了的了,只能通过减少操作期间函数执行的次数了,即最初提到的函数节流

    而组件销毁的时候,会判断组件是否为keepAlive型,可以尝试一下给Item组件加上,这能解决操作期间组件创建和销毁带来的内存开销,不过会导致所有组件都会驻留在内存中,综合考虑下,这种方案不可取

    最后想想,再挤出一点优化方案,既然操作过程中会创建组件,而组件里可能还有子组件,所以,还可以优化子组件

    即Item组件内部,能不用组件的可以不用组件,改为普通HTMl标签代替,经过测试,确实能改善那么一丢丢

    一个性能问题的排查分析和解决,文章略长略啰嗦,到这里就结束了

    总结一下,主要的五个优化

    1. 将表格实现改为其他元素标签实现

    2. 仅渲染视窗可见的数据

    3. 进行函数节流

    4. 减少驻留的VNode和Vue组件,不使用显示的子组件slot方式,改为手动创建虚拟DOM来切断对象引用

    5. 减少操作期间增加的对象,操作时组件必然会更新创建,可以减少组件中子组件的数量

    展开全文
  • 经历:我在加载一个长列表。每次加载50条数据。基本上无限次数加载。可以在移动端感觉出明显的卡顿 下面是我的优化 图片必须懒加载,是否懒加载蛮明显的。 图片懒加载也要优化好。例如onPageScroll事件中就可以...
  • vue 长列表渲染优优化

    千次阅读 2019-11-16 16:47:14
    //所有列表数据 listData: { type: Array, default: () => [] }, //预估高度 estimatedItemSize: { type: Number, required: true }, //容器高度 100px or 50vh height: { type: String, default: ...
  • 虚拟滚动 先看一下mockjs语法: 参考: mockjs介绍 mock.js官方示例 先上服务端源码: ...// 根据传入的参数num 生成num条模拟的数据列表 function generatorList(num) { return Mock.mock({ [`lis
  • vue 滚动加载无限长列表

    千次阅读 2020-01-09 16:29:49
    结合vue-infinite-scroll和vue-virtual-sroller实现滚动加载无限长列表,带有虚拟化(virtualization)功能,能够提高数据量大时候长列表的性能,解决列表数据量大页面卡顿问题 滚动加载vue-infinite-scroll vue-...
  • vue下拉列表

    2019-07-24 17:43:00
    vue处理下拉列表 vue绑定下拉列表 <select id="selected" v-model="selected"> <option value="" >{{selecttext}}</option> </select> selected:...
  • Vue列表渲染

    2019-07-02 14:02:29
    本博客记录了小编在学习Vue过程中的体会与感悟,简单来说就是小编的学习笔记,学习地址主要为Vue的官网,官网... 我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊...
  • VUE中解决长列表方法

    2021-07-01 20:41:54
    yarn add vue-virtual-scroll-list --save 使用 在需要的页面 import virtualList from 'vue-virtual-scroll-list' 页面 <template> <div class="about"> <h1>This is an about page</h1&...
  • 虚拟滚动插件 1 背景 一个长列表 Web 页面,如果需要展示成千上万条数据,那么页面中就会有数万甚至数十万的HTML节点,会巨大的消耗浏览器性能,进而给用户造成非常不友好的体验。...长列表数据不可视部分使用使用
  • vue实现列表滚动

    2021-03-24 14:36:15
    实现列表滚动 代码解释:两张表,第一张表显示表头,第二张表滚动 vue 描述:当列表中数据需要滚动时 html部分: <el-table :data="list" style="width: 100%; height: 50px" class="custom-table-2 hidden-...
  • vue实现列表文字太显示展开收起

    千次阅读 2020-04-17 11:50:24
    <div class="content" v-for="(item, index) in evaluate" :key="index" > <div class="photo"> <img src="@/assets/15Qrg.png" alt=...
  • vue 项目列表滑动卡顿

    千次阅读 2019-09-10 11:21:41
    本人在vue项目中使用美赞的vant组件 列表滑动卡顿 修改添加以下属性就可以来了 .van-pull-refresh { overflow-y: auto; -webkit-overflow-scrolling:touch }
  • vue滚动列表嵌套

    千次阅读 2017-12-27 20:38:20
    如图vue实现滚动列表中嵌套图片,且图片可被遮挡 整个页面布局如下: 布局代码如下:.music-list position: fixed z-index: 100 top: 0 left: 0 bottom: 0 right: 0 background: $color-background .back...
  • vue虚拟化长列表插件推荐

    千次阅读 2020-04-03 11:54:32
    vue-virtual-scroller
  • Vue 3 列表渲染

    2020-10-16 22:41:54
    列表渲染实验介绍列表渲染,其实给人的感觉就是 JavaScript 中的 for 循环,只不过将其提取到 HTML 模板层面了,将其指令化,本质还是 for 循环。用 v-for 把一个...
  • 1.安装vue-virtual-scroll-list npminstallvue-virtual-scroll-list--save 2. 引入下载好的组件 记得局部注册一下组件 importvirtualListfrom'vue-virtual-scroll-list' 3.v-for循环的方式 循环数据 <...
  • 安装 vue-virtual-scroll-list npm install vue-virtual-scroll-list --save 2.引入下载好的组件 记得局部注册一下组件 import virtualList from 'vue-virtual-scroll-list' 3.Hello.vue <template> ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,667
精华内容 7,466
关键字:

vue长列表

vue 订阅