1. 概述
通常来讲前端性能优化是指从用户开始访问我们的网站到整个页面完整的展现出来的过程中,通过各种优化策略和优化方法让页面加载的更快,让用户的操作响应更及时,给用户更好的使用体验。
这里我们介绍的是前端性能优化知识的解决方案,从静态资源优化开始入手,从表象深入体系化的讲解页面渲染架构,掌握搞笑的技术方案。
本文并非细节的讲述如何实现性能优化,而是从各个方面介绍性能优化的方式方法,并且不仅限于H5
,因为当今的前端也不仅仅只有H5
。
jpeg:一种针对彩色照片而广泛使用的有损压缩图形格式。是一种栅格图形,常用文件扩展名为jpg
,jpeg
,jpe
。在互联网上常被应用于存储和传输照片。不适合线条图形和文字,图标图形,因为他的压缩算法不支持这些类型的图形,并且不支持透明度。常用于色彩丰富的照片,彩色图大焦点图banner
等结构不规则的图形。
png:便携式网络图形,是一种无损压缩的位图图形格式,支持索引、灰度、RGB
三种颜色,以及Alpha
通道等特性。他最初的设计是为了代替GIF
,能够支持半透明和透明特性,最高支持24
位彩色图形和8
位灰度图像。不过由于是无损压缩所以文件体积太大。比较适合纯色,透明,线条绘图,图标以及颜色较少的需要半透明的图片。
GIF:位图图形文件格式,8
位色重现真彩色的图像,采用LZW
压缩算法进行编码。支持256
色,仅支持完全透明和完全不透明,可以支持动图,不过每个像素只有8
比特,不适合存储彩色图片。常用与动画和图标。
webp:是一种现代图像格式,可以提供无损压缩和有损压缩两种。可以同时办证一定程序上的图像质量和较小的体积,可以插入多帧,实现动画效果。支持透明度。采用8
位压缩算法,无损的webp
比png
小26%
,有损的webp
比jpeg
小25-34%
,比gif
有更好的动画。不过最多可以处理256
色,不适合彩色图片。常用于图形和半透明图像。
对于png
图片来说,可以使用jdf-png-native
进行压缩, 他是node-pngquant-native
工具的封装包,这个工具跨平台,压缩比高,而且压缩png24
也非常的好。
const pngquant = require('jdf-png-native');
const fs = require('fs');
fs.readFile('./in.png', (err, buffer) => {
if (err) {
throw err;
}
const resBuffer = pngquant.option({}).compress(buffer);
fs.writeFile('./out.png', resBuffer), {
flags:'wb'
}, () => {})
})
压缩jpg
可以使用jpegtran
这个工具,他也是一个node
工具。使用方法比较简单,直接使用命令即可。大概压缩10%
的占比。
jpegtran -copy node -optimize-outfile out.jpg in.jpg
对于gif
文件来说可以使用gifsicle
工具,他是通过改变每帧比例,减小gif
文件大小,同时可以使用透明来达到更小的文件体积。是一个公认的解决方案。可以去http://www.lcdf.org/gifsicle/
中去安装。使用方式同样也是命令行方式。
gifsicle --optimize=3 --crop-transparency -o out.gif in.gif
这里的优化级别不要小于2
,1
的话代表不压缩。压缩后基本不失帧。
还有一种压缩方式是图片可以根据网络环境来展示不同尺寸和像素的图片,通过在url
后缀加不同参数来实现。比如下面的地址,430
可以修改为800
来获得不同体积的图片。
https://img.alicdn.com/imgextra/i1/2616970884/O1CN01x6HnoK1IOuj5IosXO_!!2616970884.jpg_430x430q90.jpg
响应式图片是我们可以在用户不同的窗口大小还有设备像素的情况下来展示不同大小的图片,可以用以下三种方式来实现, 第一种是可以使用js
来绑定事件检测窗口大小,以此来设置图片的大小。第二种方式就是css
的媒体查询。
@media screen and (max-width:640) {
my_image { width:640px; }
}
第三种可以使用html5
的srcset
来设置,他会根据设备的像素比来自动选择需要的图片。而且不支持srcset
的浏览器也可以正常展示src
的属性。
<img srcset="img-320w.jpg, img-640w.jpg 2x, img-960w.jpg 3x" src="img-960w.jpg" alt="img" />
其实就是延迟加载,在真实的图片加载出来之前,可以使用一张公共的图片,一般是公司的logo
,先将布局撑起来,然后再换成真实的图片。
lqip
这个工具可以将真实的图片虚化,转换为很小的base64
编码。这样我们可以先使用base64
加载虚化的图片。
npm install lqip
const lqip = require('lqip');
const file = './in.png';
// image
lqip,base64(file).then(res => {
console.log(res); // 输出base64
})
// color
lqip.paletter(file).then(res => {
console.log(res); // 图片颜色值
})
也可以使用低质量图片占位符, 他是基于SVG的图像占位符实现的。
npm install sqip
const sqip = require('sqip');
const result = sqip({
filename:'./in.png',
numberOfPrimitives:10 // 效果值
});
console.log(result.final_svg); // 输出svg格式
相比lqip
来说sqip
效果会好很多,而且可以设置不同的大小。
可以使用web font
来代替图片,比如说小图标等业务小图片。
也可以用dataurl
的方式,也就是前面的base64
的方式来代替图片,这样用户就不需要发送http
请求了。
也可以采用雪碧图将多个小图片合成一个大图,这样也会节省很多的图片请求。
图片服务器优化是指可以在图片url
连接上增加不同特殊参数,让服务器自动生成不同格式,大小,质量的图片。
比如说可以对图片做一些裁剪,裁剪成我们需要的图片,也可以支持不同格式的转换,比如说jpg
,gif
,png
,webp
等也可以设置图片的压缩比。
也可以对图片添加一些水印,高斯模糊,重心处理等还可以增加一些AI的能力,比如说用户上传的图片是否涉黄。还可以通过智能抠图,智能排版,智能配色智能合成等功能完善图片。
可以减少html
的嵌套也就是层级关系尽量减小,也可以减小DOM
节点数也就是尽量压缩优化DOM
的节点数, 让浏览器渲染的DOM
节点数最少。
减少一些无语义的代码,比如说空标签清浮动那种代码<div class="clear"></div>
能不用最好不要用。
建议连接中删除http
或者https
,因为一般链接的协议头和页面的协议头都是一致的,写他们多了4-5
个字符其实是没有什么意义的。而且可以减少代码体积。
也可以删除多余的空格,换行符,缩进和不必要的注释,一般会用压缩工具来处理这个过程。可以省略一些标签和属性。使用相对路径的url
,最大范围的减少字节数。
css
文件链接尽量放在页面头部,css
加载不会阻塞DOM Tree
解析,但是会阻塞DOM Tree
渲染,也会阻塞后面js
执行。也就是说DOM Tree
在渲染前就要解析好CSS
,从而减少浏览器重排文档的次数。而且css
放在页面底部会导致页面白屏时间变长。
js
文件一般放在页面底部,这是防止js
的加载和解析阻塞页面元素的正常渲染。
设置favicon.ico
, 如果不设置控制台会报错,而且用户访问的时候地址栏也是空的,不利于品牌记忆。
增加首屏必要的css
和js
,一般页面需要在等待所有的依赖加载完成才会展示,这样就会导致页面存在空白。永祥用户体验,可以增加背景图或者loading
或者骨架屏,比空白页好很多。
谨慎使用一些expensive
的属性,比如nth-child
伪类或者position:fixed
定位,因为这些比较消耗浏览器的渲染性能。
尽量减少一些样式层级的级数,比如,div ul li span i { color:red}
, 其实我们可以给i标签设置class
,直接书写样式。
避免使用占用过多cpu
和内存的属性,比如text-indent
不要设置太大的值。
尽量避免使用耗电量大的属性,比较占用GPU
, 比如transfrom
是,transitions, opacity
。
合适的使用css
选择器, 尽量避免使用通配符,避免使用css
表达式。color:expression((new Date()).getHours() % 2 ? "#fff" :"#000")
。
避免类正则的属性选择器。*=
,|=
,^=
,$=
,使用外链的css
,可以单独形成文件放在cdn
,使用缓存形式加载。避免使用@import
因为他的加载会阻塞进程,需要加载完毕才会向下执行。
精简css
代码,使用缩写的语句,比如margin-top
可以写在margin
中,再者如果值为0
能删除就删除,删除不必要的单位值,删除过多的分号,删除空格和注释。尽量减小样式表的体积。其实这些都可以使用压缩工具来处理,会方便很多。
可以将字体文件部署到cdn
上,加快用户端的加载速度,也可以将字体以base64
的形式保存在css
中,并通过localStorage
进行缓存。一些谷歌字体库应该使用国内托管服务不要直接使用源地址。
避免同时动画,也就是说用户访问的屏幕区间里面不要有过多的动画,动画太多会干扰用户正常浏览网站,而且动画多也影响浏览器的性能。
延迟动画的初始化,可以让其它css
先渲染,让动画延迟,比如说0.5
或1
。
可以借助svg
去展示动画,样式放在css
里面控制。
首先我们是当需要的时候才去优化,不是为了优化而优化,一般的优化是在某一个时间点进行的,而且优化也需要考虑可维护性这是要结合团队的研发水平和代码的规范。
这个基本每个人都知道,就是css
文件放在head标签中,js
文件放在body
结尾的地方。这个是js
的加载不要影响html
的渲染。
尽量使用id
选择器,因为id
选择器在查询效果上效率最快。
避免使用eval
,这个方法比较消耗能行。
js
函数尽可能保持简洁,不要把太多内容写在一个函数中。也建议使用事件的节流函数。事件委托等等。
尽量避免添加大量的js
动画,css3
动画和canvas
动画都比js
动画性能好。
使用requestAnimationFrame
来代替setTimeout
和setInterval
,因为requestAnimationFrame
可以在正确的时间进行渲染,setTimout
和setInterval
无法保证渲染时机。不要在定时器里面绑定事件。
缓存dom
对象,也就是用一个变量来存储do
m对象,不要每次使用都查询。
缓存列表长度,也就是说用变量存储dom
元素的个数,而不是每次都重新计算。
比如百度M
站,会把页面的css
和js
放在本地存储里面,这样后面再加载的时候就直接从本地存储里面取,实现秒考的效果。不过本地存储空间有限,要谨慎使用。
避免过多的样式嵌套,最好可以快速的定位到元素。
避免使用css
表达式,css
表达式会在css
绘制的过程中都会执行,会增加重排和回流的次数。
可以使用绝对定位让动画元素脱离文档流。
避免使用table
布局他会引起浏览器的多次重绘,也不要使用float
布局。
图片最好设置好设置width
和height
,这样图片在加载之后布局就可以确定了。
简化浏览器不必要额任务,使用viewport
设置屏幕缩放级别。
避免频繁设置样式,将多个样式操作合并修改,一次性的更新。
为了减少回流发生次数,应该避免频繁操作DOM
,可以合并多次对DOM
的修改,一次性批量处理。
控制绘制过程和绘制区域,绘制过程开销比较大的属性设置应当避免使用。
众所周知,页面交互卡顿和流畅度很大一部分原因就是页面有大量DOM
元素,想想一下,从一个上万节点的DOM
树上,使用querySelectorAll
或getElementByTagName
方法查找某一个节点,是非常耗时的,另外元素绑定事件时,事件冒泡和事件捕获的执行也会相对耗时。所以一般我们应该合理的不熟业务逻辑,DOM
节点过多时应该延迟即将呈现的DOM
内容。
对DOM
的操作最好统一处理后再统一插入到DOM Tree
中。可以使用fragment
对DOM
和样式设置好再统一放到页面中去。
目前比较流行的框架,比如Angular
,React
和Vue
都是使用虚拟DOM
技术,通过diff
算法简化和减少DOM
操作。
html-minifier:压缩html
clean-css:css
的压缩工具
uglify-js:js
文件的压缩工具
首先浏览器会解析HTML
生成DOM Tree
,然后解析CSS
生成CSSOM Tree
。接着JS
会通过DOM Api
和CSSOM Api
来操作DOM Tree
和CSS Rule Tree
将DOM Tree
和CSSOM Tree
合成一颗渲染树Render Tree
。
根据生成的渲染树进行回流,以计算每个节点的几何信息,包括位置,大小,样式等等。然后根据渲染树和回流得到的几何信息,得到每个节点上的绝对像素。
最后将像素发送给图片处理器也就是GPU
进行页面展示。
前端页面渲染可以分为服务端渲染和客户端渲染。服务端渲染有传统的后端同步渲染,同构直出比如php
,java
,.net
或者大家熟悉的node
。
客户端渲染也就是js
渲染,前后端分离,单页面应用。react
,vue
,ios
,安卓
,hybird app
,flutter
等。
懒加载也叫延迟加载,指的是长网页中延迟加载特定元素,可以是图片也可以是js
和css
。懒加载的好处是可以减少当前屏无效资源的加载。
一般我们会把img
标签的src
属性设置为空字符串,真实的图片地址放在data-lazy
中,当页面scroll
到对应的位置时再通过DOM
操作将src
的值替换为data-lazy
的值。
预加载是让浏览器预先加载某些资源,同样也是图片,js
或者css
,这些资源是在将来才会被使用的。
简单来说就是讲所需要的资源提前加载到浏览器本地,后面在需要的时候可以直接从浏览器的缓存中获取,而不用再重新开始加载。好处是减少用户后续加载资源等待的时间。
可以使用new Image
的方式也可以使用标签的方式preload
,prefetch
,preconnect
<link rel="preload" href="src/style.css" />
<link rel="prefetc" href="src/image.png" />
<link rel="dns-prefetch" href="https://zhiqianduan.com" /> <!-- 提前将dns缓存-->
<link rel="preconnect" href="https://zhiqianduan.com" /> <!-- 提前加载需要的资源 -->
另一种预加载组件的方式就是提前渲染它,在页面中渲染组件,但是并不在页面中展示,也就是渲染好后先隐藏起来,用的时候再直接展示。可以使用prerender
将https://zhiqianduan.com
页面先提前渲染好。
<link rel="prerender" href="https://zhiqianduan.com" />
原生的webview
对于IOS
来说有两种,一种是UIWebView
,他从IOS2
开始就作为App
内展示web
内容的容易,而且排版布局能力比较强。
不过UIWebView
也有很多的问题,比如说内存泄漏,运行期间会有极高的内存峰值,Touch Delay
延迟300毫秒。js
运行性能不高,在2018
年的ios12
以后就不再维护了。
WKWebView
是苹果在WWDC 2014
上推出的新一代WebView
组件,WKWebView
的内存开销比UIWebView
要小很多,而且在性能,稳定性,内存占用方面都有很大提升。可以实现60fps
的滚动刷新率,自身就支持了右滑返回手势,支持更多的HTML
属性。内存占用是UIWebView
的1/4 ~ 1/3
, 加载速度比UIWebView
提升了一倍左右。大幅度提升了js
执行速度。允许js
的Nitro
库的使用,UIWebView
是不允许的。可以和js
直接互调函数,不需要使用jsbridge
来协助。
当然WKWebView
不支持页面缓存,需要自己注入cookie
,而UIWebView
是自动注入cookie
的。他也无法发送POST
参数。
对于安卓来说存在webkit for webview
和chromium for webview
。
webkit
是一个开源项目,前身是khtml
和kjs
,专注于网页内容的展示,做了一流的页面渲染引擎,他不是浏览器而且也不想成为浏览器,这个项目包含两个部分,第一个部分是WebCore
, 其中包括对html
,css
很多w3c
规范的实现,第二部分就是狭义上的webkit
主要是各个平台的移植并提供相应的接口。也就是webview
和类似于webview
,这样的接口提供操作和显示网页的能力。
目前使用WK
的主流浏览器或者webview
包括chrome
,safari
, 安卓平台以及众多的移动浏览器。
chromium
是基于webkit
之上的一个浏览器项目,由谷歌来发起,这个项目发展的还是比较迅速的,他对新特性的支持还是比较好的,比如webgl
,css3
,h5
等等,在性能方向也非常不错chrome
一般选择稳定的chromium
作为基础。
Webkit for Webview | Chromium from Webview | 备注 | |
---|---|---|---|
版本 | Android4.4以下 | Android4.4以上 | -- |
JS解释器 | WebCore JavaScript | V8 | -- |
H5 | 278 | 434 | -- |
远程调试 | 不支持 | 支持 | Android4.4及以上支持 |
内存占用 | 小 | 大 | 相差20-30M |
WebAudio | 不支持 | 支持 | Android 5.0及以上支持 |
WebGL | 不支持 | 支持 | Android 5.0及以上支持 |
WebRTC | 不支持 | 支持 | Android 5.0及以上支持 |
安卓第三方内核,主要是安卓的版本较多,对WebView
二次封装产生的,这里主要说下X5
内核。
他的速度是比较快的相比系统WebView
的网页打开速度有30%
的提升,在流量方面使用云端优化技术节省20%
以上。安全问题可在24
小时内修复。更稳定经过亿级用户的使用考研,CRASHE
(崩溃)率低于0.15%
。没有系统内核的碎片化问题,更小的兼容性问题,支持夜间模式,适屏排版,字体设置等浏览器增强功能。在H5
和ES6
上有更完整的支持,集成了强大的视频播放器,支持视频格式远多于系统的WebView
,视频和文件的格式支持X5内核多于系统内核,自带防劫持。
一般webview
选型,IOS
建议使用WKWebView
, 安卓建议使用X5
。
当App
首次打开时,默认是不初始化浏览器内核的,当创建WebView
实例的时候,才会启动浏览器内核,打开事件需要70-700
毫秒,并创建webview
的基础框架。
可以使用全局的Webview
对延迟的毫秒进行优化,就是在客户端启动的时候,就初始化一个全局的WebView
待用,当用户访问Webview
的时候直接使用这个WebView
加载对应网页。
这样会减少首次打开WebView
的时间,缺点是会有一些额外的内存消耗。
导航栏可以预加载,以前是在webview
加载完成之后进行初始化,可以改为和webview
并行一起加载。
对于登录来说H5
页面上接口每次查询Cookie
中是否有登录态,无登录态H5
跳转统一登录页,App
登录成功写入Cookie
。可以改为Cookie
统一在Webview
中设置cookie
。也就是初始化Webview
的时候判断是否登录,如果登录了就打开H5
页面,如果没登录就自动跳转登录页面。
webview
加载页面的url
尽量前置,不要放在最后,可以和业务逻辑并行处理,总而言之减少页面的白屏时间,让用户最快的看到页面。
提升滚动条的使用体验,原本是使用系统自带的滚动条的进度值,可以自己模拟滚动条的加载过程,让用户感觉页面加载变快了。也就是初始快速的加载到60%
以上,给用户感觉加载很快的感觉。其实真实速度并没有变…
js-sdk
优化,也就是oc
和js
通信的一个方式。一般jssdk
有三种方式实现,第一种就是常见的scheme
的方式,就是我们在h5
页面里面定义一些特殊的链接,拿到这个scheme
之后原生拦截。然后把需要的回调函数和参数进行拦截,但这样有个问题,url
一般是256
个字符,有长度的限制不能无限的传递。
第二种是iframe
的形式,通过后台启一个页面进行拦截取iframe
的变更拿到js
的方法给oc
实现通信,iframe
是依赖jssdk.js
文件的,需要sdk
文件作为桥梁实现通信目的。
第三种是webkit
的方式,他是一种直接调用的方式,无需依赖任何的sdk
文件。
浏览器缓存策略
缓存机制 | 优势 | 适用场景 | Android 开关 | IOS开关 |
---|---|---|---|---|
浏览器缓存机制 | HTTP协议层支持 | 静态文件的缓存 | 浏览器负责 | 浏览器负责 |
Web Storage | 较大的存储空间,使用简单 | 临时,简单数据的缓存,浏览器上的LocalStorage、SessionStorage | webSettings.setDomStorageEnabled(true) | 默认开启无关闭 |
Web SQL Database | 存储,管理复杂结构数据 | 建议用IndexDB 替代 | webSettings.setDatabaseEnabled(true) | 默认开启无关闭 |
Application Cache | 方便构建离线App | 离线App,静态文件缓存 | webSettings.setAppCacheEnabled(true) | 默认开启无关闭 |
IndexDB | 存储任何类型数据,使用简单,支持索引 | 结构,关系复杂的数据存储 | webSettings.setJavaScriptEnabled(true) | 默认开启无关闭 |
全局离线包,包含公共的资源,可供多个应用共同使用。
还可以有私有化的离线包,只可以被某个应用单独使用。
离线包的工作原理:
首先会加载一个全局的包就是一些基础的文件,加载之后会把包释放放在内存里,接着会做一个检测,查看本地是否安装,如果已经安装就释放到内存,如果没有安装就触发离线包的下载,就是我们做好的包放在服务器中,然后从服务器获取过来,在下载之前会进行一个本地和线上版本的对比,版本不一致的话就会下载最新的包,如果一致就取本地的就可以了。
最终这个包会解压释放在内存里面,当webview
在加载url
的时候会直接从内存里面读取,如果能读取到就加载内存中的页面数据进行展示,假设读取不到也就是说本地没有这个业务就会使用线上的url
地址让页面加载就可以了。因为我们一般不会把所有的业务都做成离线化的形式,假设webview
查询的到就用离线化,查询不到就用垫底的线上url
展示。无论本地离线包加载失败还是没有这个离线包,都使用线上url
来垫底。
离线包的下载一包情况下如果用户处于移动网络状态下,不会在后台下载离线包,如果当前用户点击app,离线包没有下载好,用户就要等待离线包下载好才能用。可以采取wifi
静默下载的方案。
从服务器请求的离线包信息存储到本地数据库的过程中,离线包信息包括离线包的下载地址,离线包版本号,加密签名信息等,安装离线包其实就是将离线包从下载目录拷贝到手机安装目录。
一些大厂的离线包方案比如美团的LsLoader
通用移动端WebApp
离线化方案,腾讯的Alloykit
离线包,阿里的极致Hybrid
航旅离线包再加速。原理基本上都是一致的,细节上可以做些参考。
React Native
是基于React
语法的, 希望实现的是一套代码可以在各个端使用。他的优势很明显,代码是可以共享的无论是IOS
还是安卓还是H5
,性能方面几乎也与Native
相同。并且提供了非常流畅的动画,因为他在渲染之前代码就已经转换为了原生视图。
调试时无需每次代码变更都编译打包,可即时查看更小效果,极大提高了开发人力。
支持热更新,不需要每次发版都发布应用到商店,发版时间可以自由控制,安卓和ios
同时发版。
一共分成四层实现,最下面是native
的原生层也就是OC
和Java
,在这之上是UI渲染器,图片处理,网络通信,和一些工具库,再向上是C++
:JSCore
,Bridge
也就是js
的运行环境和js
和native
的桥接。最上面才是js
层也就是js
的一些组件。
RN
的jsx
文件通过JSBridge
会针对不同平台打包成不同的格式,比如IOS
的.m
文件,安卓的.xml
文件,以及H5
的.html
文件。
为什么会有RN
其实是因为应用商店发版的问题,每一次发版都需要审核,可能审核不通过,而且安卓可能要发布多个商店,还有两端研发不同步的问题,也就是安卓和ios
相同的业务需要开发两遍。
如果你公司的技术是React
全家桶,那还是建议选用RN
的。
小程序的愿景是触手可及,用户扫一扫或者搜索下就可以打开应用,不需要安装太多应用。
程序相比App
,开发门槛更低,优于H5
接近Native
的体验,可以使用相机,位置,网络,存储等丰富的原生能力。支持顶部下拉,搜索,扫码等入口,简单方便,用完即走,不需要像App那样下载,直接打开支持热更新。
小程序出现的行业背景,对于App
大厂来说需要流量变现,比如微信,他是没办法变现的,所以可以使用小程序生态将第三方引入进来,形成了一个小型的应用市场。对于企业应用来说,移动流量枯竭,获客比较困难,可以降低获客成本和开发成本,业务上提供更多的试错机会。
平台类产品如果输出给商家端,相比多个app
的方式,比较推荐使用小程序。
号称编写一次可以部署到各个终端,web
,android
,ios
,mac
,linux
,windows
,fuchsia os
。
底层使用Skia
图形引擎,图形性能媲美原生应用,界面更像一个全屏应用程序或2D
游戏,速度比较快,使用本机ARM
二进制文件,做到提前编译,不需要JVM
,也就是java
虚拟机。
底层实现
flutter
在2017
年5
月份出现,生态不够丰富,学习曲线相对较高,但是他的性能较好,如果考虑性能,团队人员足够的话建议选择fluttr
。
CDN
是内容分发网络,利用每一台最靠近用户的服务器,更快更可靠的将文件发送给用户。以加快访问速度。
CDN
的有点很明显,因为会给用户指派较近,较顺畅的服务器节点,所以速度会比较快,服务器放在不同地点,减少了互联的流量,也降低了快带成本,当某个服务器故障时,自动调用临近地区的服务器。
回源是指浏览器访问CDN
上静态文件时,文件缓存过期,直接穿透CDN
而访问源站机器的行为。这是CDN
的一个策略。
CDN
的缓存分为三级,浏览器本地缓存也就是header
头配置的缓存,CDN
边缘节点缓存,CDN
源站缓,一般是三级,也可能业务比较少就采用两级缓存,浏览器缓存和CDN
源站缓存。
缓存时间设置过短的话,CDN
边缘节点缓存经常失效,导致频繁回源,增大了源站负载,访问速度也会变慢,缓存时间设置的过长,文件更新慢,用户本地缓存不能及时更新,所以一般是结合业务情况而定。
一般不同资源类型缓存时间设置不用,html
一般3
分钟左右,js
,css
可以10
分钟,一天
,一个月
,看变更情况。
现在一般我们文件的命名都会以hash
串的形式,如果文件有变更生成的文件名就会有变更,否则还是之前的名字,这样我们缓存的时间就可以设置的长一些。
CDN
可以灰度发布,也就是在部分地区部分运营商优先发布静态资源,验证通过后再进行全量发布。具体实施可以从域名方面下手,设置特殊VIP
解析到要灰度的城市或者运行商。也可以调整源站机器,给灰度的城市或者运营商配置单独的源站机器。
一般在活动期间比如说大促,需要增加机房宽带,增加运营商流量,增大CDN
缓存时间等等。
DNS
是将网站域名和地址互相映射的一个分布式数据库,我们访问一个网站首先会通过DNS
将域名匹配为ip
地址,然后再通过ip地址去访问对应的服务器。
客户端里面有一个http dns
, 客户端直接访问http
的接口,可以获取业务在域名访问系统中配置的最优ip
,基于容灾的考虑,app
内是需要保留使用运营商DNS
解析方式的,客户端再获取到ip
后会直接向ip中发送业务请求。
比如一个http
请求,在header
中会指定host
字段,向ip
发送标准的http
请求就可以了,总的来说采用http-dns
来解析域名能绕过三四级运营商接续域名出现的一些问题,在http-dns
返回的正确ip
之后是直接使用ip
去发送http
请求的,只需要关注通信内容的安全就可以了。
安卓系统可以采用okhttp
模块,他支持http2
,http2
可以在一个链接上一次性发送多个请求,支持gzip
,也支持响应缓存避免网络重复请求,如果服务器配置了多个ip
地址,当第一个ip
链接失败的时候,okhttp
会自动尝试下一个ip
地址。
ios
没有现成的模块,我们可以在app
启动时,缓存所有可能要用到的域名ip
比如接口,网关,同时异步处理,客户端无需得到缓存结果。如果cache
中有此域名的圆环,直接返回缓存的ip
,如果缓存中没有此域名,则重新向httpdns server
进行申请然后缓存下来。
H5
的做法一般是设置多个域名,因为浏览器对并发数是有限制的,一个域名一般最大连接数是6,所以我们可以将用户访问的一些api
接口作为一个域名,页面中的样式和资源可以设置成一个域名,图片也可以单独设置成一个域名,甚至多个域名,来打破浏览器的这种限制。
http
的优化主要就是减少请求数,这可能是我们日常工作中经常遇到的,也是大家耳熟能详的。
图片可以使用雪碧图,dataurl
,webfont
。
可以考虑将业务中的js
或者css
合并,不要切割的太小。如果不想合并成一个可以使用Combo
的方式让服务去返回,可以在url
上通过参数的形式告诉服务加载那些资源。
接口也可以合并,不要拆分的太细,可以让服务去合并,不经常变化的接口和资源也可以存储在LocalSrorage
,有变化就更新,没有变化就从本地取。
有些时候我们的某个页面会出现问题,或者打开白屏,但是接口没有问题,页面也没有问题,资源也是可以访问通的,这个时候可能就是cookie
太大了,已经超出了原本可控的范围,我们都知道cookie
是会随着页面间的跳转携带的,这就肯能导致页面无法访问,这种问题不常见,但实际工作中确实会遇到。
可以在页面中设置cookie
白名单,意思就是定期检查我们的cookie
,如果是需要的就保留,不需要的就删除,定期整理。cookie
控制可以减小页面间传输的大小,也可以对cookie
进行有效的管理。
当一个文件被浏览器加载的时候我们实际上是不知道这个文件是否是过期的,所以浏览器和服务器之间存在一种约定,通过header
头的配置,确定文件是否过期。
一般在响应头中包含一个expires
的头信息,他的值为日期+时间,表示在此时间之后,响应过期,如果数值为0
,表示资源已经过期。
当然如果响应头中包含Cache-Control
, 设置了max-age
或者s-max-age
指令,那么就会忽略expires
,而取Cache-Control
。
Cache-Control
通过制定的指令来实现缓存机制,缓存指令是单向的,这意味着在请求中设置的指令不一定被包含在响应中。他的语法比较简单,Cache-Control:max-age=秒
设置缓存存储的最大周期,超过这个时间缓存被认为过期。
ETag
是资源版本的标识符,可以让缓存更搞笑,并节省带宽,因为如果内容没有改变,Web
服务器不需要发送完整的响应,如果发生了改变,使用ETag
有助于防止资源的同时更新相互覆盖。
ETag
类似于指纹,也可能被某些服务器用于跟踪,比较ETag
能快速确定资源是否变更,但也可能被跟踪服务器永久存留。
ETag:"5c6cccc123-1d45"
Last-Modified
是一个响应收不,其中资源包含源头服务器认定的资源做出修改的日期和时间。他常被用作一个验证器来判断接收到的或者存储的资源是否一致。由于精度比ETag
要低,所以这是一个备用机制,包含有if-Modified-Since
或If-Unmodified-Since
首部的条件请求会使用这个字段。
Date
是通用的首部,其中包含了报文创建的日期和时间。
Gzip
,可以对文本进行压缩,一般是html
,css
,js
,对于非文本不会压缩,比如说图片资源,压缩比率可以达到50%-70%
这个内容不应该写在这,但是实在不知道写在哪了。
https
这里就不过多介绍了,毕竟不是这篇该有的内容。
浏览器目前基本上已经默认开启了https
,所以为了SEO
我们也建议使用https
,而且https
也更加安全。
如果是对外的网站我们需要和经销商购买ssl
证书,可以在gogetssl
,ssls.com
,sslmate.com
中去购买,当然这些证书是有时效的。
如果本地测试的话,也可以在本地安装一个测试证书,我们可以通过mkcert
来实现。首先需要安装它。
brew install mkcert
安装根证书
mkcert ---install
生成本地签名,给123.com
mkcert 123.com
这样就生成了一对证书,我们可以将这对证书配置在nginx
里面,具体配置方法可参考我之前写的nginx
文章。
http2
是http
的第二版,简称h2
或h2c
,它采用二进制传输数据,多路复用,允许通过一个链接发起多个请求,所以一般使用h2
雪碧图就没什么用了,他超出了浏览器限制最大连接数的局限,对header
头进行压缩从而降低传输体积,支持服务端推送(server push
),可以从服务端将数据推送给客户端。
开启HTTP2
可以降低服务器压力,提升网站访问速度,而且可以更好的保护网站安全因为他是强制使用https
的。
开启http2
其实也很简单,我们需要重新编译nginx
,并且开启http_ssl_module
和http_v2_module
cd nginx-xxx
./configure --with-http_ssl_module --with-http_v2_module
make && make install
同样这里可以参照之前我写的nginx
文章,其实就是在listen 443
端口后添加http2
标识。
server {
listen 443 ssl http2;
}
首先是技术选型,包括页面渲染技术和混合式开发技术,然后是项目的初始化,包括React
,Vue
,Angular
,依赖模块引入,一般会存在一个私有的NPM
,接着开始本地开发,方便前端调试和看到效果,项目联调,产品和设计师对效果进行确认,最后项目整体部署上线。
项目开始之前前后端会指定一些数据接口,有了接口文档前后端就可以并行开发,前端开发页面和交互,后端开发业务逻辑。都开发完后前后端开始进行联调,最后发布上线。
UI
自动化,上手比较简单,不过稳定性较差,常用的工具有appium
,他是一个开源的工具用于自动化ios
手机,安卓手机还有windows
桌面的一个测试工具,robot framework
是基于python
可扩展的关键字测试框架用于端到端,验收测试以及测试驱动开发,可用于测试分布式异构应用程序包括可以验证涉及多种技术的接口,selenium
用于web
应用程序测试工具可以直接运行在浏览器上,可以和用户的真正操作是一样的,支持ie
,火狐
,谷歌
,欧朋
等常用浏览器,主要用来测试浏览器的兼容性。airtest
支持自动化的脚本录制一键回收,轻而易举就能实现自动化测试流程,还是比较常用的。
接口自动化,使用稳定,性价比非常高,工具有java + restassured
,是java
实现的,轻量级的可以通过编写代码向客户端发送请求并且验证返回结果。python + requests
主要对python
接口进行测试, JMeter用于对软件做压力测试,HttpRunner
只需要一份脚本就可以实现自动化测试性能测试,线上监控,持续集成等多种测试的需求。
单元测试,性价比极高,一般由开发完成,但是有一些单元测试框架,Junit5
,pytest
,unittest
。