浅谈自己知道的首屏加载时间的优化策略

    |     2020年4月17日   |   web前端技术, 优化   |     0 条评论   |    1155

SPA 已成主流,Angular、React、Vue 三分天下。单页应用首屏慢、不利于 SEO,所以 vue / react 全家桶都会把服务端渲染当成路由落地的选项之一。

服务端渲染的意思是:用户第一次请求时,服务器把组件渲染成 HTML 字符串再返回。客户端拿到的就是可直接呈现的 HTML,不必先跑一遍 JS 再生成 DOM。源码里能看到页面上的内容,也就是“所见即所得”。

客户端渲染和 SSR 本质都是字符串拼接:把数据填进固定 HTML。拼在服务器上就耗 server;拼在浏览器上,常见做法是插 DOM 或用模板引擎替换 {{text}} 一类标记。用过框架都知道,SPA 最大短板就是首屏时间长,下面按我实际踩过的坑整理五条策略。

策略 核心收益 主要代价
缩小打包体积 少传 JS,解析更快 要按需引入、会查依赖
服务端渲染 SSR 首屏有内容、利于 SEO 必须 Node,配置重
预渲染 静态 HTML 先出来,配置相对简单 页面多时 build 很慢
gzip 传输体积可压到约 30% 要服务器配合
路由/组件懒加载 首包只含当前页 请求次数变多,靠缓存兜底

一、缩小打包体积

生产依赖尽量少,按需引用,别让无用代码进包。刚开始优化时往往不知道哪个库占空间,甚至会挨个删引用试。后来才用上 webpack-bundle-analyzer:图里面积越大,占得越多。

先装插件:

npm install --save-dev webpack-bundle-analyzer

再在 webpack.prod.conf.js 里配置:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [
new BundleAnalyzerPlugin(
     {
          analyzerMode: 'server',
          analyzerHost: '127.0.0.1',
          analyzerPort: 8888, // 运行后的端口号
          reportFilename: 'report.html',
          defaultSizes: 'parsed',
          openAnalyzer: true,
          generateStatsFile: false,
          statsFilename: 'stats.json',
          statsOptions: null,
          logLevel: 'info'
        }
     ),
]

正常 npm run build 结束后会在默认浏览器打开模块组成图。

╬в╨┼╜╪═╝_20200415133730

上图里 vue 模块面积最大,可以按这块下手,不会再无从下手。

再记一个 element-ui 的坑:全局引入时 vendor 至少 700KB。改成局部引入,main.js 类似:

import {
  Autocomplete,
  Checkbox
  ……
} from 'element-ui'
Vue.use(Autocomplete);
Vue.use(Checkbox);

结果体积完全没变。官方文档还要求装 babel-plugin-component,局部引用才生效。看文档一定要细。

二、服务端渲染

这里的 SSR 不是传统后端拼页面,而是把前端框架的一部分 JS 放到服务器上跑,浏览器拿到的就不是空壳 HTML。白屏时间会短,搜索引擎也更容易收录。

局限也很明确:服务端必须是 Node,不能只丢静态服务器。Vue 官方 SSR 配置复杂,从 vue-cli 项目中途改 SSR 很麻烦。真要走 SSR,开工就用 Nuxt.js 更省事。

和客户端渲染对比:首屏只需当前页内容,不必像 React / Vue CSR 那样先拉完整 JS;SEO 方面,搜索引擎也收不到“ajax 拉完数据再动态渲染”的那一版页面。

  • 好处:首屏快、利于 SEO、可做缓存片段和静态化,相对客户端渲染更省电。
  • 坏处:交互体验往往不如 CSR;前端改 HTML/CSS,后端也常要跟着改。

三、预渲染

打包时预先跑一遍 JS,把一部分静态页写成 HTML 塞进生成的 index.html。HTML 加载完就能看到界面,不必等 JS 再渲染。首屏和 SEO 都能改善,配置也比 SSR 简单。

页面很多时 build 会非常慢。常见工具:

prerender-spa-plugin
react-snap
snapshotify
presite

原理差不多:本地跑指定 JS,把无头浏览器渲染出的页面写入目标 HTML。我自己打包时各种报错,几个插件轮了一遍也没得到满意页面——那是我的问题,不是轮子的问题。

最后用了“土办法”:浏览器打开目标页,右键另存为,得到预渲染 HTML。CSS 太多就用 Chrome 插件 css used(开发者工具 Element 里和 Style 并排),只留下用到的 CSS。再把打包生成的 JS/CSS 路径写回去(和正式 index.html 一样)。HTML 先画出预渲染页,依赖加载完后再被正式代码覆盖。

土归土,首屏时间大约短了 50%。也可以先做骨架屏,等 JS/CSS 时只显示框架,比全白屏好受得多。

四、用 gzip 减小传输体积

gzip 最早用于 UNIX 文件压缩。HTTP 上的 gzip 要服务器和浏览器都支持。Chrome、Firefox、IE 以及 Apache、Nginx、IIS 都支持。静态文件常能压到约 30%,对首屏很划算,配置服务器即可。

以 Nginx 为例:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    proxy_buffering on;
开启gzip
	gzip on;
	# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
	gzip_min_length 1k;
	# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间
	gzip_comp_level 2;
	# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
	gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
	# 是否在http header中添加Vary: Accept-Encoding,建议开启
	gzip_vary on;
	# 禁用IE 6 gzip
	gzip_disable "MSIE [1-6]\.";
	server {
        listen       80;
        server_name  localhost;
        location /{
            alias /;
            proxy_connect_timeout 1; 
            proxy_send_timeout 30; 
            proxy_read_timeout 60;
            # 这里反向代理到本地的开发服务器上
            proxy_pass http://localhost:8080/;
         }
    }
}

这样 gzip 就开了。

五、按页面或组件懒加载

每个组件的 JS 独立出去,用到再请求;请求过一次会缓存,再用就走缓存。vue-router 里改一下即可:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router);

export default new Router({
  mode: 'history',
  linkActiveClass: 'router-link-active',
  routes: [
    {
      path: '/',
      // 这里只需要把原来从外部引入的组件换成以下的语句就可以了
      component: resolve => require(['../components/(你的组件)'], resolve)
    },
  ]
})

别处不用改。打包后 JS 文件会比不分块时多不少。

以上是我用过的 SPA 首屏优化。更多优化见 https://www.cnblogs.com/mianbaodaxia/p/10751453.html。

一句话总结:首屏优化是组合拳:先看清包体积,再按场景选 SSR、预渲染、gzip 和懒加载,而不是一上来就改架构。

转载请注明来源:浅谈自己知道的首屏加载时间的优化策略
本文链接地址:https://ai.zhousir.top/?p=2711
回复 取消