首屏做为直面用户的第一屏,其重要性不言而喻,如何加快加载的速度是很是重要的一课。javascript
本文讲解的是:笔者对本身搭建的我的博客网站的速度优化的经历。css
效果体验地址:http://biaochenxuying.cnhtml
2018 年 8 月,百度搜索资源平台发布的《百度移动搜索落地页体验白皮书 4.0 》中提到:页面的首屏内容应在 1.5 秒内加载完成。前端
也许有人有疑惑:为何是 1.5 秒内?哪些方式可加快加载速度?如下将为您解答这些疑问!java
移动互联网时代,用户对于网页的打开速度要求愈来愈高。百度用户体验部研究代表,页面放弃率和页面的打开时间关系以下图所示:node
根据百度用户体验部的研究结果来看,普通用户指望且可以接受的页面加载时间在 3 秒之内。若页面的加载时间过慢,用户就会失去耐心而选择离开,这对用户和站长来讲都是一大损失。react
百度搜索资源平台有 “闪电算法” 的支持,为了可以保障用户体验,给予优秀站点更多面向用户的机会,“闪电算法”在 2017 年 10 月初上线。webpack
闪电算法 的具体内容以下:nginx
移动网页首屏在 2 秒以内完成打开的,在移动搜索下将得到提高页面评价优待,得到流量倾斜;同时,在移动搜索页面首屏加载很是慢(3 秒及以上)的网页将会被打压。git
未优化以前,首屏时间竟然大概要 7 - 10 秒,简直不要太闹心。
开始分析问题,先来看下 network :
主要问题:
我还用了 Lighthouse 来测试和分析个人网站。
Lighthouse 是一个开源的自动化工具,用于改进网络应用的质量。 你能够将其做为一个 Chrome 扩展程序运行,或从命令行运行。 为 Lighthouse 提供一个须要审查的网址,它将针对此页面运行一连串的测试,而后生成一个有关页面性能的报告。
未优化以前:
上栏内容分别是页面性能、PWA(渐进式 Web 应用)、可访问性(无障碍)、最佳实践、SEO 五项指标的跑分。
下栏是每个指标的细化性能评估。
再看下 Lighthouse 对性能问题给出了可行的建议、以及每一项优化操做预期会帮咱们节省的时间:
从上面能够看出,主要问题:
知道问题所在就已经成功了一半了,接下来便开始优化之路。
网页速度优化的方法实在太多,本文只说本次优化用到的方法。
本项目前端部分是用了 react 和 antd,可是 webpack 用的仍是 3.8.X 。
由于 webpack4 对打包作了不少优化,好比 Tree-Shaking ,因此我用最新的 react-create-app 重构了一次项目,把项目升级了一遍,全部的依赖包都是目前最新的稳定版了,webpack 也升级到了 4.28.3 。
用最新 react-create-app 建立的项目,不少配置已是很好了的,笔者只修改了两处地方。
// Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; // 把上面的代码修改成: const shouldUseSourceMap = process.env.NODE_ENV === 'production' ? false : true;
生产环境下,打包去掉 SourceMap,静态文件就很小了,从 13M 变成了 3M 。
{ test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/,/\.jpg$/,/\.svg$/], loader: require.resolve('url-loader'), options: { limit: 40000, // 把默认的 10000 修改成 40000 name: 'static/media/[name].[hash:8].[ext]', }, }
好比以前可能以为会有用的文件,后面发现用不到了,注释或者删除,好比 reducers 里面的 home 模块。
import { combineReducers } from 'redux' import { connectRouter } from 'connected-react-router' // import { home } from './module/home' import { user } from './module/user' import { articles } from './module/articles' const rootReducer = (history) => combineReducers({ // home, user, articles, router: connectRouter(history) })
把一些静态文件再用 photoshop 换一种格式或者压缩了一下, 好比 logo 图片,本来 111k,压缩后是 23K。
首页的文章列表图片,修改成懒加载的方式加载。
以前由于不想为了个懒加载功能而引用一个插件,因此想本身实现,看了网上关于图片懒加载的一些代码,再结合本项目,实现了一个图片懒加载功能,加入了 事件的节流(throttle)与防抖(debounce)。
代码以下:
// fn 是事件回调, delay 是时间间隔的阈值 function throttle(fn, delay) { // last 为上一次触发回调的时间, timer 是定时器 let last = 0, timer = null; // 将throttle处理结果看成函数返回 return function() { // 保留调用时的 this 上下文 let context = this; // 保留调用时传入的参数 let args = arguments; // 记录本次触发回调的时间 let now = +new Date(); // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值 if (now - last < delay) { // 若是时间间隔小于咱们设定的时间间隔阈值,则为本次触发操做设立一个新的定时器 clearTimeout(timer); timer = setTimeout(function() { last = now; fn.apply(context, args); }, delay); } else { // 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应 last = now; fn.apply(context, args); } }; } // 获取可视区域的高度 const viewHeight = window.innerHeight || document.documentElement.clientHeight; // 用新的 throttle 包装 scroll 的回调 const lazyload = throttle(() => { // 获取全部的图片标签 const imgs = document.querySelectorAll('#list .wrap-img img'); // num 用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出 let num = 0; for (let i = num; i < imgs.length; i++) { // 用可视区域高度减去元素顶部距离可视区域顶部的高度 let distance = viewHeight - imgs[i].getBoundingClientRect().top; // 若是可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出 if (distance >= 100) { // 给元素写入真实的 src,展现图片 let hasLaySrc = imgs[i].getAttribute('data-has-lazy-src'); if (hasLaySrc === 'false') { imgs[i].src = imgs[i].getAttribute('data-src'); imgs[i].setAttribute('data-has-lazy-src', true); // } // 前 i 张图片已经加载完毕,下次从第 i+1 张开始检查是否露出 num = i + 1; } } }, 1000);
注意:给元素写入真实的 src 了以后,把 data-has-lazy-src 设置为 true ,是为了不回滚的时候再设置真实的 src 时,浏览器会再请求这个图片一次,白白浪费服务器带宽。
具体细节请看文件 文章列表
后端用到的技术是 node、express 和 mongodb。
后端主要问题是接口速度很慢,特别是文章列表的接口,已是分页请求数据了,为何还那么慢呢 ?
因此查看了接口返回内容以后,发现返回了不少列表不展现的字段内容,特别是文章内容都返回了,而文章内容是很大的,占用了不少资源与带宽,从而使接口消耗的时间加长。
从上图能够看出文章列表接口只要返回文章的 标题、描述、封面、查看数,评论数、点赞数和时间便可。
因此把不须要给前端展现的字段注释掉或者删除。
// 待返回的字段 let fields = { title: 1, // author: 1, // keyword: 1, // content: 1, desc: 1, img_url: 1, tags: 1, category: 1, // state: 1, // type: 1, // origin: 1, // comments: 1, // like_User_id: 1, meta: 1, create_time: 1, // update_time: 1, };
一样对其余的接口都作了这个处理。
后端作了处理以后,全部的接口速度都加快了,特别是文章列表接口,只用了 0.04 - 0.05 秒左右,相比以前的 4.3 秒,速度提升了 100 倍,简直不要太爽, 效果以下:
此刻心情以下:
你觉得先后端都优化一下,本文就完了 ?小兄弟,你太天真了,重头戏在后头 !
笔者服务器用了 nginx 代理。
作的优化以下:
通常来讲,软件的漏洞都和版本相关,因此咱们要隐藏或消除 web 服务对访问用户显示的各类敏感信息。
如何查看 nginx 版本号? 直接看 network 的接口或者静态文件请求的 Response Headers 便可。
没有设置以前,能够看到版本号,好比我网站的版本号以下:
Server: nginx/1.6.2
设置以后,直接显示 nginx 了,没有了版本号,以下:
Server: nginx
nginx 对于处理静态文件的效率要远高于 Web 框架,由于可使用 gzip 压缩协议,减少静态文件的体积加快静态文件的加载速度、开启缓存和超时时间减小请求静态文件次数。
笔者开启 gzip 压缩以后,请求的静态文件大小大约减小了 2 / 3 呢。
gzip on; #该指令用于开启或关闭gzip模块(on/off) gzip_buffers 16 8k; #设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。16 8k表明以8k为单位,安装原始数据大小以8k为单位的16倍申请内存 gzip_comp_level 6; #gzip压缩比,数值范围是1-9,1压缩比最小但处理速度最快,9压缩比最大但处理速度最慢 gzip_http_version 1.1; #识别http的协议版本 gzip_min_length 256; #设置容许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。默认值是0,无论页面多大都压缩。这里我设置了为256 gzip_proxied any; #这里设置不管header头是怎么样,都是无条件启用压缩 gzip_vary on; #在http header中添加Vary: Accept-Encoding ,给代理服务器用的 gzip_types text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/opentype font/ttf application/x-font-ttf application/vnd.ms-fontobject image/x-icon; #进行压缩的文件类型,这里特别添加了对字体的文件类型 gzip_disable "MSIE [1-6]\.(?!.*SV1)"; #禁用IE 6 gzip
把上面的内容加在 nginx 的配置文件 ngixn.conf 里面的 http 模块里面便可。
是否设置成功,看文件请求的 Content-Encoding 是否是 gzip 便可。
server { listen 80; server_name localhost; location / { root /home/blog/blog-react/build/; index index.html; try_files $uri $uri/ @router; autoindex on; expires 7d; # 缓存 7 天 } }
我从新刷新请求的时候是 2019 年 3 月 16 号,是否设置成功看以下几个字段就知道了:
注意:上面最上面的用红色圈中的 Disable cache 是不是打上了勾,打了勾表示:浏览器每次的请求都是请求服务器,不管本地的文件是否过时。因此要把这个勾去掉才能看到缓存的效果。
终极大招:服务端渲染 SSR,也是笔者接下来的方向。
一切优化测试的结果脱离了实际的场景都是在耍流氓,并且不一样时间的网速对测试结果的影响也是很大的。
因此笔者的测试场景以下:
优化以后的首屏速度是 2.07 秒。
最后加了缓存的结果为 0.388 秒。
再来看下 Lighthouse 的测试结果:
比起优化以前,各项指标都提高了很大的空间。
优化之路漫漫,永无止境,天下武功,惟快不破。
本次优化的前端与后端项目,都已经开源在 github 上了,欢迎围观。
前端:https://github.com/biaochenxuying/blog-react
后端:https://github.com/biaochenxuying/blog-node
github 博客地址:https://github.com/biaochenxuying/blog
若是您以为这篇文章不错或者对你有所帮助,请给个赞或者星呗,你的点赞就是我继续创做的最大动力。