前端知识整理 - 浏览器 & html 篇

1,383 阅读1小时+

1. 使用 chrome 浏览器打开一个页面, 要启动哪些进程?

打开 chrome浏览器 时,启动的 进程 包括: 一个浏览器主进程、一个GPU进程一个网络进程多个渲染进程多个插件进程

各个 进程 的功能如下:

  • 浏览器主进程

    主要负责 界面展示用户交互子进程管理,同时 提供存储功能 等, 如 控制标签栏地址栏书签前进后退按钮文件访问 等。

  • 网络进程

    主要负责 网络资源的加载

  • 渲染进程

    HTMLcssjs 转化为 用户可与之交互的网页默认情况下,会为每一个 tab 页开启一个渲染进程

  • GPU进程

    用于处理其他进程发出的 图像处理任务

  • 插件进程

    主要负责 插件的运行

2. 从输入 url 到页面展示,中间经历了什么过程?

输入url到最后页面展示,需要 浏览器各个进程 之间的分工协作,具体如下:

  1. 浏览器进程处理用户输入

    当用户在 浏览器地址栏 中输入 关键字 时, 地址栏 会判断输入的 关键字搜索的内容还是 请求的 url

    • 如果是 搜索内容地址栏 会使用 浏览器默认的搜索引擎,来 合成新的搜索关键字的 URL

    • 如果是 请求 url地址栏 会将 输入的关键字 转化为 完整的 url

    当浏览器开始加载一个 url 地址后,页面还是原来的页面,还没有替换为要加载的 url 对应的页面

    处理完用户输入以后,浏览器进程 会将 URL 请求 发送给 网络进程

  2. 网络进程处理浏览器进程发送的 URL 请求

    具体的处理过程如下:

    1. 查找本地缓存是否有缓存资源

      如果有,直接将 缓存的资源 传递给 浏览器进程;如果没有,进入第二步,开始 网络请求流程

    2. 获取请求域名对应的 IP 地址

      系统会首先自动从 hosts 文件中寻找域名对应的 IP 地址,一旦找到,和服务器建立 TCP 连接;如果没有找到,则系统会将网址提交 DNS 域名解析服务器 进行 IP 地址的解析

    3. 利用 IP 地址,通过 三次握手, 建立与服务器之间的 TCP 连接

    4. 建立连接以后,构建 http 请求报文,发送给服务器。

    5. 服务器接收到请求信息以后,构建 响应信息, 发送给网络进程。

    6. 网络进程 接收到 响应信息 以后,解析响应信息。

      如果返回的响应信息的状态码是 301302,根据响应头提供的 Location 字段 进行重定向,然后重头开始。

      如果返回的响应信息的状态码是 200,说明服务器返回了请求的数据。此时,需要根据响应头提供的 content-type 字段来选择对应的方式来处理响应内容。如果 content-type 的值为 'application/octet-stream', 为 下载类型,该请求会提交给浏览器的下载管理器,同时该 URL 请求流程 结束。如果 content-type 的值为 'text/html', 为 网页类型,通知 浏览器进程准备渲染界面

  3. 准备渲染进程

    为新页面分配一个 渲染进程

    分配策略:

    • 通常情况下,打开新的页面都会使用单独的渲染进程;

    • 如果从 A 页面打开 B 页面,且 A 和 B 都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程;如果是其他情况,浏览器进程则会为 B 创建一个新的渲染进程。

    渲染进程 准备好之后,还不能立即进入 文档解析状态,因为此时的文档数据还在 网络进程 中,并没有提交给 渲染进程,所以下一步就进入了 提交文档阶段

  4. 提交文档

    所谓 提交文档,就是指 浏览器进程将网络进程接收到的 HTML 数据提交给渲染进程

    具体流程是这样的:

    1. 首先当 浏览器进程接收到网络进程的响应头数据 之后,便向渲染进程发起 “提交文档” 的消息;

    2. 渲染进程 接收到 “提交文档” 的消息后,会和 网络进程 建立 传输数据的“管道”

    3. 文档数据 传输完成之后,渲染进程 会返回 “确认提交” 的消息给 浏览器进程

    4. 浏览器进程 在收到 “确认提交” 的消息后,会更新 浏览器界面状态,包括了 安全状态地址栏的 URL前进后退的历史状态,并 更新 Web 页面

  5. 渲染页面

    渲染进程 渲染页面。

3. 浏览器渲染的过程

  1. 构建 dom 树

    浏览器无法直接理解和使用 HTML, 所以要使用 解析器HTML 转化为浏览器能够理解的结构 - dom 树

    通过 document 可以直接访问 dom 树

  2. 样式计算

    样式计算 的目的是为了 计算出 DOM 节点中每个元素的具体样式,这个阶段分为三步完成:

    1. 把 css 转化为浏览器能够理解的的结构

      css 样式 的来源有三种:外部样式表内部样式表内联样式

      HTML 文件 一样,浏览器无法理解 纯文本的 CSS 样式,所以当 渲染引擎 接收到 CSS 文本 时,会执行一个转换操作,将 CSS 文本 转化为 浏览器能够理解的结构 - styleSheets

      通过 documnet.styleSheets 可以访问 styleSheets

    2. 转化样式表中的属性值,使其标准化

      对属性值进行标准化操作,将 属性值 转化为 渲染引擎容器理解的标准化的计算值,如将 em 转化为 px。

    3. 计算出 DOM 树中每个节点的具体样式

      样式计算 的目的是为了 计算 DOM 节点中每个元素的具体样式,在计算过程中需要遵循 CSS 继承层叠 两个规则。这个阶段最终输出的内容是 每个 DOM 节点的样式, 并被保存在 ComputedStyle 的结构内。

      CSS 继承 就是 每个 DOM 节点都包含父节点可继承的样式

      CSS 层叠 定义了 如何合并来自多个源的属性(优先级)

  3. 布局阶段

    布局阶段 的主要任务是计算 DOM 树中可见节点的几何位置

    布局阶段 分为两步 - 创建布局树布局计算

    1. 创建布局树

      在显示之前, 我们还需要构建一颗 只包含可见元素的布局树

      布局树 的结构和 dom 树 的结构相同,只是 不包含不可见的节点,如 head 节点、display 为 none 的 节点等。

    2. 布局计算

      计算 布局树节点几何位置

  4. 分层

    页面中有很多复杂效果,如复杂的的 3D 变换、页面滚动、使用 z-index 做 z 轴排序等。 为了实现这些效果,渲染引擎还要为特定的节点生成专用的图层,并生成一颗对应的 图层树 - layer tree

    浏览器的页面实际上被分成了图层,这些图层叠加后生成了最后的页面

    并不是 布局树 中的每一个节点都包含一个 图层,如果一个节点没有对应的图层,那么这个节点就从属于 父节点的图层

    通常情况下,只要满足下面两点中的任意一点,元素都会被提升到一个单独的图层:

    • 拥有层叠上下文上下文属性的元素会被提升到单独的一层

      层叠上下文 由满足下面任意一个条件生成:

      • 文档根元素 - html;

      • position 为 absolute 或者 relative 且 z-index 不为 auto;

      • position 为 fixed;

      • flex 容器的子元素,且 z-index 不为 auto;

      • grid 容器的子元素,且 z-index 不为 auto;

      • opacity 属性值小于 1 的元素

      • transform、filter、perspective、clip-path、mask、mask-img、mask-border 不为 none 的元素;

      • isolation 属性值为 isolate 的元素;

      • contain 属性值为 layout、paint 或包含他们其中之一的合成值;

      z-index, 默认值为 auto,图层顺序与父节点的一致

      子级层叠上下文的 z-index 只有在父级中才有意义。子元素的 z-index 相对应于父元素生效

    • 需要 clip 的地方也会被创建为图层

      当元素内容超出且 overflowauto 或者为 scroll 时,内容会被 clip, 创建一个新的图层。

      overflow 为 visible、hidden,内容不会被 clip

  5. 为每个图层生成绘制列表,并将其提交到合成线程

    绘制阶段 并不是真正的绘制图片,而是 将每一个图层转化为一个绘制指令列表

  6. 合成线程 将图层分成图块,并在光栅化线程池中将图块转换成位图。

    根据图层对应的绘制指令列表,生成图片,然后将多层图片合成为一张图片。

  7. 合️成线程 发送绘制图块命令 DrawQuad 给 浏览器进程

  8. 浏览器进程 根据 DrawQuad 消息生成页面,并显示到显示器上

