乐趣区

关于前端:浏览器详解

1. Event Loop

(1) 为什么会须要事件循环
JavaScript 是单线程,意味着所有的工作都须要排队,前一个工作完结,才会执行后一个工作。如果前一个工作耗时很长,后一个工作就不得不始终等着。为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,必须应用事件循环(Event Loop);
(2) 事件循环
event loop 会始终运行,来执行进入队列的宏工作。一个 event loop 有多种的宏工作源,这些宏工作源保障了在本工作源内的程序。然而浏览器每次都会抉择一个源中的一个宏工作去执行。这保障了浏览器给与一些宏工作(如用户输出)以更高的优先级。


*  首先会执行同步代码,** 这属于宏工作 **
* 当执行完所有的同步代码后,执行栈为空,查问是否有异步代码须要执行
* 执行完所有的微工作,如果微工作的执行中又退出了新的微工作,也会在这一步执行
* 当执行完所有的微工作后,开始下一轮的 EventLoop,执行宏工作中的异步代码,也就是 setTimeout 中的回调函数

(3)宏工作
浏览器为了可能使得 JS 外部 task 与 DOM 工作可能有序的执行,会在一个 task 执行完结后,在下一个 task 执行开始前,对页面进行从新渲染(task-> 渲染 ->task->…)
鼠标点击会触发一个事件回调,须要执行一个宏工作,而后解析 HTMl。还有上面这个例子,setTimeout
setTimeout 的作用是期待给定的工夫后为它的回调产生一个新的宏工作。这就是为什么打印‘setTimeout’在‘script end’之后。因为打印‘script end’是第一个宏工作外面的事件,而‘setTimeout’是另一个独立的工作外面打印的。
宏工作次要包含:I/O, setInterval,setTimeout,requestAnimationFrame
(4) 微工作
微工作通常来说就是须要在以后 task 执行完结后立刻执行的工作,比方对一系列动作做出反馈,或或者是须要异步的执行工作而又不须要调配一个新的 task,这样便能够减小一点性能的开销。只有执行栈中没有其余的 js 代码正在执行且每个宏工作执行完,微工作队列会立刻执行。如果在微工作执行期间微工作队列退出了新的微工作,会将新的微工作退出队列尾部,之后也会被执行。微工作包含了 mutation observe 的回调还有接下来的例子 promise 的回调。
微工作次要包含:MutationObserver,promise.then catch finally,process.nextTick()
百度面试题

console.log('script start')
async function async1() {await async2()
    console.log('async1 end')
 }
 async function async2() {console.log('async2 end')
 }
 async1()
setTimeout(function() {console.log('setTimeout')
}, 0)
new Promise(resolve => {console.log('Promise')
    resolve()})
    .then(function() {console.log('promise1')
    })
    .then(function() {console.log('promise2')
     })     
console.log('script end')
/** 代码后果 **/
* script start
* async2 end
* Promise
* script end
* async1 end
* promise1
* promise2
* setTimeout     

字节面试题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> 挑战 js 面试题 </title>
</head>
<body>
    <script>
        async function async1(){console.log('async1 start');
            await async2();
            console.log('async1 end');
        }
        async function async2(){console.log('async2');
        }
        console.log('script start');
        setTimeout(function(){console.log('setTimeout');
        },0)
        async1();
        new Promise(function (resolve){console.log('promise1');
            resolve();}).then(function(){console.log('promise2');
        });
        console.log('script end');
    </script>
</body>
</html>

// 执行后果
*script start
*async1 start
*async2
*promise1
*script end
*async1 end
*promise2
*setTimeout

2. dom 操作

(1) 创立节点
createElement(标签名)
createTextNode(节点文本内容)
createDocumentFragment() 创立一个 dom 片段

(2) 插入节点
appendChild(节点)
insertBefore(a,b) a 节点会插入到 b 节点之前

(3) 删除节点
removeChild(节点)

(4) 复制节点
cloneNode(),用于复制节点,接管一个布尔值参数,true 示意深复制(复制节点,曾经其所有子节点),false 示意浅复制(复制节点,不复制子节点)

(5) 替换节点
replaceChild 用于替换节点,接管两个参数,第一个参数,是要插入的节点,第二个参数,是要被替换的节点,返回的是被替换的节点

(6) 查找节点
getElementByTagName() 通过标签名称
getElementByName() 通过元素 name
getElementById() 通过元素 id,唯一性


3. 事件机制

(1)事件代理
把本来须要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理 dom 元素的事件冒泡。
(2)事件冒泡
一个事件触发后,会在子元素和父元素之间流传。

