Vue 初始化机能优化

原文: https://github.com/Coffcer/Bl…

媒介

一般来说,你不须要太体贴vue的运行时机能,它在运行时异常快,但支付的价值是初始化时相对较慢。在近来开辟的一个Hybrid APP里,Android Webview初始化一个较重的vue页面居然用了1200ms ~ 1400ms,这让我最先注重vue的初始化机能,并终究优化到200 ~ 300ms,这篇文章分享我的优化思绪。

机能瓶颈在那里?

先看一下罕见的vue写法:在html里放一个app组件,app组件里又援用了其他的子组件,构成一棵以app为根节点的组件树。

<body>
    <app></app> 
</body>

而恰是这类做法引发了机能题目,要初始化一个父组件,必定须要先初始化它的子组件,而子组件又有它本身的子组件。那末要初始化根标签<app>,就须要从底层最先冒泡,将页面一切组件都初始化完。所以我们的页面会在一切组件都初始化完才最先显现。

这个结果显著不是我们要的,更好的结果是页面能够从上到下按递次流式衬着,如许能够整体时候增长了,但首屏时候缩减,在用户看来,页面翻开速率就更快了。

要完成这类衬着形式,我总结了下有3种体式格局完成。第3种体式格局是我以为最合适的,也是我在项目中实际运用的优化要领。

第一种:不运用根组件

这类体式格局异常简朴,比方:

<body>
    <A></A>
    <B></B>
    <C></C>
</body>

扬弃了根组件<app>,从而使A、B、C每个组件初始化完都马上展现。但根组件在SPA里是异常必要的,所以这类体式格局只实用小型页面。

第二种:异步组件

异步组件在官方文档已有申明,运用异常简朴:

<app>
    <A></A>
    <B></B>
</app>
new Vue({
    components: {
        A: { /*component-config*/ },
        B (resolve) {
            setTimeout(() => {
                resolve({ /*component-config*/ })
            }, 0);
        }
    }
})

这里<B>组件是一个异步组件,会比及手动挪用resolve函数时才最先初始化,而父组件<app>也没必要守候<B>先初始化完。

我们应用setTimeout(fn, 0)将<B>的初始化放在行列末了,结果就是页面会在<A>初始化完后马上显现,然后再显现<B>。假如你的页面有几十个组件,那末把非首屏的组件全设成异步组件,页面显现速率会有显著的提拔。

你能够封装一个简朴的函数来简化这个历程:

function deferLoad (component, time = 0) {
    return (resolve) => {
        window.setTimeout(() => resolve(component), time)
    };
}

new Vue({
    components: {
        B: deferLoad( /*component-config*/ ),
        // 100ms后衬着
        C: deferLoad( /*component-config*/, 100 )
    }
})

看起来很优美,但这类体式格局也有题目,斟酌下如许的构造:

<app>
    <title></title>
    <A></A>
    <title></title>
    <B></B>
    <title></title>
    <C></C>
</app>

照样根据上面的异步组件做法,这时候就须要斟酌把哪些组件设成异步的了。假如把A、B、C都设成异步的,那结果就是3个<title>会起首衬着出来,页面衬着的历程在用户看来异常新鲜,并非预期中的从上到下递次衬着。

第三种:v-if 和 terminal指令

这是我引荐的一种做法,简朴有用。照样谁人构造,我们给要耽误衬着的组件加上v-if:

<app>
    <A></A>
    <B v-if="showB"></B>
    <C v-if="showC"></C>
</app>
new Vue({
    data: {
        showB: false,
        showC: false
    },
    created () {
        // 显现B
        setTimeout(() => {
            this.showB = true;
        }, 0);
        // 显现C
        setTimeout(() => {
            this.showC = true;
        }, 0);
    }
});

这个示例写起来略显烦琐,但它已完成了我们想要的递次衬着的结果。页面会在A组件初始化完后显现,然后再按递次衬着其他的组件,全部页面衬着体式格局看起来是流式的。

有些人能够会忧郁v-if存在一个编译/卸载历程,会有机能影响。但这里并不须要忧郁,因为v-if是惰性的,只有当第一次值为true时才会最先初始化。

这类写法看起来很贫苦,假如我们能完成一个相似v-if的组件,然后直接指定若干秒后衬着,那就更好了,比方:

<app>
    <A></A>
    <B v-lazy="0"></B>
    <C v-lazy="100"></C>
</app>

一个简朴的指令即可,不须要js端任何合营,而且能够用在一般dom上面,Nice!

在vue里,相似v-ifv-for这类是terminal指令,会在指令内部编译组件。假如你想要本身完成一个terminal指令,须要加上terminal: true,比方:

Vue.directive('lazy', {
    terminal: true,
    bind () {},
    update () {},
    unbind () {}
});

这是vue在1.0.19+新增的功用,因为比较冷门,文档也没有迥殊细致的叙说,最好的体式格局是参照着v-ifv-for的源码来写。

我已为此封装了一个terminal指令,你能够直接运用:
https://github.com/Coffcer/vu…

其他的优化点

除了组件上的优化,我们还能够对vue的依靠革新入手。初始化时,vue会对data做getter、setter革新,在当代浏览器里,这个历程实际上挺快的,但依然有优化空间。

Object.freeze()是ES5新增的API,用来凝结一个对象,制止对象被修正。vue 1.0.18+今后,不会对已凝结的data做getter、setter转换。

假如你确保某个data不须要跟踪依靠,能够运用Object.freeze将其凝结。但请注意,被凝结的是对象的值,你依然能够将援用全部替换调。看下面例子:

<p v-for="item in list">{{ item.value }}</p>
new Vue({
    data: {
        // vue不会对list里的object做getter、setter绑定
        list: Object.freeze([
            { value: 1 },
            { value: 2 }
        ])
    },
    created () {
        // 界面不会有相应
        this.list[0].value = 100;

        // 下面两种做法,界面都邑相应
        this.list = [
            { value: 100 },
            { value: 200 }
        ];
        this.list = Object.freeze([
            { value: 100 },
            { value: 200 }
        ]);
    }
})

跋文

vue 1.0+ 的组件实在不算轻量,初始化一个组件包含依靠网络、转换等历程,但实在有些是能够放在编译时提前完成的。vue 2.0+ 已在这方面做了不少的革新:分离了编译时和运行时、供应函数组件等,能够预感,vue 2.0的机能将有很大的提拔。

v-lazy-component: https://github.com/Coffcer/vu…

    原文作者:Coffce
    原文地址: https://segmentfault.com/a/1190000006893428
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