vue项目性能优化方案

vue项目性能优化方案

前言

面试过程中经常会被问到关于前端项目优化的问题,故在复习期间总结一下前端优化的几种方法和方向,这里仅介绍使用了vue技术栈的项目,分别从三个方向:

  • vue代码层面的优化
  • webpack配置层面的优化
  • 基础web技术层面的优化

一、代码层面的优化

1.1v-if和v-show区分使用场景
v-if是真正的条件渲染,实质就是dom节点根据设定的条件是否选择渲染节点,对节点进行销毁和重建,注意它有惰性,也就是说如果一开始的渲染条件为假的时候,则什么都不会做,直到条件第一次变为真才会渲染;
v-show就相对简单,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。
1.2computed和watch的区分使用场景
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算, 在 computed 中定义的每一个计算属性,都会被缓存起来,只有当计算属性里面依赖的一个或多个属性变化了,才会重新计算当前计算属性的值。;

当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

补充一下:computed 跟 methods 里面的方法的区别,computed 的话访问的时候会直接返回已缓存的结果,而不会像 methods 一样再次计算。

//computed使用
<template>
  <div>
    <p>{{ reversedMessage }}</p>
  </div>
</template>
 
<script>
export default {
  name: 'test1',
  data () {
    return {
      message: 'hello world',
      number: 1
    }
  },
  computed: {
    // 字符串反转
    reversedMessage () {
      return this.message.split('').reverse().join('') + this.number
    }
  }
}
</script>
//watch使用场景
<template>
  <div>
    <p>{{ this.number }}</p>
  </div>
</template>
 
<script>
export default {
  name: 'test1',
  data () {
    return {
      number: 1
    }
  },
  created () {
    setTimeout(() => {
      this.number = 100
    }, 2000)
  },
  watch: {
    number (newVal, oldVal) {
      console.log('number has changed: ', newVal)
    }
  }
}
</script>

1.3v-for 遍历必须为 item 添加 key,且避免同时使用 v-if

(1)v-for 遍历必须为 item 添加 key

在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,key值最好不要是数组的下方索引,防止出现不必要的bug,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。

(2)v-for 遍历避免同时使用 v-if

v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
推荐:

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
 return user.isActive
    })
  }
}

不推荐:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>

也可以使用第二种方法,将v-if放到循环列表元素的父元素中或使用template将v-for渲染的元素包起来,再在template上使用v-if。

<template v-if="shouldShowItem">
	<div v-for="item in itemList"></div>
</template>

1.4Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

export default {
 data: () => ({
 users: {}
 }),
 async created() {
 const users = await axios.get("/api/users");
 this.users = Object.freeze(users);
 }
};

1.6图片资源懒加载
(1)安装插件

npm install vue-lazyload --save-dev

(2)在入口文件 man.js 中引入并使用

import VueLazyload from 'vue-lazyload'

然后再 vue 中直接使用

Vue.use(VueLazyload)

或者添加自定义选项

Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',//img加载失败时现实的图片的路径
loading: 'dist/loading.gif',//img的加载中的显示的图片的路径
attempt: 1//尝试加载的次数
 listenEvents['scroll','wheel','mousewheel',
 'resize','animationend','	transitionend','touchmove'],  //你想让vue监听的事件
})

(3)在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示:

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

1.7路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
方式一:

/* vue异步组件技术 */

{ path: '/home', name: 'home', component: resolve => require(['@/components/home'],resolve) },

{ path: '/index', name: 'Index', component: resolve => require(['@/components/index'],resolve) },

{ path: '/about', name: 'about', component: resolve => require(['@/components/about'],resolve) }

方式二:

const 组件名=() => import('组件路径');

// 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。

 const Home = () => import('@/components/home')

const Index = () => import('@/components/index')

const About = () => import('@/components/about') */

// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。

把组件按组分块

const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')

const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index')

const About = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/about')

方式三:webpack提供的require.ensure()

vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。 
这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

/* 组件懒加载方案三: webpack提供的require.ensure() */

{ path: '/home', name: 'home', component: r => require.ensure([], () => r(require('@/components/home')), 'demo') },

{ path: '/index', name: 'Index', component: r => require.ensure([], () => r(require('@/components/index')), 'demo') },

{ path: '/about', name: 'about', component: r => require.ensure([], () => r(require('@/components/about')), 'demo-01') }

 

