189 8069 5689

浅谈Vue初始化性能优化

前言

成都创新互联公司专注骨干网络服务器租用10多年,服务更有保障!服务器租用,川西大数据中心 成都服务器租用,成都服务器托管,骨干网络带宽,享受低延迟,高速访问。灵活、实现低成本的共享或公网数据中心高速带宽的专属高性能服务器。

一般来说,你不需要太关心vue的运行时性能,它在运行时非常快,但付出的代价是初始化时相对较慢。在最近开发的一个Hybrid APP里,Android Webview初始化一个较重的vue页面竟然用了1200ms ~ 1400ms,这让我开始重视vue的初始化性能,并最终优化到200 ~ 300ms,这篇文章分享我的优化思路。

性能瓶颈在哪里?

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


   

而正是这种做法引发了性能问题,要初始化一个父组件,必然需要先初始化它的子组件,而子组件又有它自己的子组件。那么要初始化根标签,就需要从底层开始冒泡,将页面所有组件都初始化完。所以我们的页面会在所有组件都初始化完才开始显示。

这个结果显然不是我们要的,更好的结果是页面可以从上到下按顺序流式渲染,这样可能总体时间增长了,但首屏时间缩减,在用户看来,页面打开速度就更快了。

要实现这种渲染模式,我总结了下有3种方式实现。第3种方式是我认为最合适的,也是我在项目中实际使用的优化方法。

第一种:不使用根组件

这种方式非常简单,例如:


  
  
  

抛弃了根组件,从而使A、B、C每一个组件初始化完都立刻展示。但根组件在SPA里是非常必要的,所以这种方式只适用小型页面。

第二种:异步组件

异步组件在官方文档已有说明,使用非常简单:


  
  

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

这里组件是一个异步组件,会等到手动调用resolve函数时才开始初始化,而父组件也不必等待先初始化完。

我们利用setTimeout(fn, 0)将的初始化放在队列最后,结果就是页面会在初始化完后立刻显示,然后再显示。如果你的页面有几十个组件,那么把非首屏的组件全设成异步组件,页面显示速度会有明显的提升。

你可以封装一个简单的函数来简化这个过程:

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 )
  }
})

看起来很美好,但这种方式也有问题,考虑下这样的结构:


  
  
  
  
  
  

