优化
网页优化
网页中有大量图片加载很慢,如何优化
- 图片懒加载,在图片未可视区域加一个滚动条事件,检测图片是否在可视区域,如果在,将URL赋值给img标签的src进行加载
- 使用图片预加载技术,将当前展示的前一张和后一张优先下载(可以使用css将预加载的图片加载在屏幕外的背景上)
- 使用css sprite或者svg sprite
常见web性能优化
- 降低请求量:
合并资源,减少HTTP请求数
minify/gzip压缩
webP
lazyLoad
图片压缩、合并(精灵图)、使用字体图表代替小图片、使用base64、图片懒加载 - 加快请求速度:
预解析DNS
减少域名数
并行加载
CDN分发 - 缓存:
HTTP协议缓存请求
离线缓存manifest
离线数据缓存localStorage - 渲染
js/css优化
加载顺序
服务端渲染
pipeline
减少重排操作,例如使用transform书写动画效果,在for循环结束后再去操作dom等 - webpack优化
treeShaking
组件按需加载
使用chunck
模板预编译 - 其他
数据懒加载、数据按需加载(上拉加载)、分页
路由懒加载
频繁触发的事件进项防抖和节流
异步加载
减少闭包、递归优化(深浅拷贝)、使用高效算法
字库用gb2312不要utf-8,一个汉字少一个字节
CDN
CDN概念
内容分发网络(Content Delivery Network),是一种通过互联网互相连接的电脑网络系统,利用最靠近用户的服务器,更快、更可靠地将图片、音视频、应用程序已经其它文件发送给用户。从而实现传递给用户的网络内容更高的性能、更好的扩展性以及更低的成本。
典型CDN系统组成:
-
分发服务系统
基本工作单元是Cache设备,cache(边缘cache)负责直接响应最终用户的访问请求,把缓存在本地的内容快速地提供给用户。
Cache还负责与源站点进行内容同步,把更新的内容以及本地没有的内容从源站点获取并保存在本地。
衡量一个CDN系统服务能力的基本指标是:Cache设备的数量、规模、总服务能力。 -
负载均衡系统
负责对所有发起服务请求的用户进行访问调度,确定提供给用户的实际访问地址。
两级调度体系分为全局负载均衡(GSLB) 和 本地负载均衡(SLB)。
全局负载均衡(GSLB) 主要根据用户就近性原则,通过对每个服务节点进行"最优"的判断,以确定提供服务的Cache物理地址。
本地负载均衡(SLB) 主要负责节点内部的设备负载均衡。 -
运营管理系统
分为运营管理和网络管理子系统,负责处理业务层面与外界系统交互所需要的收集、整理、交付工作。包含客户管理、产品管理、计费管理、统计分析等功能。
CDN作用
一般用来托管Web资源(图片、文本、脚本等)、可供下载的资源(音视频等媒体文件、软件、文档等)、应用程序(门户网站等),从而加速这些资源的访问。CDN作为基础的云服务,具有资源托管、按需扩展(应对流量高峰)等方面的优势。
-
性能方面
用户接收的内容由最近的数据中心发送,可以降低延迟,缩短内容加载时间;
部分资源请求分配给CDN进行处理,可以降低服务器的负载。 -
安全方面
有助于防御DDos、MITM等网络攻击。
- DDos:通过监控异常流量,限制其请求频率。
- MITM:从原服务器到CDN节点再到ISP(Internet Service Provider),全链路使用HTTPS通信。
CDN原理
-
用户使用CDN缓存资源
- 根据数据URL,经过本地DNS系统解析,如果该URL对应的是一个CDN专用的DNS服务器,DNS系统将域名解析权交给CNAME指向的CDN专用DNS服务器。
- CDN专用DNS服务器将CDN的全局负载均衡设备IP返回给用户。
- 用户向CDN的全局负载均衡设备发送数据请求。
- CDN的全局负载均衡设备根据用户IP以及用户请求的URL,选择一台用户所属区域的区域负载均衡设备,告诉用户设备IP,让用户向这台设备发送请求。
- 区域负载均衡选择一台合适的缓存服务器来提供服务,将缓存服务器的IP返回给全局负载均衡设备。
- 全局负载均衡设备把服务器IP返回给用户
- 用户向对应的缓存服务器发送请求,缓存服务器响应服务,将内容发送到用户终端。
若缓存服务器没有用户需要的内容,缓存服务器向它的上一级缓存服务器请求内容,直到获取需要的内容,若最终未找到,会回到自己的服务器获取内容。
-
用户未使用CDN缓存资源
- 浏览器通过DNS对域名进行解析,依次得到域名对应的IP地址
- 浏览器根据IP地址,向域名服务器主机发送数据请求
- 服务器向浏览器返回响应数据
CDN使用场景
- 进行静态资源的缓存
将网站的静态资源,例如css、图片、js脚本等,放在CDN上,也可以将整个项目放在CDN上,完成一键部署。 - 使用第三方CDN服务
开源项目,可以使用第三方的CDN服务 - 直播传送流媒体
直播本质上时使用流媒体传送,CDN支持流媒体传送,所以直播可以通过使用CDN来提高访问速度。
CDN在处理流媒体时,与普通静态文件不同,普通文件边缘节点没找到的话,回去上一层接着寻找,但是由于流媒体本身数据量非常大,如果使用回源的方式,会带来性能问题,所以流媒体一般采用主动推送的方式。
懒加载
懒加载又叫按需加载或者延迟加载,是指在长网页中延迟加载图片数据,是一种较好的网页性能优化方式。
在长网页或应用中,如果图片比较多,这种情况下,所有的图片都加载,但是用户只看到可视区域内的那一部分图片数据,这样就浪费了性能。图片懒加载可以很好的解决上述问题。滚动屏幕之前,可视区域外的图片不会加载,当屏幕滚动,当图片进入可是区域后,才加载,这样就可以提高网页的加载速度,也降低服务器的负载。
特点:
- 减少无用资源的加载
使用懒加载可以很明显的降低服务器的压力和流量,同时也减少浏览器的负担。 - 提升用户体验
如果同时加载很多图片,可能需要长时间的等待,会影响用户体验。而使用懒加载,就可以大大提高用户体验。 - 防止加载过多图片,影响其他资源文件的加载
原理:
当将图片的src属性复制后,就会请求图片资源,然后加载图片。那么就可以使用HTML5的data-xxx属性来存储图片路径,当需要加载图片的适合,将data-xxx内存储的图片路径赋值给图片的src,就实现了懒加载。
例子:
<div class="imgContainer">
<img src="1.jpg" data-src="1.jpg" data-set="true" />
<img src="loading.gif" data-src="2.jpg" />
<img src="loading.gif" data-src="3.jpg" />
<img src="loading.gif" data-src="4.jpg" />
<img src="loading.gif" data-src="5.jpg" />
<img src="loading.gif" data-src="6.jpg" />
<img src="loading.gif" data-src="7.jpg" />
</div>
<script>
const images = document.querySelectAll('img');
function lazyLoad() {
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; // 浏览器滚动过的距离
const winHeight = window.innerHeight; // 浏览器可视区域高度
for (let i = 0; i < images.length; i++) {
const img = images[i];
if (img.offsetTop < scrollTop + winHeight) {
if (img.getAttribute('data-set') === 'true') {
continue;
}
img.src = img.getAttribute('data-src');
img.data-set = 'true';
}
}
}
window.onscroll = lazyLoad();
</script>
懒加载和预加载的区别
预加载时提前加载,懒加载是延迟加载或不加载。两种方式都是提高网页性能的方式。
-
懒加载
又叫延迟加载,是指在长网页中延迟加载图片,当用户需要访问时,再去加载,可以提高首屏加载速度,提升用户体验,也可以降低服务器压力。
适用于长页面并且图片很多的网站。
实现原理是,将图片的src属性设置为空,即不加载图片,并且把真实路径存在图片的某个属性中,当页面滚动时,判断图片是否进入页面的可视区域,进入则在存放图片真实路径的属性中取出路径,并赋值给图片的src属性,这样就可以实现图片的懒加载。 -
预加载
指的是将需要加载的资源,提前请求加载到本地,这样当需要使用时,就直接从缓存中取预加载的资源。
通过预加载可以减少用户等待的时间,从而提高用户体验。
例如,使用image对象,为image对象设置src属性;或者将图片提前加载在可视区域外;提前载入图片,使用css隐藏预加载的图片,等方法来实现图片的预加载。
回流和重绘
-
回流也叫重排(Reflow)
当元素因为规模尺寸、布局、隐藏等改变,需要重新渲染部分或者全部文档时则称为回流。
导致回流的操作:
- 页面的首次渲染
- 浏览器窗口大小发生变化(resize)
- 元素的内容发生变化
- 元素的尺寸或者位置发生变化
- 元素的字体大小发生变化
- 激活css伪类
- 查询某些属性或者调用某些方法
- 添加或者删除可见的DOM元素
-
重绘(Repaint)
一些元素需要更新属性,而这些属性只影响元素的外观、风格,而不会影响布局则叫重绘。重绘不一定回流,但是回流一定重绘。
导致重绘的操作:
- color、background相关属性(background-color、background-image等)
- outline相关属性:outer-color、outer-width、text-decoration
- border-radius
- visibility
- box-shadow
如何避免回流和重绘
- 操作DOM时,尽量在低层级的DOM节点进行操作
- 不使用table,因为小的改动可能会导致整个table的重新布局
- 不频繁操作元素的样式,对于静态页面,可以通过修改类名的方式,而不是样式直接进行修改
- 使用css表达式
- 频繁改变的元素,使用absolute或者fixed,使元素脱离文档流,这样发生变化就不会影响其他元素
- 避免频繁操作DOM元素,可以使用documentFragment,完成所有的DOM操作后,再添加到文档流中
- 需要操作的元素元素可以先设置display: none,操作结束后再显示出来。因为在display属性为none的元素上进行DOM操作不会引发回流和重绘
- 将DOM的读写操作尽可能放一起,而不是读写操作穿插进行。这得益于浏览器的渲染队列机制
渲染队列机制
浏览器会将所有回流、重绘的操作放在一个队列中,当队列的操作到了一定的数量或者时间间隔,浏览器就会对对流进行批处理。
这样的好处就是,可以让多次回流、重绘变成一次回流重绘。
例如上述中读操作放一起,会等待所有读操作进入队列之后,一起执行,这样原本要触发多次回流就变成了触发一次回流。
动画优化
动画会频繁的操作DOM,这样可能会导致页面的性能问题。
可以通过将动画的position属性设置为absolute或者fixed,将动画脱离文档流,这样动画的回流就不会影响到页面了。
documentFragment
MDN:
DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的Document使用,就像标准的而document一样,存储由节点(nodes)组成的文档结构。与document相比,最大的区别是DocumentFragment不是真实DOM树的一部分,它的变化不会触发DOM树的重新渲染,且不会导致性能等问题。
当把DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它所有的子孙节点。
当需要频繁的操作DOM时,可以将DOM元素插入DocumentFragment中,所有DOM操作结束后,一次性将DocumentFragment的子孙节点插入文档中。
DocumentFragment节点插入DOM树,不会触发页面的重绘,可以大大提高页面性能。
防抖和节流
防抖
指在事件被触发n秒后再执行回调,如果在n秒内,事件再次触发,重新计时。
应用场景:
- 表单按钮提交:防止重复提交,只执行最后一次
- 需要发送请求的验证场景:例如表单验证需要向服务端发送请求,当连续输入事件一定时间间隔内的最后一次完成后,再向服务端进行请求;还有搜索联想词功能等。
可以使用lodash中的lodash.debounce方法
实现
function debounce(fn, wait) {
let timer = null;
return function() {
const context = this;
const args = [...arguments];
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
}
}
节流
指规定的单位时间内,只能有一次触发事件的回调函数执行,如果同一个单位时间内,事件被多次触发,只生效一次。
应用场景:
- 拖拽:固定事件内只执行一次,防止高频次触发位置变动
- 缩放(浏览器resize)
- scroll
- 动画:避免短时间高频率触发动画引起性能问题
实现
// 时间戳
function throttle(fn, delay) {
let preTime = Date.now();
return function() {
const context = this;
const args = [...arguments];
const nowTime = Date.now();
if (nowTime - preTime >= delay) {
preTime = Date.now();
return fn.apply(context, args);
}
}
}
// 定时器
function throttle(fn, wait) {
let timer = null;
return function() {
const context = this;
const args = [...arguments];
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, wait);
}
}
}
图片优化
优化手段:
- 减少使用图片。修饰图片可以用css代替。
- CDN加载,根据适配的屏幕宽度,请求裁剪好的图片。
- 小图像使用base64格式
- 精灵图:多个图标图片整合到一个图片中
- 选择正确的图片格式
- 能够显示WebP格式的浏览器尽量使用WebP格式(更好的图像数据压缩算法带来更小的图片体积,但是兼容性差)。
- 小图使用PNG,类似图标图篇,用SVG代替。
- 照片尽可能使用JPEG。
图片格式对应的使用场景:
- BMP
无损、支持索引色也支持直接色的点阵图。
这种格式基本上没有对数据进行压缩,所以BMP格式的图片通常是较大的文件。 - JPEG
有损、采用直接色的点阵图。
JPEG格式的图片优点是采用了直接色,得益于更丰富的色彩,JPEG非常适合存储照片。
但是与GIF相比,JPEG不适合存储线框类、logo类的图,有损压缩会降低图片的清晰度,导致图片模糊。 - PNG-8
无损、使用索引色的点阵图,是非常好的GIF格式替代者。
所以尽可能使用PNG-8替代GIF,相同图片效果下,PNG-8体积更小,并且支持透明度的调节。 - PNG-24
无损、使用直接色的点阵图。
PNG-24的优点在于它压缩了图片数据,同样效果的图片, PNG-24格式比BMP文件要小的多。但是 PNG-24的图片要比JPEG、GIF、PNG-8大的多。 - GIF
无损、采用索引色的点阵图。
采用LZW压缩算法进行编码。
优点:文件小、支持动画、透明
缺点:仅支持8bit的索引色,对色彩要求高的场景不友好
GIF适用于对色彩要求不高同时需要文件体积较小的场景。 - SVG
无损矢量图。由直线、曲线以及绘制它们的方法组成。放大SVG图片,不会失真,看到的线和曲线,不会出现像素点。
适合绘制Logo和Icon。例如iconfont就支持svg格式的icon输出。 - WebP
谷歌开发,支持无损以及有损压缩、使用直接色的点阵图。
与其他格式相比,相同质量的图片,WebP格式的图片文件体积更小。但是兼容性差,目前仅Chrome和Opera支持。- 无损压缩,相同质量的WebP图片,文件比PNG小26%
- 有损压缩,相同质量的WebP图片,文件比PNG小25%-34%
- WebP格式支持图片透明度,一个无损压缩的WebP格式图片,如果需要支持透明度,仅需要22%的额外文件大小
webpack优化
提高webpack打包速度
-
loader优化
对于loader,最影响打包速度的可能是Babel,Babel需要将代码转为字符串生成AST,然后对AST继续进行转变,最后生成新代码。项目越大,转换的代码量越大,速度越慢优化
-
优化文件搜索范围
module.exports = { module: { rules: [ { test: /\.js$/, // 规定js文件使用babel loader: 'babel-loader', include: [resolve('src')], // 规定在src目录下查找需要babel编译的文件 exclude: /node_modules/ // 排除不查找的路径 } ] } } ```
-
缓存babel编译过的文件
loader: 'babel-loader?cacheDirectory=true'
-
HappyPack
Node是单线程运行的,所以Webpack打包过程也是单线程的,特别是在执行loader的时候,长时间编译的任务很多,会导致等待的情况。
HappyPack可以将Loader的同步执行转换为并行,这样就可以利用系统资源来加快打包速度。module: { loaders: [ { test: /\.js$/, include: [resolve('src')], exclude: /node_modules/, loader: 'happypack/loader?id=happybabel' // id对应plugins中HappyPack配置中的id } ], plugins: [ new HappyPack({ id: 'happybabel', loaders: ['babel-loader?cacheDirectory=true'], threads: 4 // 开启4个线程 }) ] }
-
DllPlugin
可以将特定的类库提前打包然后引入。
可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离称单独文件的优化方案。// 需要单独配置在一个文件中 webpack.dll.conf.js const path = require('path'); const webpack = require('webpack'); module.exports = { entry: { vendor: ['react'] // 需要统一打包的类库 }, output: { path: path.join(__dirname, 'dist'), filename: '[name].dll.js', library: '[name]-[hash]' }, plugins: [ new webpack.DllPlugin({ // name需要和output.library一致 name: '[name]-[hash]', // 需要与webpack配置中DllReferencePlugin一致 context: __dirname, path: path.join(__dirname, 'dist', '[name]-manifest.json') }) ] }
// webpack.conf.js module.exports = { plugins: [ new webpack.DllReferencePlugin({ context: __dirname, // manifest就是之前打包出来的json文件 manifest: require('./dist/vendor-manifest.json') }) ] }
-
代码压缩
Webpack3中,一般使用uglifyJS来压缩代码,但是单线程运行的,为了加速速度,可以使用webpack-parallel-uglify-plugin来运行UglifyJS,从而提高速度。
在Webpack4中,不要上述操作了,只需要将mode设置为production就可以默认开启上述功能。对于代码压缩,是必须做的性能优化,当然除了压缩js代码之外,还可以压缩html、css代码以及通过配置项删除console.log等无用代码。
-
其他
- resolve.extensions
表明文件后缀列表,默认查找顺序是[‘.js’, ‘.json’],如果导入文件有添加后缀,就会按照这个顺序查找文件,尽可能减少后缀列表长度,并将出现频率高的后缀排在前面。 - resolve.alias
通过别名的方式来映射一个路径,能让Webpack更快找到路径 - module.noParse
如果确定文件下没有其他依赖,可以使用该属性让Webpack不搜秒该文件,对大型类库有帮助
- resolve.extensions
减少webpack打包体积
-
按需加载
对于SPA项目,可以使用按需加载,将每个路由页面单独打包为一个文件。
-
Scope Hoisting
Scope Hoisting可以分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去。
例如:两个文件打包
// test.js export const a = 1; // index.js import { a } from './test.js'; // 打包后的代码 [ /* 0 */ function (module, exports, require) { // ... test.js } /* 1 */ function (module, exports, require) { // ... index.js } ] // 使用Scope Hoisting, 代码就会尽可能合并到一个函数中去 [ /* 0 */ function (module, exports, require) { // ... test.js // ... index.js } ]
使用Scope Hoisting后生成的代码明显比使用之前要少。
Webpack4中如果开启功能:module.exports = { optimization: { concatenateModules: true } }
-
Tree Shaking
用来删除项目中未引用的代码
// test.js export const a = 1; export const b = 2; // 未使用 // index.js import { a } from './test.js';
Webpack4, 开启生产环境就会自动启动该优化功能。
webpack优化前端性能
指优化webpack的输出结果,让打包的结果在浏览器运行更快速高效。
-
压缩代码
删除多余的代码、注释、简化代码写法等方式。
可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩js文件,利用cssnano(css-loader?minimize)来压缩css -
利用CDN加速
构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对output参数和各loader的publicPath参数来修改资源路径
-
Tree Shaking
讲代码中无用的片段删除,可以通过在启动webpack时追加参数->optimize-minimize来实现
-
Code Splitting
将代码按路由维度或者组件分块(chunk)。
这样可以做到按需加载,同时可以充分利用浏览器缓存。 -
提取公共第三方库
SplitChunksPlugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码。
提高webpack的构建速度
- 多入口,使用CommonsChunkPlugin来提取公共代码
- 通过externals配置来提取常用库
- 利用DllPlugin和DllReferencePlugin预编译资源模块,通过DllPlugin来对那些引用但是不会修改的npm包进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
- 使用Happypack实现多线程加速编译
- 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度。
- 使用Tree-shaking和Scope Hoisting来剔除多余代码。
内存泄露如何检查
使用Chrome DevTools
使用性能分析器可视化内存消耗
以下面的代码为例,有两个按钮,添加和移除。
点击添加按钮,通过创建dom节点,将1-10000的数字显示到dom节点中。
点击清除按钮,清除全部由添加按钮事件创建的所有dom节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="addDom()">添加</button>
<button onclick="removeDom()">清除</button>
<div id="container"></div>
<script>
let domArray = [];
const container = document.getElementById('container');
function addDom() {
for (let i = 0; i < 10000; i++) {
const dom = document.createElement('div');
dom.innerHTML = i;
container.appendChild(dom);
domArray.push(dom);
}
}
function removeDom() {
// for (let dom of domArray) {
// container.removeChild(dom);
// }
container.innerHTML = 'cleared';
domArray = [];
}
</script>
</body>
</html>
点击添加按钮,JS Heap会出现蓝色的峰值,这是因为JavaScript正在创建DOM节点并将创建的dom添加到domArray数组中。并且绿色的Nodes(dom)数量也在增加。点击清除按钮,JS Heap和Nodes只有在操作container.innerHTML = 'cleared';
时有一次上升,因为清除操作并没有移除dom节点,只是操作了container节点。
如果在实际的项目中,观察到内存持续出现峰值,并且内存消耗一直不会下降,那可能存在内存泄露。
识别分离的DOM节点
分离的DOM节点是指一个节点从DOM树中移除,但可能存在引用该dom的情况。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="addDom()">添加</button>
<button onclick="removeDom()">清除</button>
<div id="container"></div>
<script>
let dom;
const container = document.getElementById('container');
function addDom() {
dom = document.createElement('div');
dom.innerHTML = '1231231321';
container.appendChild(dom);
}
function removeDom() {
container.removeChild(dom);
}
</script>
</body>
</html>
使用heap snapshot可以检查分离的DOM节点
点击页面按钮后,点击下面蓝色的Take snapshot按钮,可以搜索输入Detached来过滤结果中的分离的dom节点
web前端性能优化ing
性能优化概念
起因
当某个功能没有满足其需求的可选方案时,用户需要忍受糟糕的体验,这个时候就存在对产品进行优化和改进的空间。
比如某个网站加载速度更快,对用户操作的响应速度更快,都会影响网站的转换率、留存率等。
性能的影响
-
用户留存率
网站的一系列经济转换是建立在用户留存率上的,比如社交网站希望用户间的互动,电商网站希望用户浏览、购买商品,等等。
用户的留存率 = 用户登录注册之日后一段时间后,仍在使用网站的用户数 / 总注册用户数。
该指标对网站的运行有重要的指导意义,而网页加载时间很大程度上影响着该指标,当然影响该指标的因素还有很多。
通过优化性能来提高留存率是必要措施。 -
网站转化率
网站转化率 = 用户目标行为的访问次数 / 用户访问总次数。
例如,电商网站上某个商品,用户购买量 / 用户浏览量,该比率可以大概得到用户作为访客到消费者的转化率。 -
用户体验和传播
当前环境下,用户通过移动设备访问网站,会产生流量数据,当网站存在冗余数据、大资源文件等问题时,就会造成网络资费的浪费以及加载时间增加等问题,降低用户体验。当然低用户体验的应用,在使用过程中,口碑会下降,不利于应用的传播。
性能评估模型
原则时以用户为中心,然后根据该原则引出知道性能优化的策略。参照RAIL性能模型。
RAIL 是 response (响应)、 animation(动画)、idle(浏览器空置状态)和 load(加载)。
-
response
对于用户的操作,比如点击表单提交,如果没有在合理的时窗内完成响应,也就是说采取动作和得到响应之间出现了断层,用户就会感知延迟。
例如输入、点击、表单提交、动画等操作,当反馈时间窗口超过100ms,用户就会感知延迟。
响应的主要交互是:
- 在首次收到操作时,在100毫秒内得到回应
- 理想情况是得到最终响应结果,如果需要花更长的时间进行处理,需要给用户还在处理中的提示,例如loading效果。
-
animation
动画主要包含以下交互效果:
- 视觉动画
- 滚动
- 拖拽因为人的视觉神经是有反应速度的,是1/24s,即物体移出后,物体不会在眼中立即消失,而会延续存在1/24s的时间。也就是说,无论动画帧率有多高,人眼只能分辨其中的30帧,但是越高的帧率就会带来更好的流畅体验,因此动画要尽可能达到60fps的帧率(一帧图像的生成预算为16.67ms, 1000ms/60,除去浏览器绘制时间,留给代码执行的时间仅为10ms左右)。
-
idle
要想响应较为精美的动画,通常都需要较长的处理时间
-
load