// r就是resolve
const list = r => require.ensure([], () => r(require('../components/list/list')), 'list');
// 路由也是正常的写法  这种是官方推荐的写的 按模块划分懒加载 
const router = new Router({
    routes: [
        {
           path: '/list/blog',
           component: list,
           name: 'blog'
        }
    ]
})

1.8、第三方插件的按需引入

(1)首先,安装 babel-plugin-component :

npm install babel-plugin-component -D

(2)然后,将 .babelrc 修改为:

{
 "presets": [["es2015", { "modules": false }]],
 "plugins": [
 [
 "component",
 {
 "libraryName": "element-ui",
 "styleLibraryName": "theme-chalk"
 }
 ]
 ]
}

(3)在 main.js 中引入部分组件:

import Vue from 'vue';
import { Button, Select } from 'element-ui';
 Vue.use(Button)
 Vue.use(Select)

1.9服务端渲染 SSR or 预渲染

服务端渲染是指 Vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染。

(1)服务端渲染的优点:

更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
(2)服务端渲染的缺点:

更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源,因此如果你预料在高流量环境下使用,请准备相应的服务器负载,并明智地采用缓存策略。

二、webpack层面的优化

2.1、Webpack 对图片进行压缩

在 vue 项目中除了可以在 webpack.base.conf.js 中 url-loader 中设置 limit 大小来对图片处理,对小于 limit 的图片转化为 base64 格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader来压缩图片:
(1)首先,安装 image-webpack-loader :

npm install image-webpack-loader --save-dev

(2)然后,在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,
      }
    }
  ]
}

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 编译出来的代码的文件大小。
(1)首先,安装 babel-plugin-transform-runtime :

npm install babel-plugin-transform-runtime --save-dev

(2)然后,修改 .babelrc 配置文件为:

"plugins": [
 "transform-runtime"
]

2.3、提取公共代码

如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:

相同的资源被重复加载,浪费用户的流量和服务器的成本。
每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。Webpack 内置了专门用于提取多个Chunk 中的公共部分的插件 CommonsChunkPlugin,我们在项目中 CommonsChunkPlugin 的配置如下:

// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。
new webpack.optimize.CommonsChunkPlugin({
 name: 'vendor',
 minChunks: function(module, count) {
 return (
 module.resource &&
 /\.js$/.test(module.resource) &&
 module.resource.indexOf(
 path.join(__dirname, '../node_modules')
 ) === 0
 );
 }
}),
// 抽取出代码模块的映射关系
new webpack.optimize.CommonsChunkPlugin({
 name: 'manifest',
 chunks: ['vendor']
})

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、在使用 npm run build 打包时 移除console
(1)、使用 babel中 的一个插件,因为webpack 打包时会使用 babel 进行代码降级,所以babel 插件可以在打包过程中将 console 移除

      安装插件
      npm install babel-plugin-transform-remove-console --save-dev

(2)、配置babel.config文件

const prodPlugins = []
if (process.env.NODE_ENV === 'production') {
  prodPlugins.push('transform-remove-console')
}
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: ['transform-remove-console']
}

这样配置后 再次打包 包中就没有console了
问题:当运行npm run serve时 也会删除console 但是开发阶段我们需要
改善babel.config文件:

const prodPlugins = []
if (process.env.NODE_ENV === 'production') {
  prodPlugins.push('transform-remove-console')
}
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [...prodPlugins]
}

2.7改善打包入口文件

    项目中的默认入口文件时main.js文件 不管是打包 还是开发阶段使用的都是这一文件
    在开发阶段我们可能调用一端口返回假数据 为调试我们的代码 但是在上线后我们需要用正式接口 
  可能不止一个,但是我们左右手动更改较为麻烦 且容易出错

(1).创建两个入口文件:
一个用于开发环境的打包 dev_env.js
一个用于项目上线的打包 prod_env.js

//main_dev.js,开发环境的入口文件
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
//导入全局样式表
import './assets/css/global.css'
//导入图表库
import './assets/fonts/iconfont.css'
import axios from 'axios'
import TreeTable from 'vue-table-with-tree-grid'
//导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
//导入富文本编辑器对应的样式
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble them


//导入 nprogress 包对应的js和css
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

//配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
//在request拦截器中展示进度条NProgress.start()
axios.interceptors.request.use(config=>{
  //console.log(config)
  NProgress.start()
  config.headers.Authorization = window.sessionStorage.getItem('token');
  //在最后一定要return config
  return config 
})
//在response拦截器中隐藏进度条,NProgress.done()
axios.interceptors.response.use(config=>{
  NProgress.done()
  return config
})
Vue.prototype.$http = axios  
Vue.config.productionTip = false
Vue.component('tree-table',TreeTable)