重排: 修改元素属性如 width、heigth, 导致重新布局、分层

重绘:修改元素属性如 background,跳过布局、分层,直接绘制

合成跳过布局、绘制,直接合成

js 的执行,会阻塞 dom 树的生成

CSS 阻塞了,会阻塞 dom 树的生成,不会阻塞页面的显示

4. 事件循环 - event loop

浏览器渲染进程 工作的时候,有一个 主线程 在运行,这个主线程主要负责 js 代码的执行浏览器渲染

js 代码的执行浏览器渲染 过程是互斥的,即在某一个时间点,主线程只能 执行 js 代码 或者 浏览器渲染

主线程循环工作 的,每一个循环是一个 tick,在这个 tick 内,如果有 js 代码 需要执行,就要去 执行 js 代码;如果需要有 dom 操作需要更新渲染,就要去 渲染

主线程 要执行的 js 代码, 是从 任务队列 中获取的。任务队列 中的每一个 任务,都对应主线程要执行的 js 代码,如 主 js 代码事件回调请求回调setTimeout 回调 等。 由于 js 是 单线程 的,为了 防止主线程被阻塞, 在遇到 ajax 请求setTimeout 等操作时,会为对应的 callback 构建一个任务,然后继续处理剩下的代码。 等 请求成功setTimeout 延时到达 时,将对应的任务添加到任务队列中, 等待 下一次循环 中被 主线程处理。

主线程 在每次循环过程中, 如果 任务队列 不为空,会从任务队列中获取一个任务,将对应的 js 代码 放入 执行栈 中执行。

在执行 js 代码 的过程中, 如果遇到 promisemutationObserver 等异步任务时, 会将对应的 callback 添加到 微任务队列 中。等 执行栈中的 js 代码处理完毕以后,处理 微任务队列 中的任务。等 微任务队列 中的代码处理完毕以后, 本次事件循环的 js 代码才算处理完毕。

在一次 事件循环 中, 执行 js 代码时, 如果发生 dom 操作,主线程并不会在 js 代码执行完毕以后立即去渲染。 何时去渲染是由浏览器的刷新频率决定的,只有浏览器要刷新,且有 dom 操作需要更新,主线程才会进行浏览器渲染。

一次 事件循环, 主线程需要做的事情:

  1. 从任务队列中获取任务

  2. 任务对应的 js 代码 放入 执行栈 中处理,如果遇到 promisemutationObserver 等,将对应的 callback 添加到 微任务队列 中;

  3. 微任务队列 中任务对应的 js 代码放入执行栈中处理,直到 微任务队列 清空;

  4. 如果有 dom 操作需要更新且浏览器要刷新,进行 浏览器渲染

5. requestAnimationFrame

requestAnimationFrame,在进行 浏览器渲染 的时候,执行 js 代码requestAminationFrame处理 css(样式计算)绘制 之前执行。

requestAnimationFrame 的执行取决与浏览器的刷新频率。每一轮事件循环, requestAnimationFrame 不一定会触发,只有在浏览器刷新且更新dom的时候才会触发。

要使用 requestAnimationFrame 做动画, 不要使用 setTimeout 做动画。

,图形处理器 每秒钟能够刷新的次数,通常用 fps (Frames Per Seconds)

每一帧都是 静止的图象,快速连续地显示帧便形成了运动的假象。高的帧率可以得到更流畅、更逼真的动画。每秒钟帧数 (fps) 愈多,所显示的动作就会愈流畅。

浏览器的刷新频率是 60 Hz, 即 1 秒钟可以刷新60次,也就说是浏览器对每一帧画面的渲染工作要在 16ms 内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。

使用 requestAnimationFrame 不需要设置具体的时间,由 系统来决定回调函数的执行时间requestAnimationFrame 里面的回调函数是在页面刷新之前 执行,它跟着屏幕的刷新频率走,保证每个刷新间隔只执行一次,如果页面未激活的话,requestAnimationFrame 也会停止渲染,这样既可以保证页面的流畅性,又能节省主线程执行函数的开销。

setTimeout 是在 特定的时间间隔去执行任务,不到时间间隔不会去执行,这样浏览器就没有办法去自动优化

6. 宏任务&微任务

宏任务: script 整体代码setTimeoutmessageChanelI/O渲染setInterval用户交互网络请求

setTimeout 默认的最小延时时间为: 4.x ms, 即 setTimeout 执行以后, 4.x ms 之后, callback 对应的任务才会添加到任务队列中。

messageChanelpostMessage 执行以后, 0.1(0.2) ms以后, callback 对应任务才会添加到任务队列中。

微任务: PromiseMutationObserverprocess.nextTick

MutationObserver 方案的核心就是采用了 微任务机制,有效地权衡了实时性和执行效率的问题。

微任务宏任务 是绑定的,每个 宏任务 在执行时,会创建自己的 微任务队列

微任务 的执行时长会影响到 当前宏任务的时长。

在一个 宏任务 中,分别创建一个用于回调的 宏任务微任务,无论什么情况下,微任务都早于宏任务执行

7. 变量提升(Hoisting)

所谓 变量提升, 是指 javascript 代码 在执行过程中,javascript 引擎将 变量、函数的声明部分 提升到 代码头部 的行为。

变量被提升以后,会被设置默认值,即 undefined

变量提升 的原因: 一段 javascript 代码 的执行要经历两个阶段:编译阶段和执行阶段。javascript 代码在经过 编译阶段 后,会生成两个部分:执行上下文 和 可执行代码执行上下文javascript 引擎 执行一段代码的 运行环境,包含 this定义的变量(undefined)定义的函数 等。进入 执行阶段 后,javascript 引擎开始执行可执行代码,从执行上下文中获取需要的变量和函数以及给执行上下文中的变量赋值

编译阶段生成执行上下文时,函数会覆盖同名变量,并且后定义的函数会覆盖先定义的同名函数

重点: javascript 的执行机制: 先编译,后执行

变量提升 可能导致的问题:

  1. 变量容器在不被察觉的情况下被覆盖
    var name = 'zhangjh'
    function showName() {
        console.log(name)  // undefined 被覆盖
        var name = 'xxq'
        console.log(name) // xxq
    }
    showName()
    
  2. 本应销毁的变量没有被销毁
    if(true) {
        var name = '123'  // if 语句块结束以后, name 还存在;
    }
    

8. 执行上下文(Execution Context)

执行上下文,是 javascript 引擎执行一段代码的 运行环境, 在 javascript 代码的 编译阶段 构建。

一般情况下,执行上下文 的构建有如下几种情况:

  • 全局执行上下文

    javascript 在执行 全局代码 的时候,会 编译全局代码 并构建一个 全局执行上下文全局执行上下文有且只有一个,只有在页面关闭的时候才会被销毁

  • 函数执行上下文

    调用一个函数 的时候,函数内部的代码 会先被编译生成一个 函数执行上下文。一般情况下,函数执行完毕以后,对应的 函数执行上下文 会被销毁。

    当使用 eval函数 时,eval 中的代码也会被编译生成一个 函数执行上下文

9. 调用栈(call stack)

调用栈 是用来 管理函数调用关系 的一种 数据结构(栈),存储的数据为 执行上下文

执行 javascript 代码时, 会先构建一个 全局执行上下文,然后 入栈。遇到 函数调用 时,构建一个 函数执行上下文,然后 入栈。当 函数执行完毕 以后, 函数执行上下文出栈全局执行上下文 会一直存在于 调用栈 的底部。

通过 调用栈,我们可以 追踪到哪个函数正在被执行以及各个函数之间的调用关系

调用栈是有大小限制的。 当 入栈的执行上下文 超过一定数量后, javascript 引擎 就会报错, 这种错误称为 栈溢出(stack overflow)

一般情况下,写 递归代码 时,没有终止条件,容易出现 栈溢出

10. 作用域(scoped)

作用域,指变量和函数的可访问范围,决定了变量和函数的可见性和生命周期

