likes
comments
collection
share

这一次,我要给面试官把项目中实践落地的Vue性能优化讲清楚!

作者站长头像
站长
· 阅读数 11

一、代码层面的优化

1.1、v-if 和 v-show 区分使用场景

v-if真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。

所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

1.2、v-for key值的运用(且避免同时使用 v-if)

这一次,我要给面试官把项目中实践落地的Vue性能优化讲清楚! 在 Vue 中,v-for 指令用于循环渲染列表。为了提高渲染性能,Vue 使用了虚拟 DOM (Virtual DOM) 技术,而 key 是与此技术密切相关的一个属性。使用 key 可以帮助 Vue 识别哪些节点可以被重用、移动或删除。在详细解释之前,先了解一下虚拟 DOM。

虚拟 DOM (Virtual DOM) 是一种编程概念,用于提高 UI 更新性能。它通过在内存中创建轻量级的 JavaScript 对象结构来描述真实的 DOM 结构。当 UI 发生变化时,Vue 会创建一个新的虚拟 DOM,然后将新旧虚拟 DOM 进行对比,最终只更新发生变化的部分,而不是重新渲染整个页面。这个比较过程叫做“Diffing”。

在使用 v-for 时,为每个循环项添加唯一的 key,有以下好处:

  1. 更高效的更新:当数据发生变化时,Vue 通过 key 可以精确识别哪些节点需要更新,哪些节点可以保持不变,从而减少不必要的 DOM 操作,提高渲染性能。
  2. 状态维护:如果循环项包含组件,key 可以帮助 Vue 更好地维护组件的状态。当列表顺序发生变化时,没有 key 的情况下,Vue 会尽量进行就地更新,这可能导致组件状态混乱。而有了 key,Vue 可以正确地识别和追踪每个组件。

举个例子:

<ul>
  <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>

假设 items 是一个包含三个对象的数组:

[
  { id: 1, text: 'Item 1' },
  { id: 2, text: 'Item 2' },
  { id: 3, text: 'Item 3' }
]

当 items 更新为以下数据: [ { id: 2, text: 'Item 2' }, { id: 1, text: 'Item 1' }, { id: 3, text: 'Item 3' } ]

有 key 的情况下,Vue 可以识别出只有前两个元素发生了顺序调整,因此只需要交换它们的位置,而不需要重新渲染整个列表。这样可以大大提高性能。

总之,使用 key 可以帮助 Vue 更高效地更新和维护列表项,提高应用性能。

这里简单说一下Diff算法

  1. 简介:Diff 算法是用于比较两个虚拟 DOM 树的差异,并找出其中的最小操作集合,以便将这些变化应用到真实的 DOM 树上,从而实现高效的更新。它是一种解决 Web 应用中 UI 更新性能问题的重要技术。

  2. 基本原理:Diff 算法采用深度优先遍历 (DFS) 的方式比较两棵虚拟 DOM 树。在比较过程中,如果发现某个节点发生了变化,就会执行相应的更新操作。为了提高性能,算法会尽量复用没有变化的节点,而不是重新创建它们。

  3. 四种基本变化:Diff 算法主要关注以下四种基本变化:

    • 节点类型不同:当比较两个节点时,如果它们的类型不同(例如,一个是 div,另一个是 span),算法会认为整个节点及其子节点都需要重新创建,并执行相应的更新操作。
    • 节点类型相同但属性不同:如果两个节点类型相同,算法会继续比较它们的属性。如果属性有变化,算法会仅更新这些变化的属性。
    • 文本节点变化:对于文本节点,算法会直接比较它们的内容。如果内容有变化,就会更新文本节点。
    • 列表节点变化:对于包含列表的节点,算法会尝试找出列表项之间的最小变化集合。为了提高性能,Vue 使用了 key 属性来帮助识别和复用列表项。
  4. 优化策略:为了提高比较性能,Diff 算法采用了一些优化策略,如:

    • 同层比较:算法只会比较同一层级的节点,而不会跨层级进行比较。这样可以减少比较的复杂性。
    • 最小操作集合:通过寻找最小操作集合,算法可以避免执行不必要的更新操作,从而提高性能。
  5. 应用场景:Diff 算法在很多前端框架和库中都有应用,如 React、Vue、Inferno 等。这些框架和库通常都有各自的实现方式,但其核心思想和原理都是相似的。

