1、路由懒加载

老生常谈的话题,很简单。

  1. const router = new VueRouter({
  2. routes: [
  3. { path: '/', component: () => import('./home.vue') }
  4. ]
  5. })

2、keep-alive缓存页面

可以通过在router设置meta来区分哪些页面需要缓存

  1. <template>
  2. <div id="app">
  3. <keep-alive v-if="$route.meta.keep">
  4. <router-view/>
  5. </keep-alive>
  6. <router-view v-else/>
  7. </div>
  8. </template>

3、v-show复用dom

对于需要反复切换dom的操作,可以用v-show,比v-if更好。

4、v-for 遍历避免同时使用 v-if

  1. <tempalte v-is="">
  2. <div v-for="item in items" :key="item.id"></div>
  3. </template>

5、静态长列表性能优化

对于静态展示的长列表,没有数据交互的,可以设置list数组属性为configurable为false,或者直接冻结该数组。

  1. export default {
  2. data: () => ({
  3. users: []
  4. }),
  5. async created() {
  6. const users = await axios.get("/api/users");
  7. this.users = Object.freeze(users);
  8. // 或者
  9. },
  10. methond: {
  11. freezeObj(obj) {
  12. // 传入的不是数组或者对象,则不操作
  13. if(!typeof obj === 'object') {
  14. return false
  15. }
  16. Object.defineProperties(obj, {
  17. configurable: false
  18. })
  19. }
  20. }
  21. };

6、动态长列表性能优化

此类列表有两种优化方案:

  • 时间分片
  • 虚拟列表

时间分片是将动态列表切片,分段渲染。
虚拟列表是动态渲染视口展示的列表数据。这种相对是目前比较流行的做法。相关文档

我们可以采用插件来完成。
插件名:vue-virtual-scroller

插件官网:https://github.com/tangbc/vue-virtual-scroll-list

在组件中使用

  1. <template>
  2. <div>
  3. // 这里是用法,这里的class要设置高度,且他包裹的元素高度要固定,不能是auto
  4. <recycle-scroller
  5. class="items"
  6. :items="items" // items中传入数据
  7. :item-size="24"
  8. content-tag="div"
  9. >
  10. <template v-slot="{ item }">
  11. // 下面是引入的自定义的滚动组件,和vue-virtual-scroller五官
  12. <FetchItemView :item="item"/>
  13. </template>
  14. </recycle-scroller>
  15. </div>
  16. </template>
  17. import Vue from 'vue'
  18. import { RecycleScroller } from 'vue-virtual-scroller'
  19. Vue.component('RecycleScroller', RecycleScroller)
  20. export default {
  21. data() {
  22. return {
  23. list: []
  24. }
  25. },
  26. }

这里需要解释一下,组件直接传入了包含所有的数据数组,而不需要分批传入。首先,我们明确一点,接口请求返回数据(几千条上万条)速度都是很快的,几百毫秒。慢的是渲染过程。因此,这里是一次性的数据传入。当然,不会一次性的渲染,而是按需渲染。

7、内存泄漏问题-自定义监听事件及时销毁

1、自定义事件

vue组件上绑定的事件,在组件销毁时候是可以自动销毁的。但是一些自定义事件,比如定时器事件,监听的scroll事件等,是无法及时销毁的,这时候需要手动销毁,来防止内存泄漏。

  1. created() {
  2. this.timer = setInterval(this.refresh, 2000)
  3. },
  4. beforeDestroy() {
  5. clearInterval(this.timer)
  6. }

当然,我们也可以使用$once方法来清除定时器

  1. const timer = setInterval(() =>{
  2. // 某些定时器操作
  3. }, 500)
  4. this.$once('hook:beforeDestroy', () => {
  5. clearInterval(timer);
  6. })

这种方法将生命定时器和清除定时器写在了一块,代码看起来更清除一些。

当然,引起内存泄漏的情况有很多种。下面简单列举一些,都是开发中需要注意的。

  • v-if移除的元素没有被真正释放掉
  • 生命到window下的全局变量
  • keep-alive的组件用deactivated来处理自定义的事件
  • 闭包函数
  • dom清空或删除时,事件未清除导致的内存泄漏
  • 路由切换

8、图片懒加载

对于小图片,例如icon标志之类的,我们可以不做处理当然,可以的话,制作精灵图,将小图片放在一张图片中是最好的,然后通过背景图定位来展示。
那么大图片怎么优化呢,目前主流的就是懒加载。
(1)懒加载插件:vue-lazyload

  1. <img v-lazy="/static/img/1.png">

