Vue的常见性能优化 | 青训营

34 阅读6分钟

编码阶段

不要在模板里面写过多表达式

<template>
    <div id="app">
        <p>{{ flag?'a:'b' }}</p> //少一点
        <button @click="exchange">转换</button>
    </div>
</template>

尽量减少 data 中的数据。 data 中的数据都会增加getter 和 setter,会收集对应的watcher,值改变时整个应用会重新渲染,可以使用computed (当新的值需要大量计算才能得到,缓存的意义就非常大)

data后续不使用的数据,使用Object.freeze()。这样可以避免vue初始化时候,做一些无用的操作,从而提高性能。

 data(){
    return{
    //直接调用为浅冻结
        list:Object.freeze({'我不需要改变'})
    }
}
//深冻结
//Object.freeze()原理
//Object.definedProperty()
Object.defineProperty(person, 'name', {
 configurable: false,//能否修改属性的特性
 enumerable: false,// 是否可以枚举。默认true
 writable: false,// 能否修改属性值。默认true
 value: 'xm'// 表示属性的值。访问属性时从这里读取,修改属性时,也保存在这里。
})
//Object.seal()
//实现不能删除,不能新增对象属性
function myFreeze(obj) {
if (obj instanceof Object) {
    Object.seal(obj); 
    let p;
     for (p in obj) {
    if (obj.hasOwnProperty(p)) {
      Object.defineProperty(obj, p, {
          writable: false
             });
          myFreeze(obj[p]);
            }
        }
    }
}

对象层级不要过深,否则性能就会差

computed 和 watch 区分使用场景

  • v-if 和 v-for 不能连用

当和 v-for 一起使用时,v-for 的优先级比 v-if 更高;

<div v-for="item in list" 
     v-if="item .show" 
     :key="item.id">
</div>
//每一次都这样运算
this.list.map( item=> {
     if (item.active) {
         return item.name
     }
});
//解决办法 
//1.使用空标签 template.
<template v-for="item in list" >
        <div v-if="show" :key="item .id">
            {{item.name}}
        </div >
</template>
//2.使用compted过滤属性
computed:{
	items:function(){
		return this.list.filter(item=>{
			return item.show
		})
	}
}

SPA 页面采用 keep-alive 缓存组件 两个生命周期钩子:activated 与 deactivated activated在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用 deactivated:组件被停用(离开路由)时调用 注意:使用了keep-alive就不会调用beforeDestroy(组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了。

mounted: function() {
     this.loaddata(1)
},
activated: function () {
     this.productclass.name=""//查询条件
     this.loaddata(1) //查询结果的方法
}

频繁切换的使用 v-show,不频繁切换的使用 v-if v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if key存在意义:为了跟踪每个节点的特征,使其具有唯一性,高效更新虚拟dom vue在更新已经渲染的元素序列时,会采用就地复用策略,都会在对顺序进行破坏时,不仅会产生真实dom更新,浪费资源,耿永导致产生错误更新。 比如两个inputAB输入值,在头部添加一个InputC,结果按顺序,CA有值,B无值。

使用路由懒加载、异步组件 路由懒加载

{ path:'./about', name:'About', component:() => import('../views/Aboout.vue')//在此处引入为懒加载 }

只有在使用该路由时才加载路由。可缩减首屏加载时间。

搜索引擎 SEO 优化

预渲染

vue是一个单页面应用(spa),只有一个 html 文件(内容只有一个#app根节点),通过加载js脚本来填充页面要渲染的内容,然而这种方式无法被爬虫和百度搜索到。 构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html 文件已经有静态数据,需要ajax数据的部分未构建

解决问题

SEO:单页应用的网站内容是根据当前路径动态渲染的,html 文件中往往没有内容,网络爬虫不会等到页面脚本执行完再抓取; 弱网环境:当用户在一个弱环境中访问你的站点时,你会想要尽可能快的将内容呈现给他们。甚至是在 js 脚本被加载和解析前; 低版本浏览器:用户的浏览器可能不支持你使用的 js 特性,预渲染或服务端渲染能够让用户至少能够看到首屏的内容,而不是一个空白的网页。

//1.安装预渲染插件
npm install prerender-spa-plugin -D  #安装或者编译出错,npm换成cnpm
//一个 webpack 插件用于在单页应用中预渲染静态 html 内容。因此,该插件限定了你的单页应用必须使用 webpack 构建,且它是框架无关的,无论你是使用 React 或 Vue 甚至不使用框架,都能用来进行预渲染。
//原理:在 webpack 构建阶段的最后,在本地启动一个 phantomjs,访问配置了预渲染的路由,再将 phantomjs 中渲染的页面输出到 html 文件中,并建立路由对应的目录。
 //2.配置vue.config.js
 const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {
    configureWebpack: {
        plugins: [
            new PrerenderSPAPlugin({
         // 生成文件的路径,与webpack打包一致即可
        // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
                staticDir: path.join(__dirname, 'dist'),
                // 需要预渲染的路由 
                // 对应自己的路由文件
                routes: ['/', '/about'],
                 // 这个很重要,如果没有配置这段,也不会进行预编译
                renderer: new Renderer({
                    inject: {
                        foo: 'bar'
                    },
                    //renderer.headless为表示是否以无头模式运行,无头即不展示界面,如果设置为false,则会在浏览器加载页面时候展示出来,一般用于调试
                    headless: false,
                    //renderer.renderAfterTime可以让控制页面加载好后等一段时间再截图,保证数据已经都拿到,页面渲染完毕
                    renderAfterTime: 5000,
                   // 在 main.js 中 document.dispatchEvent(new Event('render-event')),
                    //两者的事件名称要对应上。在程序入口执行
                    renderAfterDocumentEvent: 'render-event',
                    
                })
            })
        ]
    }
}
//4.修改main.js
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
  // 添加mounted,不然不会执行预编译
  mounted () {
    document.dispatchEvent(new Event('render-event'))
  }
 })
//5.相关路由文件
export default new Router({
<!-- 要用history模式 -->
  mode: 'history',
  routes
})
//npm run build
看一下生成的 dist 的目录里是不是有每个路由名称对应的文件,有就对了

小知识:seo为啥对vue单页面不友好?

爬虫在爬取的过程中,不会去执行js,所以隐藏在js中的跳转也不会获取到。 vue通过js控制路由然后渲染出对应的页面,而搜索引擎蜘蛛是不会去执行页面的js的,导致搜索引擎蜘蛛只能收录index.html一个页面,在百度中就搜索不到相关的子页面的内容。 我们加载页面的时候,浏览器的渲染包含:html的解析、dom树的构建、cssom构建、javascript解析、布局、绘制,当解析到javascript的时候才回去触发vue的渲染,然后元素挂载到id为app的div上,这个时候我们才能看到我们页面的内容,所以即使vue渲染机制很快我们仍然能够看到一段时间的白屏情况,用户体验不好。 服务端渲染 SSR,nuxt.js 服务端渲染:网页上面呈现的内容在服务器端就已经生成好了,当用户浏览网页时,服务器把这个在服务端生成好的完整的html结构内容响应给浏览器,而浏览器拿到这个完整的html结构内容后直接显示(渲染)在页面上的过程 SSR=> 后端把.vue文件编译成.html文件返回给前端渲染,它的好处就是有利于SEO