前端中的深度优先遍历(DFS)一般用于树形结构的遍历,可以通过递归或者栈来实现。下面举一个简单的例子进行说明:

假设有以下树形结构:

复制
      A
     / \
    B   C
   / \   \
  D   E   F

其中,A为根节点,B和C是A的子节点,D和E是B的子节点,F是C的子节点。

实现深度优先遍历的过程如下:

  1. 从根节点A开始遍历,输出A;
  2. 遍历A的子节点B,输出B;
  3. 遍历B的子节点D,输出D;
  4. 遍历D的子节点,发现没有子节点,回退到B;
  5. 继续遍历B的另一个子节点E,输出E;
  6. 遍历E的子节点,发现没有子节点,回退到B;
  7. 因为B的所有子节点都已经遍历完毕,回退到A;
  8. 继续遍历A的另一个子节点C,输出C;
  9. 遍历C的子节点F,输出F;
  10. 遍历F的子节点,发现没有子节点,回退到C;
  11. 因为C的所有子节点都已经遍历完毕,回退到A;
  12. 由于A的所有子节点都已经遍历完毕,遍历结束。

以上过程就是深度优先遍历的过程,输出的顺序是:A -> B -> D -> E -> C -> F。

在实际前端开发中,深度优先遍历常用于DOM树的遍历、路由系统的实现等场景。

1.2、computed 和 watch 区分使用场景

computedwatch都是Vue中常用的属性,用于响应式地处理数据变化。它们虽然有一些相似之处,但是在使用场景上还是有区别的。

  • computed:计算属性,用于动态计算并返回一个值。当依赖的响应式数据发生变化时,计算属性会重新计算其值。计算属性一般用于需要进行复杂计算的场景,例如从列表中过滤出满足某些条件的数据。另外,计算属性具有缓存功能,只有在其依赖的响应式数据发生变化时,才会重新计算其值。

例如,我们有一个列表,需要过滤出年龄大于等于18岁的人,可以使用计算属性实现:

<template>
  <div>
    <ul>
      <li v-for="person in adults" :key="person.id">{{ person.name }},{{ person.age }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      persons: [
        { id: 1, name: 'Tom', age: 20 },
        { id: 2, name: 'Jack', age: 16 },
        { id: 3, name: 'Lucy', age: 18 },
        { id: 4, name: 'Mike', age: 22 }
      ]
    }
  },
  computed: {
    adults() {
      return this.persons.filter(person => person.age >= 18)
    }
  }
}
</script>
  • watch:观察者,用于监听一个响应式数据的变化,并在变化时执行相应的操作。当我们需要在响应式数据发生变化时执行一些异步操作,或者需要对变化做出一些复杂处理时,可以使用watch。另外,watch还可以监听多个响应式数据的变化。

例如,我们有一个表单,需要在输入框中输入内容时实时计算输入字符数,可以使用watch实现:

<template>
  <div>
    <input v-model="content" placeholder="请输入内容">
    <div>已输入{{ count }}个字符</div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      content: ''
    }
  },
  watch: {
    content(newVal) {
      this.count = newVal.length
    }
  },
  data() {
    return {
      count: 0
    }
  }
}
</script>

总之,computed适用于需要动态计算某个值的场景,而watch适用于需要监听某个响应式数据的变化,并在变化时执行相应的操作的场景。在实际开发中,需要根据具体业务需求选择使用。

1.3、路由懒加载

使用import()语法来实现异步加载。

const Foo = () => import('./Foo.vue') 
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })

1.4、十万级数据列表渲染

使用虚拟滚动技术,自己通过每页需要几条数据和每条数据得度计算出滚动条,再去渲染当前可以看到的区域的Dom(vue-virtual-scroll-listvue-virtual-scroller),或者用上拉加载和下拉刷新,配合后端来实现。

1.5、图片资源懒加载

对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:

(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',
loading: 'dist/loading.gif',
attempt: 1
})

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

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