目前,javascript 中的 作用域 有三种:

  • 全局作用域

    作用域 中的 变量函数代码的任何位置 都可以被访问,其 生命周期伴随着页面的生命周期

  • 函数作用域

    函数中定义的变量、函数,只能在 函数内部 被访问,不能在 函数外部 被访问。函数执行完毕以后,函数内部定义的变量、函数就会被销毁。(闭包除外)

  • 块级作用域

    ifforswitch{}while 等区域块中,如果有通过关键字 letconst 定义变量,那么这些变量不能被 区域块外部 访问。等 区域块对应的代码 执行完毕以后,通过 letconst 关键字定义的变量会被 销毁

11. let & const

E6 中引入了 letconst, 使得 js 有了 块级作用域。块级作用域定义的变量,不能被外部使用。

在代码块内,使用 let、const 命令声明变量之前,该变量都是不可用的。这在语法上,称为 “暂时性死区”。凡是在声明之前就使用这些变量,就会报错。

暂时性死区 的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

可以理解为在编译阶段已经为 let、const 声明的变量分配内存, 但在赋值之前,这些内存是不能被访问的,否则会报错

const 声明的变量不得改变值,这意味着,const 一旦声明变量,就必须立即初始化,不能留到以后赋值。 先声明后赋值,会报错

const 并不能保证变量的值不被改变。如果变量的值是 数字字符串布尔,值不能被改变;如果值是 对象(引用类型),变量不能被 重新赋值,但是 对象的属性可以改变

const 声明的变量保存在栈空间中,只能保证栈空间中的值不能改变。如果栈空间中存的是引用类型的在堆空间的地址,堆空间地址不可变,但是堆空间中存储的值可以变

12. 作用域链

在函数内部查找某个变量, 如果函数内部没有,就去外部函数内部查找,直到全局作用域为止,这样的查找链条,就称之为作用域链。

函数定义 的时候,有一个内部属性 - [[scope]][[scope]] 属性对应一个列表,该列表称之为函数的 作用域链。当一个函数创建后,它的 作用域链 会被 全局作用域 或者 外部函数对应的执行上下文的作用域链 初始化。当函数执行时,会构建一个 活动对象 添加到 执行上下文作用域链 的前端。这样在函数内部查找一个变量时,按照作用域链按序查找。

13. 闭包

一般情况一下,一个函数执行完毕, 函数内部定义的变量都会被销毁,函数外部不可访问。如果这个函数定义了一个内部函数并返回,那么外部函数定义的变量不会被销毁,还可以被内部函数访问,这样就形成了闭包。

内部函数 定义的时候,外部函数执行时构建的 活动对象 也会保存到 内部函数 的作用域链中,导致 外部函数定义的还被使用的变量 不被销毁。

14. this

函数执行上下文this 不能混为一谈,函数执行上下文包含 this

嵌套函数this 指向 window, 不会从外部函数继承。

普通函数 执行时, this 默认指向 window严格模式 下, 指向 undefined

15. new 关键字

执行 new Func() 时, javascript 引擎做了如下工作:

  1. 创建一个 继承 自 Func 的空对象 - tempObj;

  2. 接着调用 Func.call 方法,并将 tempObj 作为 call 方法的参数,这样当 Func 的执行上下文创建时,它的 this 就指向了 tempObj 对象;

  3. 执行 Func

  4. 如果 Func 没有指定返回值, 返回 tempObj

function simulateNew(func) {
    if (typeof func !== 'function') {
        throw 'param is not a constructor'
    }
    var tempObj = {};
    Object.setPrototype(tempObj, func.prototype)
    return func.call(tempObj) || tempObj
}

16. 静态语言 & 动态语言 & 弱类型语言 & 强类型语言

静态语言:使用之前需要确认变量的数据类型;

动态语言:运行时才能确认变量的数据类型;

弱类型语言支持隐式类型转换

强类型语言不支持隐式类型转换

17. 数据类型

原始类型NumberStringBooleanSymbolNullundefinedBigInt

引用类型Objet

原始类型 的赋值是 完整复制变量值,而 引用类型 的赋值是 复制引用地址

js 代码运行时,原始类型 存储在 栈空间(执行栈) 中, 引用类型 存储在 堆空间

栈空间存储的仅仅是对象在堆空间中的地址

18. 垃圾回收

由于 js 程序运行时,原始类型的数据 通过 执行上下文 保存在 栈空间 中, 对象类型的数据 保存在 堆空间中,所以垃圾回收分为两种: 栈中数据回收堆中数据回收

  • 栈中数据回收

    程序执行完毕以后,执行上下文出栈,所占具的 栈空间自动回收

  • 堆中数据回收

    堆空间 中的的垃圾数据,通过 js 的 垃圾回收器 来处理。

    V8 会把 分为 新生代老生代 两个区域,新生代中存放的是生存时间短的对象老生代中存放的生存时间久的对象新生区 通常只支持 1~8M 的容量,而 老生区 支持的容量就大很多了。

    堆空间的垃圾数据回收要经历: 垃圾数据标记删除垃圾数据整理内存

    对于这两块区域,V8 分别使用两个不同的垃圾回收器,以便更高效地实施垃圾回收:

    • 副垃圾回收器

      负责回收 新生区中的数据。

      新生区对半分为 对象区域空闲区域。新加入的对象都会存储到 对象区域 中。等对象区域快被填满时,进行垃圾回收。先将 对象区域 内的 垃圾数据 标记,然后将 存活的数据 转移到 空闲区域,并 按序排列,然后清空 对象区域。这样原来的对象区域变为空闲区域,原来的空闲区域变为对象区域。

      为了执行效率,一般新生区的空间会被设置得比较小

      JavaScript 引擎采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中

    • 主垃圾回收器

      负责回收 老生区 中的数据。除了新生区中晋升的对象,一些大的对象会直接被分配到老生区。

      回收过程: 垃圾数据标记删除垃圾数据整理内存

      为了降低 老生代 的垃圾回收而造成的卡顿,V8 将 标记过程 分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为 增量标记(Incremental Marking)算法

      weakSet 中存储的引用,不计入垃圾回收机制。只要对象在外部消失, weakSet 里面的引用会自动消失。weakSet 不支持遍历操作,不支持 keys、values、entries 方法, 也没有 size 属性,仅支持 add、has、delete 方法

      weakMap 中键名所引用的对象,也不计入垃圾回收机制。只要对象在外部消失,weakMap 里面的键名和键值自动消失。因此,由于垃圾回收机制, weakMap 没有遍历操作(即不支持 keys、values、entries),也没有 size 属性,不支持 clear 方法,仅支持 set、get、has、delete

19. 编译器(compiler) & 解释器(Interpreter)

之所以存在 编译器解释器,是因为机器不能直接理解我们所写的代码,所以在执行程序之前,需要将我们所写的代码“翻译”成机器能读懂的机器语言。按语言的执行流程,可以把语言划分为 编译型语言解释型语言

编译型语言: 编译型语言在程序执行之前,需要经过编译器的编译过程,并且编译之后会直接保留机器能读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重新编译了。比如 C/C++、GO 等都是编译型语言。 一次编译,多次执行

解释型语言: 每次运行时都需要通过 解释器 对程序进行动态解释和执行。比如 Python、JavaScript 等都属于解释型语言。

20. V8 引擎运行 js 代码

V8 引擎运行 js 代码的过程:

  1. 通过 解释器源代码 转化为 抽象语法树(AST),并生成执行上下文;

    AST 是代码的 结构化表示, 是一种非常重要的数据结构。BabelEslintwebpack 工作过程中都使用了 AST

  2. 根据 AST 生成 字节码

    字节码 就是介于 AST机器码 之间的一种代码。但是与特定类型的机器码无关,字节码 需要通过 解释器 将其转换为机器码后才能执行。

  3. 执行字节码

    解释器 逐条执行字节码。

    如果一段代码被执行多次( 热点代码 ),就会被编译为 机器码

21. Babel 的工作流程

Babel 的工作流程:

  1. parse - 将 ES6 代码 转化为 AST

  2. transform - 分析 AST, 将 ES6 对应的 AST 转化为 ES5 对应的 AST;

  3. generator - 将 ES5 对应的 AST 转化为 ES5 代码;

22. TCP 可靠传输

TCP(Transmission Control Protocol,传输控制协议) 是一种 面向连接的可靠的、基于 字节流传输层通信协议

TCP 的特点:

  • 对于数据包丢失的情况,TCP 提供 重传机制

  • TCP 引入了数据包 排序机制,用来保证把乱序的数据包组合成一个完整的文件。

