前端首屏优化

首屏优化是指在网页加载过程中,尽快展示给用户可见的内容,以提高用户体验和页面加载速度。

压缩和合并资源

通过压缩 CSS、JavaScript 和 HTML 等静态资源文件,并将它们合并为较少的文件,可以减少网络请求次数和文件大小,加快页面加载速度。

压缩

以下是一个示例 Webpack 配置文件,展示如何使用css-minimizer-webpack-pluginmini-css-extract-plugin压缩 CSS 文件和terser-webpack-plugin压缩 JavaScript 文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  // 入口文件
  entry: "./src/index.js",
  // 输出文件
  output: {
    filename: "bundle.min.js",
    path: __dirname + "/dist",
  },
  // 模块加载器
  module: {
    rules: [
      // 处理CSS文件
      {
        test: /\\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      // 处理JavaScript文件
      {
        test: /\\.js$/i,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
          },
        },
      },
    ],
  },
  // 插件
  plugins: [
    new MiniCssExtractPlugin({
      filename: "styles.min.css",
    }),
  ],
  // 优化配置
  optimization: {
    minimizer: [
      // 压缩CSS文件
      new CssMinimizerPlugin(),
      // 压缩JavaScript文件
      new TerserPlugin(),
    ],
  },
};

合并静态资源

index.html文件中,我们将引入压缩和合并后的静态资源文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>React App</title>
    <link rel="stylesheet" href="styles.min.css" />
  </head>
  <body>
    <div id="root"></div>

    <script src="bundle.min.js"></script>
  </body>
</html>

通过上述代码,我们将压缩和合并后的 CSS 文件和 JavaScript 文件引入到了index.html中。这样做可以减少网络请求次数和文件大小,提高页面加载速度。

图片优化

使用适当的图片格式(如 JPEG、PNG、WebP)和压缩算法,对图片进行优化,减小图片文件大小,提高加载速度。同时,可以使用懒加载技术,延迟加载非首屏可见区域的图片,减少首屏的加载时间。

Vue 项目中可以使用 Vue-Lazyload ****快速实现图片懒加载

  • 安装依赖
1
npm i vue-lazyload -S
  • main.js 引入插件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import Vue from "vue";
import App from "./App.vue";
import VueLazyload from "vue-lazyload"; //引入这个懒加载插件

Vue.use(VueLazyLoad, {
  preLoad: 1.3,
  error: require("./assets/img/error.jpg"), // 图片加载失败时默认的显示图片
  loading: require("./assets/img/homePage_top.jpg"), // 图片加载中显示的图片
  attempt: 2, // 尝试次数
});

基础实用案例

1
2
3
4
5
<div v-lazy-container="{ selector: 'img' }">
  <img data-src="//domain.com/img1.jpg" />
  <img data-src="//domain.com/img2.jpg" />
  <img data-src="//domain.com/img3.jpg" />
</div>

更多使用方法及插件配置详见 使用文档

异步加载第三方 JS

将不影响首屏展示的资源(如统计代码、广告等)使用异步加载方式引入,避免阻塞首屏内容的加载。

可以使用 JavaScript 的动态脚本加载技术。通过动态创建<script>标签,并将其插入到文档中,可以实现异步加载资源而不阻塞首屏内容的加载。

举个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function loadExternalScript(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = url;
    script.async = true;

    script.onload = () => {
      resolve();
    };

    script.onerror = () => {
      reject(new Error(`Failed to load script ${url}`));
    };

    document.head.appendChild(script);
  });
}

// 异步加载统计代码
loadExternalScript("<https://example.com/analytics.js>")
  .then(() => {
    // 统计代码加载完成后执行的逻辑
    console.log("Analytics script loaded");
  })
  .catch((error) => {
    console.error(error);
  });

// 异步加载广告代码
loadExternalScript("<https://example.com/advertisement.js>")
  .then(() => {
    // 广告代码加载完成后执行的逻辑
    console.log("Advertisement script loaded");
  })
  .catch((error) => {
    console.error(error);
  });

在上面的代码中,loadExternalScript()函数用于动态加载外部脚本。通过将async属性设置为true,确保脚本以异步方式加载,不会阻塞其他资源的加载。然后,我们可以在then()方法中执行相应的逻辑,以响应脚本加载完成的事件。

预加载关键资源

通过预加载关键资源(如字体文件、重要的 CSS 和 JavaScript 文件等),可以在首屏展示之前提前加载这些资源,加快后续页面加载的速度。

