前端网页优化

288 阅读7分钟

网页越来约炫酷,往往请求的资源越来越多,耗费的流量越来越大,似乎不要钱一样。作为前端开发,有哪些技术手段可以帮助用户快速打开并展现页面呢?

1. 一个很慢站点

现在的网速似乎越来越快了,流量不要钱一样,无意间打开了网站,以为是直播业务,没想到是游戏,页面中央大大的数字一直在走,从 1% 到 100%,等了 1 分多钟,打开控制台看了看,好像没有 http 请求。

两分钟后,页面显示出来了,然后全是视频,从几 KB 到 几 MB

网络请求

如果我是产品经理,我是不能接受这样的设计,一个页面将近 100MB 的流量,也没有懒加载。

视频没有点击,不可以搞个 poster 占位符吗?

从前端开发的角度,静态资源大小要控制(上图 100MB),请求数量要控制(上图 109 个 http 请求)

2. 轻量级优化

打开千寻位置网首页( www.qxwz.com ),滚动完整个页面,等全部资源加载完成,约 5.5MB,如果第二次访问,只有接口动态数据约 520KB 

千寻官网

千寻官网优化,经历了公司2015年成立到现在的多个版本的迭代:

  • 2015年公司成立,从成立前的筹备阶段,已经开始策划公司的门面“官网”,第一版找外包来开发的,纯静态 HTML 页面
  • 2016-2017 年,成立了前端小组,在纯静态页面的基础上,改成 jade 渲染,通过 node 服务托管,记得当时是:
  1. 服务器端用“自研”的 node 框架,包含 master-slave 等复杂的脚本;
  2. 浏览器端,传统 jQuery 事件,部分服务器端渲染,部分客户端渲染,有自己的 jss、css、jade/html 压缩脚本
  3. 封装了 grunt + spirit,还有一个 grunt-jade-replace 插件,我还改过两次 bug
  • 2018年,前端准备用 cdn,静态资源统一替换成 cdn 域名 static.qxwz.com,回源到 ECS 服务器
  1. 静态资源还是用 node 托管,在服务器短渲染了 html 骨架和部分用户信息,商品信息的拼接
  2. 有些资源在多个子域名(一般来说,每个子域名对应一个子应用)公用,比如公共头部导航、右上角用户菜单、底部导航
  3. 启动了一个 nyx-partial 的应用,提供渲染好的 html 给其他应用调用(也是 node + jade),可以 node 引用再嵌入 html,也可以浏览器页面引用(用 document.write,看得出是很古老的技术了吧)
  • 2019年准备做页面级别的前后端分离,先尝试了用户中心的改版,不需要搜索引擎收录,完全的 SAP 模式
  1. 开发采用了 node(api proxy)+ vue(static html、js、css)
  2. 还在多次的尝试服务器端的 SSR(准备用于官网,搜索引擎要收录嘛。SSR 返回渲染好的html,用户访问也会快很多,没有白屏时间)
  3. 针对 node 服务,也自研了一整套框架,类似 egg.js/nest.js/midway,包含功能强大的 router、controller、service、plugin、middleway 机制
  • 2020年全面开启纯静态
  1. 所有子站点(包含主站官网)等,都使用 React 重新开发,资源托管在 ecs,通过 nginx 映射,区分静态资源和 api 请求
  2. 开始探索前端 CICD,优化 webpack 打包(完全废弃了 grunt)
  3. 前端也开始做数据存储的工作,而不是简单的调接口,开发 CMS、CCMS,配置网站常用的文本、图片、视频等
  4. 准备做接口聚合层,花了不少时间做前端 gateway,裁剪、聚合、封装后端的Java http API
  5. 服务器端渲染放弃,通过 nginx 判断是否为搜索引擎,进行 prerender 预渲染,“投食”给爬虫
  • 2021 年开启 CDN 加速、前端微服务
  1. 前端接触了 CICD 后,发布的频率提高,资源直接“增量”托管到阿里云 OSS,服务不需要重启,无缝升级
  2. 前端大部分应用采用 OSS/CDN + 后端 Java API 透穿,节省了大量服务器端资源
  3. 部分业务比较复杂,需要 node 处理,开始用 Nest.js + GraphQL
  4. 后端研发升级原子接口,他们不处理用户态等信息,促成了 BFF(backend for frontend) 
  5. 在 BFF 的基础上,封装前端 node 微服务,管理多个应用和模块
  6. 前端多个应用,也尝试通过微服务拼接在一起,是用 qiankun(single-spa)

