一、客户端和服务端信息交互模型
1.【问题】:什么是客户端和服务端?
- 客户端:可以向服务器发请求,并接收返回的内容进行处理
- 服务器端:能够接收客户端请求,并且把相关资源信息返回给客户端的
2.【面试题】:当用户在地址栏中输入网址,到最后看到页面,中间都经历了什么?
答:以下图中所讲的7大步骤,即为答案(后面会详细解析每个步骤干的事)。
二、URL地址解析
1.URI/URL/URN的区别
- URL(Uniform Resource Locator):统一资源定位符,根据这个地址能找到对应的资源
- URN(Uniform Resource Name):统一资源名称,一般指国际上通用的(标准的)一些名字(例如:国际统一发版的编号)
- URI(Uniform Resource Identifier):统一资源标识符,URL和URN是URI的子集
2.一个完整的URL解析
【完整的URL】:http://www.zhufengpeixun.cn:80/stu/index.html?from=wx&lx=1#zhenyu
- 协议(http://):传输协议就是,能够把客户端和服务器端通信的信息,进行传输的工具(类似于快递小哥)
+ http 超文本传输协议,除了传递文本,还可以传递媒体资源文件(或者流文件)及XML格式数据 + https 更加安全的http,一般涉及支付的网站都要采用https协议(s:ssl 加密传输) + ftp 文件传输协议(一般应用于把本地资源上传到服务器端) - 域名(www.zhufengpeixun.cn):一个让用户方便记忆的名字(不通过域名,直接用服务器的外网IP也能访问到服务器,但是外网IP很难被记住)
+ 顶级域名 qq.com + 一级域名 www.qq.com + 二级域名 sports.qq.com + 三级域名 kbs.sports.qq.com + .com 国际域名 + .cn 中文域名 + .com.cn + .edu 教育 + .gov 政府 + .io 博客 + .org 官方组织 + .net 系统类 - 端口号(:80):端口号的取值范围0~65535,用端口号来区分同一台服务器上的不同项目
+ http默认端口号:80 + https默认端口号:443 + ftp默认端口号:21 + 如果项目采用的就是默认端口号,我们在书写地址的时候,不用加端口号,浏览器在发送请求的时候会帮我们默认给加上 - 请求资源路径名称(/stu/index.html)
+ 默认的路径或者名称 (xxx.com/stu/ 不指定资源名,服务器会找默认的资源,一般默认资源名是default.html、index.html…当然这些可以在服务器端自己配置) + 注意伪URL地址的处理(URL重写技术是为了增加SEO搜索引擎优化的,动态的网址一般不能被搜索引擎收录,所以我们要把动态网址静态化,此时需要的是重写URL) https://item.jd.hk/2688449.html => https://item.jd.hk/index.php?id=2688449 - 问号传参信息(?from=wx&lx=1)
+ 客户端想把信息传递给服务器,有很多的方式 ??+ URL地址问号传参 ??+ 请求报文传输(请求头和请求主体) + 也可以不同页面之间的信息交互,例如:从列表到详情 - HASH值(#zhenyu)
+ 也能充当信息传输的方式 + 锚点定位 + 基于HASH实现路由管控(不同的HASH值,展示不同的组件和模块)
3.URL编码解析方式
link.onclick = function () {
let url = window.location.href;
window.location.href = "http://www.zhufengpeixun.cn/stu/?from=" + encodeURIComponent(url);
}
三、DNS域名解析
DNS服务器:域名解析服务器,在服务器上存储着 域名<=>服务器外网IP 的相关记录
- 而我们发送请求时候所谓的DNS解析,其实就是根据域名,在DNS服务器上查找到对应服务器的外网IP
DNS优化
- DNS缓存(一般浏览器会在第一次解析后,默认建立缓存,时间很短,只有一分钟左右)
- 减少DNS解析次数(一个网站中我们需要发送请求的域名和服务器尽可能少即可)
- DNS预获取(dns-prefetch):在页面加载开始的时候,就把当前页面中需要访问其他域名(服务器)的信息进行提前DNS解析,以后加载到具体内容部分可以不用解析了
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
</body>
</html>
四、建立TCP连接(TCP三次握手)
五、发送HTTP请求
1.HTTP请求报文
- 请求报文:所有经过传输协议,客户端传递给服务器的内容,都被成为请求报文
+ 起始行 + 请求头(请求首部) + 请求主体
2.强缓存、协商缓存、数据缓存
缓存位置:
-
Memory Cache : 内存缓存 -
Disk Cache:硬盘缓存
打开网页:查找 disk cache 中是否有匹配,如有则使用,如没有则发送网络请求
普通刷新 (F5):因TAB没关闭,因此memory cache是可用的,会被优先使用,其次才是disk cache
强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache,服务器直接返回 200 和最新内容
强缓存【 Expires / Cache-Control】
浏览器对于强缓存的处理:根据第一次请求资源时返回的响应头来确定的
-
Expires:缓存过期时间,用来指定资源到期的时间(HTTP/1.0) -
Cache-Control:cache-control: max-age=2592000第一次拿到资源后的2592000秒内(30天),再次发送请求,读取缓存中的信息(HTTP/1.1) -
两者同时存在的话,Cache-Control优先级高于Expires
协商缓存【 Last-Modified / ETag】
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
数据缓存
六、服务器得到并处理请求(HTTP响应内容)
1.响应报文和HTTP报文
- 响应报文:所有经过传输协议,服务器返回给客户端的内容,都被成为响应报文 + HTTP状态码 + 响应头 + 响应主体
- HTTP报文:请求报文+响应报文
=>谷歌浏览器F12 =>Network(所有客户端和服务器端的交互信息在这里都可以看到) =>点击某一条信息,在右侧可以看到所有的HTTP报文信息
2.HTTP状态码
1~5开头,三位数字
- 200 OK:成功
- 201 CREATED:一般应用于告诉服务器创建一个新文件,最后服务器创建成功后返回的状态码
- 204 NO CONTENT:对于某些请求(例如:PUT或者DELETE),服务器不想处理,可以返回空内容,并且用204状态码告知
- 301 Moved Permanently:永久重定向(永久转移)
- 302 Moved Temporarily:临时转移,很早以前基本上用302来做,但是现在主要用307来处理这个事情,307的意思就是临时重定向Temporary
Redirect =>主要用于:服务器的负载均衡等 - 304 Not Modified:设置HTTP的协商缓存
- 400 Bad Request:传递给服务器的参数错误
- 401 Unauthorized:无权限访问
- 404 Not Found:请求地址错误
- 500 Internal Server Error:未知服务器错误
- 503 Service Unavailable:服务器超负荷
【Question】:如何构建一个网站?
七、客户端渲染页面
一个需要注意的地方:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
let link = document.getElementById('link');
console.log(link);
</script>
</head>
<body>
<button id="link">我是按钮</button>
</body>
</html>
1.浏览器渲染页面的步骤
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Dispay:将像素发送给GPU,展示在页面上
2.DOM的重绘和回流
- 重绘(repaint):元素样式的改变(但宽高、大小、位置等不变)
+ 如color、visibility、outline、background-color等 - 回流(reflow):元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染
- 【注意】:回流一定会触发重绘,而重绘不一定会回流
3.前端性能优化之:避免DOM的回流
- 放弃传统操作DOM的时代,基于vue、react开始数据影响视图模式
+ mvvm / mvc / dom diff .... - 分离读写操作(现代的浏览器都有渲染队列的机制)
+ offsetTop、offsetWidth、clientTop、clientHeight、scrollTop、getComputedStyle、currentStyle .....都会刷新渲染队列 - 样式集中改变
+ div.style.cssText = 'width:20px;height:20px;' +div.className = 'box'; - 缓存布局信息
+div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px'; 改为: var curLeft = div.offsetLeft ; var curTop = div.offsetTop ; div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px'; - 元素批量修改
+文档碎片:creatDocumentFragment +模板字符串拼接 - 动画效果应用到position属性为absolute或fixed的元素上(脱离文档流)
- CSS3硬件加速(GPU加速)
+比起考虑如何减少回流重绘,我们更期望的是,根本不要回流和重绘:transform、opacity、filters.....这些属性会触发硬件加速,不会引发回流和重绘 +可能引发的坑:过多使用会占用大量内存,性能消耗严重,有时候会导致字体模糊等 - 牺牲平滑度换取速度
- 避免table布局和使用css的JavaScript表达式
减少DOM的回流:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>减少DOM回流-珠峰培训</title>
<style>
.box {
margin: 20px auto;
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="box">
</div>
<script>
</script>
<script>
</script>
<script>
</script>
</body>
</html>
八、断开TCP连接(四次挥手)
九、前端性能优化汇总
1.减少HTTP请求次数和请求的大小 (三大类)
- 文件的合并和压缩:(1)(6)
- 延迟加载:(3)(4)
- 用新的文件格式代替传统文件格式:(2)(5)(8)
(1)CSS SPRITE(雪碧图、图片精灵)技术 (2)使用字体图标(ICON FONT)或者 SVG 等矢量图
- 减少 HTTP请求次数或者减少请求内容的大小
- 渲染更快:因为他们是基于代码渲染的,而对于位图(png/jpg/gif)是需要先把图片编码再渲染
- 不容易失真变形
- 也可以使用 webp 格式图片,这种格式要小一些(但是需要服务器端支持这种格式的请求处理)
(3)图片懒加载(延迟加载)技术
- 第一次加载页面的时候不去请求真实的图片,提高第一次渲染页面的速度
- 当页面加载完,把出现在用户视野区域中的图片做真实加载,没有出现的先不加载(节约流量,也能减少对服务器的请求压力)
- 对于数据我们也尽可能分批加载(不要一次请求过多的数据,例如分页技术)
(4)音视频文件取消预加载(preload=‘none’),这样可以增加第一次渲染页面的速度,当需要播放的时候再加载
(5)客户端和服务器端的数据传输尽可能基于 JSON 格式完成,XML 格式比 JSON 格式要大一些(还可以基于二进制编码或者文件流格式,这种格式比文件传输好很多) (6)把页面中的 CSS/JS/图片等文件进行合并压缩
- 合并:争取 CSS 和 JS 都只导入一个(webpack 可以实现自动合并压缩)
- 压缩:基于 webpack 可以压缩、对于图片自己找工具先压缩、还可以使用服务器的 GZIP 压缩
(7) 图片地图:对于多次调取使用的图片(尤其是背景图),我们尽可能把它提取成为公共的样式,而不是每一次重新设置 background (8) 图片 BASE64(用 BASE64 码代表图片,减少 HTTP请求,增加浏览器渲染的速度,所以真实项目中,尤其是移动端,如果图片加载缓慢,可能 BASE64 一下就好了;但是,BASE64会导致文件中的代码超级恶心,不利于维护和开发,所以少使用;webpack 中可以配置图片的BASE64;)
function func(){
func();
}
func();
function func(){
setTimeout(func,0);
}
func();
let obj1={
name:'OBJ1'
};
let obj2={
name:'OBJ2',
x:obj1
};
obj1.x=obj2;
2.建立缓存机制
网络缓存:(1)(2)(3)(6)(7) 本地缓存:(4) 加服务器:(5)
- (1)把不经常更改的静态资源做缓存处理(一般做的是 304 或者 ETAG 等协商缓存)
- (2)建立 Cache-Control 和 Expires HTTP 的强缓存
- (3)DNS 缓存或者预处理(DNS PREFETCH),减少 DNS 的查找
- (4)设置本地的离线存储(manifest)或者把一些不经常更改的数据做本地存储(webstorage、indexdb)等
- (5)有钱就做 CDN(地域分布式服务器),还有一个财大气粗的方式:加服务器
- (6)建立 Connection:keep-alive TCP 长连接
- (7)使用 HTTP2 版本协议(现在用的一般都是 HTTP1.1), 可以多条 TCP 通道共存 =>管道化链接
- (8)一个项目分为不同的域(不同的服务器),例如:资源 WEB 服务器、数据服务器、图片服务器、视频服务器等,这样合理利用服务器资源,但是导致过多的 DNS 解析
3.代码上的优化
重点记住:(1)~(8)
- (1)减少直接对 DOM 的操作(原因是减少 DOM 的回流和重绘…),当代项目基本上都是基于 mvvm/mvc 数据驱动视图渲染的,对 DOM 的操作框架本身完成,性能要好很多
- (2)低耦合高内聚(基于封装的方式:方法封装、插件、组件、框架、类库等封装,减少页面中的冗余代码,提高代码使用率)
- (3)减少对闭包的使用(因为过多使用闭包会产生很多不销毁的内存,处理不好的话,会导致内存溢出“栈溢出”),减少闭包的嵌套(减少作用域链的查找层级)
- (4)对于动画来说:能用 CSS 解决的不用 JS(能够用 transform 处理的,不用传统的 css 样式,因为 transform 开启硬件加速,不会引发回流,再或者使用定位的元素也会好很多,因为定位的元素脱离文档流,不会对其它元素的位置造成影响),能用requestAnimationFrame 解决的不用定时器 (requestAnimationFrame 还有一个好处,当页面处于休眠无访问状态,动画会自己暂停,直到恢复访问才开始,而定时器是不论什么状态,只要页面不管,就一直处理)
- (5)尽可能使用事件委托
- (6)函数的防抖和节流
- (7)避免出现死循环或者嵌套循环(嵌套循环会成倍增加循环的次数)
- (8)项目中尽可能使用异步编程来模拟出多线程的效果,避免主线程阻塞(异步操作基于 PROMISE 设计模式来管理)
- (9)避免使用 iframe(因为 iframe 会嵌入其它页面,这样父页面渲染的时候,还要同时把子页面也渲染了,渲染进度会变慢)
- (10)JS 中不要使用 with
- (11)避免使用 CSS 表达式
- (12)减少使用 eval(主要原因是防止压缩代码的时候,由于符号书写不合规,导致代码混乱)
- (13)减少 filter 滤镜的使用
- (14)尽可能减少选择器的层级(选择器是从右向左解析) .box a{} 和 a{}
- (15)尽可能减少 TABLE 布局
- (16)手动回收堆栈内存(赋值为 null)
|