(3)三个阶段
事件流传个别分为三个阶段

  • 捕捉阶段:从 window 对象传导到指标对象称为“捕捉阶段”,捕捉阶段不会响应任何事件。
  • 指标阶段:在指标节点上触发,称为“指标阶段”
  • 冒泡阶段:从指标节点传导回 window 对象,称为“冒泡阶段”。事件代理即是利用事件冒泡的机制把里层所须要响应的事件绑定到外层

(4)事件代理的益处

  • 能够大量地节俭内存占用,缩小事件注册
  • 能够实现当新增子对象时无需再次对其绑定

(5)根本实现

<ul id="link">
    <li id="1">1</li>
    <li id="2">2</li>
    <li id="3">3</li>
<ul>

document.addEventListener("click",function(event){
    var target = event.target
    switch(target.id){
        case 1:
            //do something
            break;
         case 2:
             //do something
             break;
         case 3:
             //do something
             break;      
    }
})

4. 同源策略

浏览器的同源策略是浏览器平安的基石。同源是指:协定雷同,域名雷同,端口雷同
目前非同源,共有三种行为受到限制:
(1)Cookie、LocalStorage 和 IndexDB 无奈读取
(2)DOM 无奈取得:禁止操作非源页面的 DOM 与 JS 对象
(3)Ajax 申请无奈发送:禁止应用 XHR 对象向不同源的服务器地址发动 HTTP 申请,即不能发送跨域 ajax 申请


5. 跨域

什么是跨域:违反了同源策略
怎么解决?

  • jsonp:利用 <script> 标签没有跨域限度的破绽,通过 <script> 标签的 src 属性指向一个须要拜访的地址并提供一个回调函数来接管回调数据,script 获取到的内容会被当做 js 脚本进行执行
  • CORS:跨域资源共享(cross origin resource share),容许浏览器向跨域服务器发送 ajax 申请,实现 CORS 通信要害是服务器,服务端在响应头设置 Access-Control-Allow-Origin 就能够开启 CORS
  • 反向代理:因为跨域是针对浏览器做出的限度,对后端没有影响,能够应用 Nginx,Node Server,Apache 等技术计划为申请做一个转发
  • websocket:是 HTML5 一种新的协定,实现了浏览器与服务器的全双工通信,同时容许跨域通信,是服务器推送技术的一种很好的实现。

jsonp 的实现原理:

如须要发送一个 get 申请 http://sugarat.top/path1/path…

  1. 客户端注册一个全局办法 function callbackFunName(res){}
  2. 服务端收到申请后获取到 url 上的参数
  3. 服务端返回字符串 callbackFunName({“name”:”sugar”,”age”:18})
  4. 客户端当做 js 脚本间接解析执行,就调用了办法 callbackFunName 并把外面的{“name”:”sugar”,”age”:18} 当做一个对象进行了传递

6. 跨站

同站:只有两个 URL 的 eTLD+1 雷同即是同站, 不须要思考协定和端口
eTLD: (effective top-level domain) 无效顶级域名,注册于 Mozilla 保护的公共后缀列表。例如.com、.co.uk、.github.io,.top
eTLD+1: 无效顶级域名 + 二级域名


7. 预检申请

应用后端开启 CORS 解决跨域的形式,浏览器会把申请分成两种类型:简略申请和简单申请

  • 简略申请:申请形式仅限于:get,head,post。content-type 仅限于:text/plain,multipart/form-data,application/x-www-form-urlencoded
  • 简单申请:非简略申请即简单申请

对于简单申请,首先会发动一个 预检申请, 申请办法为 options, 通过该申请来判断服务器是否容许跨域


8. 浏览器内核

浏览器内核是浏览器的外围,能够分成两局部,渲染引擎和 js 引擎。最开始渲染引擎和 js 引擎并没有很显著的辨别,然而起初 js 引擎越来越独立,内核就偏向于只指渲染引擎。常见的浏览器内核能够分为四种:Trident,Gecko,Blink,Webkit。
总结:浏览器内核次要指的是浏览器的渲染引擎,2013 年以前,代表有 Trident(IE),Gecko(firefox),Webkit(Safari chrome 等)。2013 年,谷歌开始研发 blink 引擎,chrome 28 当前开始应用,国内各种 chrome 系的浏览器(360、UC、QQ、2345 等等)也纷纷放弃 webkit,投入 blink 的怀抱。


9. 浏览器渲染机制

  • 解决 HTML 构建 DOM 树
  • 解决 CSS 构建 CSSOM 树
  • 将 DOM 树和 CSSOM 树合并成渲染树
  • 布局,依据渲染树计算每个节点的几何信息
  • 渲染绘制

10. 回流、重绘

实践上每一次 DOM 更改或者 css 几何属性批改,都会引起一次浏览器重排 / 重绘过程,如果是 css 非几何属性更改,只会引起重绘。
回流(重排):浏览器渲染页面默认采纳流式布局模型,当某一个 DOM 节点信息更改了,须要对 DOM 构造进行从新计算,从新布局界面,引发回流。
重绘:当页面中的元素款式扭转,但不影响他在文档流中的地位,引发重绘。


11. 浏览器缓存机制

浏览器的缓存能够分为强缓存和协商缓存。缓存策略都是通过 HTTP Header 来实现的。

(1)强缓存
Expires:response header 里的过期工夫,浏览器再次加载资源时,如果在这个工夫内,则命中强制缓存。

expires:Fri, 14 Apr 2017 10:47:02 GMT
// 示意资源在这个工夫生效

Cache-Control:当值设为 max-age=300 时,则代表在这个申请正确返回工夫(浏览器也会记录下来)的 5 分钟内再次加载资源,就会命中强缓存。
cache-control:max-age=691200

Cache-Control 字段:
public:能够被所有用户缓存,包含终端和 CDN 等两头代理服务器
private:只能被终端浏览器缓存,不容许 中继 缓存服务器进行缓存
no-cache:先缓存本地,然而在命中缓存之后必须与服务器验证缓存的新鲜度能力应用
no-store:不会产生任何缓存

(2)协商缓存
当第一次申请时服务器返回的响应头没有 Cache-Control 和 Expires 或者 Cache-Control 和 Expires 过期或者它的属性值设置为 no-cache 时,那么浏览器第二次申请的时候就会与服务器进行协商。
如果缓存和服务端资源的最新版本是统一的,那么就无需再次下载该资源,服务端间接返回 304 Not Modified 状态码,如果服务器发现浏览器中的缓存曾经是旧版本了,那么服务器就会把最新资源的内容返回给浏览器,状态码就是 200。
协商缓存有两种:1)Etag+If-None-Match 2)Last-Modified+If-Modified-Since

Etag + If-None-Match
Etag 是上一次加载资源时,服务器返回的 response header 中的值,是对该资源的一种惟一标识,只有资源有变动,Etag 就会从新生成。浏览器在下一次加载资源向服务器发送申请时,会将上一次返回的 Etag 值放到 request header 里的 If-None-Match 里,服务器承受到 If-None-Match 的值后,会拿来跟该资源文件的 Etag 值做比拟,如果雷同则命中协商缓存。
etag: "58bfb82f-400b8"

Last-Modified + If-Modified-Since
Last-Modified 是该资源文件最初一次更改工夫,服务器会在 response header 里返回,同时浏览器会将这个值保存起来,在下一次发送申请时,放到 request header 里的 If-Modified-Since 里,服务器在接管到后也会做比对,如果雷同则命中协商缓存。
last-modified: Fri, 14 Apr 2017 10:47:02 GMT

(3)浏览器缓存过程

  • 浏览器第一次加载资源,服务器返回 200,浏览器将资源文件从服务器上申请下载下来,并把 response header 及该申请的返回工夫一并缓存;
  • 下一次加载资源时,先比拟以后工夫和上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,命中强缓存,不发申请间接从本地缓存读取该文件(如果浏览器不反对 HTTP1.1,则用 expires 判断是否过期);如果工夫过期,则向服务器发送 header 带有 If-None-Match 和 If-Modified-Since 的申请;
  • 服务器收到申请后,优先依据 Etag 的值判断被申请的文件有没有做批改,Etag 值统一则没有批改,命中协商缓存,返回 304;如果不统一则有改变,间接返回新的资源文件带上新的 Etag 值并返回 200;
  • 如果服务器收到的申请没有 Etag 值,则将 If-Modified-Since 和被申请文件的最初批改工夫做比对,统一则命中协商缓存,返回 304;不统一则返回新的 last-modified 和文件并返回 200;

12. 本地存储计划

在浏览器端存储数据对咱们是很有用,这相当于赋予浏览器记忆的性能,能够记录用户的所有状态信息,加强用户体验。
浏览器常见的存储计划有三种

  • Cookie
  • web 存储(localStorage 和 sessionStorage)
  • indexDB

(1)Cookie
cookie 会主动在 web 浏览器和 web 服务器之间流传,因而在服务器端脚本就能够存储一些罕用的数据,比方用户的登录状态。
长处:浏览器的兼容性很好
毛病:

  • 存储量小
  • 影响性能,Cookie 会作为申请头发送,当 cookie 存储信息过多时,会影响效率
  • 只能存储字符串
  • 平安问题,cookie 中的数据能够被任何人拜访,所以不能存储重要信息

(2)web 存储
LocalStorage:本地存储
通过 localStorage 存储的数据时永久性的,除非咱们应用 removeItem 来删除或者用户通过设置浏览器配置来删除,负责数据会始终保留在用户的电脑上,永不过期。
localstorage 的作用域限定在文档级别,同源能力共享。

SessionStorage:会话存储
sessionStorage 只存储以后会话页的数据,当用户敞开以后会话页或者浏览器时,数据会被革除。

(3)IndexDB
长处:

  • 领有更大的存储空间
  • 可能解决更加简单的数据结构
  • 领有更大的交互管制

毛病:

  • 存储空间限度:一个独自的数据库我的项目的大小没有限度。然而可能会限度每个 IndexedDB 数据库的大小
  • 浏览器兼容性问题
  • 受同源策略的限度

13. 页面循环系统

音讯队列 事件循环


14. 性能优化

(1)渲染阻塞的优化计划
优化 CSS:让 css 在特定条件下能力应用,这样这些资源在首次渲染时先不构建 CSSOM 树,只有在合乎特定条件下,才会让浏览器进行阻塞渲染,而后构建 CSSOM 树。
CSS 的媒体查问 就是用来实现这个性能的。应用媒体查问能够让 css 资源不在首次加载中阻塞渲染,但不论是哪种 css 资源,它们的下载申请都不会被疏忽,浏览器依然会先下载 css 文件。

<!-- 没有应用媒体查问,这个 css 资源会阻塞渲染  -->
<link href="style.css"    rel="stylesheet">
<!-- all 是默认类型,它和不设置媒体查问的成果是一样的 -->
<link href="style.css"    rel="stylesheet" media="all">
<!-- 动静媒体查问,将在网页加载时计算。依据网页加载时设施的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。-->
<link href="portrait.css" rel="stylesheet" media="orientation:portrait">
<!-- 只在打印网页时利用,因而网页首次在浏览器中加载时,它不会阻塞渲染。-->
<link href="print.css"    rel="stylesheet" media="print">

优化 JavaScript:
应用 async 能够告诉浏览器该脚本不须要在在援用地位执行,这样浏览器能够持续构建 DOM,
(2)其余优化办法

  • 服务端在接管到申请时只响应 HTML 的初始局部,后续的 HTML 内容在须要时再通过 AJAX 获取。然而这个办法,不能用在 CSS 文件上,浏览器不容许 CSSOM 只构建局部内容。
  • 对数据进行压缩,在进行压缩前,首先要对文件进行冗余压缩,即去除掉正文,空格符,换行符。在进行完冗余压缩后,再应用压缩算法,对数据自身进行压缩。例如 GZIP(GZIP 是一个能够作用于任何字节流的通用压缩算法)
  • 应用缓存
  • 资源预加载,pre-fetching 是一种提醒浏览器事后加载用户之后可能会应用到的资源的办法。
    应用 dns-prefetch 来提前进行 DNS 解析,以便之后疾速地拜访另一个主机名
<link rel="dns-prefetch" href="other.hostname.com">

应用 prefetch 属性能够事后下载资源,不过它的优先级是最低的

<link rel="prefetch"  href="/some_other_resource.jpeg">

prerender 能够事后渲染好页面并暗藏起来,之后关上这个页面会跳过渲染阶段,间接出现在用户背后

<link rel="prerender"  href="//domain.com/next_page.html">

15.ajax

(1)xhr
XMLHttpRequest API 的毛病:

  • 不合乎关注点拆散的准则
  • 配置和调用形式凌乱
  • 基于事件的异步模型比拟凌乱
var xhr = new XMLHttpRequest();

xhr.open('GET',url);

xhr.responseType = 'json';

xhr.onload = function(){console.log(xhr.response);
}

xhr.onerror = function(){console.log('xhr error');
}

xhr.send();

(2)fetch
fetch 是原生的 js,是一种 http 数据申请的形式,是 XMLHttpRequest 的一种代替计划。
长处:

  • 语法简洁,更加语义化
  • 基于规范 Promise 实现,反对 async/await
fetch(url).then(response=>response.json())
    
    .then(data=>console.log(data))
    
    .catch(e=>console.log('error' + e));

16. 路由原理

(1)hash
(2)history


参考文章:
https://juejin.cn/post/692673…
https://www.jianshu.com/p/f4b…

退出移动版