一个完整的 TCP 连接的生命周期包括 建立连接传输数据断开连接 三个阶段:

  • 首先,建立连接阶段

    这个阶段是通过 “三次握手” 来建立客户端和服务器之间的连接。

    TCP 提供面向连接的通信传输。面向连接是指在数据通信开始之前先做好两端之间的准备工作。

    所谓 三次握手,是指在建立一个 TCP 连接 时,客户端和服务器总共要发送 三个数据包 以确认连接的建立。

    三次握手:

    1. 建立连接时,客户端发送 请求连接包,等待服务器确认;

    2. 服务器收到 请求包,发出 确认包

    3. 客户端收到服务器的 确认包,向服务器发送 确认包

  • 其次,传输数据阶段

    在该阶段,接收端需要对每个数据包进行确认操作,也就是接收端在接收到数据包之后,需要发送确认数据包给发送端。所以当发送端发送了一个数据包之后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的 重发机制

    同样,一个大的文件在传输过程中会被拆分成很多小的数据包,这些数据包到达接收端后,接收端会按照 TCP 头中的序号为其排序,从而保证组成完整的数据。

  • 最后,断开连接阶段

    数据传输完毕之后,就要终止连接了,涉及到最后一个阶段 “四次挥手” 来保证双方都能断开连接。

    四次挥手:

    1. 客户端 进程发出 连接释放报文,并且停止发送数据;

    2. 服务器 收到 连接释放报文,发出 确认报文;

    3. 客户端 收到 服务器确认请求 后,进入 等待状态,等待 服务器 发送 连接释放报文

    4. 服务器 将最后的数据发送完毕后,就向客户端发送 连接释放报文

    5. 客户端 收到 服务器连接释放报文 后,必须发出确认;

      2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入 CLOSED 状态。

    6. 服务器 只要收到了客户端发出的确认,立即进入 CLOSED 状态。

23. setTimeout

通过 setTimeout 可以定义一个定时器。

setTimeout 定义的 callback 对应的任务会在定时器时间到了以后,添加到任务队列中。

setTimeout 的任务会被延时,即定时器时间到了也不会按时执行

setTimeout(callback, 0), 实际是 4.x ms 以后执行

24. callback

一个函数作为参数传递给另外一个函数,那 作为参数的这个函数 就是 回调函数

同步回调: callback 在主函数内部执行

异步回调: callback 在主函数外部执行

25. Promise

解决的问题:

  1. 消灭嵌套调用
  2. 合并多个任务的错误处理

延迟绑定技术

var a = new Promise(function(resolve, reject) {
    resolve(1)
    Promise.resolve(2).then(function(e) {console.log(e)})
})
a.then(function(e) {console.log(e)})

// 结果是 2 1

promise 实例的 then 方法执行时, 如果 promise 对象的状态变为 resolved, 将 callback 添加到 微任务队列 中;如果 promise 对象的状态为 pending, 先缓存 callback, 等到状态变为 resolved, 才放入 微任务队列 中。

resolve 方法会将 promise 对象的状态变为 resolved, 如果此时 promise 对象没有注册 callback,无法将 callback 添加到微任务队列中

26. DOM

从网络传给渲染引擎的 HTML 文件字节流 是无法直接被 渲染引擎 理解的,所以要将其转化为 渲染引擎 能够理解的 内部结构,这个结构就是 DOM

DOM 提供了对 HTML 文档结构化的表述。

渲染引擎 中,DOM 有三个层面的作用:

  • 页面的视角 来看,DOM 是生成页面的 基础数据结构

  • JavaScript 脚本 视角来看,DOM 提供给 JavaScript 脚本操作的接口,通过这套接口,JavaScript 可以对 DOM 结构进行访问,从而改变文档的结构、样式和内容。

  • 从安全视角来看,DOM 是一道安全防护线,一些不安全的内容在 DOM 解析阶段就被拒之门外了

渲染引擎 内部,有一个叫 HTML 解析器(HTMLParser) 的模块,它的职责就是负责将 HTML 字节流 转换为 DOM 结构

HTML 解析器 并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML 解析器便解析多少数据。

js 脚本DOM 解析的影响:

  • js 脚本的执行,会阻塞 DOM 解析

  • js 脚本的下载,也会阻塞 DOM 解析

    针对 js 脚本的下载,Chrome 做了 预解析优化。当渲染引擎收到字节流之后,会开启一个 预解析线程,用来分析 HTML 文件中包含的 JavaScriptCSS 等相关文件,解析到相关文件之后,预解析线程会提前下载这些文件。

如果 JavaScript 文件中没有操作 DOM 相关代码,就可以将该 JavaScript 脚本设置为 异步加载,通过 asyncdefer 来标记代码。asyncdefer 虽然都是异步的,不过还有一些差异,使用 async 标志的脚本文件一旦加载完成,会立即执行;而使用了 defer 标记的脚本文件,需要在 DOMContentLoaded 事件之前执行。

如果有多个 defer 脚本,会按照它们在页面出现的顺序加载,而多个 async 脚本是不能保证加载顺序的

如果 js 代码中可能需要操作 外部样式表 中的 样式,在执行 js 之前,还需要等待 外部的 CSS 文件下载完成,并解析生成 CSSOM 对象之后,才能执行 JavaScript 脚本。

解析 DOM 过程 中,如果有外部样式表、外部js文件,会开启一个预解析线程,并行请求外部文件,和主线程 并行工作。js 脚本的执行,需要等待所有的外部样式表下载并解析为CSSOM以后才能执行。

尽管js脚本是并行下载的,但如果不是异步脚本,是按序执行的,如果还没有下载完成,需要等待下载完成以后才能执行。

不管 CSS 文件和 JavaScript 文件谁先到达,都要先等到 CSS 文件下载完成并生成 CSSOM,然后再执行 JavaScript 脚本,最后再继续构建 DOM,构建布局树,绘制页面。

HTML 解析器不属于主线程。 DOM 树的构建不会影响浏览器渲染。当浏览器屏幕刷新时,会把构建好的 dom 结构先渲染出来

27. CSSOM

HTML 一样,渲染引擎 也是无法直接理解 CSS 文件内容 的,所以需要将其解析成渲染引擎能够理解的结构,这个结构就是 CSSOM

DOM 一样,CSSOM 也具有两个作用:

  • 提供给 JavaScript 操作样式表的能力;

  • 第二个是为 布局树的合成 提供基础的样式信息。

这个 CSSOM 体现在 DOM 中就是 document.styleSheets

28. 解析白屏

从发起 URL 请求 开始,到 首次显示页面的内容,在视觉上经历的三个阶段:

  • 第一个阶段,等请求发出去之后,到提交数据阶段,这时页面展示出来的还是之前页面的内容;

  • 第二个阶段,提交数据之后渲染进程会创建一个 空白页面,我们通常把这段时间称为解析白屏,并等待 CSS 文件和 JavaScript 文件的加载完成,生成 CSSOMDOM,然后 合成布局树,最后还要经过一系列的步骤准备首次渲染;

  • 第三个阶段,等首次渲染完成之后,就开始进入完整页面的生成阶段了,然后页面会一点点被绘制出来

在第二个阶段,如果白屏时间过久,就会影响用户体验。

影响白屏时间的因素,主要有 css 脚本下载js 脚本下载js 脚本执行

优化措施

  • 可以内联,尽量内联;
  • 无法内联,尽量减少外部文件的体积;
  • 不需要操作 dom 的外部 js 脚本, 设置为异步;
  • css 脚本体积较大时,可以分离成多个,根据媒体查询,按需加载;

29. 分层 & 合成

准备知识

  • 显示器如何显示图像

    每个显示器都有固定的刷新频率,通常是 60HZ,也就是每秒更新 60 张图片,更新的图片都来自于显卡中一个叫前缓冲区的地方,显示器所做的任务很简单,就是每秒固定读取 60 次前缓冲区中的图像,并将读取的图像显示到显示器上。

  • 显卡做了什么

    显卡的职责就是合成新的图像,并将图像保存到后缓冲区中,一旦显卡把合成的图像写到后缓冲区,系统就会让后缓冲区和前缓冲区互换,这样就能保证显示器能读取到最新显卡合成的图像。

  • 帧 & 帧率

    渲染流水线生成的每一副图片称为一帧

    帧率渲染流水线每秒更新了多少帧称为帧率