此外,可以将本地图片放到云端-->将图片等静态资源放到云端是一种常见的方式来减小Vue应用的打包体积。通过将静态资源托管在云端,可以减少应用程序的初始加载大小,提高页面加载速度。下面是一些实现的方法:

  1. 使用云存储服务:选择一个云存储服务提供商,例如亚马逊S3、Google Cloud Storage或Microsoft Azure Blob Storage等。这些服务提供了稳定、可靠的存储和分发静态资源的功能。
  2. 上传静态资源:将需要托管的静态资源(如图片)上传到云存储服务。你可以使用相应云存储服务提供商的管理控制台、命令行工具或API来上传资源。
  3. 获取资源URL:云存储服务会为上传的资源分配一个URL。获取这些资源的URL,可以通过云存储服务提供的接口或管理控制台查看。
  4. 在Vue应用中使用云端资源:在Vue应用的代码中,使用获取到的云端资源URL来引用静态资源。可以通过在模板中使用<img>标签、CSS中的background-image属性或JavaScript中动态加载资源的方式来引用。

需要注意的是,当使用云端资源时,确保云存储服务的访问权限设置正确,以允许应用程序能够正确访问这些资源。

1.6 第三方插件按需引入

在Vue中实现第三方插件的按需引入通常需要使用到Webpack的按需加载(code splitting)功能。下面以Element UI为例,介绍如何按需引入它的组件。

  1. 安装Element UI:首先,使用npm或yarn安装Element UI包。
shellCopy code
npm install element-ui

2. 配置按需引入:为了按需引入Element UI的组件,你可以使用babel-plugin-component插件来实现。在项目根目录下创建.babelrc文件,并配置以下内容:

jsonCopy code
{
  "plugins": [
    ["component", {
      "libraryName": "element-ui",
      "styleLibraryName": "theme-chalk"
    }]
  ]
}

3. 使用需要的组件:在Vue组件中,只需按需引入需要使用的Element UI组件。例如,在某个组件中只需要使用ButtonInput组件,可以这样引入:

vueCopy code
<template>
  <div>
    <el-button>按钮</el-button>
    <el-input></el-input>
  </div>
</template>

<script>
import { Button, Input } from 'element-ui';

export default {
  components: {
    'el-button': Button,
    'el-input': Input
  }
}
</script>

通过以上配置,只有在需要使用到的组件时,才会按需引入相应的组件代码,减小了打包体积。

1.7 keep-alive

<keep-alive> 组件是 Vue 提供的一个内置组件,用于在 Vue 应用中缓存已经渲染过的组件实例。它可以提高组件的性能和用户体验,特别是在频繁切换显示的组件上。(导航菜单,tab切换,个人中心)。

二、Webpack 层面的优化

2.1、代码分割(Code Splitting)

将代码拆分成多个小块,按需加载,减少初始加载的文件大小。可以使用Webpack的动态导入(dynamic import)或代码拆分插件(如@babel/plugin-syntax-dynamic-import)来实现。示例配置:

javascriptCopy code
// webpack.config.js
module.exports = {
  // ...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      cacheGroups: {
        vendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

2.2 压缩代码(Minification)

使用Webpack的terser-webpack-pluginuglifyjs-webpack-plugin插件来压缩和混淆代码,减小文件体积。示例配置:

javascriptCopy code
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true
          }
        }
      })
    ]
  }
};

2.3 sourceMap+tree sharking

禁用SourceMap:在生产环境中,可以禁用SourceMap的生成,以减小文件大小和提高性能。这样可以防止源代码泄露,但也会使调试变得困难。

  1. 生成简化的SourceMap:在生产环境中,可以生成简化版的SourceMap,只包含必要的映射信息,以减小文件大小。这样可以在一定程度上平衡调试和性能之间的关系。
  2. 使用ES6模块语法:确保你的代码使用ES6模块语法(importexport),因为Webpack只能对ES6模块进行静态分析。
  3. 配置Webpack的modeproduction:在生产环境下,Webpack会自动启用Tree Shaking功能。
  4. 使用Babel进行转译:如果你的代码中使用了其他语言特性(如类属性、装饰器等),确保将代码转译为ES6模块语法,以便Webpack能够正确地进行Tree Shaking。
  5. 配置Webpack的optimization选项:可以使用optimization选项来进一步优化Tree Shaking的效果。例如,设置optimization.usedExportstrue,可以让Webpack只导出被使用的代码。示例配置:
javascriptCopy code
// webpack.config.js
module.exports = {
  // ...
  optimization: {
    usedExports: true
  }
};

2.4 npm run build --report构建结果输出分析

