DNS(Domain Name System,域名系统)是指由域名到ip地址的映射,DNS协议运行在udp协议之上,使用端口号53。该域名->ip地址的映射分两种:静态映射(本地维护一个映射表),动态映射(需要一套域名解析系统)
主机名 : 三级域名.二级域名.顶级域名
一个完整的域名由二个或二个以上部分组成,各部分之间用英文的句号".“来分隔,倒数第一个”.“的右边部分称为顶级域名(TLD,也称为一级域名,包含一个合法字符串,和一个域名后缀),顶级域名的左边部分字符串到下个”."为止称为二级域名(SLD),二级域名的左边部分称为三级域名,以此类推,每一级的域名控制它下一级域名的分配。
本地主机、域名服务器中都有缓存,保存了域名->ip地址的映射关系,能加快查询速度。本地主机仅仅向本地DNS进行查询,而本地DNS则分别需要向根、顶级、二级、三级等域名服务器查询,最终得到查询结果,返回给客户端。下面举个实例进行分析
当主机A需要和 www.abc.com
主机进行通信时,它就需要知道www.abc.com
域名所对应的ip地址。下面是它需要做的几个查询步骤:
(1)主机A先查找本地缓存,看有没有该主机所对应的ip地址,若没有,再向本地域名服务器(DNS)发出查询请求;
(2)同样,本地DNS先查找本地缓存,看有没有该主机所对应的ip地址,若没有,则直接向根DNS发出请求;
(3)根DNS回复本地DNS,告诉本地DNS “.com”顶级域名服务器的ip地址;
(4)本地DNS收到.com域名服务器的ip地址之后,向该.com DNS 发出查询请求,查询.abc.com的二级域名服务器的ip地址;
(5)同理,.com告诉本地DNS .abc.com的地址;
(6)本地DNS向.abc.com发出查询请求,询问www.abc.com
的地址;
(7).abc.com二级域名服务器告诉本地DNS www.abc.com
主机的ip地址;
(8)本地DNS得到主机www.abc.com
的ip地址后,在本地缓存下来,并发送给主机A;
(9)主机A就得到www.abc.com
主机的ip地址。
DNS劫持是通过劫持DNS服务器,从而修改某域名的解析结果,将错误ip地址反馈给客户,导致用户不能正常上网或者访问的是虚假网址,即:DNS劫持的目的就是为了告诉用户一个错误的ip地址,这样就能达到钓鱼、窃取用户信息的目的。它发生在本主机与本地DNS服务器之间的通信。
DNS劫持的表现为,你访问www.abc.com
网址是,结果却被解析成百度的首页。
浏览器加载和渲染html的顺序
用户输入网址(假设是个 HTML 页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回 HTML 文件;
浏览器开始载入 HTML 代码,发现 标签内有一个 标签引用外部 CSS 文件;
浏览器又发出 CSS 文件的请求,服务器返回这个 CSS 文件;
浏览器继续载入 HTML 中 部分的代码,并且 CSS 文件已经拿到手了,可以开始渲染页面了;
浏览器在代码中发现一个 标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
浏览器发现了一个包含一行 JavaScript 代码的
各个步骤的加载渲染时间,可以通过各个浏览器的插件跟踪到,例如IE的 httpwatch、火狐的firebug等等。
如何加快HTML页面加载速度
1. 页面减肥
a. 页面的肥瘦是影响加载速度最重要的因素。
b. 删除不必要的空格、注释。
c. 将inline的script和css移到外部文件。
d. 可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥。
2. 减少文件数量
a. 减少页面上引用的文件数量可以减少HTTP连接数。
b. 许多JavaScript、CSS文件可以合并最好合并,人家财帮子都把自己的JavaScript. functions和Prototype.js合并到一个base.js文件里去了。
3. 减少域名查询
a. DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好。
4. 缓存重用数据
a. 对重复使用的数据进行缓存。
5. 优化页面元素加载顺序
a. 首先加载页面最初显示的内容和与之相关的JavaScript和CSS,然后加载HTML相关的东西,像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载。
6. 减少inline JavaScript的数量
a. 浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大。
b. 不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容。
7. 使用现代CSS和合法的标签
a. 使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片。
b. 使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥。
8. Chunk your content
a. 不要使用嵌套table,而使用非嵌套table或者div。将基于大块嵌套的table的layout分解成多个小table,这样就不需要等到整个页面(或大table)内容全部加载完才显示。
9. 指定图像和table的大小
a. 如果浏览器可以立即决定图像或table的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作。
b. 这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。
c. image使用height和width。
参考文章:https://segmentfault.com/a/1190000013119813
浏览器包括4个进程:
渲染进程主要包括GUI渲染线程、Js引擎线程、事件循环线程、定时器线程、http异步线程。
先看看浏览器得到一个网站资源后干了哪些事:
GUI就是来干这个事情的,如果修改了一些元素的颜色或者背景色,页面就会重绘(Repaint),如果修改元素的尺寸,页面就会回流(Reflow),当页面需要Repaing和Reflow时GUI多会执行,进行页面绘制。
这里提示一点:Reflow比Repaint的成本更高,在js性能优化中会将如何避免Reflow和Repaint
js引擎线程就是js内核,负责解析与执行js代码,也称为主线程。浏览器同时只能有一个JS引擎线程在运行JS程序,所以js是单线程运行的。
需要注意的是,js引擎线程和GUI渲染线程同时只能有一个工作,js引擎线程会阻塞GUI渲染线程
<html>
<body>
<div id="div1"> a div>
<script>
document.getElementById('div1').innerHTML = 'b'
script>
<div id='div2'> div2 div>
body>
html>
在浏览器渲染的时候遇到
事件循环线程用来管理控制事件循环,并且管理着一个事件队列(task queue),当js执行碰到事件绑定和一些异步操作时,会把对应的事件添加到对应的线程中(比如定时器操作,便把定时器事件添加到定时器线程),等异步事件有了结果,便把他们的回调操作添加到事件队列,等待js引擎线程空闲时来处理。
由于js是单线程运行,所以不能抽出时间来计时,只能另开辟一个线程来处理定时器任务,等计时完成,把定时器要执行的操作添加到事件任务队列尾,等待js引擎线程来处理。这个线程就是定时器线程。
当执行到一个http异步请求时,便把异步请求事件添加到异步请求线程,等收到响应(准确来说应该是http状态变化),把回调函数添加到事件队列,等待js引擎线程来执行。
上面介绍了渲染进程中的5个主要的线程,可能看完上面对各个线程简单的介绍,还有点不明白他们之间到底怎么协作工作的,下面就从Event Loop的角度来聊一聊他们之间是怎样那么愉快合作的。
已经知道了js是单线程运行的,也知道js中有同步操作和异步操作。同步和异步大家应该很熟了,不多介绍。
同步操作运行在js引擎线程(主线程)上,会形成一个执行栈,而异步操作则在他们对应的异步线程上处理(比如:定时操作在定时器线程上;http请求则在异步请求线程上处理)。
而事件循环线程则监视着这些异步线程们,等异步线程们里面的操作有了结果(比如:定时器计时完成,或者http请求获取到响应),便把他们的毁掉函数添加到事件队列尾部,整个过程中执行栈、事件队列就构成Event Loop。
定时器是规定在一段时间之后执行一段代码,但是在js执行中不会准确无误的按照预期的时间去执行定时器里面的代码。
一个原因是W3C标准规定setTimeout中最小的时间周期是4毫秒,凡是低于4ms的时间间隔都按照4ms来处理。
其实还有一个重要的原因,如果仔细看上面的文章,大家应该会想到在js执行的时候,主线程碰到定时器的时候,是不会直接处理的,应该是先把定时器事件交给定时器线程去处理,这时主线程继续执行下面的代码,同时定时器线程开始计时处理,等到计时完毕,事件循环线程会把定时器要执行的操作放在事件队列末尾,等主线程空闲的时候再来执行事件队列里面的操作。
这样js碰到定时器,会交给定时器线程处理,然后等计时完毕,定时器里面的操作添加到事件队列,等主线程空闲去执行,主线程执行的时候又会发遇到定时器,这是又开始执行上面的一系列操作。
你会发现,这样做会在每一次定时器执行完毕才开始下一个定时器,其中的误差只是等待主线程空闲所需要等待的时间。
而setInterval是规定每隔固定的时间就往定时器线程中推入一个事件,这样做有一个问题,就是累积效应。
累积效应会导致有些事件丢失
microtask是Promise里一个新的概念。
所以js运行过程: