vue 性能优化

目前想到的有 两 类:
● 代码优化
○ v-for 优化
○ v-for / v-if 优化
○ v-if / v-show 优化
○ 长列表优化
○ 事件销毁
○ 变量本地化
○ 嵌套数据优化
○ 持久化数据优化
○ 频繁更改组件共用数据对象优化
○ indexedDB数据存储优化

● 模块使用优化
○ 1. 路由使用懒加载模式加载组件
○ 2. 图片懒加载
○ 3. 如果使用了组件库(vant/element-ui/iview),按需加载,不要全局引用
○ 4. 第三方库使用CDN方式引入
○ 5. webpack打包优化
■ 打包去除Map文件
■ 开启gZip压缩( compression-webpack-plugin )
■ 压缩JS文件( uglifyjs-webpack-plugin )

1. 代码优化

1.1 v-for 优化

v-for 遍历需要添加唯一值key,且不为index,保证其唯一性!发生变化时能够快速定位查找,迅速diff更新

<li v-for="item in list" :key="item.id">{{item.name}}</li>

1.2 v-for / v-if 优化

v-for 和 v-if 避免同一时间使用,如果每一次都需要遍历整个数组,将会波及速度,尤其是当之需要渲染很小一部分的时候,必须状态下可以替换成 computed 属性,过滤出需要的数据

  • 不推荐使用
<template>
    <ul>
        <li v-for="item in list" :key="item.id">
            <span v-if="item.id != '1'">{{ item.name }}</span>
        </li>
    </ul>
</template>

<script>
export default {
    data() {
        return {
            list: [
                {
                    id: '1',
                    name: '张三'
                },
                {
                    id: '2',
                    name: '李四'
                }
            ]
        };
    }
};
</script>

  • 推荐使用
<template>
    <ul>
        <li v-for="item in filterList" :key="item.id">
            <span>{{ item.name }}</span>
        </li>
    </ul>
</template>

<script>
export default {
    data() {
        return {
            list: [
                {
                    id: '1',
                    name: '张三'
                },
                {
                    id: '2',
                    name: '李四'
                }
            ]
        };
    },
    computed: {
        filterList() {
            return this.list.filter(item => item.id != '1');
        }
    }
};
</script>

1.3 v-if / v-show 优化
单次改变使用v-if,频繁改变使用v-show。

v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。

1.4 长列表优化
1、当我们只是纯粹地展示列表,而不对数据进行修改时,需要冻结Object.freeze(data)对象!
从而避免vue数据劫持来实现响应式!

原理:当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter,这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。但 Vue 在遇到像 Object.freeze() 这样被设置为不可配置之后的对象属性时,不会为对象加上 setter getter 等数据劫持的方法。

2、当渲染长列表canvas时,可以将未在视图区域内的canvas隐藏(visibility: hidden 或 display: none),当滚动到视图内后,可以将canvas显示(visibility: visible 或 display: block;); 可以提高canvas的渲染性能;类似于懒加载原理;

1.5 事件销毁
Vue 组件销毁时,会全自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。如果在 js 内使用 addEventListene 等方法是不会全自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露。

<script>
export default {
    mounted() {
        elem.addEventListener('click', this.clickEvent);
    },
    beforeUnmount() {
        // 手动销毁
        elem.removeEventListener('click', this.clickEvent);
    }
};
</script>

1.6 变量本地化

<script>
export default {
    data() {
        return {
            name: '张三'
        };
    },
    methods: {
        initData() {
            // 变量本地化,,, 不要频繁引用this.name,会频繁触发 它 的getter事件
            const name = this.name;
            for (let i = 0; i < 10; i++) {
                return name + i;
            }
        }
    }
};
</script>

1.7 嵌套数据优化
对于响应式数据,避免深层嵌套。

假如直接把深层嵌套的结构存储在 store 中,如果想修改某个 zindex4 的信息,我们需要一层层去遍历找到这个用户的信息,同时有可能这个用户的信息出现了多次,还需要把其他地方的用户信息也进行修改,每次遍历的过程会带来额外的性能开销。假设我们把用户信息在 store 内统一存放成 users[id]这样的结构,修改和读取用户信息的成本就变得非常低。

