方法 | 描述 |
---|---|
合并文件 | 将多个文件合并为一个文件,减少HTTP请求次数。 |
压缩文件 | 将文件进行压缩,减小文件大小,减少HTTP请求时间。 |
使用CDN | 使用CDN加速服务,减少服务器的请求压力,提高页面加载速度。 |
按需加载 | 将页面分为多个模块,按需加载,减少不必要的请求和页面加载时间。 |
懒加载 | 对于图片等资源,采用懒加载的方式,当用户需要查看时再加载,减少页面加载时间。 |
预加载 | 对于一些重要资源,采用预加载的方式,提前加载,提高用户体验。 |
使用WebP格式 | WebP格式的图片比JPEG和PNG格式的图片更小,加载速度更快,可以减少HTTP请求时间。 |
使用字体图标 | 使用字体图标代替图片,减少HTTP请求次数和文件大小。 |
使用缓存 | 合理使用浏览器缓存和服务器缓存,减少HTTP请求次数和请求时间。 |
代码分割可以将代码按照不同的逻辑或业务分割成不同的模块,从而实现按需加载,减少页面加载时间。在Vue中,可以使用以下方式进行代码分割:
使用Vue异步组件:将组件定义为函数返回一个Promise对象,在需要的时候再加载组件。
Vue.component('async-component', () => import('./AsyncComponent.vue'));
使用Webpack的代码分割功能:在Webpack配置文件中配置optimization.splitChunks进行代码分割。
optimization: {
splitChunks: {
chunks: 'all'
}
}
代码分割实例:
在项目中,有一个比较大的组件需要加载,但是这个组件并不是每个页面都需要用到。在使用Vue异步组件进行代码分割之前,每次进入页面都需要加载这个组件,导致页面加载时间过长。使用Vue异步组件进行代码分割后,只有在需要用到这个组件的时候才会进行加载,从而减少了页面加载时间。
在项目中,有多个页面都需要使用到一个公共的模块,但是这个模块比较大,每个页面都将这个模块打包进去会导致打包后的文件过大。使用Webpack的代码分割功能进行代码分割后,公共的模块只会被打包一次,从而减小了打包后文件的大小。
懒加载是将某些组件或资源推迟到实际需要的时候再加载,可以有效减少首屏加载时的资源压力。
可以使用 Vue.lazy()
方法实现懒加载,也可以使用第三方库如 vue-lazyload
。
示例代码:
<template>
<div>
<img v-lazy="imgUrl">
div>
template>
<script>
export default {
data() {
return {
imgUrl: 'https://example.com/image.jpg'
}
}
}
script>
import { add, sub } from 'utils.js'
表格:
优化手段 | 示例 |
---|---|
使用Webpack的UglifyJsPlugin插件进行代码压缩 | optimization: {minimize: true} |
使用Tree shaking技术去除未引用的代码 | optimization: {usedExports: true} |
将常量提取出来,避免重复定义 | new webpack.DefinePlugin({ ‘process.env.NODE_ENV’: JSON.stringify(‘production’) }) |
使用CDN引入第三方库 | |
使用Gzip进行压缩 | server { gzip on; } |
使用Web Workers进行代码分离 | const worker = new Worker(‘worker.js’) |
使用异步加载组件 | const Foo = () => import(‘./Foo.vue’) |
使用路由懒加载 | const Home = () => import(‘./Home.vue’) |
示例:
原图大小 | 压缩后大小 | 压缩率 |
---|---|---|
1.2MB | 350KB | 70% |
vue-lazyload
插件实现图片懒加载src
属性改为v-lazy
指令loading
属性来展示图片加载过程中的占位图error
属性来展示图片加载失败时的占位图示例代码:
<template>
<div>
<img v-for="img in imgList" :key="img.id" :src="img.src" v-lazy="img.lazySrc" />
div>
template>
<script>
import VueLazyload from 'vue-lazyload'
export default {
data() {
return {
imgList: [
{
id: 1,
src: 'https://example.com/image1.jpg',
lazySrc: 'https://example.com/image1_lazy.jpg'
},
{
id: 2,
src: 'https://example.com/image2.jpg',
lazySrc: 'https://example.com/image2_lazy.jpg'
},
// ...
]
}
},
// 注册插件
created() {
Vue.use(VueLazyload, {
loading: 'https://example.com/loading.gif',
error: 'https://example.com/error.jpg'
})
}
}
script>
// 在数据不需要响应式化时使用Object.freeze()
export default {
data() {
return {
// 不需要响应式化的数据
list: Object.freeze(['apple', 'banana', 'orange'])
}
}
}
合理使用计算属性和缓存,避免重复计算。
<template>
<div>
<p>商品数量:{{ count }}p>
<p>商品总价:{{ totalPrice }}p>
div>
template>
<script>
export default {
data() {
return {
goodsList: [
{ name: 'apple', price: 5, count: 2 },
{ name: 'banana', price: 3, count: 3 },
{ name: 'orange', price: 2, count: 4 }
]
}
},
computed: {
count() {
// 计算商品数量
return this.goodsList.reduce((sum, item) => sum + item.count, 0)
},
totalPrice() {
// 计算商品总价
return this.goodsList.reduce((sum, item) => sum + item.price * item.count, 0)
}
}
}
script>
<template>
<div>
<button @click="addCount">点击增加button>
<p>计数器:{{ count }}p>
div>
template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
addCount() {
// 点击按钮增加计数器
this.count++
}
},
computed: {
// 缓存计数器的值
cachedCount() {
return this.count
}
}
}
script>
使用v-if和v-show合理控制组件的显示和隐藏。
<template>
<div>
<button @click="toggle">点击切换button>
<p v-if="show">显示内容p>
div>
template>
<script>
export default {
data() {
return {
show: false
}
},
methods: {
toggle() {
// 点击按钮切换show的值
this.show = !this.show
}
}
}
script>
<template>
<div>
<button @click="toggle">点击切换button>
<p v-show="show">显示内容p>
div>
template>
<script>
export default {
data() {
return {
show: false
}
},
methods: {
toggle() {
// 点击按钮切换show的值
this.show = !this.show
}
}
}
script>
使用key值避免不必要的组件销毁和重建。
<template>
<div>
<button @click="toggle">点击切换button>
<child-component :key="show">child-component>
div>
template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data() {
return {
show: false
}
},
methods: {
toggle() {
// 点击按钮切换show的值
this.show = !this.show
}
}
}
script>
beforeRouteEnter
钩子函数,在路由进入组件前预加载数据。created
生命周期钩子函数中使用异步请求加载数据,并使用v-if
指令控制组件的显示,避免页面渲染时出现空白的情况。keep-alive
组件缓存组件的状态,避免每次进入组件都需要重新加载数据,提高页面加载速度。使用activated
生命周期钩子函数在组件被激活时重新加载数据。表格:
方式 | 优点 | 缺点 |
---|---|---|
beforeRouteEnter | 可以在路由进入组件前预加载数据 | 只能在路由进入组件前使用,无法在组件内部控制 |
created + v-if | 可以在组件的created生命周期钩子函数中预加载数据,并使用v-if指令控制组件的显示 | 页面渲染时可能出现空白的情况 |
keep-alive + activated | 可以缓存组件的状态,避免每次进入组件都需要重新加载数据 | 只有在组件被激活时才会重新加载数据 |
2.5 服务端优化
module.exports = {
//...
configureWebpack: {
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'element-ui': 'ELEMENT',
}
}
}
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>My Apptitle>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
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>
<div id="app">div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="https://cdn.jsdelivr.net/npm/vue-router/dist/vue-router.js">script>
<script src="https://cdn.jsdelivr.net/npm/vuex/dist/vuex.js">script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js">script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js">script>
body>
html>
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>
表格示例:
技术 | 描述 |
---|---|
CSS Sprites | 将多个小图片合成一张大图,减少HTTP请求次数 |
WebP | 一种图片格式,可以大幅度减小图片大小,提升加载速度 |
lazyload | 只有当图片进入可视区域才进行加载,减少不必要的网络请求 |
图片压缩工具 | 对于大图,可以使用图片压缩工具进行压缩,减小图片大小 |
响应式图片处理 | 使用srcset和picture标签进行响应式图片处理,提升移动端的加载速度 |
canvas和SVG | 对于需要动态生成的图片,可以使用canvas或SVG代替传统的图片格式 |
CDN | 使用CDN加速图片加载,提升用户体验 |
优化手段 | 实例 |
---|---|
避免频繁的数据更新 | 合并多个数据更新操作为一次更新 |
使用v-if和v-show合理控制组件的渲染 | 对于不常用的组件使用v-show代替v-if |
使用懒加载和分页加载大量数据 | 对于列表数据使用分页加载或滚动加载 |
使用localStorage或IndexedDB等本地存储技术缓存数据 | 对于静态数据使用localStorage进行缓存 |
3.5 服务端优化的实践- 服务端渲染(SSR):使用vue-server-renderer将vue组件在服务端渲染成HTML字符串,减少浏览器的渲染压力,提升首屏渲染速度。