进阶优化
# css和js的装载与执行
# html 渲染过程的一些特点
# 顺序执行、并发加载
- 词法分析
- 浏览器对 html 从字节流到字符流的分析,从上到下地去匹配 token
- 并发加载
- html 中所引入的 web 资源是并发请求的
- 并发上限
- 对某个域名下请求的资源数是有上限的
# 是否阻塞
- css 阻塞
- css head 中阻塞页面的渲染
- css 阻塞 js 的执行
- css 不阻塞外部脚本的加载
- js 阻塞
- 直接引入的 js 阻塞页面的渲染
- js 不阻塞资源的加载
- js 顺序执行,阻塞后续 js 逻辑的执行
# 依赖关系
- 页面渲染依赖于 css 的加载
- js 的执行顺序的依赖关系
- js 逻辑对于 dom 节点的依赖关系
# js引入方式
- 直接引入
- 异步动态引入 js
- defer
- 加载后续文档元素的过程将和JS的加载并行进行(异步),但 js 可以延迟到文档完全被解析之后再执行。只对外部脚本文件有效
- async
- 加载后续文档元素的过程将和JS的加载与执行并行进行(异步)。
- defer
附加 defer和async
共同点
(1)不会阻塞文档元素的加载。
(2)使用这两个属性的脚本中不能调用document.write方法。
(3)允许不定义属性值,仅仅使用属性名。
(4)只适用于外部脚本(虽然IE4-IE7还支持对嵌入脚本的defer属性,但在IE8及之后的版本就只支持外部脚本,对不支持的会直接忽略defer属性,因此把延迟脚本放在页面底部仍然是最佳选择)。
不同点
(1)每一个async属性的脚本一旦加载完毕就会立刻执行,一定会在window.onload之前执行,但可能在document的DOMContentLoaded之前或之后执行。不保证按照指定它们的顺序来执行,如果JS有依赖性就要注意了。指定异步脚本的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容,因此,建议异步脚本不要在加载期间修改DOM。
(2)每一个defer属性的脚本都是在文档元素完全载入后,一般会按照原本的顺序执行,同时一般会在document的DOMContentLoaded之前执行,相当于window.onload,但应用上比 window.onload 更灵活!实际上,defer 更接近于DomContentLoad。事实上,延迟脚本不一定会按顺序执行,也不一定会在DOMContentLoaded事件触发之前执行,因此最好只包含一个延迟脚本。
# 加载和执行的一些优化点
- css 样式置顶
- 用 link 代替 import
- js 脚本置底
- 合理使用 js 的异步加载能力
# 懒加载与预加载
# 懒加载(需要时再加载)
- 图片进入可视区域之后请求图片资源
- 对于电商等图片很多,页面很长的业务场景适用
- 减少无效资源的加载
- 并发加载的资源过多会阻塞 js 的加载,影响网站的正常使用
# 懒加载操作步骤
- 当我们的图片进入可视区域,去请求资源
- 需要去监听 scroll 事件,再 scorll 事件的回调中,去判断我们的懒加载的图片是否进入可视区域
# 懒加载工具
- 自己编写
- 找 jquery 插件,如 zepto.lazyload.js
# 预加载(提前加载)
- 图片等静态资源在使用之前的提前请求
- 资源使用到时能从缓存中加载,提升用户体验
- 页面展示的依赖关系维护
# 预加载操作步骤
- 设置 display:none ,在需要的时候显示就行,缺陷在于加载时间
- 使用 js 的 image 对象先加载图片资源,后续再使用,缺陷在于性能
- 使用 XMLHttpRequest 对象请求,缺陷在于跨域
# 预加载工具
PreLoad.js
# 重绘与回流
问题: css 性能会让 js 变慢?
答案:会,频繁触发重绘与回流,会导致UI频繁渲染,最终导致 js 变慢
# 回流(reflow)
当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,这就称为回流。 当页面布局和几何属性改变时就需要回流
# 重绘(repaint)
当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观。风格,而不会影响布局,比如 background-color,则就称为重绘
注意
回流必将引起重绘,而重绘不一定会引起回流
# 触发页面重布局的属性
- 盒子模型相关属性
- 定位属性及浮动属性
- 改变节点内部文字结构属性
# 只触发重绘的属性
# 浏览器新建 DOM 的过程
- 获取 DOM 后分割为多个图层
- 对每个图层的节点计算样式结果(Recalculate style --样式重计算)
- 为每个节点生成图形和位置(Layout -- 回流和重布局)
- 将每个节点绘制填充到图层位图中(Paint Setup 和 Paint --重绘)
- 图层作为纹理上传至GPU
- 符合多个图层到页面上生成最终屏幕图像(Composite Layers --图层重组)
# 优化
# 方法
- 避免使用触发重绘、回流的 CSS 属性
- 将频繁重绘回流的 DOM 元素单独作为一个独立图层,那么这个 DOM 元素的重绘和回流的影响就会限制在在这个单独的图层中
# 实战方法
原理
- 将 DOM 元素变成新的图层
- 以 Chrome 创建图层的条件为例
- 3D 或透视变换(perspective transform)CSS 属性
- 使用加速视频解码的
<video>
节点 - 使用 3D(WebGL)上下文或加速的 2D 上下文的
<canvas>
节点 - 混合插件(如 Flash)
- 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
- 拥有加速 CSS 过滤器的元素
- 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
- 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
- 图层(Layer)不能滥用,滥用反而会导致性能下降
步骤
- 用 translate 替代 top 改变
- 在使用图层的基础上,用 opacity 替代 visibility
- 不要一条一条地修改 DOM 的样式,预定义好 class,然后修改 DOM 的className
- 把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 reflow),然后比修改 100 次,然后把它显示出来
- 不要把 DOM 节点的属性值放在一个循环里当作循环里的变量
- 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
- 动画实现的速度的选择,选择的速度越快,重绘和回流的次数越多,但是动画的平滑度会提高,看业务需求进行选择。
- 对于动画新建图层,启动 GPU 硬件加速,但是不能滥用图层。
Chrome开发者工具中的 Performance,Layer,Rendering 面板可以进行查看
# 浏览器存储
问题:多种浏览器存储方式并存,如何选择?
# Cookie
# 产生缘由
因为 HTTP 请求无状态,所以需要 cookie 去维持客户端状态
# 生成方式
- 服务端生成:http response header 中的 set-cookie,可以设置 httponly,防止恶意使用 js 进行修改
- 客户端生成:js 中可以通过 document.cookie 可以读写 cookie
# 用途
- 用于浏览器端于服务器端的交互
- 客户端自身数据的存储
# 限制
- 作为浏览器存储,大小 4 kb 左右
- 需要设置过期时间 expire
注意
随着技术发展,cookie 存储数据能力被 localstorage 替代
问题:cookie 中在相关域名下面——cdn 的流量损耗
解决方法:cdn 的域名和主站的域名要分开
# LocalStorage
- HTML5 设计出来专门用于浏览器存储的
- 大小为 5 M 左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 浏览器本地缓存方案
# SessionStorage
- 会话级别的浏览器存储
- 大小为 5 M 左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 对于表单信息的维护
# IndexedDB
- IndexedDB 是一种低级 API ,用户客户端存储大量结构化数据,该 API 使用索引来实现对该数据的高性能搜索。虽然 WebStorage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexedDB 提供了一个解决方案
- 为应用创建离线版本
# Service Workers
Service Worker 是一个脚本,浏览器独立于当前网页,将其在后台运行,为实现一些不依赖页面或者用户交互的特性打开了一扇大门。 在未来这些特性将包括推送消息,背景后台同步,geofencing(地理围栏定位),但它将退出的第一个首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应
# 产生的意义
Service Worker 可以在浏览器后台挂起新线程,来缓解 JavaScript 的单线程问题。并且,我们可以用 Service Worker 拦截网络请求进行本地缓存或请求转发,相当于充当服务端与浏览器、浏览器与 Web 应用程序之间的代理服务器。
# 好处
- Service Worker 带来了速度,极大的提高了用户体验。
- Service Worker 可有效加快重复访问网络应用的速度。
- 拥有拦截请求、消息推送、静默更新、地理围栏等服务。
- 可以在客户端通过 indexedDB API 保存持久化信息。
- Service Worker 大量使用 Promise 对象。
# 用途
使用拦截和处理网络请求的能力,去实现一个离线应用 使用 Service Worker 在后台运行同时能和页面通信的能力,去实现大规模后台数据的处理
# 生命周期
# 工具
chrome://serviceworker-internals/
chrome://inspect/#service-workers
# PWA(Progressive Web Apps)
它是一种 Web App 新模型,并不是具体指某一种前沿的技术或者某一个单一的知识点,这是一个渐进式的 Web App,是通过一系列新的 Web 特性,配合优秀的 UI 交互设计,逐步的增强 Web App 的用户体验。
# 三个方向
- 可靠性
- 在没有网络的环境中也能提供基本的页面访问,而不会出现“未连接到互联网”的页面
- 快速性
- 针对网页渲染以及网络数据访问有较好优化
- 融入性(Engaging)
- 应用可以被增加到手机桌面,并且和普通应用一样有全屏、推送等特性
# 工具
chrome插件:lighthouse(现在 chrome 开发工具也自带了)