【优化】h5项目优化实践

2023/05/08 09:47:15

项目架构

本 h5 项目分为两个子项目:服务端项目和客户端项目。

服务端的职责是响应用户对页面的请求,服务端拿到客户端生成的 ejs 文件,注入数据后响应。使用 NestJS 搭建,采用 EJS 模板引擎。

客户端的职责是生成 ejs 模板供服务端使用,采用 webpack 多入口方式打包,每个页面都是单独的入口和出口,最终都被编译为 ejs 文件。这样的模式下一个页面可以根据具体需求使用不同的技术框架实现,现有的页面实现方式有两种:SEO 要求较高的页面,使用 zepto + html 实现;无 SEO 要求的页面使用 Vue + html 实现。

页面展现速度优化方案

减少首屏 http 请求数量

并发的 http 请求会抢占带宽,并且浏览器有 http 最大并发数量限制(一般在 4-10 之间)。

减少小尺寸 chunk 数量

尺寸较小的 chunk(flexible.js、css)可以直接打包到 ejs 文件中。

观察下面这个 chunk 的 http 请求,可以看到建立连接(16ms)比下载内容(3ms)更耗时,像这样的 chunk 完全可以直接打包到页面中,而不是通过额外的 http 请求加载。

小尺寸chunk响应时间

图片懒加载

vue 项目使用 vue-lazyload@1.3.3 实现图片懒加载。

精灵图

合并小图片。

减少页面响应数据尺寸

减少页面响应数据的尺寸可以减少浏览器下载页面数据的时间,从而提高页面展现速度。

模块动态导入

直接导入的模块会被打包到 vendor.js 中(得看具体 webpack 配置),导致单一 chunk 体积过大,而且通常情况下这个很大的 chunk 覆盖率并不高。

低覆盖率chunk

通过动态导入的方式可以让 webpack 在编译时将这个模块分离出来成为单独的 chunk,并只在被用到的时候加载(通过 http 请求)。

  • 组件懒加载
// 直接导入
import Address from "@/components/Address";

// 动态导入
const Address = () =>
  import(/* webpackChunkName: "c.Address" */ "@/components/Address");

需要注意,未动态判断组件的情况下,会直接加载组件。

<template>
  <!-- 直接加载组件 -->
  <Address />

  <!-- isShowAddress 变为true后加载组件 -->
  <Address v-if="isShowAddress" />
</template>
  • 模块懒加载
// 直接导入
import Http from "@/services/Http";

// 动态导入
const { default: Http } = await import(
  /* webpackChunkName: "s.Http" */ "@/services/Http"
);

提高服务端响应速度

减少服务端的同步接口请求

在服务端发送同步接口请求,会提高页面响应时间,因此尽可能将同步请求放到客户端去实现。

静态化 SSR

浏览器侧优化

dns 预解析

<head>
  <!--告诉浏览器开启 DNS 预解析-->
  <meta http-equiv="x-dns-prefetch-control" content="on" />
  <!--添加需要预解析的域名-->
  <link rel="dns-prefetch" href="https://xxx" />
</head>

预加载页面

<head>
  <link rel="prefetch" href="https://main.m.taobao.com/search/index.html" />
</head>

参考

浏览器同域名请求的最大并发数限制open in new window

preload、prefetch、preconnect 和 dns-prefetch 知多少open in new window

淘宝承接页是如何实现秒开的open in new window