还是按照上面的异步组件做法,这时候就需要考虑把哪些组件设成异步的了。如果把A、B、C都设成异步的,那结果就是3个会首先渲染出来,页面渲染的过程在用户看来非常奇怪,并不是预期中的从上到下顺序渲染。</p><p><strong>第三种:v-if 和 terminal指令</strong></p><p>这是我推荐的一种做法,简单有效。还是那个结构,我们给要延迟渲染的组件加上v-if:</p><div><pre><app> <A></A> <B v-if="showB"></B> <C v-if="showC"></C> </app> </pre></div><div><pre>new Vue({ data: { showB: false, showC: false }, created () { // 显示B setTimeout(() => { this.showB = true; }, 0); // 显示C setTimeout(() => { this.showC = true; }, 0); } }); </pre></div><p>这个示例写起来略显啰嗦,但它已经实现了我们想要的顺序渲染的效果。页面会在A组件初始化完后显示,然后再按顺序渲染其余的组件,整个页面渲染方式看起来是流式的。</p><p>有些人可能会担心v-if存在一个编译/卸载过程,会有性能影响。但这里并不需要担心,因为v-if是惰性的,只有当第一次值为true时才会开始初始化。</p><p>这种写法看起来很麻烦,如果我们能实现一个类似v-if的组件,然后直接指定多少秒后渲染,那就更好了,例如:</p><div><pre><app> <A></A> <B v-lazy="0"></B> <C v-lazy="100"></C> </app></pre></div><p>一个简单的指令即可,不需要js端任何配合,并且可以用在普通dom上面,Nice!</p><p>在vue里,类似v-if和v-for这种是terminal指令,会在指令内部编译组件。如果你想要自己实现一个terminal指令,需要加上terminal: true,例如:</p><div><pre>Vue.directive('lazy', { terminal: true, bind () {}, update () {}, unbind () {} });</pre></div><p>这是vue在1.0.19+新增的功能,由于比较冷门,文档也没有特别详细的叙述,最好的方式是参照着v-if和v-for的源码来写。</p><p>我已经为此封装了一个terminal指令,你可以直接使用:https://github.com/Coffcer/vu... </p><p><strong>其他的优化点</strong></p><p>除了组件上的优化,我们还可以对vue的依赖改造入手。初始化时,vue会对data做getter、setter改造,在现代浏览器里,这个过程实际上挺快的,但仍然有优化空间。</p><p><code>Object.freeze()</code>是ES5新增的API,用来冻结一个对象,禁止对象被修改。vue 1.0.18+以后,不会对已冻结的data做getter、setter转换。</p><p>如果你确保某个data不需要跟踪依赖,可以使用Object.freeze将其冻结。但请注意,被冻结的是对象的值,你仍然可以将引用整个替换调。看下面例子:</p><div><pre><p v-for="item in list">{{ item.value }}</p></pre></div><div><pre>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 } ]); } }) </pre></div><p><strong>后记</strong></p><p>vue 1.0+ 的组件其实不算轻量,初始化一个组件包括依赖收集、转换等过程,但其实有些是可以放在编译时提前完成的。vue 2.0+ 已经在这方面做了不少的改进:分离了编译时和运行时、提供函数组件等,可以预见,vue 2.0的性能将有很大的提升。</p><p>v-lazy-component: https://github.com/Coffcer/vu...  </p><p>以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。</p> <br> 文章题目:浅谈Vue初始化性能优化 <br> URL链接:<a href="http://cdxtjz.cn/article/psoocp.html">http://cdxtjz.cn/article/psoocp.html</a> </div> </div> <div class="other"> <h3>其他资讯</h3> <ul> <li> <a href="/article/ehdcos.html">做企业网站有哪些系统,现在做网站一般都用什么建站系统</a> </li><li> <a href="/article/ehdcop.html">怎么能免费建站</a> </li><li> <a href="/article/ehdded.html">域名与网站区别,域名与网址的区别:域名可理解为-0</a> </li><li> <a href="/article/ehddgc.html">php网站有哪些,php有什么好网站吗</a> </li><li> <a href="/article/ehddpp.html">tm商标域名权限,关于域名的五个知识点</a> </li> </ul> </div> </div> <footer> <div class="foot container"> <div class="footl fl"> <h3>联系我们</h3> <dl> 您好HELLO!<br> 感谢您来到成都网站建设公司,若您有合作意向,请您为我们留言或使用以下方式联系我们, 我们将尽快给你回复,并为您提供真诚的设计服务,谢谢。 </dl> <ul> <li>电话:028- <span>86922220 18980695689</span></li> <li>商务合作邮箱:631063699@qq.com</li> <li>合作QQ: 532337155</li> <li>成都网站设计地址:成都市青羊区锣锅巷31号五金站写字楼6楼</li> </ul> </div> <div class="footr fr"> <h3>小谭建站工作室</h3> <dl> 成都小谭网站建设公司拥有多年以上互联网从业经验的团队,始终保持务实的风格,以"帮助客户成功"为已任,专注于提供对客户有价值的服务。 我们已为众企业及上市公司提供专业的网站建设服务。我们不只是一家网站建设的网络公司;我们对营销、技术、管理都有自己独特见解,小谭建站采取“创意+综合+营销”一体化的方式为您提供更专业的服务! </dl> <h3>小谭观点</h3> <dl> 相对传统的成都网站建设公司而言,小谭是互联网中的网站品牌策划,我们精于企业品牌与互联网相结合的整体战略服务。<br> 我们始终认为,网站必须注入企业基因,真正使网站成为企业vi的一部分,让整个网站品牌策划体系变的深入而持久。 </dl> </div> </div> <div class="link"> <div class="container"> <span> 友情链接:</span> <a href="http://m.cdcxhl.cn/seo/ " title="成都网站优化" target="_blank">成都网站优化</a>   <a href="https://www.cdxwcx.com/tuiguang/" title="成都网络推广" target="_blank">成都网络推广</a>   <a href="https://www.cdxwcx.com/jifang/deyang.html" title="高防机房" target="_blank">高防机房</a>   <a href="http://www.gyjike.cn/" title="川银永信" target="_blank">川银永信</a>   <a href="http://www.cxjianzhan.com/" title="网络推广公司" target="_blank">网络推广公司</a>   <a href="http://www.kswsj.cn/tuiguang/" title="网站seo优化" target="_blank">网站seo优化</a>   <a href="http://www.hzjierui.cn/" title="彭州网站建设" target="_blank">彭州网站建设</a>   <a href="http://www.sichuanairmedia.com/" title="航空文化传媒" target="_blank">航空文化传媒</a>   <a href="http://www.cdhuace.com/faguangzi/wbfgz.html" title="无边发光字" target="_blank">无边发光字</a>   <a href="https://www.cdxwcx.com/wangzhan/h5.html" title="h5页面制作" target="_blank">h5页面制作</a>    </div> </div> <div class="copy"> © Copyright 2023 <a href="http://www.cdxtjz.cn/">小谭建站工作室</a>All Rights Reserved.  <a href="https://www.cdxwcx.com" target="_blank">成都网站建设</a> / <a href="https://www.cdcxhl.com/" target="_blank">成都网站建设</a> / <a href="https://www.cdxwcx.com" target="_blank">响应式网站建设</a> / <a href="https://www.cdcxhl.com/zuyong/" target=" _blank">成都服务器租用</a></div> </footer> </body> </html> <script> $(".con img").each(function(){ var src = $(this).attr("src"); //获取图片地址 var str=new RegExp("http"); var result=str.test(src); if(result==false){ var url = "https://www.cdcxhl.com"+src; //绝对路径 $(this).attr("src",url); } }); window.onload=function(){ document.oncontextmenu=function(){ return false; } } </script>