// 不推荐使用
<script>
export default {
    data() {
        return {
            user: {
                id: 'id1',
                zindex1: {
                    zindex2: {
                        zindex3: {
                            zindex4: ''
                        }
                    }
                }
            }
        };
    }
};
</script>

// 推荐使用
<script>
export default {
    data() {
        return {
            user: {
                id: '1',
                zindex4: ''
            }
        };
    }
};
</script>

1.8 持久化数据优化
比如 localstorage 的缓存在某些浏览器只有 5M,我们不能无限制的将所有数据都存起来,这样很容易达到容量限制,同时数据过大时,读取和写入操作会增加一些性能开销,同时内存也会上涨。尤其是将 API 数据进行 normalize 数据扁平化后之后,会将一份数据散落在不同的实体上,下次请求到新的数据也会散落在其他不同的实体上,这样会带来持续的存储增长。因此,当设计了一套持久化的数据缓存策略的时候,同时应该设计旧数据的缓存清除策略,例如请求到新数据的时候将旧的实体逐个进行清除。

对于组件间共享数据,我们可以使用store(vue2.0)、provide/inject、props/$emit、v-model、window.globalData。。。

window.globalData数据共享实现

// main.js // 入口文件定义全局变量 window.globalData = window.globalData || {}; // 组件使用
(组件中定义变量,赋值为全局变量,并且可以在组件内更新全局变量属性) // 组件1
<script>
export default {
    data() {
        return {
            commonData1: window.globalData
        };
    },
    methods: {
        changeCommonData() {
            this.commonData1.name = '组件1改公共变量属性啦';
        }
    }
};
</script>

// 组件2
<script>
export default {
    data() {
        return {
            commonData2: window.globalData
        };
    },
    methods: {
        changeCommonData() {
            this.commonData2.name = '组件2改公共变量属性啦';
        }
    }
};
</script>

1.9 频繁更改组件共用数据对象优化

vue 是单向数据流,子组件不能直接修改父组件的值。但是当父组件传递给子组件的值为对象类型(Object、Array)时,因为传入的是引用,所以子组件可直接修改父组件传递过来的对象,无需深拷贝,增加性能开销;子组件修改时,父组件会同时更新;

// 父组件 parent.vue
<template>
	<div><child v-model:data="data"></child>
</template>

<script>
  export default {
  	data() {
    	return {
      	data: [{id: '1', name: 'zhangsan'}, {id: '2', name: 'lisi'}]
      }
    }
  }
</script>

// 子组件 child.vue
// 不推荐使用方式
<script>
  export default {
    props: {
    	data: {type: Array, default() {return []}, required: true}
    },
  	computed: {
    	dataCopy() {
      	return JSON.parse(JSON.stringify(this.data));
      }
    },
    methods: {
    	changeData() {
      	this.dataCopy.push({id: '3', name: '王五'});
        this.$emit('update:data', this.dataCopy);
      }
    }
  }
</script>
// 推荐使用方式
<script>
  export default {
    props: {
    	data: {type: Array, default() {return []}, required: true}
    },
    methods: {
    	changeData() {
      	this.data.push({id: '3', name: '王五'});
      }
    }
  }
</script>

2. 模块使用优化

2.1 路由懒加载

// 懒加载模式
const routes = [
  {path: '/home', component: resolve=>(require(['@/pages/home'], resolve)), meta: {title: '首页'}}
];

// 非懒加载模式
const routes = [
  {path: '/home', component: () => import('../pages/home'), meta: {title: '首页'}}
];

2.2 图片懒加载
使用vue-lazyload插件,安装并注册并使用。
2.2.1 安装

npm install vue-lazyload --save

2.2.2 注册

// main.js
import VueLazyload from 'vue-lazyload'
​
// 注册使用
Vue.use(VueLazyload, {
  error: '/static/img/error.png',
  loading: '/static/img/loader.gif',
});