//将富文本编辑器注册为全局可用的组件
Vue.use(VueQuillEditor)

Vue.filter('dateFormat',function(originVal){
      const dt=new Date(originVal)

     const y= dt.getFullYear()
     const m= (dt.getMonth()+1+'').padStart(2,'0')
     const d= (dt.getDate()+'').padStart(2,'0')

     const hh= (dt.getHours()+'').padStart(2,'0')
     const mm= (dt.getMinutes()+'').padStart(2,'0')
     const ss= (dt.getSeconds()+'').padStart(2,'0')
     return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

//main_prod.js,生产环境的入口文件
import Vue from 'vue'
import App from './App.vue'
import router from './router'
//import './plugins/element.js'
//导入全局样式表
import './assets/css/global.css'
//导入图表库
import './assets/fonts/iconfont.css'
import axios from 'axios'
import TreeTable from 'vue-table-with-tree-grid'

//导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'

 

//导入 nprogress 包对应的js和css
import NProgress from 'nprogress'


//配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
//在request拦截器中展示进度条NProgress.start()
axios.interceptors.request.use(config=>{
  //console.log(config)
  NProgress.start()
  config.headers.Authorization = window.sessionStorage.getItem('token');
  //在最后一定要return config
  return config 
})
//在response拦截器中隐藏进度条,NProgress.done()
axios.interceptors.response.use(config=>{
  NProgress.done()
  return config
})
Vue.prototype.$http = axios  
Vue.config.productionTip = false
Vue.component('tree-table',TreeTable)

//将富文本编辑器注册为全局可用的组件
Vue.use(VueQuillEditor)

Vue.filter('dateFormat',function(originVal){
      const dt=new Date(originVal)

     const y= dt.getFullYear()
     const m= (dt.getMonth()+1+'').padStart(2,'0')
     const d= (dt.getDate()+'').padStart(2,'0')

     const hh= (dt.getHours()+'').padStart(2,'0')
     const mm= (dt.getMinutes()+'').padStart(2,'0')
     const ss= (dt.getSeconds()+'').padStart(2,'0')
     return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

(2)在没有build文件夹的项目中(即用vue create创建的项目),在项目中创建vue.config.js,并在里面配置入口选择:

module.exports={
    chainWebpack:config=>{
        config.when(process.env.NODE_ENV === 'production',config=>{
            config.entry('app').clear().add('./src/prod_env.js')
        })
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/dev_env.js')
        })
    }
}
开发时,使用 npm run serve 命令,NODE_ENV 的值就是 developmnent ,所以会将 dev.env.js 作为入口文件

部署时,使用 npm run build 命令,NODE_ENV 的值就是 production,所以会将 prod_evn.js 作为入口文件

(3)若用vue init webpack 创建的项目,修改入口文件请参考:
修改入口文件方法一
修改入口文件方法二

三、基础的 Web 技术优化

(一) 将样式表放在首部-使用link标签将样式表放在文档的HEAD中

首先加一个知识点:fouc(无样式闪烁):在引用css的过程中,如果方法不当或者位置引用不对,会导致某些页面在windows下的ie出现一些奇怪的现象,以无样式显示页面内容的瞬间闪烁,这种现象称之为文档样式短暂失效,简称FOCU

遵循HTML规范,将样式表放在头部,可以有效避免白屏和无样式内容的闪烁。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <!-- 使用link标签将样式表放在文档的HEAD-->
  <link rel="stylesheet" href="example.css">
</head>
<body></body>
</html>
(二)将脚本放在底部
将脚本放在顶部会造成的影响: 脚本阻塞对其后面内容的显示; 脚本会阻塞对其后面组件的下载;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <link rel="stylesheet" href="example.css">
</head>
<body>
  <!-- 将脚本放在底部 -->
  <script src="example.js"></script>
</body>
</html>
(三)减少http请求

1)CSS Sprites (雪碧图)

将多个图片合成一张图片,通过background-position来定位所需要的图片。每次请求的话只需要请求一张图片减少http请求。(如果使用图标的话建议使用svg,也可以使用iconfont)
合成雪碧图的工具有很多

本地工具:https://github.com/iwangx/sprite(国人写的)
在线工具https://www.toptal.com/developers/css/sprite-generator

2)内联图片和脚本
通过内联图片和脚本无需额外的HTTP请求,图片小于10K的可以设置内联为base64位。