3. 前端优化要点

3.1 缓存策略

一般都会把静态资源托管在 oss,通过 cdn 域名访问。静态资源可以设置缓存时间,优先级从高到低:

  1. 缓存开关:
  • Pragma:值为 no-cache 时,表示禁用缓存,已经逐步抛弃,有些网站为了向下兼容还保留了这个字段。
  • cache-control:符合缓存策略时,服务器不会发送新的资源(但客户端还是可能会发请求到服务器)
  1. 缓存校验:
  • expires:值是一个GMT时间,表示该缓存的有效时间
  • last-modified:服务端在返回资源时,会将该资源的最后更改时间通过 Last-Modified字段返回给客户端。客户端下次请求时通过If-Modified-Since或者If-Unmodified-Since带上Last-Modified,服务端检查该时间是否与服务器的最后修改时间一致:如果一致,则返回304状态码,不返回资源;如果不一致则返回200和修改后的资源,并带上新的时间。
  • etag:服务器通过某个算法对资源进行计算,取得一串值(类似于文件的md5值),之后将该值通过etag返回给客户端,客户端下次请求时通过If-None-Match或If-Match带上该值,服务器对该值进行对比校验:如果一致则不要返回资源。

3.2 其他资源压缩

HTML 也要压缩、图片、甚至页面不可见区域懒加载,可以通过 nginx 做gzip。

下图,网易的 HTML 居然存在大段大段的没用代码,难道是 HTML 模版,好像不是?

任何时候,都要删除代码中,大段大段的不用的代码,以后也不会有人看了,不是有 git 嘛,保留了历史记录。

3.3 Cache-Control 扩展

下面图表来自:blog.csdn.net/u012375924/…

在响应中使用,在请求中也可以使用,开发者工具勾选 Disable cache 时,请求头就会带上 “Cache-Control: no-cache”。

在Cache-Control 中,值可以自由组合,no-store优先级最高。

在请求中使用Cache-Control 时,它可选的值有:

在响应中使用Cache-Control 时,它可选的值有:

cache-control的各个值关系如下图:

优化效果

经过前面几年的迭代,达到了现在的 SAP 单页前后端分离模式。 你会发现这种模式会越来越流行,infoQ 中国 经过改编,也是这种模式。

  1. 前端开发是 Vue/React 等,打包后的资源 HTML、JS、CSS、IMG 等,直接上传到 OSS
  2. 这些静态资源配置 CDN 域名,通过 CDN 域名访问的时候,有 1 年的缓存
  3. 后端资源和入口 index.html 文件没有缓存,因为入口 index.html 加载了不同后缀的静态资源
  4. 入口文件托管在 OSS 时,有环境标识,如 index.test.html, index.pro.html

Nginx 配置

server {
    listen 80;
    server_name www.qxwz.com;

    # 接口有统一前缀
    location /api {
        proxy_pass http://x2.x2.api.ip;
    }

    # 入口文件,其他资源都是通过 cdn 域名访问,配置在打包的 PUBLIC_PATH 中
    location / {
        add_header Cache-Control "no-cache, no-store";
        rewrite .* /portal/index.pro.html break;
        proxy_set_header Host portal.oss-cn-beijing-internal.aliyuncs.com;
        proxy_pass http://portal.oss-cn-beijing-internal.aliyuncs.com;
    }
}

最后

从用户发出请求到渲染完整页面给用户,时间越短越好,链路(经过的节点)越简单越少越好,服务越稳定越好,为了达到这个目的,我们经过了几年的探索,不断造轮子,不断摸索中前进,希望给用户带来更好的体验,更少的成本,更多的收益,同时能够做到排查问题更直接,数据流转更清晰。