2.2.3 使用

<!-- 页面引用 -->
<img v-lazy="imgSrc" />

2.3 组件库按需加载
看vant、element-ui、iview官网示例

// 以vant的图标为例
<template>
    <van-icon name="chat-o"></van-icon>
</template>

<script>
import { Icon } from 'vant';
export default {
    components: {
        'van-icon': Icon
    }
};
</script>

2.4 CDN引入第三方库
bootstrap CDN:https://www.bootcdn.cn
Staticfile CDN:https://www.staticfile.org
jsDelivr CDN:https://www.jsdelivr.com
75 CDN:https://cdn.baomitu.com
UNPKG:https://unpkg.com
cdnjs:https://cdnjs.com

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

2.5 webpack打包优化

参考webpack官网:
https://www.webpackjs.com/plugins/uglifyjs-webpack-plugin/

2.5.1 打包去除map文件
在/vue.config.js文件中设置productionSourceMap=false;编译时将不再生成.map文件

productionSourceMap = false;

2.5.2 开启gZip压缩
compression-webpack-plugin 实现gZip压缩,可以很大程度减少包的大小。经过gzip压缩,页面可以变为原来的30%甚至更小。提升用户浏览页面速度。

  • 第一步:安装
// 需要安装1.1.12版本,别的版本可能会报错;
npm install compression-webpack-plugin@1.1.12 -D
  • 第二步:配置vue.config.js
// vue.config.js
​
const CompressionPlugin = require('compression-webpack-plugin');
​
const pluginsArray = [];
if (process.env.VUE_APP_ENV == 'production') {
  pluginsArray.push(
      new CompressionPlugin({
      minRatio: 0.8, // 压缩率
      test: /\.js|\.css/, //处理所有匹配资源
      deleteOriginalAssets: false, // 是否删除原资源
    })
  );
}
​
module.exports = {
  plugins: [...pluginsArray]
};

2.5.3 压缩JS文件

  • 第一步:安装
npm install uglifyjs-webpack-plugin -D
  • 第二步:配置vue.config.js
// vue.config.js
​
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
​
const pluginsArray = [];
if (process.env.VUE_APP_ENV == 'production') {
  pluginsArray.push(
    new UglifyJsPlugin({
      uglifyOptions: {
        // 生产环境自动删除console
        compress: {
          drop_degugger: true,
          drop_console: true,
          pure_funcs: ['console.log']
        }
      },
      sourceMap: false, // 是否使用sourceMap将错误消息位置映射到模块(这会减慢编译速度)
      parallel: true, // 是否使用多进程并行运行来提高构建速度
    })
  );
}
​
module.exports = {
  plugins: [...pluginsArray]
};

2.5.4 压缩IMG文件

  • 第一步:安装
  • npm install image-webpack-loader -D
  • 第二步:配置vue.config.js
// vue.config.js
​
module.exports = {
  chainWebpack: config => {
    if(process.env.VUE_APP_ENV == 'production') {
      config.module
        .rule('images')
        .use('image-webpack-loader')
        .loader('image-webpack-loader')
        .options({bypassOnDebug: true})
        .end()
    }
  }
};

额外配置

打包时,生成.zip

  • 第一步:安装
npm install filemanager-webpack-plugin -D
  • 第二步:配置
vue.config.js// vue.config.js

const FileManagerPlugin = require('filemanager-webpack-plugin');

const packageName = 'dist'; // 打包的文件夹名
const pluginsArray = [];
if (process.env.VUE_APP_ENV == 'production') {
  pluginsArray.push(
    new FileManagerPlugin({ // 打包为 dist.zip
      onEnd: {
        delete: [`./${packageName}.zip`], // 删除项目根目录下的dist.zip
        archive: [{ // 选择将dist文件夹打包为dist.zip并放在根目录下
          source: packageName,
          destination: `./${packageName}.zip`,
        }]
      },
    })
  );
}

module.exports = {
	plugins: [...pluginsArray]
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值