要实现预加载关键资源,可以使用 HTML 中的linkscript标签的rel属性来指定资源的加载方式。

举个示例,演示如何预加载字体文件、CSS 和 JavaScript 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
  <head>
    <title>预加载关键资源示例</title>
    <link
      rel="preload"
      href="font.woff2"
      as="font"
      type="font/woff2"
      crossorigin
    />
    <link rel="preload" href="styles.css" as="style" crossorigin />
    <link rel="preload" href="script.js" as="script" crossorigin />
    <style>
      /* 首屏样式 */
      body {
        font-family: "FontName", sans-serif;
      }
    </style>
    <link rel="stylesheet" href="styles.css" />
    <script src="script.js" defer></script>
  </head>
  <body>
    <!-- 页面内容 -->
  </body>
</html>

在上述示例中,我们使用了link标签来预加载字体文件、CSS 和script标签来预加载 JavaScript 文件。具体解释如下:

  • link标签用于预加载字体文件。href属性指定字体文件的 URL,as属性设置为 “font”,表示该资源是字体文件,type属性指定资源的 MIME 类型,crossorigin属性用于处理跨域请求。
  • 同样地,我们使用link标签预加载 CSS 文件。as属性设置为 “style”,表示该资源是样式表。
  • 最后,我们使用script标签预加载 JavaScript 文件。as属性设置为 “script”,表示该资源是脚本文件,defer属性用于延迟脚本的执行。

通过以上代码,浏览器在解析 HTML 时会提前加载这些关键资源,以加快后续页面加载的速度。

CSS 优化

避免使用过多的 CSS 文件和行内样式,尽量减少 CSS 文件的大小。另外,可以将 CSS 放在页面头部,以便尽早渲染页面。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html>
  <head>
    <title>示例页面</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>欢迎来到示例页面</h1>
    <p>这是一个简单的示例页面。</p>
  </body>
</html>

这样,浏览器在加载页面时会先加载 CSS 文件,并根据 CSS 规则渲染页面,从而提高页面的加载速度和性能。

尽量避免使用行内样式,因为它会增加 HTML 文件的大小,使得页面加载更慢。如果必须使用行内样式,也应该尽量减少其数量和大小,以便提高页面性能。

JavaScript 优化

将 JavaScript 代码放在页面底部,以减少对页面渲染的阻塞。另外,可以通过代码压缩、去除不必要的注释和空格等方式来减小 JavaScript 文件的大小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE html>
<html>
  <head>
    <title>JavaScript代码放在页面底部示例</title>
  </head>
  <body>
    <!-- 页面内容 -->
    <script src="script.js"></script>
  </body>
</html>

示例中,将script标签放在了body标签的最后。这样做可以让浏览器在加载完页面主要内容后再加载 JavaScript 文件,从而减少对页面渲染的阻塞。

缓存策略

合理设置缓存策略,利用浏览器缓存来减少重复加载相同资源的次数,提高页面加载速度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 假设我们有一个名为 "api.js" 的模块,用于发送网络请求

// 缓存对象,用于存储已经请求过的数据
const cache = {};

// 发送网络请求的函数
async function fetchData(url) {
  // 检查缓存中是否存在数据
  if (cache[url]) {
    console.log("从缓存中获取数据:", cache[url]);
    return cache[url];
  }

  // 发送网络请求
  const response = await fetch(url);
  const data = await response.json();

  // 将数据存入缓存
  cache[url] = data;

  console.log("从服务器获取数据:", data);
  return data;
}

// 在页面中使用 fetchData 函数来获取数据
async function getData() {
  const url = "<https://api.example.com/data>";

  try {
    const data = await fetchData(url);
    // 处理数据
    console.log("处理数据:", data);
  } catch (error) {
    console.error("请求失败:", error);
  }
}

// 调用获取数据的函数
getData();

示例中,定义了一个 fetchData 函数来发送网络请求,并使用一个名为 cache 的对象作为缓存来存储已经请求过的数据。

当调用 fetchData 函数时,它首先检查缓存中是否已经存在对应 URL 的数据。如果存在,则直接从缓存中获取数据,并返回。如果不存在,则发送网络请求获取数据,并将数据存入缓存中。

在调用 getData 函数时,它会调用 fetchData 函数来获取数据,并对返回的数据进行处理。