由于用户很容易观察到那些丢失的帧,如果在一次动画过程中,渲染引擎生成某些帧的时间过久,那么用户就会感受到卡顿,这会给用户造成非常不好的印象。

要解决卡顿问题,就要解决每帧生成时间过久的问题,为此 Chrome 对浏览器渲染方式做了大量的工作,其中最卓有成效的策略就是引入了分层和合成机制。

浏览器重新渲染的方式有三种:重排重绘合成:

  • 重排: 需要重新布局,然后分层、绘制、合成

  • 重绘不需要重新布局、分层,直接绘制、合成

  • 合成不需要重新布局、分成、绘制, 直接合成

合成操作是在合成线程上完成的,不会影响主线程

css 动画比 js 动画高效的原因: 渲染引擎会通过合成线程直接处理变换,而不设计主线程

js 动画优化:可以通过 will-change 属性,将 对应的元素单独实现一层。等 will-change 指定的属性发生变换时,通过 合成线程 处理这些变换。

30. 系统优化页面

加载阶段 优化:

  • 尽量内联;
  • 减小请求文件的大小;
  • 使用 CDN;

在加载阶段,核心的优化原则是:优化关键资源的加载速度,减少关键资源的个数,降低关键资源的 RTT 次数

交互阶段 优化:

  • 减少 js 脚本执行的时间

    分解任务、web worker

  • 避免同步强制布局

    js 中进行 dom 操作以后,马上读取 dom 操作改变的属性,导致渲染引擎执行一次布局操作,导致多布局一次。

    先读取属性, 再进行 dom 操作

  • 避免布局抖动

    所谓 布局抖动,是指在一次 JavaScript 执行过程中,多次执行 强制布局抖动操作

  • 合理利用 css 合成动画

    合成动画 是直接在 合成线程 上执行的,这和在 主线程 上执行的 布局绘制 等操作不同,如果主线程JavaScript 或者一些布局任务占用,CSS 动画依然能继续执行。所以要尽量利用好 CSS 合成动画,如果能让 CSS 处理动画,就尽量交给 CSS 来操作。

    另外,如果能提前知道对某个元素执行动画操作,那就最好将其标记为 will-change,这是告诉渲染引擎需要将该元素单独生成一个图层。

  • 避免频繁的垃圾回收

    优化数据结构。

在交互阶段,核心的优化原则是:尽量减少一帧的生成时间。可以通过减少单次 JavaScript 的执行时间、避免强制同步布局、避免布局抖动、尽量采用 CSS 的合成动画、避免频繁的垃圾回收等方式来减少一帧生成的时长

31. 渐进式网页应用 - PWA

稍后...

32. WebComponent

稍后...

33. 浏览器安全

浏览器安全 可以分为三类: web 页面安全浏览器网络安全浏览器系统安全

34. 同源策略

同源策略: 如果两个 URL协议域名端口 都相同,我们就称这 两个 URL 同源

同源策略 主要表现在 DOMWeb 数据网络 这三个层面:

  • DOM 层面

    同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作。

    跨文档消息机制(window.postMessage),实现不同源 dom 通信。

  • Web 数据

    同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。

  • 网络

    同源策略限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。

    跨域资源共享(CORS), 实现跨域请求数据。

35. XSS 攻击

XSS跨站脚本攻击,指黑客往 HTML 文件 中或者 DOM 中注入 恶意脚本,从而在 用户浏览页面时 利用注入的 恶意脚本 对用户实施攻击的一种手段。

恶意脚本 的危害:

  • 通过 document.cookie 窃取 cookie 信息,造成用户信息丢失;

  • 监听用户行为

    恶意 JavaScript 可以使用 “addEventListener” 接口来监听键盘事件,比如可以获取用户输入的信用卡等信息,将其发送到恶意服务器。黑客掌握了这些信息之后,又可以做很多违法的事情

  • 可以通过修改 DOM 伪造假的登录窗口,用来欺骗用户输入用户名和密码等信息

  • 在页面内生成浮窗广告,这些广告会严重地影响用户体验

恶意脚本注入的方式:

  • 存储型 XSS 攻击

    存储型 XSS 攻击 大致需要经过如下步骤:

    • 首先黑客利用站点漏洞将一段恶意 JavaScript 代码提交到网站的数据库中;

    • 然后用户向网站请求包含了恶意 JavaScript 脚本的页面;

    • 当用户浏览该页面的时候,恶意脚本就会将用户的 Cookie 信息等数据上传到服务器。

    存储型 XSS 攻击漏洞属于服务端安全漏洞

  • 反射型 XSS 攻击

    用户将一段含有恶意代码的请求提交给 Web 服务器Web 服务器 接收到请求时,又将恶意代码 反射 给了 浏览器端,这就是 反射型 XSS 攻击

    恶意代码一般添加到 url 中,然后通过服务器将恶意代码添加到请求页面中

    Web 服务器不会存储反射型 XSS 攻击的恶意脚本,这是和存储型 XSS 攻击不同的地方

    反射型 XSS 攻击漏洞属于服务端安全漏洞

  • 基于 DOM 的 XSS 攻击

    基于 DOMXSS 攻击 是不牵涉到页面 Web 服务器 的。

    具体来讲,黑客通过各种手段将恶意脚本注入用户的页面中,比如通过网络劫持在页面传输过程中修改 HTML 页面的内容,这种劫持类型很多,有通过 WiFi 路由器劫持的,有通过本地恶意软件来劫持的,它们的共同点是 在 Web 资源传输过程或者在用户使用页面的过程中修改 Web 页面的数据

    基于 DOM 的 XSS 攻击漏洞属于 前端安全漏洞

要阻止 XSS 攻击,我们可以通过 阻止恶意 JavaScript 脚本的注入恶意消息的发送 来实现。