<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4SUJRXhpZgAATU>
  1. 合并脚本和样式表

一般来说,使用外部脚本和样式表对性能更有利,然而如果将模块化的代码分开放到多个小文件中,会降低性能,每个文件都会导致一个额外的HTTP请求

(四)使用CDN(content delivery network)

CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。–摘自百度百科

bootcdn(免费)
使用CDN需要在webpack和index.html进行相关配置
第一步 配置vue.config.js,只需要在刚才配置Gzip压缩的基础上再加一段代码:

const CompressionWebpackPlugin = require('compression-webpack-plugin')

const ENV = process.env.NODE_ENV || 'development'

module.exports = {
	configureWebpack: config => {
		if (ENV === 'production') {
			config.plugins.push(new CompressionWebpackPlugin({
				algorithm: 'gzip',
				test: /\.(js|css|html)$/,
				threshold: 10240,
				minRatio: 0.8
			}))
            // 配置externals就是当使用CDN进入的js文件在当前项目中可以引用
            // 比如在开发环境引入的vue是import Vue from 'vue', 这个大写的Vue就是对应的下面的大写的Vue
			config.externals = {
			  'vue': 'Vue',
			  'vue-router': 'VueRouter',
			  'axios': 'axios'
			}
		}
	}
}

第二步 配置index.html,在body里使用EJS语法判断是否为生产环境

<body>
  <div id="app"></div>
  <% if (NODE_ENV === 'production') { %>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
    <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
  <% } %>
</body>

(五)使用缓存

a、添加强缓存(添加expire头)
在这里插入图片描述
可以在nginx中配置

location ~.*\.(svg|woff|js|css){
	root /yourFilePath;//你的需要进行强缓存的文件路径
	expires 1d;// 1 day
}

Web服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本,直到指定的时间为止 HTTP规范中简要地称该头为“在这一日期时间之后,响应将被认为是无效的”。它在HTTP响应中发送

expires: Thu, 30 May 2019 20:51:42 GMT

上面的Expires头告诉浏览器该响应的有效性持续到2019年5月30日为止。如果为页面中的一个图片返回了这个头,浏览器在后续的页面浏览中会使用缓存的图片,将HTTP请求的数量减少一个。

Max-Age和mod_expires

  • 个人站点的css文件使用强缓存cache-control: max-age
    在这里插入图片描述
    可以在nginx中配置
server {
    add_header Cache-Control max-age=72000;
}
  1. 在解释缓存如何很好地改善传输性能之前,需要提及除了Expires 头之外的另一种选择。HTTP 1.1引入了Cache-Control头来克服Expires头的限制
  2. 因为Expires头使用一个特定的时间,它要求服务器和客户端的时钟严格同步。另外,过期日期需要经常检查,并且一旦未来这天到来了,还需要在服务器配置中提供个新的日期。
  3. Cache-Control使用max-age指令指定组件被缓存多久, 如果从组件被请求开始过去的秒数少于max-age,浏览器就使用缓存的版本,这就避免了额外的HTTP请求。一个长久的max-age头可以将刷新窗设置为未来10年。
(六)开启gzip服务

开启gzip服务,需要在前后端一起设置

  • 服务端开启gzip服务
gzip on; # 开启Gzip
gzip_static on; # 开启静态文件压缩
gzip_min_length  1k; # 不压缩临界值,大于1K的才压缩
gzip_buffers     4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types     text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss; # 进行压缩的文件类型
gzip_vary on;
gzip_proxied   expired no-cache no-store private auth;
gzip_disable   "MSIE [1-6]\.";

前端在vue.config.js中设置

// 需要 npm install compression-webpack-plugin --save-dev
const CompressionWebpackPlugin = require('compression-webpack-plugin')

// 定义当前环境
const ENV = process.env.NODE_ENV || 'development'

module.exports = {
	configureWebpack: config => {
		// 如果是生产环境的话,开启压缩
		if (ENV === 'production') {
			//  参数配置文档: https://www.webpackjs.com/plugins/compression-webpack-plugin/
			config.plugins.push(new CompressionWebpackPlugin({
				algorithm: 'gzip',
				test: /\.(js|css|html)$/,
				threshold: 10240,
				minRatio: 0.8
			}))
		}
	}
}

(七)开启http2服务
HTTP2在前端性能上主要表现在:请求和响应的多路复用、头部压缩;
nginx服务器配置HTTP2:
在这里插入图片描述
使用http2需要配合https使用
使用https需要ca证书 阿里云证书购买 (有免费的ca证书)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值