这一次,我要给面试官把项目中实践落地的Vue性能优化讲清楚! 1.monent.js打包优化

仅导入需要的部分:Moment.js 支持按需导入,你可以只导入需要的模块或方法,而不是整个库。这可以通过使用 Webpack 的动态导入(dynamic import)或者使用按需加载的插件来实现。

例如,如果你只需要 Moment.js 的日期格式化功能,可以这样导入:

```
javascriptCopy code
import moment from 'moment';
import 'moment/locale/zh-cn';  // 导入指定的语言包

const formattedDate = moment().format('YYYY-MM-DD');
```

通过仅导入需要的部分,可以减小打包后的文件大小。
  1. 使用插件或配置优化工具:Moment.js 提供了一些插件和配置优化工具,用于减小库的体积。例如,可以使用 moment-locales-webpack-plugin 插件来仅导入需要的语言包,而不是全部语言包。

  2. 使用 Webpack 的 IgnorePlugin:通过配置 Webpack 的 IgnorePlugin,可以忽略 Moment.js 中不需要的模块,减小打包后的文件大小。例如,你可以忽略所有的本地化模块,因为你只需要特定的语言包。

    javascriptCopy code
    const webpack = require('webpack');
    
    module.exports = {
      // ...
      plugins: [
        new webpack.IgnorePlugin(/^./locale$/, /moment$/),
      ],
    };
    

2.vue.config.js 中配置vue(vue、vue-router、vuex)相关依赖采用cdn形式,不再打进包里边。

外网:要配置 vue.config.js 文件以使用 CDN 引入 Vue、Vue Router 和 Vuex,可以按照以下步骤进行:

  1. 在项目根目录下创建一个名为 vue.config.js 的文件(如果已存在,请直接编辑该文件)。
  2. vue.config.js 文件中,使用 chainWebpack 方法来配置 Webpack:
javascriptCopy code
module.exports = {
  chainWebpack: config => {
    // 配置CDN引入
    config.externals({
      vue: 'Vue',
      'vue-router': 'VueRouter',
      vuex: 'Vuex'
    });

    // 如果需要配置其他的CDN引入,可以继续添加
    // config.externals({
    //   'element-ui': 'ELEMENT'
    // });

    // 优化CDN加载速度
    config.plugin('html').tap(args => {
      args[0].cdn = {
        css: [],
        js: [
          'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js',
          'https://cdn.jsdelivr.net/npm/vue-router@3.5.2/dist/vue-router.min.js',
          'https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js'
        ]
      };
      return args;
    });
  }
};

在上述配置中,我们使用 externals 方法将 Vue、Vue Router 和 Vuex 配置为外部引用,即告诉 Webpack 这些模块将通过 CDN 引入,而不是打包到最终的输出文件中。

然后,我们使用 plugin('html') 方法来修改 HTML 插件的配置,将 CDN 引入的资源添加到 HTML 文件中。在上述示例中,我们添加了 Vue、Vue Router 和 Vuex 的 CDN 资源链接。

你可以根据需要继续添加其他需要使用 CDN 引入的库,例如 Element UI 等。

注意,使用 CDN 引入的资源将不会被打包到最终的输出文件中,所以确保在部署应用时,HTML 页面可以正常访问到这些 CDN 资源。

完成以上配置后,重新运行项目,你将会看到 Vue、Vue Router 和 Vuex 通过 CDN 引入,而不是打包到最终的输出文件中。

内网:如果你的应用是在内网环境中运行,无法直接通过 CDN 引入外部资源,你可以使用以下方法进行处理:

  1. 本地引入:将需要的 Vue、Vue Router 和 Vuex 文件下载到本地,并放置在你的项目目录中。然后在 vue.config.js 中配置本地引入路径:
javascriptCopy code
module.exports = {
  chainWebpack: config => {
    config.resolve.alias
      .set('vue$', path.resolve(__dirname, 'path/to/vue.min.js'))
      .set('vue-router$', path.resolve(__dirname, 'path/to/vue-router.min.js'))
      .set('vuex$', path.resolve(__dirname, 'path/to/vuex.min.js'));
  }
};

'path/to/vue.min.js''path/to/vue-router.min.js''path/to/vuex.min.js' 替换为相应的本地文件路径。

  1. 使用本地服务器:在内网环境中搭建一个本地服务器,将 Vue、Vue Router 和 Vuex 文件托管在该服务器上。然后在 vue.config.js 中配置本地服务器地址:
javascriptCopy code
module.exports = {
  chainWebpack: config => {
    config.plugin('html').tap(args => {
      args[0].cdn = {
        css: [],
        js: [
          'http://localhost:8080/path/to/vue.min.js',
          'http://localhost:8080/path/to/vue-router.min.js',
          'http://localhost:8080/path/to/vuex.min.js'
        ]
      };
      return args;
    });
  }
};

'http://localhost:8080/path/to/vue.min.js''http://localhost:8080/path/to/vue-router.min.js''http://localhost:8080/path/to/vuex.min.js' 替换为你本地服务器的地址和文件路径。

  1. 使用内部 CDN:如果你的内网环境中有一个内部 CDN 或类似的资源服务器,将 Vue、Vue Router 和 Vuex 文件上传到该服务器,并在 vue.config.js 中配置内部 CDN 地址:
javascriptCopy code
module.exports = {
  chainWebpack: config => {
    config.plugin('html').tap(args => {
      args[0].cdn = {
        css: [],
        js: [
          'http://internal-cdn/path/to/vue.min.js',
          'http://internal-cdn/path/to/vue-router.min.js',
          'http://internal-cdn/path/to/vuex.min.js'
        ]
      };
      return args;
    });
  }
};

'http://internal-cdn/path/to/vue.min.js''http://internal-cdn/path/to/vue-router.min.js''http://internal-cdn/path/to/vuex.min.js' 替换为你内部 CDN 的地址和文件路径。

三、基础的 Web 技术优化

要在前端开启 GZIP 压缩,你可以通过配置服务器来实现,或者使用构建工具来生成已经压缩的文件。下面是两种常见的方法:

3.1 开启Gzip压缩

1. 服务器配置

在服务器端进行 GZIP 压缩配置,当浏览器请求文件时,服务器会自动压缩响应内容。

如果你使用的是 Apache 服务器,你可以通过在服务器配置文件(如 httpd.conf)或虚拟主机配置中添加以下代码来开启 GZIP 压缩:

bashCopy code
# 开启 GZIP 压缩
<IfModule mod_deflate.c>
    SetOutputFilter DEFLATE
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/json
</IfModule>

如果你使用的是 Nginx 服务器,你可以在服务器配置文件或虚拟主机配置中添加以下代码来开启 GZIP 压缩:

bashCopy code
# 开启 GZIP 压缩
gzip on;
gzip_types text/html text/plain text/xml text/css application/javascript application/json;

配置完成后,服务器会自动对相应的文件类型进行 GZIP 压缩,并在响应时添加相应的压缩头信息。

2. 构建工具配置

另一种方法是使用构建工具来生成已经压缩的文件。例如,如果你使用 Webpack 进行项目构建,可以通过配置插件来生成 GZIP 压缩的文件。

首先,安装 compression-webpack-plugin 插件:

bashCopy code
npm install compression-webpack-plugin --save-dev

然后,在你的 Webpack 配置文件中添加以下代码:

javascriptCopy code
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  // ...其他配置...
  plugins: [
    new CompressionPlugin({
      filename: '[path].gz[query]',
      algorithm: 'gzip',
      test: /.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8
    })
  ]
};

上述配置会将符合条件的文件进行 GZIP 压缩,并生成以 .gz 结尾的压缩文件。

这样,在构建时,Webpack 会自动为指定类型的文件生成已经压缩的版本,并将它们存储在输出目录中。然后,你可以通过服务器配置,让服务器在接收到请求时返回已经压缩的文件。

无论是服务器配置还是构建工具配置,开启 GZIP 压缩都可以显著减小文件大小,提高前端资源加载速度。

3.2使用缓存机制(强缓存和协商缓存)

具体的缓存配置和使用时机会依赖于你使用的服务器和框架。下面是一些通用的指导原则:

1. 强缓存配置:

  • 如果你使用 Apache 服务器,可以在服务器配置文件(如 httpd.conf)或虚拟主机配置中使用 ExpiresCache-Control 字段来配置强缓存。
  • 如果你使用 Nginx 服务器,可以在服务器配置文件或虚拟主机配置中使用 expiresadd_header 字段来配置强缓存。
  • 如果你使用 Node.js 作为服务器端,可以在响应头中设置 Cache-ControlExpires 字段来配置强缓存。