通过这样的缓存策略,当多次调用 getData 函数时,如果 URL 相同,那么后续的请求会直接从缓存中获取数据,而不需要再次发送网络请求。这样可以减少重复加载相同数据的次数,提高页面加载速度和用户体验。

缓存策略需根据项目实际需求而定

服务端渲染(SSR)

缘由

SSR(服务器端渲染)能够提高首屏渲染的原因有以下几点:

  1. 减少客户端渲染时间:在传统的客户端渲染中,浏览器需要下载 HTML 文件,然后执行 JavaScript 代码来生成并填充页面内容。而在 SSR 中,服务器会在响应请求时直接生成完整的 HTML 页面,并包含所需的数据。这样,浏览器只需要下载已经渲染好的 HTML,减少了客户端渲染的时间。
  2. 提前获取数据:在客户端渲染中,通常需要通过异步请求获取数据,然后再进行页面渲染。而在 SSR 中,服务器会在渲染过程中获取所需的数据,并将数据直接注入到 HTML 中。这样可以避免客户端请求数据的延迟,加快了首屏渲染的速度。
  3. 更好的 SEO:由于搜索引擎爬虫更容易解析和索引静态 HTML 页面,使用 SSR 可以提供更好的 SEO 效果。因为在 SSR 中,服务器返回的是已经渲染好的 HTML 页面,而不是由 JavaScript 生成的动态内容。这使得搜索引擎可以更好地理解和索引网页内容,提高了网站在搜索结果中的可见性。
  4. 更好的用户体验:由于 SSR 能够更快地呈现出首屏内容,用户可以更快地看到页面的基本结构和内容,减少了等待时间,提升了用户体验。

可选用框架

前端可以使用一些框架或库来实现服务器端渲染(SSR),下面是几个常用的前端框架:

  1. Next.js:Next.js 是一个基于 React 的 SSR 框架。它提供了简单的 API 和约定,使得构建 SSR 应用变得更加容易。Next.js 支持自动代码分割、预取和缓存,以提高性能。它还提供了静态导出功能,可以将应用程序预渲染为静态 HTML 文件。
  2. Nuxt.js:Nuxt.js 是一个基于 Vue.js 的 SSR 框架。它提供了类似于 Next.js 的功能,可以帮助开发者快速构建 Vue.js 应用的 SSR 版本。Nuxt.js 支持自动路由、代码分割和服务端数据获取等特性。
  3. Angular Universal:Angular Universal 是 Angular 官方提供的 SSR 解决方案。它允许在服务器上预渲染 Angular 应用,以提供更好的性能和 SEO 效果。Angular Universal 可以与 Angular CLI 集成,方便开发者进行构建和部署。

CDN 加速

使用内容分发网络(CDN)来加速静态资源的传输,将资源缓存在离用户较近的服务器上,减少网络延迟。

下面以一个网站为例,说明 CDN 如何工作:

假设有一个名为 www.example.com 的网站,该网站拥有许多静态资源,如图像、CSS 文件和 JavaScript 文件等。这些资源可能存储在网站的主机上,但是由于主机的位置和用户之间的距离不同,因此用户在访问这些资源时可能会遇到网络延迟。

为了解决这个问题,网站所有者可以使用 CDN 服务。CDN 服务通常由多个服务器组成,这些服务器分布在全球各地,每个服务器都缓存了网站的静态资源。当用户请求这些资源时,CDN 会将请求路由到最近的服务器,从而减少网络延迟并提高资源加载速度。

例如,当用户从中国访问 www.example.com 时,CDN 会将用户请求路由到位于中国的服务器。如果该服务器上已经缓存了所需的静态资源,那么用户将立即获得资源。否则,CDN 将从主机上获取资源,并将其缓存到服务器上,以便以后的请求可以更快地获取资源。

总之,使用 CDN 可以提高网站性能,减少网络延迟,提高用户体验。

一般直接在模板中使用script标签src属性引用。 也可以在项目中配置基础路径

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  // 其他配置项...

  output: {
    // 设置输出路径和文件名
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[contenthash].js",
    publicPath: "<https://cdn.example.com/>", // 设置CDN路径
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      // 在HTML模板中使用CDN路径
      cdnPath: "<https://cdn.example.com/>",
    }),
    // 其他插件...
  ],

  // 其他配置项...
};

延迟加载非关键资源

将非关键资源(如广告、推荐内容等)的加载延迟到首屏渲染完成之后,以提高首屏展示速度。