(2)自定义图片懒加载

  1. const imgList = document.querySelectorAll(".lazyload");
  2. const observer = new IntersectionObserver(entries => {
  3. entries.forEach(item => {
  4. if (item.isIntersecting) {
  5. // 判断在可视区了,把data-origin的值放到src
  6. item.target.src = item.target.dataset.origin;
  7. observer.unobserve(item.target); // 已经加载过的图片停止进行监听
  8. }
  9. });
  10. });
  11. imgList.forEach(item => observer.observe(item));
  12. // 节流函数
  13. function throttle(func, wait) {
  14. let timeout
  15. return function() {
  16. let context = this
  17. let args = arguments
  18. if (!timeout) {
  19. timeout = setTimeout(() => {
  20. timeout = null
  21. func.apply(context, args)
  22. }, wait)
  23. }
  24. }
  25. }
  26. window.onscroll = throttle(loadImage, 500)

参考文档:https://mp.weixin.qq.com/s/z588pAlBa5PWGpeEQebKNQ

9、按需引入三方插件,以element为例

这里是我之前业务中的代码,直接复制过来了

  1. import Vue from 'vue'
  2. import './index.scss'
  3. import {
  4. // Basic
  5. Row,
  6. Col,
  7. Button,
  8. ButtonGroup,
  9. // Form
  10. Radio,
  11. RadioGroup,
  12. Checkbox,
  13. CheckboxGroup,
  14. Input,
  15. Autocomplete,
  16. InputNumber,
  17. Select,
  18. Option,
  19. OptionGroup,
  20. DatePicker,
  21. Upload,
  22. Form,
  23. FormItem,
  24. // Data
  25. Table,
  26. TableColumn,
  27. Progress,
  28. Tree,
  29. Pagination,
  30. Tag,
  31. // Notice
  32. Alert,
  33. Loading,
  34. Message,
  35. MessageBox,
  36. // Navigation
  37. Menu,
  38. Submenu,
  39. MenuItem,
  40. MenuItemGroup,
  41. Tabs,
  42. TabPane,
  43. Dropdown,
  44. DropdownMenu,
  45. DropdownItem,
  46. Steps,
  47. Step,
  48. // Others
  49. Dialog,
  50. Tooltip,
  51. Popover,
  52. Scrollbar,
  53. Card
  54. } from 'element-ui'
  55. const components = [
  56. // Base
  57. Row,
  58. Col,
  59. Button,
  60. ButtonGroup,
  61. // Form
  62. Radio,
  63. RadioGroup,
  64. Checkbox,
  65. CheckboxGroup,
  66. Input,
  67. Autocomplete,
  68. InputNumber,
  69. Select,
  70. Option,
  71. OptionGroup,
  72. DatePicker,
  73. Upload,
  74. Form,
  75. FormItem,
  76. // Data
  77. Table,
  78. TableColumn,
  79. Progress,
  80. Tree,
  81. Pagination,
  82. Tag,
  83. // Notice
  84. Alert,
  85. // Navigation
  86. Menu,
  87. Submenu,
  88. MenuItem,
  89. MenuItemGroup,
  90. Tabs,
  91. TabPane,
  92. Dropdown,
  93. DropdownMenu,
  94. DropdownItem,
  95. Steps,
  96. Step,
  97. // Others
  98. Dialog,
  99. Tooltip,
  100. Popover,
  101. Scrollbar,
  102. Card
  103. ]
  104. // 循环注册全局组件
  105. components.forEach(item => {
  106. Vue.use(item)
  107. })
  108. Vue.use(Loading.directive)
  109. Vue.prototype.$loading = Loading.service
  110. Vue.prototype.$msgbox = MessageBox
  111. Vue.prototype.$alert = MessageBox.alert
  112. Vue.prototype.$confirm = MessageBox.confirm
  113. Vue.prototype.$message = Message

10、无状态组件声明为函数式

使组件无状态 (没有 data) 和无实例 (没有 this 上下文)。他们用一个简单的 render 函数返回虚拟节点使它们渲染的代价更小。
声明组件为functional

  1. <template functional>
  2. <div class="cell">
  3. <div v-if="props.value" class="on"></div>
  4. <section v-else class="off"></section>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. props: ['value']
  10. }
  11. </script>

11、子组件的分割

对于功能状态等完全独立的两个模块可以且分为子组件,便于代码的维护。

12、变量本地化

对于一些反复使用,但是无需进行响应式的数据,我们可以放在computed计算属性中。

参考视频:
点击查看【bilibili】
参考文档:https://zhuanlan.zhihu.com/p/111310865

13、vue打包时候后的优化

happypack + dll
happypack插件负责开启多个node子进程,来达到加快编译速度的目的。
dll用来剥离第三方插件,例如vue、vue-router(一切非业务的组件)。来进行提前打包,以保证我们打包的时候只进行业务代码的打包。

14、首页加载优化

具体方法有骨架屏,ssr渲染首页等