2. 协商缓存配置:

  • 协商缓存需要服务器支持,因此你需要在服务器端进行相应的配置。
  • 服务器端需要设置 Last-Modified 和/或 ETag 字段,用于判断资源是否发生变化。
  • 当服务器收到带有 If-Modified-SinceIf-None-Match 请求头的请求时,需要进行比较判断是否返回 304 Not Modified 响应。

3. 使用时机:

  • 强缓存适用于那些不经常变化的静态资源,例如图片、CSS 和 JavaScript 文件等。这些资源可以设置较长的缓存时间,使浏览器在一定时间内直接使用缓存,减少请求和网络开销。
  • 协商缓存适用于那些可能会经常变化的资源,例如动态生成的内容或频繁更新的数据。通过协商缓存,服务器可以在资源未发生变化时返回 304 响应,告知浏览器使用缓存,减少带宽和服务器负载。

4. 不适用情况:

  • 对于一些需要实时获取最新数据的请求,例如实时股票行情或聊天消息等,缓存可能不适用,因为每次都需要从服务器获取最新的数据。
  • 对于一些需要严格控制安全性和一致性的请求,例如用户个人信息或敏感数据的获取和修改,也不适合使用缓存,以确保数据的准确性和保密性。

当浏览器请求资源时,可以使用两种主要的缓存机制:强缓存和协商缓存。它们在不同的场景下起作用,可以显著减少网络请求并提高网页加载速度。

强缓存是指在一定时间内,浏览器直接使用本地缓存副本,而不发送请求到服务器。这是通过设置响应头中的缓存控制字段来实现的。常见的缓存控制字段包括:

  • Expires:指定资源的过期时间,由服务器返回一个具体的过期日期/时间。例如:Expires: Fri, 01 Jan 2024 00:00:00 GMT
  • Cache-Control:指定资源的缓存策略。常见的值包括 public(允许任何缓存),private(仅允许私有缓存),max-age(缓存的最大有效时间)等。例如:Cache-Control: max-age=3600

下面是一个强缓存的示例场景:

假设用户首次访问了一个网站,并下载了 main.js 脚本文件。服务器返回的响应头中设置了强缓存策略,比如 Cache-Control: max-age=3600 或者 Expires: Fri, 01 Jan 2024 00:00:00 GMT

当用户再次访问网站时,浏览器会检查缓存,并根据缓存策略判断是否使用缓存。如果缓存仍然有效(在缓存有效期内),浏览器将直接使用本地缓存的 main.js 文件,而不发送请求到服务器。这样可以大大减少网络请求,加快页面加载速度。

协商缓存是指浏览器发送一个请求到服务器,检查资源是否已经发生变化。如果资源没有变化,则服务器返回一个 304 Not Modified 响应,并告知浏览器可以使用缓存副本。这是通过使用请求头和响应头的字段来实现的。常见的字段包括:

  • 请求头中的 If-Modified-Since:浏览器发送请求时将上次缓存的响应头中的 Last-Modified 字段值发送给服务器。
  • 请求头中的 If-None-Match:浏览器发送请求时将上次缓存的响应头中的 ETag 字段值发送给服务器。
  • 响应头中的 Last-Modified:服务器返回的响应头中包含资源的最后修改时间。
  • 响应头中的 ETag:服务器返回的响应头中包含一个表示资源唯一标识的字符串。

下面是一个协商缓存的示例场景:

假设用户首次访问了一个网站,并下载了 styles.css 样式文件。服务器返回的响应头中设置了协商缓存的信息,比如 Last-Modified: Wed, 01 Jan 2023 00:00:00 GMTETag: "abc123"

当用户再次访问网站时,浏览器发送请求,并将上次缓存的响应头中的 Last-ModifiedETag 值发送给服务器。服务器收到请求后,会根据这些值判断资源是否发生了变化。如果资源未发生变化,服务器返回一个 304 Not Modified 响应,并告知浏览器可以使用缓存的 styles.css 文件。浏览器接收到响应后,会使用本地缓存的文件,而不是重新下载。

通过强缓存和协商缓存机制,可以在合适的情况下减少网络请求,提高网页加载速度,节省带宽消耗。