前言
Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM;
但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题,所以我们仍然需要去关注 Vue 项目性能方面的优化,使项目具有更高效的性能、更好的用户体验
首屏加载
首屏加载可以说是用户体验中最重要的环节
首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
通过DOMContentLoad
或者performance
来计算出首屏时间
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime
// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
name: "first-contentful-paint",
entryType: "paint",
startTime: 507.80000002123415,
duration: 0,
};
Vue 项目性能优化内容分为以下三部分组成:
- Vue 代码层面的优化;
- webpack 配置层面的优化;
- 基础的 Web 技术层面的优化。
一、代码层面的优化
1.1、v-if 和 v-show 区分使用场景
v-if
-
是通过控制 DOM 元素的存在与否来控制元素的显隐;
-
切换时,是对 DOM 元素进行一个创建和销毁的动作;
-
是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译;
-
有更高的切换消耗;
v-show
-
是通过设置 DOM 元素的
display
样式,block
为显示,none
为隐藏; -
切换时,只是简单的基于CSS切换;
-
是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且 DOM 元素保留;
-
有更高的初始渲染消耗;
v-show 与 v-if 的区别
-
v-if 是
“真正”的条件渲染
,因为它会在切换时对条件块内的事件监听器和子组件进行创建和销毁。通过控制 DOM 元素的存在与否来控制元素的显隐; -
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块(局部编译)。
-
相比之下,v-show 就简单得多——不管初始条件是什么,
元素总是会被渲染
,并且只是简单地基于 CSS(display:block/none
) 进行切换。
所以,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show 较好;
如果在运行时条件很少改变,则使用 v-if 较好。
1.2、computed 和 watch 区分使用场景
computed (计算属性)
- 属性的结果会被缓存,当
computed
中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取,除非依赖的响应式属性变化时才会重新计算; - 不支持异步,当
computed
内有异步操作时无效,无法监听数据的变化; computed
中的函数必须用return
返回最终的结果。computed
更高效,优先使用;- 当一个属性受多个属性影响的时候,一般用
computed
(购物车商品结算功能); - 如果
computed
属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed
中的,属性都有一个get和一个set方法,当数据变化时,调用set方法;
watch(监听属性)
- 不支持缓存,数据变,直接会触发相应的操作;
- 支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值(newVal);第二个参数是输入之前的值(oldVal);
- 当一条数据影响多条数据的时候,一般使用
watch
(搜索数据); - 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate
:组件加载立即触发回调函数执行,
deep
: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
运用场景
-
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;(一个属性受多个属性影响的时候)
-
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。(一条数据影响多条数据的时候)
减少不必要的计算属性:计算属性是基于它们的依赖进行缓存的,只有在相关依赖发生改变时才会重新求值。
但是,如果计算属性中的逻辑过于复杂,或者计算依赖的数据过于频繁地变动,那么计算属性的开销就会变大。
因此,应尽量减少不必要的计算属性,或者将其拆分为更小的计算属性。
1.3、v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
-
v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,来为每个节点提供一个跟踪标识,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。 -
v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高
这意味着,如果在一个元素上同时使用v-for和v-if,v-for会先执行,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
推荐:
<ul v-if="users.length">
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
不推荐:
<ul>
<li v-for="user in users" v-if="user.isActive">
{{ user.name }}
</li>
</ul>
1.4、⚡️图片资源懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:
-
安装插件
npm install vue-lazyload --save-dev
-
在入口文件 man.js 中引入并使用
import VueLazyload from 'vue-lazyload'
-
然后再 vue 中直接使用
Vue.use(VueLazyload)
-
或者添加自定义选项
Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', loading: 'dist/loading.gif', attempt: 1 })
-
在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示:
<img v-lazy="/static/img/1.png">
1.5、⚡️路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
路由懒加载:
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
import('./Foo.vue')
以函数的形式加载路由,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会加载路由组件
1.6、⚡️按需引入加载
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。
我们可以借助插件babel-plugin-import
或 babel-plugin-component
,然后可以只引入需要的组件,以达到减小项目体积的目的。
babel-plugin-import
:由ant-design团队开发,是大部分按需引入插件的基础babel-plugin-component
:由饿了么团队开发,是在babel-plugin-import的基础上做了一些改动而创建的
使用步骤:
-
首先,安装
babel-plugin-component
:
npm install babel-plugin-component -D
-
然后,将 .babelrc 修改为:
{ "presets": [["es2015", { "modules": false }]], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
-
在 main.js 中引入部分组件
以下为项目中引入 element-ui 组件库中的按钮,分页,表格,输入与警告等组件:import Vue from 'vue'; import { Button, Input, Pagination, Table, TableColumn, MessageBox } from 'element-ui'; Vue.use(Button) Vue.use(Input) Vue.use(Pagination)
1.7、事件的销毁
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。
如果在 js 内使用 addEventListene 等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
created() {
addEventListener('click', this.click, false)
},
beforeDestroy() {
removeEventListener('click', this.click, false)
}
1.8、长列表性能优化
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化。
当把一个普通的JavaScript对象传给Vue组件的数据属性时,Vue会将其转换为响应式对象。这个转换过程是有开销的,特别是在处理大量数据时。
然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间。
那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze
方法来冻结一个对象,使其不可修改。
当一个对象被冻结后,任何对其属性的修改都会被禁止,包括添加、删除和修改属性的值。使用Object.freeze()
方法可以防止对象被意外修改,从而提高代码的安全性和稳定性。一旦对象被冻结,就无法再对其进行修改,这对于保护对象的数据完整性非常有用。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
1.9、优化无限列表性能
如果你的应用存在非常长或者无限滚动的列表,那么需要采用 窗口化 的技术来优化性能,只需要渲染少部分区域的内容,减少重新渲染组件和创建 dom 节点的时间。
你可以参考以下开源项目 vue-virtual-scroll-list
和 vue-virtual-scroller
来优化这种无限列表的场景的。
vue-virtual-scroll-list
是一个 Vue 组件,用于实现虚拟滚动(virtual scrolling)的功能,以优化长列表的渲染性能。
虚拟滚动是一种技术,它只渲染视口(即用户当前可见的部分)内的列表项,而不是整个列表。
这可以大大减少 DOM 的数量和渲染的开销,从而提高页面的滚动性能和响应速度。
-
安装:首先,你需要安装
vue-virtual-scroll-list
。可以通过 npm 或 yarn 进行安装:npm install vue-virtual-scroll-list --save # 或者 yarn add vue-virtual-scroll-list
-
引入:在你的 Vue 组件中引入 vue-virtual-scroll-list 并注册为局部组件或全局组件。
import VueVirtualScrollList from 'vue-virtual-scroll-list'; export default { components: { VueVirtualScrollList }, // ... };
-
使用:在模板中使用 vue-virtual-scroll-list 组件,并指定列表数据、项高度等必要属性。
<template> <div> <vue-virtual-scroll-list :items="yourLongList" :item-height="30" class="virtual-scroll-list" > <template slot-scope="{ item, index }"> <!-- 这里是列表项的模板 --> <div>{{ item.name }}</div> </template> </vue-virtual-scroll-list> </div> </template> <script> export default { data() { return { yourLongList: [] // 这里填充你的长列表数据 }; }, // ... }; </script>
虚拟滚动是解决长列表性能问题的有效方法,特别是在处理数千甚至数万个列表项时。通过只渲染视口内的内容,可以显著减少浏览器的工作量,并提高用户体验。
1.10、⚡️服务端渲染 SSR
服务端渲染是指 Vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染。
与客户端渲染(Client-Side Rendering,简称CSR)相比,SSR有以下几个主要优缺点:
优点
-
更好的首屏加载性能:由于SSR在服务器端已经渲染好了页面,因此用户可以在首屏加载时看到完整的页面内容,无需等待浏览器执行JavaScript代码来生成内容。这可以大大加快页面的加载速度,提高用户体验。
-
更好的搜索引擎优化(SEO):搜索引擎爬虫可以更容易地解析和索引SSR渲染的页面内容,因为这些内容在服务器端就已经是完整的HTML结构。这对于提高网站在搜索引擎中的排名非常有帮助。
-
更好的兼容性:由于SSR在服务器端渲染页面,因此可以确保所有用户都看到相同的页面内容,无需考虑浏览器兼容性问题。
缺点
-
服务器压力:由于SSR需要在服务器端渲染页面,因此会增加服务器的负载和计算成本。对于大型网站或高并发场景,这可能会导致服务器性能瓶颈。
-
开发复杂性:与CSR相比,SSR的开发过程可能更加复杂。开发人员需要处理服务器端和客户端之间的数据传递和状态管理,同时还需要考虑如何优化性能和提高可维护性。
-
动态内容更新:对于需要频繁更新内容的页面,SSR可能不是最佳选择。因为每次内容更新都需要重新渲染整个页面并发送给客户端,这可能导致不必要的资源浪费和延迟。
尽管存在这些挑战和限制,但SSR仍然是许多网站和应用的重要技术之一。
在特定场景下,如首屏加载性能要求较高的页面或需要更好SEO效果的网站,SSR可以发挥其优势并提供更好的用户体验。
同时,随着技术的发展和工具的进步,SSR的实现和优化也变得更加容易和高效。
二、Webpack 层面的优化
2.1、⚡️Webpack 对图片进行压缩
image-webpack-loader
是一个 Webpack 的 loader,它允许你在 Webpack 构建过程中直接对图片进行压缩。
这在 Vue 项目中非常有用,尤其是当你希望减少图片大小,从而减小最终的打包体积时。
-
首先,安装
image-webpack-loader
:
npm install image-webpack-loader --save-dev
-
然后,在
webpack.base.conf.js
中进行配置:{ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use:[ { loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } ] }
在构建过程中,image-webpack-loader
会自动压缩所有的图片文件。
2.2、减少 ES6 转为 ES5 的冗余代码
Babel 插件会在将 ES6 代码转换成 ES5 代码时会注入一些辅助函数,例如下面的 ES6 代码:
class HelloWebpack extends Component{...}
这段代码再被转换成能正常运行的 ES5 代码时需要以下两个辅助函数:
babel-runtime/helpers/createClass // 用于实现 class 语法
babel-runtime/helpers/inherits // 用于实现 extends 语法
在默认情况下, Babel 会在每个输出文件中内嵌这些依赖的辅助函数代码,如果多个源代码文件都依赖这些辅助函数,那么这些辅助函数的代码将会出现很多次,造成代码冗余。
为了不让这些辅助函数的代码重复出现,可以在依赖它们时通过 require('babel-runtime/helpers/createClass')
的方式导入,这样就能做到只让它们出现一次。
babel-plugin-transform-runtime
插件就是用来实现这个作用的,将相关辅助函数进行替换成导入语句,从而减小 babel 编译出来的代码的文件大小。
-
首先,安装 babel-plugin-transform-runtime :
npm install babel-plugin-transform-runtime --save-dev
-
然后,修改 .babelrc 配置文件为:
"plugins": [ "transform-runtime" ]
2.3、提取公共代码
如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:
- 相同的资源被重复加载,浪费用户的流量和服务器的成本。
- 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。
Webpack 内置了专门用于提取多个Chunk 中的公共部分的插件 CommonsChunkPlugin
。利用CommonsChunkPlugin
提取公共模块,即多个入口chunk中共同引用的模块。
这些公共模块可以被打包成一个单独的文件,以避免在多个chunk中重复打包相同的代码,从而提高代码的复用性和性能。
我们在项目中 CommonsChunkPlugin
的配置如下:
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/app.js',
vendor: ['react', 'react-dom'] // 这里可以列出你想要提取的公共模块
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', // 公共代码块的名字
minChunks: function (module) {
// 这个函数会作用于每个模块,当模块被两个或更多的入口 chunk 引用时,它就会被提取
return module.context && module.context.includes('node_modules');
},
}),
// 如果你想要一个额外的 "manifest" 文件来包含模块和 chunk 之间的映射关系,可以添加如下配置:
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),
],
};
上面的配置中,我们创建了两个入口:app 和 vendor。
vendor 入口包含了我们想要提取的公共模块(例如 react 和 react-dom)。
然后,我们使用 CommonsChunkPlugin 来提取这些公共模块到一个单独的 vendor.bundle.js
文件中。
minChunks 选项决定了模块被多少个入口 chunk 共享时才应该被提取。在上面的例子中,我们使用了一个函数来判断模块是否来自 node_modules 目录。
注意:从Webpack v4开始,CommonsChunkPlugin已经被移除,取而代之的是optimization.splitChunks
配置。因此,如果你在使用Webpack v4或更高版本,你需要使用splitChunks配置来实现相同的功能。
2.4、模板预编译
当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。
预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
如果你使用 webpack,并且喜欢分离 JavaScript 和模板文件,你可以使用 vue-template-loader,它也可以在构建过程中把模板文件转换成为 JavaScript 渲染函数。
2.5、提取组件的 CSS
当使用单文件组件时,组件内的 CSS 会以 style 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,如果你使用服务端渲染,这会导致一段 “无样式内容闪烁 (fouc) ” 。将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。
查阅这个构建工具各自的文档来了解更多:
- webpack + vue-loader ( vue-cli 的 webpack 模板已经预先配置好)
- Browserify + vueify
- Rollup + rollup-plugin-vue
2.6、优化 SourceMap
我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩、去掉多余的空格、babel编译化后,最终将编译得到的代码会用于线上环境,那么这样处理后的代码和源代码会有很大的差别,当有 bug的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发来说不好调式定位问题,因此 sourceMap 出现了,它就是为了解决不好调式代码问题的。
SourceMap本质上是一个信息文件,里面储存着代码转换前后的对应位置信息。具体来说,它记录了转换压缩后的代码所对应的转换前的源代码位置,是源代码和生产代码的映射。
SourceMap 的可选值如下(+ 号越多,代表速度越快,- 号越多,代表速度越慢, o 代表中等速度 )
常见:
-
source-map:
最完整、最慢。
它生成一个独立的.map文件,包含完整的源代码和编译后代码的映射关系。
-
eval-source-map:
开发模式常用,速度最快。它不会生成独立的.map文件,而是将SourceMap以DataURL的形式嵌入到每个模块中。
速度相对较快,调试友好,可以直接看到原始源代码。
由于内联的原因,可能会稍微增加打包后的文件大小。
-
cheap-module-source-map:
与eval-source-map类似,但更便宜(不包含列信息),并且包含模块加载器的映射。
开发模式常用。它只包含行映射,没有列映射,所以生成速度比source-map快。
-
hidden-source-map:
与source-map类似,但不会将sourceMappingURL注释添加到打包后的文件中。
在外部生成SourceMap文件,但在目标文件中没有建立关联,因此不会暴露SourceMap的URL。
隐藏源代码,同时允许在需要时通过其他方式访问SourceMap进行调试。
建议:
- 开发环境推荐:
cheap-module-eval-source-map
或eval-source-map
,以平衡构建速度和调试友好性。 - 生产环境推荐:
cheap-module-source-map
或 直接关闭
设置:
在Webpack配置文件中,通过 devtool
选项来设置 SourceMap 的类型。例如:
-
webpack.config.js:
module.exports = { devtool: false, // 是否关闭 SourceMap。设置为 false 以关闭 SourceMap devtool: 'cheap-module-eval-source-map', // 开发环境推荐 // 或者 // devtool: 'cheap-module-source-map', // 生产环境 // ... };
-
vue.config.js:
module.exports = { productionSourceMap: true, // 控制在生产模式下是否生成SourceMap configureWebpack: config => { if (process.env.NODE_ENV === 'development') { // 开发环境配置 config.devtool = 'cheap-module-eval-source-map'; } if (process.env.NODE_ENV === 'production') { // 生产环境配置 config.devtool = 'cheap-module-source-map'; } // ... 其他webpack配置 ... }, // ... 其他配置 ... }
2.7、构建结果输出分析
Webpack 输出的代码可读性非常差而且文件非常大,让我们非常头疼。
为了更简单、直观地分析输出结果,社区中出现了许多可视化分析工具。这些工具以图形的方式将结果更直观地展示出来,让我们快速了解问题所在。
这时候就要用到 webpack-bundle-analyzer
:是一个用于可视化和分析webpack打包结果的插件。
通过读取webpack输出文件夹(通常是dist)中的stats.json文件,该插件能够生成一个详细的代码分析报告,将打包后的文件以图表和表格的形式展示出来。
webpack-bundle-analyzer提供了以下功能:
- 可视化报告:插件生成的报告以直观的方式展示了构建产物的组成部分,包括每个模块(JavaScript、CSS、图片等)的大小、占比以及它们之间的依赖关系。这使得开发人员能够快速了解整个构建结果的结构。
- 模块大小分析:通过插件,你可以清楚地看到每个模块的大小,从而识别出那些占用空间较大的模块,并据此采取优化措施,如代码拆分、压缩等。
- 依赖关系可视化:插件以图形的方式展示了模块之间的引用关系,帮助你更好地理解代码库中不同模块之间的关联性。
- 构建结果导出:此外,webpack-bundle-analyzer还提供了将构建报告导出为JSON或HTML文件的选项,方便你与团队共享分析结果或进行进一步的数据处理。
安装:
npm install --save-dev webpack-bundle-analyzer
yarn add --dev webpack-bundle-analyzer
配置webpack.config.js:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
// ... 其他webpack配置 ...
plugins: [
// ... 其他插件 ...
new BundleAnalyzerPlugin({
// 这里可以放置插件的配置选项
// 例如,你可以设置analyzerMode为'disabled'来禁止自动打开分析器
// 或者设置generateStatsFile为true来生成stats.json文件
analyzerMode: 'disabled',
generateStatsFile: true,
statsOptions: {
source: false, // 如果你不想在报告中显示源代码,可以设置这个选项为false
},
}),
],
};
如果你是在Vue项目中使用webpack-bundle-analyzer,你可能需要在vue.config.js文件中进行额外的配置。你可以通过环境变量来控制是否启用打包分析。例如:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: {
plugins: [
process.env.ANALYZE === 'true' && new BundleAnalyzerPlugin(),
],
},
};
在这个配置中,只有当环境变量ANALYZE被设置为true时,才会使用BundleAnalyzerPlugin插件。
你可以在运行构建命令之前设置这个环境变量,例如:ANALYZE=true npm run build。
配置完成后,当你运行webpack的打包命令(如npm run build或yarn build)时,webpack会根据你的配置执行相应的操作。如果你没有在插件配置中禁用自动打开分析器(analyzerMode: ‘disabled’),那么打包完成后,它会在浏览器中自动打开一个可视化的打包分析工具。
在这个工具中,你可以查看每个模块的大小、它们之间的依赖关系以及其他相关信息,从而帮助你更好地理解和优化你的打包结果。生成分析报告如下:
三、基础的 Web 技术优化
3.1、⚡️开启 gzip 压缩
使用插件compression-webpack-plugin
可以压缩webpack打包好的资源文件。
它能够将JS、CSS等文件进行压缩和优化,从而减小文件大小,优化网页加载速度。
通过配置,它可以实现多种压缩算法,包括Gzip压缩
-
安装
cnmp i compression-webpack-plugin -D
-
在
vue.congig.js
中引入并修改webpack配置const CompressionPlugin = require('compression-webpack-plugin') configureWebpack: (config) => { if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... config.mode = 'production' return { plugins: [ new CompressionPlugin({ algorithm: 'gzip', // 使用gzip压缩 test: /\.js$|\.html$|\.css/, // 对哪些文件进行压缩 threshold: 10240, // 对超过10k的数据进行压缩 minRatio: 0.8, // 压缩率小于这个值的文件不会被压缩 deleteOriginalAssets: false // 是否删除原文件 }) ] } } }
-
服务器上配置
还需要在Nginx服务器上配置gzip压缩
可以在Nginx配置文件中(通常是nginx.conf或者某个在sites-enabled目录下的文件)添加如下配置:gzip on; gzip_min_length 1k; # 最小文件大小也要压缩 gzip_buffers 4 16k; # 设置压缩缓冲区 gzip_http_version 1.1; # 压缩版本(默认1.1) gzip_comp_level 2; # 压缩级别(1-9,9最高,消耗CPU也最多) gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/json application/xml+rss; # 对哪些MIME类型进行压缩
-
重启服务,观察网络面板里面的
response header
,如果看到如下红圈里的字段则表明 gzip 开启成功 :
3.2、⚡️浏览器缓存
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的,根据是否需要重新向服务器发起请求来分类,将 HTTP 缓存规则分为两大类(强制缓存,协商缓存)。
3.3、⚡️CDN 的使用
浏览器从服务器上下载 CSS、js 和图片等文件时都要和服务器连接,而大部分服务器的带宽有限,如果超过限制,网页就半天反应不过来。
对于一些大型的第三方库,可以考虑将它们通过CDN加载,而不是打包到你的项目中。
这样可以减小项目的体积,并且利用CDN的缓存机制来提高加载速度。
CDN 可以通过不同的域名来加载文件,从而使下载文件的并发连接数大大增加,且CDN 具有更好的可用性,更低的网络延迟和丢包率 。
CDN可以将资源缓存到离用户更近的地方,加快资源的访问速度。
3.4、使用 Chrome Performance 查找性能瓶颈
Chrome 的 Performance 面板可以录制一段时间内的 js 执行细节及时间。使用 Chrome 开发者工具分析页面性能的步骤如下:
- 打开 Chrome 开发者工具,切换到 Performance 面板
- 点击 Record 开始录制
- 刷新页面或展开某个节点
- 点击 Stop 停止录制
3.5、优化网络请求
减少不必要的网络请求,合并或优化API接口,使用HTTP/2协议等,都可以降低网络延迟,提高加载速度。
总结
本文通过以下三部分组成:Vue 代码层面的优化、webpack 配置层面的优化、基础的 Web 技术层面的优化;
来介绍怎么去优化 Vue 项目的性能。 希望对读完本文的你有帮助、有启发,如果有不足之处,欢迎批评指正交流!
辛苦整理良久,还望手动点赞鼓励~