内容简介:动态的加载静态的资源
概括
涉及到的分类
- 网络层面
- 构建层面
- 浏览器渲染层面
- 服务端层面
涉及到的功能点
PWA Vue-SSR
资源合并与压缩
http 请求的过程及潜在的性能优化点
- 理解
减少http请求数量和减少请求资源大小两个优化要点 - 掌握
压缩与合并的原理 - 掌握通过
在线网站和fis3两种实现压缩与合并的方法
浏览器的一个请求从发送到返回都经历了什么
动态的加载静态的资源
-
dns是否可以通过缓存减少dns查询时间 - 网络请求的过程走最近的网络环境
- 相同的静态资源是否可以缓存
- 能否减少
http请求大小 - 能否减少
http请求数量 - 服务端渲染
资源的合并与压缩设计到的性能点
http
html 压缩
HTML 代码压缩就是压缩这些在文本文件中有意义,但是在 HTML 中不显示的字符,包括 空格 , 制表符 , 换行符 等,还有一些其他意义的字符,如 HTML 注释也可以被压缩
意义
- 大型网站意义比较大
如何进行 html 的压缩
- 使用在线网站进行压缩(走构建 工具 多,公司级在线网站手动压缩小)
-
node.js提供了html-minifier工具 - 后端
模板引擎渲染压缩
css 及 js 压缩
css 的压缩
-
无效代码删除
- 注释、无效字符
-
css语义合并
css 压缩的方式
- 使用在线网站进行压缩
- 使用
html-minifier对html中的css进行压缩 - 使用
clean-css对css进行压缩
js 的压缩语混乱
-
无效字符的删除
- 空格、注释、回车等
- 剔除注释
-
代码语意的缩减和优化
- 变量名缩短(
a,b)等
- 变量名缩短(
-
代码保护
- 前端代码是透明的,客户端代码用户是可以直接看到的,可以轻易被窥探到逻辑和漏洞
js 压缩的方式
- 使用在线网站进行压缩
- 使用
html-minifier对html中的js进行压缩 - 使用
uglifyjs2对js进行压缩
不合并文件可能存在的问题
N-1
文件合并缺点
-
首屏渲染问题
- 文件合并之后的
js变大,如果首页的渲染依赖这个js的话,整个页面的渲染要等js请求完才能执行 - 如果首屏只依赖
a.js,只要等a.js完成后就可执行 - 没有通过服务器端渲染,现在框架都需要等合并完的文件请求完才能执行,基本都需要等文件合并后的
js
- 文件合并之后的
-
缓存失效问题
js js
文件合并对应缺点的处理
- 公共库合并
-
不同页面的合并
- 不同页面
js单独打包
- 不同页面
- 见机行事,随机应变
文件合并对应方法
nodejs
图片相关优化
一张 JPG 的解析过程
jpg 有损压缩:虽然损失一些信息,但是肉眼可见影响并不大
png8 / png24 / png32 之间的区别
-
png8----256色+ 支持透明 -
png24----2^24+ 不支持透明 -
png32---2^24+支持透明
文件大小 + 色彩丰富程度
png32 是在 png24 上支持了透明,针对不同的业务场景选择不同的图片格式很重要
不同的格式图片常用的业务场景
不同格式图片的特点
-
jpg有损压缩,压缩率高,不支持透明 -
png支持透明,浏览器兼容性好 -
webp压缩程度更好,在ios webview中有兼容性问题 -
svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景(尽量使用,绘制能力有限,图片简单用的比较多)
不同格式图片的使用场景
-
jpg:大部分不需要透明图片的业务场景 -
png:大部分需要透明图片的业务场景 -
webp:android全部(解码速度和压缩率高于jpg和png,但是iossafari还没支持) -
svg:图片样式相对简单的业务场景
图片压缩的几种情况
- 针对真实图片情况,舍弃一些相对无关紧要的色彩信息
-
CSS雪碧图:把你的网站用到的一些图片整合到一张单独的图片中- 优点:减少
HTTP请求的数量(通过backgroundPosition定位所需图片) - 缺点:整合图片比较大时,加载比较慢(如果这张图片没有加载成功,整个页面会失去图片信息)
facebook官网任然在用,主要pc用的比较多,相对性能比较强
- 优点:减少
-
Image-inline:将图片的内容嵌到html中(减少网站的HTTP请求)-
base64信息,减少网站的HTTP请求,如果图片比较小比较多,时间损耗主要在请求的骨干网络
-
-
使用矢量图- 使用
SVG进行矢量图的绘制 - 使用
icon-font解决icon问题
- 使用
-
在android下使用webp-
webp的优势主要体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量; - 同时具备了无损和有损的压缩模式、
Alpha透明以及动画的特性,在JPEG和PNG上的转化效果都非常优秀、稳定和统一
-
css 和 js 的装载与执行
HTML页面加载渲染的过程
一个网站在浏览器端是如何进行渲染的
HTML渲染过程中的一些特点
-
顺序执行,并发加载
-
词法分析:从上到下依次解析
- 通过
HTML生成Token对象(当前节点的所有子节点生成后,才会通过next token获取到当前节点的兄弟节点),最终生成Dom Tree
- 通过
- 并发加载:资源请求是并发请求的
-
并发上限
Chrome
-
-
是否阻塞
-
css阻塞-
css在head中通过link引入会阻塞页面的渲染- 如果我们把
css代码放在head中去引入的话,那么我们整个页面的渲染实际上就会等待head中css加载并生成css树,最终和DOM整合生成RanderTree之后才会进行渲染 - 为了浏览器的渲染,能让页面显示的时候视觉上更好。
- 如果我们把
-
-
避免某些情况,如:假设你放在页面最底部,用户打开页面时,有可能出现,页面先是显示一大堆文字或图片,自上而下,丝毫没有排版和样式可言。最后,页面又恢复所要的效果
- `css`不阻塞`js`的加载,但阻塞`js`的执行
- `css`不阻塞外部脚步的加载(`webkit preloader 预资源加载器`)
- `js`阻塞
- 直接通过`<script src>`引入会阻塞后面节点的渲染
- `html parse`认为`js`会动态修改文档结构(`document.write`等方式),没有进行后面文档的变化
- `async`、`defer`(`async`放弃了依赖关系)
- `defer`属性(`<script src="" defer></script>`)
(这是延迟执行引入的 js 脚本(即脚本加载是不会导致解析停止,等到 document 全部解析完毕后, defer-script 也加载完毕后,在执行所有的 defer-script 加载的 js 代码,再触发 Domcontentloaded )
- `async`属性(`<script src="" async></script>`)
- 这是异步执行引入的`js`脚本文件
- 与`defer`的区别是`async`会在加载完成后就执行,但是不会影响阻塞到解析和渲染。但是还是会阻塞`load`事件,所以`async-script`会可能在`DOMcontentloaded`触发前或后执行,但是一定会在`load`事件前触发。
懒加载与预加载
懒加载
- 图片进入可视区域之后请求图片资源
- 对于电商等图片很多,页面很长的业务场景适用
- 减少无效资源的加载
- 并发加载的资源过多会会阻塞js的加载,影响网站的正常使用
img src 被设置之后, webkit 解析到之后才去请求这个资源。所以我们希望图片到达可视区域之后, img src 才会被设置进来,没有到达可视区域前并不现实真正的 src ,而是类似一个 1px 的占位符。
场景:电商图片
预加载
- 图片等静态资源在使用之前的提前请求
- 资源使用到时能从缓存中加载,提升用户体验
- 页面展示的依赖关系维护
场景:抽奖
懒加载原生 js 和 zepto.lazyload
原理
先将 img 标签中的 src 链接设为同一张图片(空白图片),将其真正的图片地址存储再 img 标签的自定义属性中(比如 data-src )。当 js 监听到该图片元素进入可视窗口时,即将自定义属性中的地址存储到 src 属性中,达到懒加载的效果。
注意问题:
- 关注首屏处理,因为还没滑动
- 占位,图片大小首先需要预设高度,如果没有设置的话,会全部显示出来
var viewheight = document.documentElement.clientHeight //可视区域高度
function lazyload(){
var eles = document.querySelectorAll('img[data-original][lazyload]')
Array.prototype.forEach.call(eles,function(item,index){
var rect;
if(item.dataset.original === '') return;
rect = item.getBoundingClientRect(); //返回元素的大小及其相对于视口的
if(rect.bottom >= 0 && rect.top < viewheight){
!function(){
var img = new Image();
img.src = item.dataset.url;
img.onload = function(){
item.src = img.src
}
item.removeAttribute('data-original');
item.removeAttribute('lazyload');
}()
}
})
}
lazyload()
document.addEventListener('scroll',lazyload)
预加载原生 js 和 preloadJS 实现
预加载实现的几种方式
- 第一种方式:直接请求下来
<img src="https://user-gold-cdn.xitu.io/2019/2/21/1690d1b216cbfa18" style="display: none"/> <img src="https://user-gold-cdn.xitu.io/2019/2/21/1690d1b21b70c8d2" style="display: none"/> <img src="https://user-gold-cdn.xitu.io/2019/2/21/1690d1b216e17e26" style="display: none"/> <img src="https://user-gold-cdn.xitu.io/2019/2/21/1690d1b217b3ae59" style="display: none"/>
- 第二种方式:
image对象
var image = new Image(); image.src = "www.pic26.com/dafdafd/safdas.jpg";
-
第三种方式:
xmlhttprequest- 缺点:存在跨域问题
- 优点:好控制
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.onreadystatechange = callback;
xmlhttprequest.onprogress = progressCallback;
xmlhttprequest.open("GET","http:www.xxx.com",true);
xmlhttprequest.send();
function callback(){
if(xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200){
var responseText = xmlhttprequest.responseText;
}else{
console.log("Request was unsuccessful:" + xmlhttprequest.status);
}
}
function progressCallback(){
e = e || event;
if(e.lengthComputable){
console.log("Received"+e.loaded+"of"+e.total+"bytes")
}
}
PreloadJS模块
- 本质 : 权衡浏览器加载能力,让它尽可能饱和利用起来
重绘与回流
css 性能让 javascript 变慢
要把 css 相关的外部文件引入放进 head 中,加载 css 时,整个页面的渲染是阻塞的,同样的执行 javascript 代码的时候也是阻塞的,例如 javascript 死循环。
一个线程 => javascript解析 一个线程 => UI渲染
这两个线程是互斥的,当 UI 渲染的时候, javascript 的代码被终止。当 javascript 代码执行, UI 线程被冻结。所以 css 的性能让 javascript 变慢。
频繁触发重绘与回流,会导致UI频繁渲染,最终导致js变慢
什么是重绘和回流
回流
- 当
render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就成为回流(reflow) - 当
页面布局和几何属性改变时,就需要回流
重绘
- 当
render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不影响布局,比如background-color。就称重绘
关系
用到 chrome 分析 performance
回流必将引起重绘,但是重绘不一定会引起回流
避免重绘、回流的两种方法
触发页面重布局的一些css属性
-
盒子模型相关属性会触发重布局
width height padding margin display border-width border min-height
-
定位属性及浮动也会触发重布局
top bottom left right position float clear
- 改变节点内部文字结构也会触发重布局
-
text-align -
overflow-y -
font-weight -
overflow -
font-family -
line-height -
vertical-align -
white-space -
font-size
优化点:使用不触发回流的方案替代触发回流的方案
只触发重绘不触发回流
-
color -
border-style、border-radius -
visibility -
text-decoration -
background、background-image、background-position、background-repeat、background-size -
outline、outline-color、outline-style、outline-width -
box-shadow
新建DOM的过程
- 获取
DOM后分割为多个图层 - 对每个图层的节点计算样式结果(
Recalculate style样式重计算) - 为每个节点生成图形和位置(
Layout回流和重布局) - 将每个节点绘制填充到图层位图中(
Paint Setup和Paint重绘) - 图层作为纹理上传至
gpu - 符合多个图层到页面上生成最终屏幕图像(
Composite Layers图层重组)
浏览器绘制 DOM 的过程是这样子的:
- 获取 DOM 并将其分割为多个层(
layer),将每个层独立地绘制进位图(bitmap)中 - 将层作为纹理(
texture)上传至GPU,复合(composite)多个层来生成最终的屏幕图像 -
left/top/margin之类的属性会影响到元素在文档中的布局,当对布局(layout)进行动画时,该元素的布局改变可能会影响到其他元素在文档中的位置,就导致了所有被影响到的元素都要进行重新布局,浏览器需要为整个层进行重绘并重新上传到GPU,造成了极大的性能开销。 -
transform属于合成属性(composite property),对合成属性进行transition/animation动画将会创建一个合成层(composite layer),这使得被动画元素在一个独立的层中进行动画。 - 通常情况下,浏览器会将一个层的内容先绘制进一个位图中,然后再作为纹理(
texture)上传到GPU,只要该层的内容不发生改变,就没必要进行重绘(repaint),浏览器会通过重新复合(recomposite)来形成一个新的帧。
chrome 创建图层的条件
将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流的影响只会在这个图层中
-
3D或透视变换 -
CSS属性使用加速视频解码的<video>元素 - 拥有
3D(WebGL) 上下文或加速的 -
2D上下文的<canvas>元素 - 复合插件(如
Flash) - 进行
opacity/transform动画的元素拥有加速 -
CSS filters的元素元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里) - 元素有一个
z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
总结:对布局属性进行动画,浏览器需要为每一帧进行重绘并上传到 GPU 中对合成属性进行动画,浏览器会为元素创建一个独立的复合层,当元素内容没有发生改变,该层就不会被重绘,浏览器会通过重新复合来创建动画帧
gif图
总结
- 尽量避免使用触发
回流、重绘的CSS属性 - 将
重绘、回流的影响范围限制在单独的图层(layers)之内 - 图层合成过程中消耗很大页面性能,这时候需要平衡考虑重绘回流的性能消耗
实战优化点总结
-
用
translate替代top属性-
top会触发layout,但translate不会
-
-
用
opacity代替visibility-
opacity不会触发重绘也不会触发回流,只是改变图层alpha值,但是必须要将这个图片独立出一个图层 -
visibility会触发重绘
-
- 不要一条一条的修改
DOM的样式,预先定义好class,然后修改DOM的className -
把DOM离线后修改,比如:先把DOM给display:none(有一次reflow),然后你修改100次,然后再把它显示出来 -
不要把
DOM节点的属性值放在一个循环里当成循环的变量-
offsetHeight、offsetWidth每次都要刷新缓冲区,缓冲机制被破坏 - 先用变量存储下来
-
-
不要使用
table布局,可能很小的一个小改动会造成整个table的重新布局-
div只会影响后续样式的布局
-
-
动画实现的速度的选择
performance
-
对于动画新建图层
-
启用
gpu硬件加速(并行运算),gpu加速意味着数据需要从cpu走总线到gpu传输,需要考虑传输损耗.transform:translateZ(0) transform:translate3D(0)
-
浏览器存储
cookies
多种浏览器存储方式并存,如何选择?
- 因为
http请求无状态,所以需要cookie去维持客户端状态 -
cookie的生成方式:-
http-->response header-->set-cookie -
js中可以通过document.cookie可以读写cookie -
cookie的使用用处:- 用于浏览器端和服务器端的交互(用户状态)
- 客户端自身数据的存储
-
-
expire:过期时间 -
cookie的限制:4kb expire
- 重要属性:
httponly不支持js读写(防止收到模拟请求攻击) - 不太作为存储方案而是用于维护客户关系
-
优化点:
cookie中在相关域名下面cdn cdn
localStorage
localstorage
HTML5 5M
sessionstorage
5M
indexedDB
-
IndexedDB是一种低级API,用于客户端存储大量结构化数据。该API使用索引来实现对该数据的高性能搜索。虽然Web -
Storage对于存储叫少量的数据很管用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexedDB提供了一个解决方案。
为应用创建离线版本
-
cdn域名不要带cookie -
localstorage存库、图片
cookie 种在主站下,二级域名也会携带这个域名,造成流量的浪费
Service Worker 产生的意义
PWA 与 Service Worker
-
PWA(Progressive Web Apps)是一种Web App新模型,并不是具体指某一种前言的技术或者某一个单一的知识点,我们从英文缩写来看就能看出来,这是一个渐进式的Web App,是通过一系列新的Web特性,配合优秀的UI交互设计,逐步增强Web App的用户体验
PWA 与 Service worker
chrome 插件 lighthouse
检测是不是一个渐进式 web app
- 当前手机在弱网环境下能不能加载出来
- 离线环境下能不能加载出来
特点
Engaging
service worker
service worker 是一个脚本,浏览器独立于当前页面,将其在后台运行,为实现一些不依赖页面的或者用户交互的特性打开了一扇大门。在未来这些特性将包括消息推送,背景后台同步, geofencing (地理围栏定位),但他将推出的第一个首要的特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应。
案例分析
chrome://serviceworker-internals/ chrome://inspect/#service-worker/
service worker 网络拦截能力,存储 Cache Storage ,实现离线应用
indexedDB
callback && callback()写法
相当于
if(callback){
callback();
}
cookie 、 session 、 localStorage 、 sessionStorage 基本操作
indexedDB 基本操作
object store:对象存储 本身就是结构化存储
function openDB(name, callback) {
//建立打开indexdb indexedDB.open
var request = window.indexedDB.open(name)
request.onerror = function(e) {
console.log('on indexedDB error')
}
request.onsuccess = function(e) {
myDB.db = e.target.result
callback && callback()
}
//from no database to first version,first version to second version...
request.onupgradeneeded = function() {
console.log('created')
var store = request.result.createObjectStore('books', {
keyPath: 'isbn'
})
console.log(store)
var titleIndex = store.createIndex('by_title', 'title', {
unique: true
})
var authorIndex = store.createIndex('by_author', 'author')
store.put({
title: 'quarry memories',
author: 'fred',
isbn: 123456
})
store.put({
title: 'dafd memories',
author: 'frdfaded',
isbn: 12345
})
store.put({
title: 'dafd medafdadmories',
author: 'frdfdsafdafded',
isbn: 12345434
})
}
}
var myDB = {
name: 'tesDB',
version: '2.0.1',
db: null
}
function addData(db, storeName) {
}
openDB(myDB.name, function() {
// myDB.db = e.target.result
// window.indexedDB.deleteDatabase(myDB.name)
});
//删除indexedDB
indexDB 事务
transcation 与 object store 建立关联关系来操作 object store
建立之初可以配置
var transcation = db.transcation('books', 'readwrite')
var store = transcation.objectStore('books')
var data =store.get(34314)
store.delete(2334)
store.add({
title: 'dafd medafdadmories',
author: 'frdfdsafdafded',
isbn: 12345434
})
Service Worker 离线应用
serviceworker 需要 https 协议
如何实现 ServiceWorker 与主页面之间的通信
缓存
期望大规模数据能自动化缓存,而不是手动进行缓存,需要浏览器端和服务器端协商一种缓存机制
- Cache-Control所控制的缓存策略
- last-modified 和 etage以及整个服务端浏览器端的缓存流程
- 基于node实践以上缓存方式
httpheader
可缓存性
public private no-cache only-if-cached
到期
-
max-age=<seconds>:设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与Expires相反,时间是相对于请求的时间。 -
s-maxage=<seconds>:覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。cdn缓存 -
max-stale[=<seconds>]
表明客户端愿意接收一个已经过期的资源。 可选的设置一个时间(单位秒),表示响应不能超过的过时时间。
-
min-fresh=<seconds>
表示客户端希望在指定的时间内获取最新的响应。
重新验证 和 重新加载
重新验证
-
must-revalidate:缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。 -
proxy-revalidate:与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。 -
immutable:表示响应正文不会随时间而改变。资源(如果未过期)在服务器上不发生改变,因此客户端不应发送重新验证请求头(例如If-None-Match或If-Modified-Since)来检查更新,即使用户显式地刷新页面。在Firefox中,immutable只能被用在https:// transactions.
重新加载
-
no-store:缓存不应存储有关客户端请求或服务器响应的任何内容。 -
no-transform:不得对资源进行转换或转变。Content-Encoding,Content-Range,Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。no-transform指令不允许这样做。
Expires
- 缓存过期时间,用来指定资源到期的时间,是服务器端的时间点
- 告诉浏览器在过期时间前浏览器可以直接从浏览器缓存中存取数据,而无需再次请求
-
expires是http1.0的时候的 -
http1.1时候,我们希望cache的管理统一进行,max-age优先级高于expires,当有max-age在的时候expires可能就会被忽略。 - 如果没有设置
cache-control时候会使用expires
Last-modified 和 If-Modified-since
- 基于客户端和服务器端协商的缓存机制
-
last-modified-->response header
if-modified-since-->request header - 需要与
cache-control共同使用
last-modified 有什么缺点?
- 某些服务端不能获取精确的修改时间
- 文件修改时间改了,但文件的内容却没有变
Etag 和 If-none-match
- 文件内容的hash值
-
etag-->reponse header
if-none-match-->request header - 需要与
cache-control共同使用
好处:
if-modified-since etage
流程图
服务端性能优化
服务端用的node.js因为和前端用的同一种语言,可以利用服务端运算能力来进行相关的运算而减少前端的运算
vue vue-ssr
vue渲染面临的问题
先加载vue.js => 执行vue.js代码 => 生成html
- 以前没有前端框架时,
- 用jsp/php
在服务端进行数据的填充,发送给客户端就是已经填充好数据的html
- 使用
jQuery异步加载数据 - 使用
React和Vue前端框架
怎么在 vue 这个层面对性能进行提升
- 构建层的模板编译(
runtime,compile拆开),构建层做模板编译工作。webpack构建时候,统一,直接编译成runtime可以执行的代码 - 数据无关的
prerender的方式 - 服务端渲染
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。