咱们在开发Vue我的项目时候都晓得,在vue开发中某些问题如果后期疏忽掉,过后不会呈现显著的成果,然而越向后开发越难做,而且我的项目做久了就会呈现问题,这就是所说的蝴蝶效应,这样前期的保护老本会十分高,并且我的项目上线后还会影响用户体验,也会呈现加载慢等一系列的性能问题,上面举一个简略的例子。
举个简略的例子
如果加载我的项目的时候加载一张图片须要0.1s,其实算不了什么能够忽略不计,然而如果我有20张图片,这就是2s的工夫, 2s的工夫不算长一下就过来了,然而这仅仅的只是加载了图片,还有咱们的js,css都须要加载,那就须要更长的工夫,可能是5s,6s...,比方加载工夫是5s,用户可能等都不会等,间接敞开咱们的网站,最初导致咱们网站流量很少,流量少就没人用,没人用就没有钱,没有钱就涨不了工资,涨不了工资最初就是跑路了。通过下面的例子能够看出性能问题是如许的重要甚至关系到了咱们薪资,那如何防止这些问题呢?废话不多说,上面分享一下本人在写我的项目的时用到的一些优化计划以及注意事项。
1.不要将所有的数据都放在data中
能够将一些不被视图渲染的数据申明到实例内部而后在外部援用援用,因为Vue2初始化数据的时候会将data中的所有属性遍历通过Object.definePrototype
从新定义所有属性;Vue3是通过Proxy去对数据包装,外部也会波及到递归遍历,在属性比拟多的状况下很消耗性能
<template> <button @click="updateValue">{{msg}}</button></template><script>let keys=true;export default { name:'Vueinput', data(){ return { msg:'true' } }, created(){ this.text = 'text' }, methods:{ updateValue(){ keys = !keys this.msg = keys?'true':'false' } }}</script>
2.watch 尽量不要应用deep:true深层遍历
因为watch不存在缓存,是指定监听对象,如果deep:true,并且监听对象类型状况下,会递归解决收集依赖,最初触发更新回调
3. vue 在 v-for 时给每项元素绑定事件须要用事件代理
vue源码中是通过addEventLisener去给dom绑定事件的,比方咱们应用v-for须要渲染100条数据并且并为每个节点增加点击事件,如果每个都绑定事件那就存在很多的addEventLisener,这里不用说性能上必定不好,那咱们就须要应用事件代理解决这个问题
<template> <ul @click="EventAgent"> <li v-for="(item) in mArr" :key="item.id" :data-set="item">{{item.day}}</li> </ul></template><script>let keys=true;export default { name:'Vueinput', data(){ return { mArr:[{ day:1, id:'xx1' },{ day:2, id:'xx2' },{ day:2, id:'xx2' }, ... ] } }, methods:{ EventAgent(e){ // 留神这里 在我的项目中千万不要写的这么简略,我只是为了不便了解才这么写的 console.log(e.target.getAttribute('data-set')) } }}</script>
4. v-for尽量不要与v-if一起应用
vue的编译过程是template->vnode,看上面的例子
// 假如data中存在一个arr数组<div id="container"> <div v-for="(item,index) in arr" v-if="arr.length" key="item.id">{{item}}</div></div>
下面的例子有可能大家常常这么做,其实这么做也能达到成果然而在性能下面不是很好,因为Ast在转化为render函数的时候会将每个遍历生成的对象都会退出if判断,最初在渲染的时候每次都每个遍历对象都会判断一次须要不须要渲染,这样就很节约性能,为了防止这个问题咱们把代码略微改一下
<div id="container" v-if="arr.length"> <div v-for="(item,index) in arr" >{{item}}</div></div>
这样就只判断一次就能达到渲染成果了,是不是更好一些那
参考 前端vue面试题具体解答
5. v-for的key进行不要以遍历索引作为key
<template> <ul> <li v-for="(item,index) in mArr" :key="item.id <input type="checkbox" :value="item.is" /> </li> <button @click="remove"> 移除 </button> </ul></template><script>let keys=true;export default { name:'Vueinput', data(){ return { mArr:[{is:false,id:1},{is:false,id:2} ] } }, methods:{ remove(e){ console.log('asd') this.mArr.shift() } }}</script>
调整一下代码
<template> <ul> <li v-for="(item,index) in mArr" :key="index"> <input type="checkbox" :value="item.is" /> </li> <button @click="remove"> 移除 </button> </ul></template><script>let keys=true;export default { name:'Vueinput', data(){ return { mArr:[{is:false,id:1},{is:false,id:2} ] } }, methods:{ remove(e){ console.log('asd') this.mArr.shift() } }}</script>
还是选中状态,就很神奇,解释一下为什么这么神奇,因为咱们选中的是0索引,而后点击移除后索引为1的就变为0,Vue的更新策略是复用dom,也就是说我索引为1的dom是用的之前索引为0的dom并没有更改,当然没有key的状况也是如此,所以key值必须为惟一标识才会做更改
6. SPA 页面采纳keep-alive缓存组件
<template> <div> <keep-alive> <router-view></router-view> </keep-alive> </div></template>
应用了keep-alive之后咱们页面不会卸载而是会缓存起来,keep-alive底层应用的LRU算法(淘汰缓存策略),当咱们从其余页面回到初始页面的时候不会从新加载而是从缓存里获取,这样既缩小Http申请也不会耗费过多的加载工夫
7. 防止应用v-html
- 可能会导致xss攻打
- v-html更新的是元素的 innerHTML 。内容按一般 HTML 插入, 不会作为 Vue 模板进行编译 。
8. 提取公共代码,提取组件的 CSS
将组件中公共的办法和css款式别离提取到各自的公共模块下,当咱们须要应用的时候在组件中应用就能够,大大减少了代码量
9. 首页白屏-loading
当咱们第一次进入Vue我的项目的时候,会呈现白屏的状况,为了防止这种难堪的状况,咱们在Vue编译之前应用加载动画防止
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title>Vue</title> <style> </style> </head> <body> <noscript> <strong>We're sorry but production-line doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"> <div id="loading"> loading </div> </div> <!-- built files will be auto injected --> </body></html>
加loading只是解决白屏问题的一种,也能够缩短首屏加载工夫,就须要在其余方面做优化,这个能够参考前面的案例
10. 拆分组件
次要目标就是进步复用性、减少代码的可维护性,缩小不必要的渲染,对于如何写出高性能的组件这里就不展现了,本人能够多看看那些比拟火的UI库(Element,Antd)的源码
11. 正当应用 v-if 当值为false时外部指令不会执行,具备阻断性能
如果操作不是很频繁能够应用v-if代替v-show,如果很频繁咱们能够应用v-show来解决
key 保障唯一性 ( 默认 vue 会采纳就地复用策略 )
下面的第五条曾经讲过了,如果key不是惟一的状况下,视图可能不会更新。
12. 获取dom应用ref代替document.getElementsByClassName
mounted(){ console.log(document.getElementsByClassName(“app”)) console.log(this.$refs['app']) }
document.getElementsByClassName获取dom节点的作用是一样的,但应用ref会缩小获取dom节点的耗费
13. Object.freeze 解冻数据
首先说一下Object.freeze
的作用
- 不能增加新属性
- 不能删除已有属性
- 不能批改已有属性的值
- 不能批改原型
- 不能批改已有属性的可枚举性、可配置性、可写性
data(){ return:{ objs:{ name:'aaa' } } }, mounted(){ this.objs = Object.freeze({name:'bbb'}) }
应用Object.freeze解决的data属性,不会被getter,setter,缩小一部分耗费,然而Object.freeze
也不能滥用,当咱们须要一个十分长的字符串的时候举荐应用
14. 正当应用路由懒加载、异步组件
当打包构建利用时,JavaScript 包会变得十分大,影响页面加载。而后当路由被拜访的时候才加载对应组件,这样就更加高效了
// 未应用懒加载的路由import Vue from "vue";import VueRouter from "vue-router";import Home from "@/views/Home";import About from "@/views/About"; Vue.use(VueRouter);const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/about", name: "About", component: About }];// 应用懒加载import Vue from "vue";import VueRouter from "vue-router"; Vue.use(VueRouter); const Home = () => import('../views/Home')const About = () => import('../views/About') const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/about", name: "About", component: About }];
15. 数据长久化的问题
数据长久化比拟常见的就是token了,作为用户的标识也作为登录的状态,咱们须要将其贮存到localStorage
或sessionStorage
起来每次刷新页面Vuex从localStorage
或sessionStorage
获取状态,不然每次刷新页面用户都须要从新登录,从新获取数据
- localStorage 须要用户手动移除能力移除,不然永恒存在。
- sessionStorage 敞开浏览器窗口就生效。
- cookie 敞开浏览器窗口就生效,每次申请Cookie都会被一起提交给服务器。
16. 防抖、节流
这两个算是陈词滥调了,就不演示代码了,上面介绍一个场景,比方咱们注册新用户的时候用户输出昵称须要校验昵称的合法性,思考到用户输出的比拟快或者批改频繁,这时候咱们须要应用节流,间隔性的去校验,这样就缩小了判断的次数达到优化的成果。前面咱们还须要须要用户手动点击保留能力注册胜利,为了防止用户频繁点击保留并发送申请,咱们只监听用户最初一次的点击,这时候就用到了节流操作,这样就能达到优化成果
17. 重绘,回流
触发重绘
浏览器从新渲染局部或者全副文档的过程叫回流
- 频繁操作元素的款式,对于动态页面,批改类名,款式
- 应用可能触发重绘的属性(background,visibility,width,height,display等)
触发回流
浏览器回将新款式赋予给元素这个过程叫做重绘
- 增加或者删除节点
- 页面首页渲染
- 浏览器的窗口发生变化
- 内容变换
回流的性能耗费比重绘大,回流肯定会触发重绘,重绘不肯定会回流;回流会导致渲染树须要从新计算,开销比重绘大,所以咱们要尽量避免回流的产生.
18. vue中的destroyed
组件销毁时候须要做的事件,比方当页面卸载的时候须要将页面中定时器革除,销毁绑定的监听事件
19. vue3中的异步组件
异步组件与上面的组件懒加载原理是相似,都是须要应用了再去加载
<template> <logo-img /> <hello-world msg="Welcome to Your Vue.js App" /></template><script setup>import { defineAsyncComponent } from 'vue'import LogoImg from './components/LogoImg.vue'// 简略用法const HelloWorld = defineAsyncComponent(() => import('./components/HelloWorld.vue'),)</script>
20. 组件懒加载
<template> <div id="content"> <div> <component v-bind:is='page'></component> </div> </div></template><script>// ---* 1 应用标签属性is和import *---const FirstComFirst = ()=>import("./FirstComFirst")const FirstComSecond = ()=>import("./FirstComSecond")const FirstComThird = ()=>import("./FirstComThird")export default { name: 'home', components: { }, data: function(){ return{ page: FirstComFirst } }}</script>
原理与路由懒加载一样的,只有须要的时候才会加载组件
21. 动静图片应用懒加载,动态图片应用精灵图
- 动静图片参考图片懒加载插件
- 动态图片,将多张图片放到一起,加载的时候节省时间
22. 第三方插件的按需引入
element-ui采纳babel-plugin-component插件来实现按需导入
//装置插件npm install babel-plugin-component -D// 批改babel文件module.exports = { presets: [['@babel/preset-env', { modules: false }], '@vue/cli-plugin-babel/preset'], plugins: [ '@babel/plugin-proposal-optional-chaining', 'lodash', [ 'component', { libraryName: 'element-ui', styleLibraryName: 'theme-chalk' }, 'element-ui' ], [ 'component', { libraryName: '@xxxx', camel2Dash: false }, ] ]};
23. 第三方库CDN减速
//vue.config.jslet cdn = { css: [], js: [] }; //辨别环境const isDev = process.env.NODE_ENV === 'development';let externals = {};if (!isDev) { externals = { 'vue': 'Vue', 'vue-router': 'VueRouter', 'ant-design-vue': 'antd', } cdn = { css: [ 'https://cdn.jsdelivr.net/npm/[email protected]/dist/antd.min.css', // 提前引入ant design vue款式 ], // 搁置css文件目录 js: [ 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js', // vuejs 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/antd.min.js' ] }}module.exports = { configureWebpack: { // 排除打包的某些选项 externals: externals }, chainWebpack: config => { // 注入cdn的变量到index.html中 config.plugin('html').tap((arg) => { arg[0].cdn = cdn return arg }) },}//index.html<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> <!-- 引入css-cdn的文件 --> <% for(var css of htmlWebpackPlugin.options.cdn.css) { %> <link rel="stylesheet" href="<%=css%>"> <% } %> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <!-- 搁置js-cdn文件 --> <% for(var js of htmlWebpackPlugin.options.cdn.js) { %> <script src="<%=js%>" ></script> <% } %> <div id="app"></div> </body> </html>
最初
以上的优化计划不紧在代码层面起到优化而且在性能上也起到了优化作用,文章内容次要是从Vue开发的角度和局部通过源码的角度去总结的,文章中如果存在谬误的中央,或者你认为还有其余更好的计划,请大佬在评论区中指出,作者会及时更正,感激!