1 从浏览器进程角度系统全面回答从输入URL到页面显示发生了什么
浏览器是一个多进程架构 所谓的多进程 就是多个人同时进行一件事
而我们浏览器主要分为 浏览器主线程 GPU线程 渲染线程以及网络线程 加上存储相关的线程
- 浏览器主线程 负责页面显示 用户交互 子线程的管理 储存功能
- 渲染线程 主要任务 就是把html css js渲染成网页
- 网络线程 顾名思义 就是负责网络的线程 网络图片加载
- 存储相关的线程 就是数据存放的线程
我们分为三个进程 浏览器主线程 渲染线程 网络线程
步骤
- 先在浏览器地址栏输入一段东西
- 判断这个东西是网址还是关键字 如果是关键字 就直接拼接搜索引擎 发送到网络进程准备进行url请求 如果是网址 直接发送到网络进程准备发起URL请求
- 发起url请求前 先查看有没有存储 如果有 并返回资源 如果没有 就发起url请求
- 进行dns解析url 获取到url上的域名和对应的服务器的ip地址
- 连接服务器 建立起一个tcp的链接 所谓的tcp 就是三次握手四次挥手 构建数据并发送到服务器 服务器收到数据 就会生成一个相应数据返回给我们 就会有响应头响应体
- 我们的浏览器会根据响应头的字段content-text 这个字段告诉浏览器 获取到什么类型的数据 这样对页面的解析
- 读取完网络进程后 就会准备渲染进程
- 渲染进程把html css js进行解析
- 是从html解析成tokens tokens解析到dom节点 同时解析css 把css和dom节点构建出render树
- 建立网络进程的管道 获取响应体数据 并渲染 然后提交到主线程
- 主线程收到确认信息 在把页面的图片等资源经过渲染进程解析出来 渲染到页面 完成了页面内容加载
2.浏览器解析渲染HTML
浏览器从html css js文件开始 到将它们最终以像素输出到屏幕上这一过程 叫关键渲染路径,包括以下几个部分
-
构建DOM
- 将html解析成tokens
- 将tokens解析成object
- 将object组合成为一棵DOM树
-
构建CSSOM
- 解析CSS文件 并构建一棵CSSOM树 (过程类似于DOM创建)
-
构建render tree
- 结合DOM树和CSSOM构建出一棵Render树
-
Layout
- 计算出元素相对于viewport的相对位置
-
paint
- 将render tree转换成像素 显示在屏幕上
注意 这个过程并不是依次进行 而是存在一定交叉的
3 css匹配规则为何从右向左
例如 .a .b .c{color:red;} 我们都以为是从左到右寻找类名 其实恰恰相反 是从右向左寻找类名、因为css太过庞大了 如果从左向右寻找 就会先找外面的类名为a的标签 再从类名为a的标签里寻找类名为b的标签 把不符合的舍弃 这样的话 一个css文件匹配规则或许有上千条 而且对于当前节点来说 大多数规则都匹配不上 到此为止 那如果是从右往左呢 那就会完全不一样了 先从最精准的位置开始 可以更快排查出不合适的节点 从而大大减少资源
10月9日
1 三次握手四次挥手
发请求是采用http请求 建立http链接之前,建立TCP链接-三次握手
- 首先客户端向服务器先发起一个链接 会携带一个参数 叫SYN=1 --客户端看服务器在不在
- 服务器接受链接 返回客户端两个参数 SYN=1 ACK=1 表示告诉客户端我活得好好的
- 客户端收到服务器两个参数 表示 我知道你是可以通信 可以建立链接了
四次挥手 断开链接的时候 需要四次挥手
- 客户端向服务器发送一个FIN=1的参数 告诉服务器 我要断开链接了
- 服务器收到FIN=1之后 先发一个ACK=1 并没有回发FIN=1 会告诉我知道了 但是我现在还不能断 因为有可能数据还没传输完成
- 当服务器传输完成后 客户端会再给服务器发送一个FIN=1 确认要断开了
- 这次客户端还会再确认断开链接 经过ZMSAwit阶段 等待关闭
2 js事件循环
js是单线程 所谓的单线程是代码依次一行一行的执行 上一行不执行 下一行没法继续执行 这样的好处就是从上到下依次执行 好理解 但也有一个问题 如果中间的某一行代码出现了错误 下面的代码就执行不了 就被堵塞了 而为了解决堵塞这个问题 js提供了事件循环机制 来模拟多线程 所谓的多线程就是同时执行多个任务 不会出现堵塞问题 谁也不会影响谁 但理解起来颇为麻烦
- 同步模式 代码按照顺序依次执行 前面没有执行 后面就不会执行
- 异步模式 代码可以同时执行 前面没有执行完 后面也可以执行
为了解决异步模式 js推出了宏任务和微任务
-
宏任务:
setTimeout setInterval Ajax DOM事件 script(整体代码) -
微任务:
Promise.then async/await Object.observe MutationObserver
执行顺序是 主线程 微任务 宏任务
- 所有的同步任务都在主线程上执行,行成一个执行栈。
- 除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记。
- 主线程完成所有任务(执行栈清空),就会读取任务列队,先执行微任务队列在执行宏任务队列。
3 闭包
闭包就是通过某种方式获取函数内部的变量 然后暴露到函数外部 并且在将来的某个节点还可以继续操作该变量 闭包可以延长函数的生命周期
- 优点 可以防止污染我们的全局作用域
- 缺点 可能会导致内存泄漏的问题
var obj={}
function f(){
let a=10
obj.get=function(){
return a
}
obj.set=function(v){
a=v
}
}
f()
//调用obj
obj.set(15) // 15
闭包会产生一种 你感觉不到它的存在 但是它确确实实存在着
10月10日
宏任务和微任务
微任务在DOM渲染前执行 比如 Promise.then 宏任务在DOM渲染之后执行 比如setTimeout
作用域链
当一个变量在当前作用域无法找到时,便会尝试寻找其外层的作用域,如果还找不到,再继续往外寻找(只会往外寻找,不会寻找兄弟作用域,更不会往内寻找)。这种如同链条一样的寻找规则便被称为作用域链。
如果找不到变量会怎样?
如果一个变量直到全局作用域也找不到便会执行以下操作。
- 非严格模式:隐式声明全局变量
- 严格模式:报错
非严格模式
非严格模式下,尝试赋值一个变量时,如果找不到则会隐性声明成全局域的变量。
{
variable = "我是一个隐性声明的变量"
}
console.log(variable)
输出:
我是一个隐性声明的变量
严格模式
加入 "use strict" 表明是严格模式,严格模式下不论赋值还是使用未事先声明的变量都会报错。
作用域的好处?
防止命名冲突:你写了一万行的代码文件,如果没有作用域,你要给每个变量取独一无二的名字,屁股想想也知道是种折磨。安全性: 变量不会被外部访问,保证了变量值不会被随意修改。你定义在函数内的变量,如果能在几千行之后不小心被修改,脚趾头想想也知道是种折磨。更高级的语法:封装、面向对象等的实现离不开对变量的隔离,这是依靠作用域所达到的。
原型
1.什么是原型
在js中,使用构造函数来创建一个对象时, 每一个构造函数中都有一个prototype属性,它的属性值 是一个对象,这个原型对象包含了该构造函数的所有实例共享的属性和方法
对象原型(隐式原型)__proto__.
当用构造函数创建一个实例后,实例对象中包含一个指针(__proto__ 对象原型) ,这个指针指向构造函数的原型对象prototype
原型链
当访问一个对象属性或方法时,首先查找这个对象自身有没有这个属性没有就会去它的原型中查找(prototype原型对象) 还没有就去查找原型对象的原型-直找到Object为止(Object的原型对象的原型为null),这条查找的路径,就是原型链