阻止 XSS 攻击的常用策略:

  • 服务器对输入脚本内容进行过滤或转码;

  • 使用 CSP

    CSP 提供了一组丰富的策略指令,可以对页面允许加载的资源进行相当精细的控制

    使用方式:

    • http header

      Content-Security-Policy: xxx (配置好并启用后,不符合 CSP 的外部资源就会被阻止加载)

      Content-Security-Policy-Report-Only: xxx (表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri选项配合使用。)

    • 通过 meat 标签在 HTML 中使用

      <meta http-equiv="content-security-policy" content="策略">
      <meta http-equiv="content-security-policy-report-only" content="策略">
      

    常见的策略指令:

    • base-uri

      定义了 URI,它可以作为文档的基准 URL。如果没有指定值,那么任何 URI 都被允许。如果没有指定这条指令,浏览器会使用 base 元素中的 URL

    • child-src

      指定定义了 web workers 以及嵌套的浏览上下文(如 和 )的源

    • connect-src

      定义了请求、XMLHttpRequest、WebSocket 和 EventSource 的连接来源

    • default-src、font-src、img-src、media-src

    • 使用 HttpOnly 标记 cookie,使得无法通过 document.cookie 读取 cookie

    • 36. CSRF 攻击

      CSRF, 英文全称是 Cross-site request forgery,所以又称为 “跨站请求伪造”,是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用 用户的登录状态 发起的 跨站请求

      简单来讲,CSRF 攻击就是 黑客 利用了 用户的登录状态,并通过第三方的站点来做一些坏事。

      CSRF 攻击的过程:

      1. 用户浏览并登陆受信任站点A;
      2. 登陆信息验证成功以后,站点A 返回给浏览器的信息中携带 cookie,保存在浏览器中;
      3. 用户没有登出站点A,访问恶意站点B;
      4. 恶意站点B提供的页面,自动向站点A发起请求,发起请求时携带用户cookie信息;
      5. 站点A根据cookie信息判断是用户请求;

      要发起 CSRF 攻击,需要满足以下条件:

      1. 黑客知道攻击受信任站点的请求,即 CSRF 漏洞;
      2. 用户登录受信任网站,并生成 cookie;
      3. 未登出状态下访问恶意网站;

      防御 CSRF 攻击的方式:

      1. 充分利用好 Cookie 的 SameSite 属性

        SameSite 属性的值如下:

        • Strict: 只有源站点发送请求时,才能携带 cookie 数据;
        • Lax:第三方站点发生 get 请求时,可携带 cookie 数据;
        • None: 第三方站点发送任何请求,都可携带 cookie 数据;
      2. 验证请求的来源站点

        通过 http header 中的 origin、referer 判断请求来源;

      3. CSRF Token

        流程分为两步:

        • 第一步,在浏览器向服务器发起请求时,服务器生成一个 CSRF Token。

          CSRF Token 其实就是服务器生成的字符串,然后将该字符串植入到返回的页面中。

        • 第二步,在浏览器端如果要发起请求,那么需要带上页面中的 CSRF Token,然后服务器会验证该 Token 是否合法。

      37. https

      HTTPS 是在 TCPHTTP 之间插入 一个安全层,所有经过 安全层数据 都会被 加密或者解密

      安全层 有两个主要的职责:对发起 HTTP 请求的数据进行加密操作对接收到 HTTP 的内容进行解密操作

      HTTPS 的实现:

      • 第一版:使用对称加密

        客户端服务端 使用相同的 加密方式秘钥

        客户端先和服务端通信,确定加密方法和秘钥,然后对发送的数据加密,对接收的数据解密。

        缺点: 密钥容易被破解,黑客可以根据破解的密钥解密用户数据,然后篡改伪造数据。即黑客可篡改服务端和客户端发送的数据。

      • 第二版:使用非对称加密

        客户端和服务端各自保留一个 密钥客户端的密钥称为公钥,服务端的密钥称为私钥

        客户端和服务端通信时, 服务端会发送给客户端加密方法和公钥。然后客户端使用公钥加密数据, 服务端使用私钥解密数据。

        由于客户端发送的加密数据, 只能通过私钥解密,黑客无法篡改客户端发送给服务端的数据。

        缺点: 由于公钥是明文发送的,可被黑客获取,所以服务端发送给客户端的数据可以被黑客解密

      • 第三版: 对称加密和非对称加密搭配使用

        生成密钥的方式:

        1. 客户端和服务端通信时, 服务端发送给客户端加密方法和公钥,自己保留私钥;
        2. 客户端生成一个随机数,然后使用公钥加密,发送给服务端,服务端使用私钥解密;
        3. 客户端和服务端根据随机数各自生成密钥;

        黑客无法解密客户端加密过的随机数, 就无法生成最终的密钥。

      • 第四版:数字证书

        数字证书,用于证明 服务端的身份

        对于浏览器来说,数字证书 有两个作用:

        • 通过 数字证书 向浏览器证明 服务器的身份

        • 数字证书 里面包含了 服务器公钥

        生成数字证书:

        1. 服务端需要准备一套私钥和公钥,私钥留着自己使用;
        2. 服务端向 CA 机构提交公钥、公司、站点等信息并等待认证,这个认证过程可能是收费的;
        3. CA 通过线上、线下等多种渠道来验证服务端所提供信息的真实性,如公司是否存在、企业是否合法、域名是否归属该企业等;
        4. 如信息审核通过,CA 会向服务端签发认证的数字证书,包含了服务端的公钥、组织信息、CA 的信息、有效时间、证书序列号等,这些信息都是明文的,同时包含一个 CA 生成的签名。

      38. 使用 Performance

      使用 Performance, 可以录制 加载阶段交互阶段的分析数据。

      两种录制方式稍微有点不同:

      • 当你录制 加载阶段 的性能数据时,Performance 会重新刷新页面,并等到页面完全渲染出来后,Performance 就会 自动停止录制
      • 如果你是录制 交互阶段 的性能数据时,那么需要 手动停止录制过程

      39. window.onload 和 DOMContentLoaded

      DOMContentLoaded, 加载阶段 dom 树构建完成 以后触发。

      window.onload, 所有文件都加载完毕以后触发。

      40. 浏览器内核

      Trident([·traidnt] IE内核):IE、360(早期为Trident,最新为Trident + Blink)、搜狗(1.x为Trident, 2.0及以后版本为Trident + Webkit);

      Gecko([`gekəʊ],火狐内核):火狐;

      Presto([`prestəʊ]): Opera前内核,现已废除;

      Webkit:chrome([krəʊm])、safari([səˈfɑ:ri])

      Blink: Webkit的分支, Opera

      js 查看 浏览器的内核navigator.userAgent

      41. <! DOCTYPE>

      <! DOCTYPE> 不是一个 html 标签,它是用来 告诉浏览器当前处理的是 html 文档,且 html 属于哪个版本(html5)

      <! DOCTYPE> 必须声明在 html 文档 的第一行,不区分大小写

      <! DOCTYPE> 声明没有结束标签, 即不存在<! DOCTYPE /> 这种写法

      42. html 4.01 和 html5 之间的差异

      两者的差别:

      • html4.01 是基于 SGML, 需要引用 dtdhtml5 不基于 SGML, 不需要引用 dtd

      • html4.01 有三种声明,而 html5 只有1种声明

        html 4.01 的声明方式

        • HTML 4.01 Strict

          包含所有的 html 元素属性,但 不包括展示性和弃用的元素(比如 font)不允许框架集(Framesets)

          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
          
        • HTML 4.01 Transitional

          包含 所有的 html 元素 和 属性,包括展示性和弃用的元素(比如 font)不允许框架集(Framesets)

           <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  "http://www.w3.org/TR/html4/loose.dtd">
          
        • HTML 4.01 Frameset

          包含 所有的 html 元素和属性,包括 展示性和弃用的元素(比如 font)允许框架集(Framesets)

          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"  "http://www.w3.org/TR/html4/framset.dtd">
          

        HTML5 的 声明方式为: <!doctype html>

      43. SGML

      标准通用置标语言,xml、html由它衍生而来

      44. HTML的DTD文档

      由于历史的原因,html有很多版本的规范。不同版本之间各有差异,为了方便浏览器正确的编译解析以及渲染我们的网页,我们需要在html文件的第一行告诉浏览器我们当前的网页是基于哪个标准规范编写的, 即使用 doctype

      DTD文档 的声明不算是一个标签,只是告诉浏览器网页是基于哪个版本。但是浏览器并不是完全依赖这个文档,它有自己的一套机制,和浏览器的内核相关,即没有这个文档也可正常运行网页。

      45. XHTML / HTML

      XHTML, 可扩展超文本标签语言,它是 使用 xml 的规则重新规范的 html,是 更严格更纯净的 HTML 版本

      HTML 最主要的不同:

      • XHTML 元素必须被 正确的嵌套

      • XHTML 元素必须被 关闭

      • 标签名必须是 小写

      • XHTML文档必须有 根元素

      • 属性名必须小写属性值必须加引号属性不能简写(disable、checked)

      46. 标准模式 & 兼容模式

      标准模式,也叫 严格模式,按 w3c标准 渲染页面。

      兼容模式:也叫 怪异模式按照浏览器自己的方式渲染页面,主要针对 IE浏览器

      兼容模式 的用途:使老网站可以正常显示

      触发兼容模式h4版本,没有 doctype 声明或者 doctype 声明不正确

      检测:document.compatModel, BackCompat - 兼容模式CSS1Compat - 标准模式

      具体示例:

      • 标准模式下,元素的 width,heigth 指元素内容区的高度和宽度,兼容模式下,还需要包含padding + border;
      • 标准模式下,span设置width、height无效, 兼容模式下有效;
      • 兼容模式下,块元素 margin:0 auto 居中无效,需使用text-align:center;

      47. html语义化

      根据 html 页面内容的结构,选择 合适的标签。比如,标题使用 h1-h6, 段落使用 p,列表使用 ul、ol等。

      语义化的好处

      • 即使 没有css,页面也能呈现出很好的 内容结构
      • 页面内容更加结构化代码更加语义化便于开发者阅读、维护
      • 有利于 搜索引擎优化,有助于爬虫抓取到更多有效的信息;

      html5 语义化标签articlefooterheadernavsectionvedio 等。

      48. html5 离线存储

      通过 离线存储功能,我们可以指定 web 应用程序所需要的资源,这样浏览器在加载 html 文档时把他们下载下来,使得用户即使无法访问网络也能继续使用我们的应用

      启用流程:

      • 创建一个 清单文件,以 .appcache 命名,并让 web服务器 将清单的内容向浏览器描述为 text/manifest

      • 定义 清单文件,文件内容第一行始终为 CACHE MANIFEST 给 html 标签添加 manifest = ‘.appcache’ 属性

        清单文件有三个区域:

        • CACHE - 指定离线应用要缓存的资源;
        • FALLBACK: 指定资源不可用时要显示的备用内容;
        • NETWORK: 指定始终向服务器请求的资源;

      判断是否离线window.navigator.onLine

      使用: window.applicationCache

      49. html5 多线程 - worker

      浏览器端的多线程

      worker 分类:

      • 专用 worker - Worker

        仅能被生成它的脚本使用

        多个脚本通过 new Worker('worker.js') 的方式使用,浏览器中会生成 多个 worker 线程

        示例:

        // main.js
        var worker = new Worker('./worker.js');
        worker.onmessage = function (e) {
            alert(e.data)
        }
        worker.postMessage(data)
        
        // worker.js
        self.addEventListener('message', function (e) {
          self.postMessage('You said: ' + e.data);
        }, false);
        
      • 共享 worker - SharedWorker

        一个 共享 worker 可以被 多个js脚本使用共享 worker 在浏览器中只会生成一个线程同源(相同的协议、域名、端口号) 的浏览器上下文都可以调用这个 共享 worker 线程

        和专用 worker 不同,共享 worker 需要通过 port 和共享线程进行交互。不同的 shared worker, 对应的 port 不同

        // main.js
        
        var myWorker = new SharedWorker("./worker.js");
        myWorker.port.start();
        myWorker.port.onmessage = function(e) {
            alert(e.data)
        }
        
        // worker.js
        
        // sharedData 可以被多个标签页的同源 shared workder 共享
        var sharedData;
        onconnect = function(e) {
            var port = e.ports[0];
            port.addEventListener('message', function(e) {
                var data = e.data;
                sharedData = data;
                port.postMessage(sharedData)
            });
            port.start();
        }
        

      接收信息: 注册 onmessage

      发送信息:postMessage

      终止 worker: terminate

      限制:worker中,不能操作 dom

      50. 不同标签页面之间的通讯

      实质就是 不同的页面都可以访问同一个数据存储结构,前提是这几个页面 同源

      可用的方式如下:

      • cookie

      • localstorage

      • shared worker

      51. html5 - websocket

      websocket 是一种 网络通信协议

      websocket 是在单个 tcp 连接上进行 全双工通信,不像 http 协议,只能有 客户端发起通信; 如果服务器有连续的状态变化,我们使用 http 就得不停的轮寻。

      具体用法:

      var ws = new WebSocket('url');
      ws.onmessage = function(e) {...}
      

      52. html5 - Geolocation

      地理定位,我们可以通过全局属性 navigation.geoLocation 获取 GeoLocation 对象

      GeoLocation.getCurrentPosition 方法获取用户的 当前地理位置

      GeoLocation.watchPosition 方法 监听用户地理位置的变化

      GeoLocation.clearWatch 方法来 清除监听

      使用 地理定位功能 时, 所有浏览器会做的第一件事情就是询问 用户是否对其授权

      53. HTTP状态码

      1xx - 信息响应类,代表请求已经被接收,需要继续处理;

      2xx - 处理结果响应类,代表请求已成功被服务器接收、理解、并处理;200(请求成功)

      3xx - 重定向响应类

      • 304(请求内容未修改)

      4xx - 客户端错误,客户端发送的请求有问题。

      • 400 - 语义有误,请求无法被服务器理解,或者请求参数有误;
      • 404 - 请求失败,资源在服务器上未发现;

      5xx - 服务端错误

      • 500 - 服务器源代码发生错误

      54. html 本地缓存

      常见的 html 本地缓存

      • cookie

        本地存储最原始的方式,它是存放在本地浏览器的一段文本,数据以键值对的形式 保存。通过 document.cookie 访问。

        会话 cookie不设定生命周期cookie,关闭浏览器窗口,cookie 对象就会被销毁;

        持久 cookie设定生命周期的 cookie,关闭浏览器窗口,cookie 对象不会被销毁,直到设定的时间过期为止。

        cookie 存储 的内容比较少,一般限制为 4k 左右;

        应用:保存用户的登录信息

      • localStorage

        通过 window.localStorage 访问。

        localStorage 的数据永久存在, 关闭浏览器也不会销毁

        方法:setItemgetItemremoveItem, clear 等;

        应用:记录网站的访问次数、用户登录信息、各个浏览器上下文之间进行通信。

        局限:一般大小为 5m,超过存储空间会报错;存储内容会消耗浏览器内存,过多存储会导致页面变卡.

        高版本浏览器支持此属性

      • sessionStorage

        通过 window.sessionStorage 访问。

        sessionStorage 中的数据在各个浏览器上下中是私有的,会在浏览器窗口关闭时移除。

        用法和 localStorage 基本相同。

      55. 浏览器缓存机制

      浏览器缓存(Brower Caching)浏览器 对之前请求过的文件进行 缓存,以便 下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力。

      http 缓存机制 主要在 http 响应头 中设定,响应头 中相关字段为 ExpiresCache-ControlLast-ModifiedEtag 等。

      浏览器缓存 分为 强缓存协商缓存

      • 强缓存

        浏览器 不会像 服务器 发送任何请求,直接从 本地缓存 中读取文件并返回 Status Code: 200 OK

        本地缓存 有两种形式:

        • from memory cache

          资源缓存在 内存 中,重复读取,从 内存 中获取。关闭浏览器再重新打开,无法获取上次缓存。

          一般 字体图片 会保存在 内存 中。

        • from disk cache

          资源缓存在 本地磁盘 中。关闭浏览器重新打开,可以获取上次缓存。

          一般 js 脚本、css 脚本 会保存在 本地磁盘 中。

        会优先访问 memory cache, 然后访问 disk cache

        强缓存 对应的字段为:

        • Expires

          设置过期时间, 在过期时间内,不会向服务器请求。

        • cache-control:max-age = xxx

          设置过期时间,在过期时间内,不会向服务器请求。

        cache-control 和 Expires 同时设置, cache-control 会覆盖 Expires。建议两者都写,防止 http 版本较低,无法识别 cache-control

      • 协商缓存

        浏览器服务器 发送请求,服务器会根据这个请求的 request header的一些参数来判断是否 命中协商缓存,如果 命中,则返回 304 状态码 并带上新的 response header 通知浏览器从 缓存 中读取资源。

        协商缓存 对应的字段:

        • Last-Modified / if-Modified-Since

          需和 Expires 一起配合使用。

          Last-Modified响应头 中的 字段,为 服务器资源最后一次修改时间

          Expires 设置的过期时间失效时,浏览器 会重新向 服务器 发起请求。发送请求时,会通过 if-Modified-Since 字段携带上一次响应信息中的 Last-Modified。服务器会根据 if-Modified-Since 的值和 资源的最后修改时间 比较,判断资源是否发生修改来判断浏览器的缓存是否失效。如果 没有修改,返回 304,告诉浏览器继续使用缓存;如果 修改,返回 最新的资源及最新的 Last-Modified更新浏览器缓存

        • Etag /If-None-Match

          需和 Cache-control 一起配合使用。

          Etag响应头 中的 字段, 为 服务器资源内容的标识(md5)

          Cache-control 设置的过期时间失效, 或者 Cache-control: no-cache 时,浏览器 会重新向 服务器 发起请求。发送请求时,会通过 If-None-Match 字段携带上一次响应信息中的 Etag。服务器会根据 Etag 的值和 当前资源 比较,判断资源是否发生修改来判断浏览器的缓存是否失效。如果 没有修改,返回 304,告诉浏览器继续使用缓存;如果 修改,返回 最新的资源及最新的 Etag, 更新浏览器缓存

      Cache-control, 常见的取值如下:

      • private

        内容只能被缓存到浏览器中,不能缓存到代理服务器中;

      • public

        内容可以缓存到浏览器、代理服务器中

      • no-cache

        内容可以被缓存到浏览器中,使用时需要和服务器通信判断之前的内容是否被修改。如果未被修改,返回304.

      • no-store

        所有的内容都不可以被缓存

      • max-age = xxxx

        缓存的内容在 xxxx 秒之后失效

      手动刷新 F5浏览器 会认为 缓存已经过期,会在请求中加上 Cache-Control:max-age=0, 向服务器验证缓存是否可用。

      强制刷新 ctrl+F5浏览器 会直接 忽略本地的缓存,在请求中添加 Cache-Control:no- cache, 从服务器请求新的资源。

      56. ETag/If-None-Match 和 Last-Modified/if-Modified-Since 的比较

      Etag/If-None-MatchLast-Modified/if-Modified-Since 功能相似,都是用来 判断 url 请求的资源是否发生改变

      Etag 主要是为了解决 Last-Modified 无法解决的一些问题:

      • 某些文件只修改时间,不修改内容,使用Etag可以确保重新请求使用缓存;
      • 某些文件修改频繁,可能1s内修改了n次,if-Modified-Since 检查的粒度是s级的,无法 判断文件是否修改;
      • 某些服务一不能精确的得到文件的最后修改时间;

      57. 请求头和响应头中的Cache-control:no-cache

      请求信息响应信息 中的 Cache-control:no-cache 代表的意思不相同。

      响应信息 中的 no- cache 表示,不管缓存有没有失效,下次访问统一 url 地址时,都要请求服务器验证缓存是 否失效。

      请求信息 中的 no-cache 表示不使用缓存,需要从服务器请求新的资源。

      58. meta标签

      meta 标签 提供了 html 文档元数据,通常用于指定网页的描述,比如关键字、作者、文件的最后修改时间等。

      元数据 可以被浏览器、搜索引擎等调用。

      meta 标签 的属性:

      • name

        name 属性主要用于描述网页, 与之对应的属性值为 contentcontent 中的内容主要便于搜 索引擎使用。

        常见的 name:

        • 关键字

          <meta name="keywords" content="***, ***">
          
        • 描述

          <meta name="description" content="***, ***">
          
        • 作者

          <meta name="author" content="">
          
        • 视口

          <meta name="viewport" content="width=device-width,initial-scale=1.0">
          
      • http-equiv

        相当于 http 的文件头,向浏览器传递一些有用的信息。

      • expires

        设定网页的到期时间,一旦过期,必须到服务器重新请求

        <meta http-equiv='expires' content="GMT时间格式">
        
      • Pragma

        禁止浏览器从本地计算机的缓存中访问页面内容

        <meta http-equiv="Pragma" content="no-cache">
        
      • Refresh

        自动刷新并转到新页面

        <meta http-equiv="Refresh" content="2;URL">
        
      • set-cookie

        设置cookie

        <meta http-equiv="Set-Cookie" content="cookievalue=xxx;“>
        
      • content-type

        设定页面使用的字符集

        <meta http-equiv="content-Type" content="text/html; charset=gb2312">
        
      • content-Language

        显示语言的设定

        <meta http-equiv="Content-Language" content="zh-cn" />
        

      59. label标签

      元素 的直观效果是 直接显示标记之间的文本,而且 不会为文本呈现任何特殊效果,可以附带一个 for 属性,只要将该属性的值设置为表单中任何一个控件的 id 属性的值,则当该用户点击标签(文本)时,浏览器就会自动将焦点转到和标签相关的表单控件上.

      60. 超链接的常用表现形式

      普通链接下载链接电子邮件链接空链接(#)链接到 js 代码

      61. 服务发送事件

      server-sent event允许网页获取来自服务器的更新

      创建一个 EventSource 对象,指定 发送更新的地址,然后注册 onmessage 事件。每接收到一次更新,就会触发 onmessage 事件。

      var sse = new EventSource('url');
      sse.onmessage = function(e) {...}
      

      62. webSocket 和 Server-sent的比较

      Websocket 既可以给浏览器发送数据,也可以从浏览器发送数据。

      var ws = new WebSocket('url');
      ws.onmessage = function(e) {...}   // 接收数据
      ws.send(...);   // 发送数据
      

      Server-sent 只能 给浏览器发送数据不能从浏览器发送数据.

      63. DOM2.0 模型

      事件处理流程 分为三个阶段,即 事件捕获阶段事件处理阶段事件冒泡阶段

      • 事件捕获:当用户触发点击事件后,顶层对象document 就会发出一个事件流,从最外层的DOM节点向目标元素节点传递,最终到达目标元素。途中如果有节点绑定了事件处理函数,这些函数就会被执行。
      • 事件处理:当到达目标元素之后,执行目标元素绑定的处理函数。如果没有绑定监听函数,则不做任何处理。
      • 事件冒泡:事件流从目标元素开始,向最外层DOM节点传递,途中如果有节点绑定了事件处理函数,这些函数就会被执行。

      64. BOM

      BOMBrowser Object Model 的缩写,即 浏览器对象模型,当一个浏览器页面初始化时,会在 内存 创建 一个全局的对象,用以描述 当前窗口的属性和状态,这个全局对象被称为 浏览器对象模型,即 BOM

      BOM核心对象 就是 windowwindow 对象也是 BOM 的顶级对象,其中包含了浏览器的 6 个核心模块

      • document

        文档对象

        渲染引擎在解析 HTML 代码时,会为每一个元素生成对应的 DOM 对象,由于元素之间有层级关系,因此整个HTML代码解析完以后,会生成一个由不同节点组成的树形结构,俗称 DOM 树,document 用于描述DOM树的状态和属性,并提供了很多操作 DOM 的 API。

      • frames

        HTML 子框架,即在浏览器里嵌入另一个窗口,父框架和子框架拥有独立的作用域和上下文。

      • history

        栈(FIFO) 的形式保存着 页面被访问的历史记录,页面前进即入栈,页面返回即出栈。

      • location

        提供了当前窗口中加载的 文档相关信息 以及一些导航功能。

      • navigator

        用来描述浏览器本身,包括浏览器的名称、版本、语言、系统平台、用户特性字符串等信息。

      • screen

        提供了浏览器显示屏幕的相关属性,比如显示屏幕的宽度和高度,可用宽度和高度。

      65. ajax & xhr

      ajax, 异步 js 和 xml,是一种创建 交互式网页应用 的开发技术,无需重新加载整个页面,即可更新部分网页。

      ajax 的核心是 xhr,即 XMLHttpRequest(可扩展超文本传输请求)

      使用 xhr创建一个 ajax

      1. 创建一个 xhr 对象, 即 xhr = new XMLHttpRequest();

      2. 初始化 http 请求,即 xhr.open('get', url, async);

      3. 设置 请求成功及失败响应方法;

        xhr.onload = function() {...}

        xhr.onerror = function() {...}

      4. 发送 http 请求, 即 xhr.send();

      5. 异步 调用返回的数据,使用 jsdom 实现 局部更新;

      xhr 对象的 readyState 属性可以判断 http 请求的状态:

      • 0初始化状态,var xhr = new XMLHttpRequest();
      • 1未发送, xhr.open(method, url, async);
      • 2已获取响应头, xhr.send()被调用;
      • 3, 接收响应主体
      • 4响应已完成

      XMLHttpRequest 有两个版本,level1level2

      level1 的缺点:

      • 无法跨域
      • 只能发送纯文本数据,不能发送二进制文件
      • 发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成 (只能注册onreadystatechange事件)

      level2 的改进:

      • 在服务器端允许的情况下,允许跨域(支持 CROS);

      • 支持发送和接收二进制文件

      • 发送和获取数据时,可获取进度信息,

        onloadstart - send方法之后立即触发

        onprocess - 下载阶段触发, readyState=3时触发

        onload - 请求完成时触发,readyState = 4时触发

      • 可以设置超时时间

      • 可以上传文件

      66. post 和 get 有什么差别

      语义 上的区别:

      • get:根据url请求指定资源; post:根据url请求向指定资源提交数据,用于修改指定资源

      表现 上的区别:

      • get参数拼接到URL中传递,会暴露; POST参数会放在Request body中,不会暴露
      • 在实际开发过程中,对请求的url的长度有限制,所以GET请求的参数是有限制的;由于post参数不是拼接在url中,理论上没有请求参数的长度限制,但实际上还是会有限制的。
      • get请求会被浏览器主动cache,而post不会,除非手动设置
      • get请求参数会被完整保留在浏览器历史记录里,而post的参数不会保留
      • get请求是安全的,不会引起服务端的状态变化(前提是服务端的实现符合方法语义);post请求是不安全的,会引起服务端的状态变化
      • get请求是幂等的,同一个方法执行多次和仅执行一次的效果一样,post请求是不幂等的(前提是服务器的实现符合方法语义)

      未完,持续更新