HTML部分
HTML5新特新有哪些
- 语义化标签(header、nav、main、article、section、aside、footer)
- 表单控件(calender、date、time、email、url、search)
- 音视频处理API(audio、video)
- Canavs/webGL
- 拖拽释放API
- History API
- 地理位置API(geolocation)
- websocket
- web存储(localStorage、sessionStorage)
CSS部分
CSS3新特性
- 选择器(属性选择器、伪类选择器、伪元素选择器)
- 盒子模型(box-sizeing: border-box)
- 图片模糊(backdrop-filter: blur(20px))
- 计算盒子宽度函数(calc)
- 过渡(transition)
- 动画(animation)
- 形状转换(transform: translate(xxx,xxx) / rotate(xxxdeg) / scale(xxx)))
- 浏览器私有前缀(-webkit- 谷歌/safari、-moz- 火狐、-o 欧朋)
9. 媒体查询
CSS选择器优先级和权重计算
选择器:
- ID选择器 #xxx
- 类选择器 .xxx
- 属性选择器 a[id=”xxx”]
- 伪类选择器 a:hover
- 标签(元素)选择器 div
- 伪元素选择器 a::after
- 相邻选择器 h1 + p
- 子选择器 div > a
- 后代选择器 div a
- 通配符选择器 *
优先级:
!important > 行内样式 > ID选择器 > 类/属性/伪类选择器 > 标签(元素)/伪元素选择器 > 通配符选择器
权重计算:
如何设置小于12px的字体大小
- 利用scale属性
- -webkit-text-size-adjust: none
Position属性的值有哪些及其区别
默认定位(static)
默认值,没有定位,top, right, bottom, left 和 z-index 属性无效
相对定位(relative)
相对于元素原来位置定位,元素占据原来位置,可以设置top, right, bottom, left值,设置之后元素原来的位置会出现空白
绝对定位(static)
相对于最近的非static的已设置定位的父元素定位,元素正常脱离文档流,不再占据原来的位置,可以设置top, right, bottom, left值,还可以设置margin值,且不会与其他边距合并
固定定位(fixed)
相对于屏幕视口定位(viewport)定位,元素脱离正常文档流,不再占据原来的位置,元素的位置在屏幕滚动时不会改变,当元素祖先的transform、persective或filter属性非none时,定位的容器由屏幕视口改为该祖先
例如:在通过filter添加灰色滤镜时,如果filter属性添加在body标签上,就会导致fixed属性失效,所以此时一般将filter属性添加到html标签上
粘性定位(sticky)
相对定位和固定定位的混合,粘性定位可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。必须指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。
box-sizeing和盒子模型
box-sizeing:规定如何计算一个元素的总宽度和总高度,主要分为两种
conent-box 标准盒子模型
盒子width和height只包括内容的宽和高,不包括边框(border),内边距(padding),外边距(margin)
计算公式
width = 内容的宽度
height = 内容的高度
border-box 怪异盒子模型(IE盒子模型)
盒子width和height包括内边距(padding)和边框(border),不包括外边距(margin)
计算公式
width = border + padding + 内容的宽度
height = border + padding + 内容的高度
文档流和脱离文档流
文档流:
文档流(Normal flow)也称为常规流,普通流。从直观上理解,指的是元素按照其在 HTML 中的位置顺序决定排布的过程,主要的形式是自上而下(块级元素),一行接一行,每一行从左至右(行内元素)
脱离文档流:
元素脱离文档流之后,将不再在文档流中占据空间,而是处于浮动状态(可以理解为漂浮在文档流的上方)。脱离文档流的元素的定位基于正常的文档流,当一个元素脱离文档流后,依然在文档流中的其他元素将忽略该元素并填补其原先的空间。
外边距合并
定义:
两个div盒子的下边距和上边距有时会合并(折叠)为单个边距,合并的边距的大小取决于下边距和上边距的更大的值,如果两个值相等,则随便其中一个即可(注意:浮动元素(display: float)和绝对定位元素(position: absolute)不会差生外边距合并,因为他们已经正常的文档流)
产生场景:
- 两个相邻的元素(除非后一个元素使用了清除浮动 clear: both)
- 空的块级元素(当两个盒子中间有第三个盒子,然后这个盒子没有设置任何属性,这两个盒子还是会发生外边距合并)
解决方案:创建BFC或者清除浮动
BFC
BFC(块级格式化上下文),它是一个独立的渲染区域,规定了内部的盒子如何布局,并且这个区域的子元素不会影响到外面的元素,主要的规则包括内部的盒子垂直放置,计算BFC的高度的时候,浮动元素也会参与运算
布局规则:
- 内部的盒子会在垂直方向一个接一个的摆放
- 元素的垂直方向的距离由margin决定,属于同一个BFC的两个相邻的元素的margin会发生重叠,又称为外边距合并(可以创建两个BFC来解决外边距合并的问题)
- BFC中的元素不会和浮动的元素发生重叠
- BFC是一个独立的容器,它内部的子元素不会影响外面的元素
- 计算BFC的高度时,浮动的元素也会参与运算
如何创建BFC:
- 根元素(<html></html>)
- 浮动元素(float值不为none)
- 绝对定位或固定定位元素(position值为absolute或fixed)
- 行内块元素(display: inline-block)
- 弹性元素或网格元素(display属性为flex或grid)
作用:
- 去除外边距重叠
- 清除浮动(让父元素的高度包含子浮动元素,给浮动的元素的父元素设置高度)
元素居中
水平居中:
行内元素:text-align: center;
块级元素:
已知宽度:
- margin: 0 auto;
- 绝对定位 + margin-left: (父盒子width – 子盒子width) / 2
未知宽度:
- display: inline-block; + text-align: center;
- 绝对定位 + margin-left: 50%; + transform: translateX(-50%)
- flex布局 + justify-content: center;
- grid布局 + justify-content: center; / justify-items: center; 或者给需要水平居中的子元素添加 jusrify-self: center;
垂直居中:
行内元素(纯文字类):line-height = 盒子高度
块级元素:
- 绝对定位 + margin-top: 50%; + transform: translateY(-50%)
- flex布局 + align-items: center;
- grid布局 + align-content: center; / align-items: center; 或者给需要水平居中的子元素添加 align-self: center;
水平垂直居中:
- 绝对定位 + margin左右各50% + transform: translate(-50%, -50%)
- Flex布局 + justify-content: center + align-items: center;
- Grid布局
父元素:justify-content: center + align-items: center;
父元素:align-content: center + justify-items: center;
子元素:justify-self: center + align-self: center;
隐藏页面中某个元素的方法
opacity: 0
将元素设置为完全透明,不会改变页面原有布局(元素仍占据原有空间),元素绑定的事件(如click)仍可触发
visibility: hidden
隐藏元素,不会改变页面原有布局(元素仍占据原有空间),元素绑定的事件无法触发,并且会触发重绘
display: none
隐藏元素,会改变页面原有布局(该元素和它的子元素不会被浏览器渲染),元素绑定的事件无法触发,且会触发回流和重绘
Flex布局常见属性
父元素
- flex-direction 设置主轴方向 row | column | row-reverse | column-reverse
- flex-warp 设置是否换行 nowarp | warp | warp-reverse
- flex-flow 前两项的组合属性
- justify-content 主轴的子元素排列方式 flex-start | center | flex-end | space-around | space-between
- align-content 侧轴的子元素排列方式(多行)flex-start | center | flex-end | space-around | space-between | stretch(高度平分父元素高度)
- align-items 侧轴的子元素排列方式(单行)flex-start | center | flex-end | stretch(拉伸,默认值)
子元素
- flex 子项所占的份数(也是flex-grow、flex-shrink和flex-basis的缩写)
- align-self 控制子项在侧轴的排列方式
- order 子项的排列顺序
- flex-grow 元素放大,默认不会放大(默认值0),值为1则放大
- flex-shrink 元素缩小,默认空间不足的时候会等比缩小(默认值1),值为0则不缩小
- flex-basis 在分配多余空间时,元素占据的主轴空间大小,默认为auto(元素本身大小),设置为0%后,可以结合flex-grow: 1 + flex-shrink: 1可以自动放大和缩小
块元素和行内元素的区别
- 块级元素默认一个元素占一行,行内元素与之相反
- 块元素不设置宽度则默认继承父元素宽度
- 行内元素不可以设置height和上下的margin和padding,可以设置左右的margin和padding
- 块级元素可以包含行内元素和块级元素。行内元素不能包含块级元素。
高度塌陷
含义:由于浮动,导致本应该在盒子内部的元素跑到了盒子的外部
解决方案:
- 将盒子的高度和宽度固定
- 将外层盒子也添加浮动
- 设置overflow属性,触发BFC
- 清除浮动
清除浮动几种方式
- 添加额外标签并设置clear属性
- 给父级元素添加overflow属性或设置高度,触发BFC
- 伪元素清除浮动
- 双伪元素
为什么CSS写在顶部,JS写在底部
- 浏览器可预先加载CSS,可以不必等待HTML加载完成即可渲染页面(一边加载一边解析)
- JS存在事件处理或DOM操作,所以需要将HTML加载完成后再使用,否则可能导致报错
伪类(:first-child)和伪元素(:after)的区别
伪类:
伪类用于选择DOM树之外的信息,或是不能用简单选择器进行表示的信息。比如:visited,:active,first-child,:first-of-type,:target等
伪元素:
伪元素为DOM树没有定义的虚拟元素。不同于其他选择器,它不以元素为最小选择单元,它选择的是元素指定内容。比如::before
区别:
- 伪元素创建了一个文档树以外的元素并为它添加样式,这个容器不包含任何DOM元素但是可以包含内容。(伪元素则创建了一个文档树以外的元素)
- 伪类使用单引号,伪元素使用双引
- 伪类通常用于选择DOM元素,而伪元素通常是创建一个类DOM元素
link和@import的区别
- link是HTML标签,@import是CSS提供的方式,link还可以引入图片等资源,@import只能加载CSS
- 加载顺序不同,link在页面载入时同时加载,@import需要网页完全载入后再加载
- 兼容性不同,link是XHTML标签,无兼容性问题,@import式CSS2.1提出的,低版本浏览器不支持
- link支持使用js控制DOM改变样式,@import不支持
可以被继承的元素有哪些
所有元素可以继承的属性:
- 元素可见性:visibility、opacity
- 光标属性:cursor
内联元素可以继承的属性:
- 字体系列属性(font-family、font-size、font-style等)
- 除text-indent、text-align之外的文本系列属性(line-height、word-spacing字间距、letter-spaceing字符间距、color、text-transform大小写、direction书写方向)
块级元素可以继承的属性:
- text-indent
- text-align
CSS中的百分比相对于谁
相对于参照物的
position: absolute;
top: 10%; // 相对于第一个有定位属性的元素
相对于自身的
position: relative;
top: 10%;
border-radius: 50%;
background-size: 10%;
transform: 50%;
text-indent: 10%;
line-height: 50%; // 相对自身的font-size
相对于父元素
margin: 10%;
padding: 10%;
font-size: 10%; // 相对父元素字体大小
em和rem
em:
相对于自身的字体(font-size)大小(字体大小可以继承父元素的字体大小),如果当前文本的字体尺寸没有设置,则相对于浏览器的默认字体尺寸
例如一个div的字体为18px,设置它的宽高为10em,那么此时宽高就是18px * 10em = 180px
rem
相对于HTML根元素的字体大小
例如HTML根元素的字体大小为16px,那么10rem就等同于10*16=160px
为什么有时候突然网页上会只有DOM结构而样式丢失的情况,刷新一下又好了
html是边解析边渲染,若css引入方式不是放在head中,会使前面已经解析的dom先渲染出来,出现几秒的样式丢失(简而言之就是DOM比CSS样式先加载出来了)
css和js是否会阻塞页面渲染
会,因为默认是从上到下,边解析便渲染,JS可以使用defer等来延迟加载
- CSS 不会阻塞 DOM 解析,但是会阻塞 DOM 渲染
- JS会阻塞DOM解析
- CSS会阻塞JS的执行(所以一般将<script>放在<link>标签前面是有道理的)
- 浏览器遇到<script>标签且没有defer或async属性时会触发页面渲染
JavaScript部分
JS数据类型有哪些
基本数据类型
undefined 未定义
Null 空
Boolean 布尔
Number 数值
String 字符串
Symbol 符号,ES6新增,表示独一无二的值,可以表示对象的唯一属性名
BigInt 任意大小的整数,ES6新增
引用数据类型
Object 对象
Function 函数
Array 数组
如何判断数据类型
- typeof
优点:可以判断所有的基本数据类型和function
缺点:不能判断null、object、array(判断的返回值均为object)
- instanceof
优点:可以判断object、array和function,适用于判断自定义类的实例对象(判断机制为判断实例的原型是否为指定的判断类型,因为类的实例的原型为该类)
缺点:不能判断基本数据类型
- Object.prototype.toString.call()
优点:可以判断所有数据类型
var、let和const的区别
- var定义的变量可以跨块级作用域访问,不能跨函数作用于访问,let定义的变量不能跨块级作用域和函数作用域访问
- const用来定义常量,声明时必须初始化赋值,对于简单数据类型定义后不支持修改,对于对象而言,const只能保证变量所指向的内存地址不被修改,换句话说,const只是禁止变量被重新赋值,对对象的修改没有任何影响
例如:
const obj = { name: “zhangsan” }
obj = {} // 会报错,const禁止变量被重新赋值,此操作又称修改变量
obj.name = “lisi” // 不会报错,因为此操作是在修改对象,const不能限制修改对象
- var可以在声明前使用(存在变量提升:检测到未声明的变量时,不会报错,会返回undefined,因为早期的js遵循的是不报错原则),let和const只能先声明后使用,否则会报错(又称暂时性死区)
- var可以重复声明,let和const不能重复声明
undefined和null的区别
undefined表示未定义的值,表示一个变量自然的、最原始的状态
以下情况会出现undefined
- 声明了一个变量但是没有赋值
- 访问对象上不存在的属性
- 函数定义了形参但是没有传递实参,当访问函数的形参时
null表示空值,表示一个变量被人为设置为空对象
造成JS内存泄露的有哪几种情况
- 意外的全局变量
- 过多的闭包
- 未被清空的定时器
- 未被销毁的事件监听
call、apply和bind的区别
- call、apply和bind的作用都是修改this指向,其中第一个参数就是this的指向
- call和apply都是调用后就直接执行了,bind不会直接执行,bind会返回一个新函数
- call和apply都是临时修改一次this指向,bind是直接锁死,不能再做修改
- call除第一个参数外的参数需要一个一个的声明,apply需要声明成一个数组
- bind除第一个参数外的参数是为新函数设置的默认值,无法被修改
箭头函数的特点
- 语法更加简洁
- 没有arguments
- 没有自己的this,箭头函数的this由创建时的外层作用域所决定
- 箭头函数的this不能被call、apply和bind修改
this指向问题
- 全局作用域中的this指向window
- 以函数形式调用(xxx()),this指向window
- 以方法形式调用(xxx.xxx()),this指向调用方法的对象
- 构造函数中的this指向当前实例
- 箭头函数没有自己的this,箭头函数的this由创建时的外层作用域所决定,且不能被call、apply和bind修改
new的执行流程和实现原理
- 创建一个普通的JS对象,简称新对象
- 将构造函数的prototype属性设置为新对象的原型
- 执行构造函数,将新对象设置为函数中的this
- 判断函数的返回值类型,如果为非原始值,则直接返回该值,如果为原始值,则返回新对象(一般情况下,不会为构造函数指定返回值)
ES6的新特性
- let和const
- 数组和对象的扩展运算符 …
- Set(类似于数组,但不允许有重复值)
- Map(存储键值对,和对象的区别在于,任何类型的值都可以作为键)
- Promise(为了解决异步调用所产生的回调地狱的一种解决方案,有pending、fulfilled和reject三种状态)
- Generator(异步编程的一种解决方案)
- Proxy
- Module(导出:export / export default 导入:import)
闭包
含义:函数中返回了一个函数,且内部函数能够访问到外部函数作用域中变量的函数
什么时候使用:当我们需要隐藏一些不希望被其他人访问的内容时可以使用闭包
形成条件:
- 函数嵌套
- 内部函数要引用外部函数中的变量
- 内部函数要作为返回值返回
生命周期:
- 闭包在外部函数调用时产生,每调用一次就会创建一个全新的闭包
- 在内部函数丢失时销毁,当内部函数被垃圾回收了,闭包才会消失
作用:
- 隐藏变量,避免使用全局变量造成变量污染
- 延长局部变量的生命周期
缺点:闭包会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄露
防抖和节流的原理和使用场景
作用:优化高频执行JS代码的一种手段,减低执行频率,节省资源
原理:
防抖:多次触发,只执行最后一次
节流:每隔一段时间执行一次
使用场景:
防抖:
- 搜索框输入(用户输入完成后,再发送请求)
- 手机号、邮箱等格式校验
- 监听窗口大小变化
节流:
- 滚动加载更多
- 搜索框输入联想
- 高频点击
- 表单重复提交
作用域和作用域链
作用域:
- 全局作用域
- 函数作用域
- 块作用域 {}
作用域链:
当我们使用一个变量的时候,JS解析器会优先在当前作用域中寻找该变量(遵循就近原则),如果找到了就直接使用,如果没找到,就到上一层作用域中去找,以此类推,一直到全局作用域中还没找到,则报错xxx is not defined
原型和原型链
每创建一个对象,JS解析器就会向该对象中添加一个prototype属性,该属性就是原型对象
如何访问:
obj.__proto__ 或 Object.prototype
作用:
原型就相当于一个公共区域,它可以被虽有该类的实例访问,我们可以将该类实例中所有的公共属性(方法)存储到原型上,这样就只需创建一个属性,即可被所有的实例访问
原型链:
当访问对象的某个属性时,会优先在对象自身查找该属性,如果对象中有,则直接使用,如果对象中没有,则去对象的原型中寻找,如果原型中有,则使用,如果原型中没有,则去原型的原型中寻找,直到找到Object对象的原型(Object对象的原型为null)还没有找到该属性,则返回undefined
slice、splice和split的区别
slice:数组方法
作用:截取数组
使用:
第一个参数为起始位置(截取的数组包含该位置),第二个参数为结束位置(截取的数组不包含该位置),如果省略第二个参数,则默认截取到末尾,索引也可以是负值,如果两个参数都省略,可以对数组进行浅拷贝,返回值为截取的新数组
splice:数组方法
作用:删除、插入、替换数组中的元素
使用:
第一个参数为删除的起始位置,第二个参数为删除的数量,为0时则为插入,此时删除的起始位置则为插入的起始位置,第三个参数为插入的元素,返回值为被删除的元素
split:字符串方法
作用:将一个字符串按照给定的参数拆分为一个数组
字符串和数组之间如何转换
字符串转数组:split()
数组转字符串:join()
数组中哪些方法会改变原数组
会改变原数组:
- push() 向数组末尾插入数据 返回新数组的长度
- unshift() 向数组头部插入数据 同上
- pop() 删除数组末尾的一个数据 返回数组的最后一个元素
- shift() 删除数组头部的一个数据 返回数组的第一个元素
- splice() 删除数组中的元素或向数组中插入元素 返回被删除的元素
- reverse() 反转数组
- sort() 对数组进行排序,默认升序,可通过回调函数指定升序还是降序,不建议对数字进行排序
不会改变原数组:
- at() 获取指定索引的数组元素
- conect() 连接数组
- indexOf() 获取指定元素第一次出现的索引,不存在则返回-1
- lastIndexOf() 获取指定元素最后一次出现的索引,不存在则返回-1
- join() 按照指定的参数将数组拼接成字符串
- slice() 截取数组
- forEach() 遍历数组
- map() 同上
- filter() 根据指定条件查找数组中的元素,存在则返回相应数组
- find() 同上,存在则返回第一个符合的元素,不存在则返回undefined
- reduce() 整合数组
- every() 根据指定条件判断数组中的元素是否符合,全部符合返回true,否则false
- some() 同上,只要有一个符合就返回true,否则返回false
浅拷贝和深拷贝
浅拷贝:
含义:
只复制对象的浅层(第一层),浅拷贝只会对对象本身进行复制,并不会赋值对象中的属性,对象中的属性的地址还是原来的地址,当修改的时候原来的也会受到影响(原始值一般不涉及深浅拷贝问题,因为原始值在内存中是唯一的,不允许重复存在)
实现方法:
- arr.slice()
- 展开运算符 …
- Object.asign(目标对象,被复制的对象)
深拷贝:
含义:
不仅赋值对象本身,对象中的属性和元素也会被复制
实现方法:
- JSON.parse(JSON.stringify(arr)) // stringify 转为JSON字符串 parse 转换为js对象
- window.structuredClone()
高阶函数
含义:如果一个函数的返回值是一个函数,则这个函数就是一个高阶函数
作用:通过高阶函数,可以实现在不修改一个函数的源代码的基础上对起重新包装,添加新的功能
案例:
Map和Set
Map:
作用:
用来存储键值对结构的数据,任何类型的值都可以作为数据的key(Object中的key只能是字符串或者符号,如果传递了其他类型的会被自动转换为字符串)
创建方式:new Map()
常用方法和属性:
- size() 获取map中的键值对数量
- set(key, value) 向map中添加键值对
- get(key) 根据key获取值
- delete(key) 删除指定数据
- has(key) 判断map中是否包含指定key
- clear() 删除map中的所有键值对
Set:
作用:
用来创建一个集合,功能同数组类似,但是Set中不允许存在重复的值
创建方式:new Set()
常用方法和属性:
- size 获取数量
- add() 添加元素
- has() 检查元素
- delete() 删除元素
事件循环(Eventloop、JS单线程怎么支持异步的效果)
函数在每次执行时都会产生一个执行环境,执行环境赋值存储函数执行时产生的一些数据,执行环境会存储到调用栈中,遵循后进先出的原则
当我们触发一个事件时,其响应函数不会直接就添加到调用栈中,因为调用栈中可能存在还没有执行完毕的代码,此时会将响应函数存储到消息队列中进行排队,当调用栈中的代码执行完毕之后,再根据先进先出的原则插入到调用栈中执行
消息队列中又分为宏任务队列和微任务队列,微任务队列的执行顺序优先于宏任务队列,微任务队列主要包括Promise的三个回调函数(.then、.catch、.finally)中的代码,其余大部分的代码(例如定时器)都是属于宏任务队列,在Nodejs中还存在一个Tick队列,Tick队列的执行顺序优先于微任务队列
简而言之,事件循环就是先执行调用栈中的代码(例如全局作用域中的代码),然后再执行消息队列中的微任务队列(如果存在Tick队列就先执行Tick队列),然后再执行宏任务队列
Promise
在JS中,为了获取异步调用的结果,需要使用return函数来解决,然而当代码变得复杂之后,就会逐渐产生回调地狱,为了解决这一问题,于是产生了Promise,Promise可以解决异步中的回调问题,同时搭配async和await的使用,会使得代码更加的简洁。
当在.then中返回值时可以在下一个.then中获取到(每个.then都会默认返回一个Promise,且结果为上一个return的值),这就是Promise的链式调用。
Promise常用函数
- resolve() 在执行正常时存储数据
- reject() 在执行错误时存储数据
- all([…]) 同时返回多个执行结果,有一个报错就返回错误
- allSetted([…) 同上,不管结果成功或失败
- race([…]) 返回执行最快的Promise(不考虑对错,错误也参与)
- any([…]) 同上(考虑对错,错误不参与)
Promise包含三种状态,但是需要注意的是,Promise的状态支支持修改一次,当修改过后就不能再被就改了,三种状态分别是
- pending 进行中
- fulfilled 完成,当通过resolve存储数据时改变
- reject 拒绝/出错了,当出现错误或通过reject存储数据时改变
Promise实现原理
queueMicrotask用于添加一个微任务
如何将伪数组转换为数组
- 扩展运算符(…)
- Array.from()
- Array.proptotype.slice.call()
async/await的原理
async是Generator函数的语法糖
严格模式(use strict)
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 禁止this指向全局对象
- 增加了保留字(比如protected、static和interface)
for in 和 for of的对比
相同点:
都无法遍历Symbol类型的值(需要使用Object.getOwnPropertySymbols()方法)
不同点:
- for in 是遍历键名,for of是遍历键值
- for in 只遍历可枚举属性
- for in 不仅可以遍历数字键名,还可以遍历手动添加的其他键,包括原型链上的键(for in 可以遍历原型)
for of可以使用break跳出吗
可以,forEach不可以中途跳出,return和break都不可以
什么是定时器
间隔一段时间后将函数放到任务队列中
事件冒泡
开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点,但是事件冒泡也有优点,例如可以利用事件冒泡实现事件委派/代理
案例:
有A(祖盒子)、B(父盒子)、C(子盒子)三个互相包含的盒子,假设此时三个盒子都绑定了点击事件,此时如果触发C盒子的点击事件,那么A、B盒子的点击事件也会被触发
如何阻止:
e.stopPropagation()
事件委派/代理
当有多个子节点都需要绑定相同的事件时,可以将事件直接绑定到他们共同的父节点或document上,然后利用事件冒泡从而影响到每个子节点(子节点触发后,冒泡到父节点,然后执行父节点绑定的事件)
事件委派就是将本该绑定给对各元素的事件,统一绑定给共同的祖先元素(例如document,注意,绑定给document时需要使用e.target判断一下是否是被触发元素,否则会导致设都可以触发事件),这样可以降低代码复杂度,提高代码可维护性(将操作多次DOM变成了只操作一次)
案例:
需要给ul下的li绑定点击事件,此时可以不用给每个li绑定点击事件,可以将点击事件绑定在ul或者document上(绑定在document上时,需要使用e.target判断触发点击事件的是否为li,否则会导致点击任何元素都会触发事件),然后在点击li时,由于事件冒泡,所以ul或者document的点击事件就会被触发
如何遍历对象
- for in 可以遍历对象的所有可枚举属性,包括对象本身的和对象继承来的属性
-
- Object.keys() 该方法返回对象自身属性名组成的数组,它会自动过滤掉原型链上的属性,然后可以通过数组的 forEach() 方法来遍历
-
- Object.values() 与Object.keys()遍历对象的特性是相同的,但是其返回的结构是以遍历的属性值构成的数组
-
- Object.entries() 返回值为Object.values()与Object.keys()的结合,也就是说它会返回一个嵌套数组,数组内包括了属性名与属性值
-
- Object.getOwnPropertyNames() 其返回结果与Object.keys()对应,但是他得特性与其相反,会返回对象得所有属性,包括了不可枚举属性
-
- Object.getOwnPropertySymbols() 返回对象自身的 Symbol 属性组成的数组,不包括字符串属性
-
- Reflect.ownKeys() 返回的是一个大杂烩数组,即包含了对象的所有属性,无论是否可枚举还是属性是symbol,还是继承,将所有的属性返回
-
JS 5种遍历对象的方式_js遍历对象_超级切图仔的博客-CSDN博客
如何判断一个对象是不是数组
- instanceof 用于检验构造函数的prototype属性是否出现在对象的原型链中的任何位置,返回一个布尔值
缺点:prototype属性是可以修改的,所以并不是最初判断为true就一定永远为true
2.constructor 实例的构造函数constructor指向构造函数
3.Object.prototype.toString.call() 获取到对象的不同类型
4.Array.isArray() 确定传递的值是否是一个数组,返回一个布尔值
js判断是否是数组的几种方法_js如何判断是不是数组_小菜篮的博客-CSDN博客
大量if的替代方案
- 三元表达式
- Switch
- 利用数组(Array.includes())
- 利用对象(对象数组),将判断条件设置为键
- Map,方式同对象
- 策略模式(设置模式中的一种)策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成。第一部分是策略类,封装了许多具体的,相似的算法。第二部分是环境类,接受客户请求,随后将请求委托给策略类。说的通俗一点就是将相同算法的函数存放在一个包装里边,每个函数用相同的方式拿出来,就叫做策略模式。
js中if else的可替代语句_ifelse可以用什么代替_朱桂彪的博客-CSDN博客
前端如何处理数据精度丢失问题
后端Long数据类型导致的精度丢失(数据过长导致精度丢失)
在js中number类型有个最大值(安全值),为9007199254740992,是2的53次方。如果超过这个值,那么js会出现不精确的问题
- 使用bingInt,bigInt会末尾携带n,可以使用toString将n去除
- 在请求时加上transformResponse,这样前端接受就不会进行转化
-
- 协商后端将数据类型转换为String
-
https://blog.csdn.net/qq_45502336/article/details/123951173
小数相加导致精度缺失问题(0.1+0.2 !== 0.3)
产生原因:计算机能读懂的是二进制,进行运算的时候,实际上是把数字转换为了二进制进行的 这个过程 丢失了精度
解决方法:
- 通常同时乘10的n,n为小数点后的位数(以大的为准),然后结果再除以10的n次方
JS运行在浏览器中是单线程还是多线程
js运作在浏览器中,是单线程的,js代码始终在一个线程上执行
js是单线程,浏览器是多线程
JS能否多线程运行
不能,JS实现多线程主要是靠异步
正则替换用户输入里面的特殊字符
垃圾回收机制
标记-清除法(标记就是从一组根元素开始,递归遍历这组根元素,能到达的对象称为活动对象,不能到达的对象称为垃圾对象)
什么是垃圾回收
定期找出那些不再用到的内存(变量),然后释放其内存(为什么不是实时的找出无用内存并释放呢?因为实时开销太大了)
TypeScript部分
什么是TS
TypeScript是JS的超集(加强版),给JS添加了可选的静态类型和基于类的面向对象编程,拓展了JS的语法,TS是纯面向对象的编程语言,包含类和接口的概念,程序员可有用它来编写面向对象的服务端或客户端程序
TS引入了很多面向对象程序设计的特征,主要包括
- interface 接口
- class 类
- enumerated type 枚举类型
- generic 泛型
- module 模块
注意:
- TS不存在兼容性问题,因为TS在最终是编译成JS
- 泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,使用时再去指定类型的一种特性。可以把泛型理解为代表类型的参数
TS和JS的区别是什么
- TS是一种面向对象语言,JS是一种脚本语言(尽管JS是基于对象的)
- TS支持可选参数、静态类型和接口,JS不支持这些
为什么要用TS(TS的好处是什么)
- TS可以在代码编写时就能给出编译错误,而JS需要在运行时才能暴露出错误
- TS是强类型语言,可以明确知道数据的类型,代码可读性更高,弥补了JS作为弱类型语言的先天性不足
- TS的流程趋势逐渐增强,但是TS是基于JS的,所以不能说TS会替代JS这种说法
Vue部分
MVVM
含义:
MVVM(Model-View-ViewModel)是视图模型双向绑定,MVVM将MVC中的C(controller)演变成了ViewModel,Model代表数据模型,View代表视图(UI组件),ViewModel是View和Model之间的桥梁,数据会绑定到ViewModel并自定将数据渲染到页面上,视图变化时会通知ViewModel更新数据,之前是通过操作DOM结构来实现视图更新,现在是数据驱动视图
优点:
- 低耦合性:View(视图)可以独立于Model(数据模型)的变化,一个Model可以绑定到不同的View上,当View变化的时候Model可以不变换,反之亦然
- 可重用性:可以将多个View放在一个Model中,使这些View重用这个Model
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面的设计
- 可测试性
什么是vue
vue是一个渐进式的JS框架,具有易用、灵活、高效的特征,可以将一个页面分割成多个组件,当其它页面有类似功能时,直接让封装的组件进行复用,他是构建用户界面的声明式框架,只关心图层,不关心具体是如何实现的
Vue响应式原理
Vue2.0
原理:
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动的时候发布消息给订阅者,触发相应的事件回调
缺点:
- 深度监听时,如果data嵌套层数过多会导致递归过多,计算量大
- 无法监听数组变化,vm.xxx[xxx] = xxx 这种是无法进行检测的
- 无法监听新增和删除属性,如果直接往data中添加或删除属性(例如:this.data.xxx = xxx),此时添加或删除的属性不是响应式的,需要使用Vue.set、Vue.delete或者使用Object.asign({},oldObj)(完全替换一个新对象)来进行添加或删除属性
Vue3.0
原理:
使用ES6的Proxy代理,利用Proxy对data返回的对象进行代理,在修改属性的时候调用set方法时触发所有使用该值的位置进行更新,获取属性时调用get方法来获取相应的值
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截
优点:
- 支持对数组变化的监听
defineProperty和Proxy的对比
- defineProperty是代理到静态的值级别的,Proxy是代理在对象级别
- defineProperty监听不了数组的变化,Proxy可以
- defineProperty监听手段比较单一,只能监听set和get, Proxy有10几种监听
- defineProperty必须得把所有的属性全部添加defineProperty, Proxy会对整个对象都会进行拦截
Vue2.0和Vue3.0的对比
- vue3.0体积更小、性能更好
- vue3.0有TS支持
- vue0新增组合式API
- vue3.0支持多根节点组件(碎片),vue2.0只支持单根节点
- vue3.0废除了vue2.0的过滤器(filter)
- vue3.0没有this
- vue3.0响应式原理是Proxy,vue2.0是Object.defineProperty+消息订阅与发布
为什么data是一个函数
因为组件在本质上就是一个class类,使用的时候会进行实例化,当一个组件被复用多次,就会创建多个实例,本质上这些实例用的都是同一个构造函数,如果data是一个对象,因为对象是引用数据类型,则此时会影响到所有的实例(因为它们指向的都是同一个地址),为了保证各个组件之间的不同实例之间的data不冲突,所以data只能是一个函数,data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响(函数创建了函数作用域,函数作用域中的变量不被外界影响)
函数中return对象可以保证每个组件实例的数据是相互独立的,互不影响,如果data是一个对象,则每个组件实例都所共享这个对象,则会导致数据混乱(例如组件为一个计数器,此时其中一个计数器发生变化,此时所有的计数器都会发生变化)
生命周期
vue2.0
create阶段:vue实例被创建
beforeCreate:最初调用触发(创建前),此时data和methods中的数据还没初始化,data和events不可用
created:创建完毕,data中有值,未挂载到DOM(不能操作DOM),data和methods中的数据以及初始化完毕,在这里可以发送请求(Ajax请求,获取数据)
mount阶段:vue实例被挂载到真实的DOM节点
beforeMount:在模板编译后,渲染之前触发(挂载前),可以发送请求获取数据
mounted:渲染之后触发,挂载完毕,vue实例被挂载到真实的DOM上,此时可以操作DOM
update阶段:vue实例里的data数据变化时,触发组件重新渲染
beforeUpdate:更新前,数据发生修改后,模板改变前触发,此时数据发生修改,页面UI结构并未重新渲染完成,切勿使用此处监听数据变化
updated:更新后,数据修改之后,模板改变之后触发,能操作最新的DOM,避免在此处修改数据(会造成更新的死循环)
destory阶段:vue实例被销毁
beforeDestory:实例销毁前,组件卸载前触发,可手动销毁定时器、事件绑定等
destoryed:实例销毁,组件卸载完成
vue3.0
选项式API
beforeDestory ===> beforeUnmount
destoryed ===> unmounted
组合式API
beforeCreate ===> setup()
created ===> setup()
beforeMount ===> onBeforeMount()
mounted ===> onMounted()
beforeUpdate ===> onBeforeUpdate()
updated ===> onUpdated()
beforeUnmount ===> onBeforeUnmount()
unmounted ===> onMounted()
为什么不在beforeCreate中发送请求获取数据?
因为此时的data还没初始化完成,获取到的数据无法转存
为什么v-if和v-for不建议一起使用
在vue2.0中,v-if的优先级高于v-if,所以当两者一起使用时,会导致v-for遍历出来的所有的元素都添加了v-if,从而造成严重的性能浪费(此时可考虑采用computed先对数据进行筛选),但是在vue3.0中,v-if的优先级高于v-for
如果非要两者一起使用,可以考虑在v-for的外层使用<div></div>或<template></template>标签包裹(建议使用<template></template>,因为<template></template>不会被渲染成真实的DOM节点,可以避免多余的DOM节点),并将v-if设置在外层的<div></div>或<template></template>上
v-if和v-show的原理和区别
原理
v-if:直接将元素删除或添加元素
v-show:通过给元素设置display: none
区别:
v-if通过删除或添加元素来切换元素的显示和隐藏,会触发浏览器的重排和重绘,v-show通过设置display样式来切换元素的显示和隐藏,只会触发重绘,两者都会触发重排和重绘- 初始化渲染时,因为v-if是直接删除和添加元素来控制显示和隐藏,所以v-if只会渲染存在的元素,而v-show是通过给元素设置display属性来控制显示和隐藏,所以尽管元素通过v-show隐藏了,但是还是会被渲染,v-if的初始化渲染性能要优于v-show(v-if可以理解为用到的时候才渲染,v-show切换速度快,v-if加载速度快)
- 如果需要频繁的切换显示和隐藏,v-if则会多次触发重排和重绘,v-if则只会切换displays属性,只会触发重绘,所以需要频繁切换显示和隐藏时v-show的性能更好
- v-if可以搭配else和<template></template>标签使用(因为v-show需要给元素设置display属性,但是template不会被渲染成真实的DOM,所以template无法设置v-show)
computed和watch的原理和区别
computed:计算属性
computed用于计算结果并返回,结果会被缓存,当计算属性中的函数所依赖的属性(响应式属性)发生变化时才会重新计算,否则调用当前函数将会从缓存中读取结果,data不改变,computed不更新
使用场景:适用于一个属性受多个属性影响时使用
- 购物车结算功能
- 处理时间格式
watch:监听器
watch用于监听某个值的变化(已经在data中定义的属性或变量,变量变化时,触发watch),然后执行相应操作
使用场景:适用于一个属性(数据)影响多个属性(数据)时使用
- 搜索框(联想搜索)
区别:
- 既能用computed又能用watch实现的功能,推荐使用computed
- computed有缓存,watch没有
- computed中的函数必须要要把结果return出去,watch不用
- computed是计算已有的值并返回结果,watch用于监听某个值(data中已经定义)的变化,然后执行相应操作
- computed默认第一次加载的时候就开始监听,watch默认第一次不监听(需要监听则需要配置immediate属性为true)
- computed不支持异步,当computed内有异步操作时是无法监听数据变化的;watch支持异步操作
key的作用和原理
作用:
- 给每个节点做一个唯一标识,便于Diff算法执行时更快的找到相应的节点,提高Diff算法执行速度,更高效的更新虚拟DOM
- 在数据变化时强制更新组件,避免“就地复用”带来的副作用
原理:
vue和react都是采用Diff算法来对比新旧节点,从而更新节点,在vue的Diff算法中,会根据新节点的key去比对旧节点数组中的key,从而找到对应的旧节点,如果没找到则新增一个节点,如果节点没有key,则会采用遍历查找的方式去找到对应的旧节点,前者是Map映射,后者是遍历查找,显然map映射的速度会更快
vue在使用v-for更新已经渲染过的元素列表时,默认采用就地复用策略,如果数据项的顺序被改变,vue不会随之移动DOM元素的舒徐,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染
组件通讯方式
父组件>子组件:props
父组件>孙组件:provide + inject
子组件>父组件:$emit + 自定义事件
兄弟组件(拥有共同父组件):eventBus($emit + $on)
跨级组件:vuex
nextTick的作用
作用:
nextTick是vue提供的一个全局API,作用是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用nextTick,则可以在回调中获取更新后的DOM
使用场景:当某些代码需要延迟执行,延迟到DOM重新渲染完毕之后
Mixin
作用:
当多个组件有相同逻辑时,可以使用mixin进行抽离
使用场景:
新闻列表和新闻详情页的右侧栏目,可以使用mixin
劣势:
- 变量来源不明确,代码不利于阅读
- 多mixin可能引发变量冲突
- Mixin和组件可能会出现多对多的关系,使得项目得复杂度变高
https://v2.cn.vuejs.org/v2/guide/mixins.html
Vuex
作用:状态管理工具,集中式管理应用所有组件的状态
属性:
- state 存储数据,数据是响应式的
- muations 处理同步操作,操作修改state数据
- actions 处理异步操作,不能直接修改数据,需要借助muations
- getters 对数据进行加工,类似于计算属性
- module 模块化vuex
在setup中如何获取vue实例
setup中没有this,所以获取vue实例可以使用getCurrentInstance
路由守卫
参数含义:
to:将要访问的路由信息对象
from:将要离开的路由对象信息
next():
省略:不允许跳转
next():直接放行
next(‘hash地址’):强制跳转页面
next(false):不允许跳转,停留在当前页面
全局前置守卫:
router.beforeEach((to,from,next)=>{})
路由独享守卫:
beforeEnter:(to,form,next)=>{}
组件内的守卫:
v-cloak
在使用插值语法({{}})展示或更新页面数据时:当网速比较慢时,会出现一个不好的过度现象,会让用户先看到我们的表达式,即所谓的闪烁问题
使用v-cloak指令,然后为其设置css样式display:none防止页面加载时出现Vue.js的变量名,当网络较慢,网页还在加载Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。我们可以使用 v-cloak 指令来解决这一问题。
keep-alive
作用:
可以实现组件缓存(将不活动的组件实例保存在内存中,而不是直接将其销毁),是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中
可以使用include和exclude来指定要缓存和不缓存
使用keep-alive会触发activated和deactivated两个生命周期函数
原理:
如何动态移除缓存的组件
- keep-alive 默认不支持动态销毁已缓存的组件,所以此处给出的解决方案是通过直接操控 keep-alvie 组件里的 cahce 列表,暴力移除缓存
- 利用keep-alive的include,新打开标签时,把当前组件名加入到include数组里,关闭标签时从数组中删除关闭标签的组件名就可以了
https://github.com/vuejs/vue/issues/6509
Vue2.0如何对数组方法进行重写的
通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面。
在Vue修改数组中的某个元素一定要用如下方法:
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
React部分
React是MVVM框架吗
不是
React专注的层面是view层,react专注的中心是Component,即组件,react认为一切元素都可以抽象成组件
React可以作为MVVM中的V,即为view,但是并不是MVVM框架,MVVM的显著特点是双向数据绑定,但react是单向数据绑定的,react是一个单向数据流的库,状态驱动视图,整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入
class和hooks的对比(类组件和函数式组件的对比)
react提供了两套API,一种是类(class),另外一种是函数钩子(hooks),但是react官方推荐使用的式函数式,因为类比较重,函数式更简洁,代码量少,比较请,而且函数式也更加符合react的函数式本质,hooks是react16.8新增的特性
区别:
- 类组件在大型组件时很难拆分、重构和测试,函数组件的业务代码更加聚合,事件绑定卸载也更加的清晰,逻辑复用方便
- 类组件引入了复杂的编程模式,如render、props和高阶组件
- 类组件中涉及到this操作,比较容易出错
- 函数组件的性能要比类组件好,因为类组件使用时还需要实例化,函数组件使用时直接执行函数即可
- 在hooks出现之前,函数组件没有实例、生命周期、state,又称无状态组件
打包工具部分
Webpack和vite的区别
- webpack的启动速度比vite慢,因为vite启动时不需要打包,无需分析模块依赖和编译等,当浏览器请求需要的模块时,再对模块进行编译,如此一来便缩短了编译的时间,项目越大、文件越大时的优势更加明显
- webpack热更新时,单个模块发生变化会对有依赖关系的所有模块进行编译,vite改动单个模块时,只需让浏览器重新请求这个模块
- vite的速度要比webpack更快,vite在开发时不会进行打包,而是采用ESM方式运行项目,在生产环境时才会对代码进行打包
loader和plugin的区别
loader:
增强webpack处理文件的能力,会对代码进行编译
plugin:
扩展webpack的功能,对开发进行辅助作用
区别:
- 两者都是为了扩展webpack的功能
- loader它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译; 而plugin不仅只局限在打包,资源的加载上,还可以打包优化和压缩,重新定义环境变量等
- loader运行在打包文件之前(loader为在模块加载时的预处理文件);plugins在整个编译周期都起作用
- 一个loader的职责是单一的,只需要完成一种转换。一个loader其实就是一个Node.js模块。当需要调用多个loader去转换一个文件时,每个loader会链式的顺序执行
- 在webpack运行的生命周期中会广播出许多事件,plugin会监听这些事件,在合适的时机通过webpack提供的API改变输出结果
Webpack常见的loader
- less-loader:用于将less编译成css
- css-loader:用于将css以CommonJS语法打包到JS中;必须配合style-loader共同使用,只安装css-loader样式不会生效。
- style-loader:用于动态创建style标签,将css引入其中
- sass-loader:css预处理器
- postcss-loader:用于补充css样式各种浏览器内核前缀,用于处理css兼容问题,需要和postcss、postcss-preset-env配合使用
- babel-loader:将Es6+ 语法转换为Es5语法;babel-loader 这是使babel和webpack协同工作的模块
- ts-loader:用于配置项目typescript
- html-loader:想引入一个html页面代码片段赋值给DOM元素内容使用,这时就用到html-loader
- file-loader:用于处理文件类型资源,如jpg,png等图片。返回值为publicPath为准。
- url-loader:处理图片类型资源,只不过它与file-loader有一点不同,url-loader可以设置一个根据图片大小进行不同的操作,如果该图片大小大于指定的大小,则将图片进行打包资源,否则将图片转换为base64字符串合并到js文件里
- vue-loader:用于编译.vue文件
- eslint-loader:用于检查代码是否符合规范,是否存在语法错误
Webpack常见的plugin
- html-webpack-plugin:根据指定模板自动创建html文件,并且引入外部资源
- mini-css-extract-plugin:将 CSS 提取为独立的文件的插件,对每个包含 css 的 js 文件都会创建一个 CSS 文件,支持按需加载 css 和 sourceMap。只能用在 webpack4 中
- optimize-css-assets-webpack-plugin:用于压缩css,减小 css 打包后的体积。
浏览器部分
从输入URL到页面加载的全过程
- 查找缓存并比较缓存是否过期
- DNS域名解析域名对应IP(DNS服务器是基于UDP的,因此此处会用到UDP协议)
- 根据IP与服务器建立TCP连接(三次握手)
- 发起HTTP请求(三次握手的第三次发送的数据)
- 服务器响应请求并返回结果
- 浏览器解析数据,渲染页面(构建DOM树 >构建CSS规则树 >构建render树 >布局 >绘制)
- 断开与服务器的TCP连接(四次挥手)
从输入URL到页面加载的全过程,涉及到哪些协议
- 浏览器要将URL解析为IP地址,解析域名需要用到DNS协议
- DNS域名解析会涉及请求根域名服务器,DNS服务器是基于UDP的,所以需要用到UDP协议
- 建立HTTP连接,需要用到HTTP协议
- HTTP生成get请求报文,并将报文传给TCP层处理,需要用到TCP协议
- 如果使用HTTPS对HTTP数据进行加密,需要用到HTTPS协议
- TCP的数据包发送给IP层,需要用到IP协议
- 网段内寻址,需要用到以太网协议
为什么建立连接是三次握手,断开连接是四次挥手
服务器收到客户端的最后的报文,此时代表客户端不再向服务器发送数据,但是客户端仍可接收数据,而且此时服务器可能还有数据需要发送给客户端,所以此时服务器可以直接关闭,但是也可以发送数据给客户端后,再发送最后的保温来表示现在同意关闭连接
完整的URL包含什么
- 协议:http或https
- 域名:www.baidu.com为域名,其中baidu.com为一级域名,www为服务
- 端口:不写默认为80
- 路径:/xx/xx
- 查询参数:?xx=xx
浏览器在渲染网页的时候,做了哪些事
- 加载页面的html和css
- 将html转换为DOM,将css转换为CSSOM
- 将DOM和CSSOM构建成一颗渲染树
- 对渲染树进行构造,计算元素的位置 / 重排
- 对网页进行绘制 / 重绘
https://www.jianshu.com/p/744edaa32fc3
重排/回流(reflow)和重绘(repaint)
重排:
当DOM元素的几何信息(大小和位置)发生变化,就会触发重排,此时浏览器需要重新计算元素的几何信息,重新生成布局,重新排列元素的过程就叫做重排
注意:几何信息每变化一次就会触发一次重排,重排次数过多后会严重影响页面性能
重绘:
当DOM元素的外观(color、透明度)发生变化,几何信息没有发生变化,就会触发重绘,重新将元素的外观绘制出来的过程叫做重绘
注意:
- 重绘不一定会出现重排,重排必然会出现重绘
- 每个页面都有一次重排和重绘(页面初始化渲染的时候)
- 使用Vue和React等框架的时候,无需担心重排和重绘问题,因为它们做了相关的优化(框架操作的都是虚拟DOM,然后再将其转化为真实DOM),但是需要避免在框架中修改原生DOM元素
如何避免:
- 集中改变样式,避免一个个的修改元素的样式,可以采用修改元素的class属性来批量改变样式
- 不要将DOM元素的属性值放在循环里当循环的变量
session、cookie、localStorage和sessionStorage的区别
- session存储在服务器,cookie、localStorage和sessionStorage存储在浏览器本地
- session要比cookie更加安全,cookie可能会被盗和篡改,可以使用read-only设置只读
- cookie会在http请求中携带,localStorage和sessionStorage只会保存在本地
- cookie存储的数据容量不能超过4k,很多浏览器限制一个站点最多保存20个cookie,localStorage和sessionStorage存储的数据容量有5M+
- cookie适合存储较小的数据,例如session会话标识
- cookie在设置的过期时间前一直有效,窗口或浏览器关闭仍然有效,sessionStorage在当前窗口关闭前有效,localStorage使用有效,窗口或浏览器关闭仍然有效
- cookie和localStorage在所有同源窗口中都是共享的,sessionStorage不是
token和session的区别
- session认证只是简单的将用户的信息存储在session里面,sessionID不可预测,session只存在于服务端
- token提供的是授权和认证,认证是针对用户,授权是针对APP,目的就是让APP有权访问到某用户的信息,token是唯一的
- session是存放在服务端,客户端只存放sessionID,token是存放在客户端的
跨域和同源策略
同源策略:
当协议、域名、端口中有任何一个不同,则为不同的域
解决方案:
- JSONP(只支持GET请求)
- 创建一个script标签
- 将script的src属性设置为接口地址
- 接口参数,必须带一个自定义函数名
- 通过定义函数名去接收返回的参数
- 配置代理(只支持开发环境)
- 服务端设置CROS(最终方案)
- 在服务器端设置Access-Control-Allow-Origin
如何获取判断浏览器类型
window.navigator.userAgent
如何获取首屏时间
在 </head> 标签前的 <script> 标签内加入代码
new Date().getTime() - performance.timing.navigationStart
如何优化白屏时间
- DNS 预解析
使用 meta 标签
<meta http-equiv="x-dns-prefetch-control" content="on" />
使用 link 标签
<link rel="dns-prefetch" href="https://www.baidu.com" />
- 使用 HTTP2
HTTP 相比于 HTTP1,解析速度更快;支持多路复用,多个请求可以共用一个 TCP 连接;提供了首部压缩功能;支持服务器推送,服务器可以在发送 HTML 页面时,主动推送其他资源,而不用等到浏览器解析到相应位置发请求再响应。
- 减少 HTTP 请求数量,减少 HTTP 请求大小
- 合并、压缩文件;按需加载代码,减少冗余代码
- 采用 svg 图片或字体图标
- 使用 defer 加载 JS(延迟加载js)
- 尽量将 CSS 放文件头部,JS 文件放在底部,以免堵塞渲染。JS 如果放在头部,给 script 标签加上 defer 属性,异步下载,延迟执行。
- 服务端渲染
客户端渲染:获取 HTML 文件,根据需要下载 JavaScript 文件并运行,生成 DOM,然后再渲染。
服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML。
优点:首屏渲染快,对搜索引擎优化(SEO)好。
缺点:配置麻烦,增加了服务器的计算压力。
- 静态资源使用 内容分发网络(CDN)
解决用户与服务器物理距离对响应时间的影响,在多个位置部署服务器,让用户离服务器更近,从而缩短请求时间。
- 资源缓存,不重复加载相同的资源
- 图片优化(雪碧图、图片懒加载、CSS 图片懒加载)
服务端渲染和客户端渲染的区别
客户端渲染:获取 HTML 文件,根据需要下载 JavaScript 文件并运行,生成 DOM,然后再渲染。
服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML。
优点:首屏渲染快,对搜索引擎优化(SEO)好。
缺点:配置麻烦,增加了服务器的计算压力。
前后端分离(RESTAPI)和不分离
不分离:服务器成本大,一个服务器只能负责一个平台(PC、Web、移动端)的页面渲染
分离:服务器成本降低,服务器只负责数据处理,不再负责页面渲染,配合RESTAPI可以实现一套接口,多个平台通用
浏览器的进程有哪些
- Browser进程:浏览器的主进程(负责协调、主控),只有一个
主要作用:
负责浏览器界面显示,与用户交互。如前进,后退等
负责各个页面的管理,创建和销毁其他进程
将渲染(Renderer)进程得到的内存中的Bitmap(位图),绘制到用户界面上
网络资源的管理,下载等
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
- GPU进程:最多一个,用于3D绘制等
- 浏览器渲染进程(即通常所说的浏览器内核)(Renderer进程,内部是多线程的):主要作用为页面渲染,脚本执行,事件处理等
浏览器的线程有哪些
UI渲染线程,JS主线程,GUI事件触发线程,http请求线程
网络部分
HTTP请求/响应的步骤
- 客户端连接到服务器
- 发送HTTP请求
- 服务器接受请求并返回HTTP响应
- 释放TCP连接
- 客户端(浏览器)解析HTML内容
HTTP报文的组成部分
请求报文:
请求首行(请求方式 + 资源路径 + HTTP协议版本) + 请求头 + 空行 + 请求体
请求首行:GET /index.html?username=sunwukong HTTP/1.1
响应报文:
响应首行(HTTP协议版本 + 状态码 + 状态码描述) + 响应头 + 空行 + 响应体
响应首行:HTTP/1.1 200 OK
HTTP状态码及常见状态码
HTTP状态码
1xx:指示信息类,表示请求已接受,继续处理
2xx:指示成功类,表示请求已成功接受
3xx:指示重定向,表示要完成请求必须进行更近一步的操作
4xx:指示客户端错误,请求有语法错误或请求无法实现
5xx:指示服务器错误,服务器未能实现合法的请求
常见状态码
200 OK:客户端请求成功
301 Moved Permanently:永久重定向,所请求的页面已经永久重定向至新的URL
302 Found:临时重定向,所请求的页面已经临时重定向至新的URL
304 Not Modified 资源未修改。
403 Forbidden:对请求页面的访问被禁止
404 Not Found:请求资源不存在
500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
503 Server Unavailable:请求未完成,服务器临时过载或宕机,一段时间后可恢复正常
其余状态码
1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态码
100 继续 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
101 切换协议 请求者已要求服务器切换协议,服务器已确认并准备切换
2xx(成功)表示成功处理了请求的状态码
200 成功 服务器已经成功处理了请求。通常,这表示服务器提供了请求的网页
201 已创建 请求成功并且服务器创建了新的资源
202 已接受 服务器已接受请求,但尚未处理
203 非授权信息 服务器已经成功处理了请求,但返回的信息可能来自另一来源
204 无内容 服务器成功处理了请求,但没有返回任何内容
205 重置内容 服务器成功处理了请求,但没有返回任何内容
3xx(重定向)表示要完成请求,需要进一步操作;通常,这些状态代码用来重定向
300 多种选择 针对请求,服务器可执行多种操作。服务器可根据请求者(user agent)选择一项操作,或提供操作列表供请求者选择
301 永久移动 请求的网页已永久移动到新位置。服务器返回此响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置
302 临时移动 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
303 查看其它位置 请求者应当对不同的位置使用单独的GET请求来检索响应时,服务器返回此代码
304 未修改 自上次请求后,请求的网页未修改过。服务器返回此响应,不会返回网页的内容
305 使用代理 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理
307 临时性重定向 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有的位置来进行以后的请求
4xx(请求错误)这些状态码表示请求可能出错,妨碍了服务器的处理
400 错误请求 服务器不理解请求的语法
401 未授权 请求要求身份验证。对于需要登录的网页,服务器可能返回此响应
403 禁止 服务器拒绝请求
404 未找到 服务器找不到请求的网页
405 方法禁用 禁用请求中指定的方法
406 不接受 无法使用请求的内容特性响应请求的网页
407 需要代理授权 此状态码与401(未授权)类似,但指定请求者应当授权使用代理
408 请求超时 服务器等候请求时发生超时
410 已删除 如果请求的资源已永久删除,服务器就会返回此响应
413 请求实体过大 服务器无法处理请求,因为请求实体过大,超出了服务器的处理能力
414 请求的URI过长 请求的URI(通常为网址)过长,服务器无法处理
5xx(服务器错误)这些状态码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错
500 服务器内部错误 服务器遇到错误,无法完成请求
501 尚未实施 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码
502 错误网关 服务器作为网关或代理,从上游服务器无法收到无效响应
503 服务器不可用 服务器目前无法使用(由于超载或者停机维护)。通常,这只是暂时状态
504 网关超时 服务器作为网关代理,但是没有及时从上游服务器收到请求
505 HTTP版本不受支持 服务器不支持请求中所用的HTTP协议版本
1xx 处理中
2xx 请求成功
200 请求成功
3xx 重定向
301 永久重定向(配合location,浏览器自动处理)
302 临时重定向(配合location,浏览器自动处理)
304 资源未被修改,浏览器存在缓存,且服务端未更新,不再向服务端发送请求
4xx 客户端错误
400 数据/格式错误
401 权限不足(身份不合格)
404 资源未找到
5xx 服务端错误
500 服务器错误
503 服务不可用(服务器超载或停机维护)
504 网关超时
HTTP常见请求方法
GET 加载数据
POST 新建或添加数据
PUT 更新数据,全部替换
PATCH 更新数据,局部更新
DELETE 删除数据
OPTIONS 预检,浏览器自动发送,检查一些服务器的权限(是否允许跨域、支持的请求方法等)
注:
什么时候会发起options请求进行预检
当请求为非简单请求,且跨域时会发起options请求进行预检
HTTP的缺点
- 明文通信,安全度低
- 不验证通信方身份
- 无法验证报文完整性
HTTPS
HTTPS是以安全为目标的HTTP通道,即HTTP下加入SSL层进行加密,作用是建立一个信息安全通道,来确保数据的传输,去报网站的真实性
HTTPS的工作原理
- 客户端使用HTTPS URL访问服务器,则要求服务器建立SSL链接
- 服务器收到客户端的请求后,会将网站的证书(证书中包含了公钥)传输给客户端
- 客户端和服务器开始协商SSL链接的安全等级(即为加密等级)
- 客户端根据双方协商的安全等级,建立会话密钥,然后通过网站的公钥对会话密钥进行加密,然后传送给服务器
- 服务器通过自己的私钥对会话密钥进行解密
- 服务器通过会话密钥加密与客户端之间的通信
HTTPS传输过程中为什么要使用对称加密(AES),可以用非堆成加密实现吗(RSA)
非对称加密有两个严重的问题:
- 效率太低,会严重影响到用户打开页面的速度;
- 服务器端只能用私钥加密,但黑客可能获取到公钥,不能保证服务器端的数据安全
对称加密:加密解密用的是同样的“钥匙”
非对称加密:加密解密用的是不同的“钥匙”
https://blog.csdn.net/Ping_Pig/article/details/90181726
HTTPS的优缺点
- HTTPS要比HTTP协议安全,可以防止数据在传输过程中被窃取和篡改,保证数据的完整性
- HTTPS握手阶段比较费时,会使页面的加载时间延长50%,增加10%-20%的耗电
- HTTPS缓存不如HTTP,会增加数据开销
- SSL证书需要钱,功能越强大的证书的费用越贵
- SSL证书需要绑定IP,不能在同一个IP上绑定多个域名,IPV4资源无法支持这种消耗
HTTP和HTTPS的区别
- HTTP是超文本传输协议,是明文传输,安全性较低,HTTPS测试具有安全性的SSL加密传输协议
- HTTPS需要CA证书,费用较高
- HTTP的默认端口是80,HTTPS的默认端口是443
- HTTP的连接很简单,是无状态的
什么时候会发起options预检请求
当请求为非简单请求,且请求跨域时
GET和POST的区别
- get在回退时没有副作用,post在回退时会再次提交请求
- get的请求地址会被浏览器主动缓存,post不会(除非手动设置)
- get的请求参数会被完整保留在浏览器历史记录里面,post的参数不会被保留
- get的参数有长度显示(受URL长度显示,最长为2048字节),post无限制
- get的参数通过URL传递,明文暴露,不安全,post的参数时存放在请求体中的,相对更安全
- get适用于获取数据,post适用于提交数据
TCP/IP网络模型和OSI七层模型
链路层:负责封装和解封装IP报文,发送和接受ARP/RARP报文等。
网络层:负责路由以及把分组报文发送给目标网络或主机。
传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文。
应用层:负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等
TCP三次握手
第一次握手 客户端 > 服务器
客户端向服务器发送连接请求(表示将要进行数据传输),等待服务器确认
第二次握手 服务器 > 客户端
服务器收到客户端的连接请求,向客户端返回消息(表示可以进行数据传输),等待客户端确认
第三次握手 客户端 > 服务器
客户端向服务器发送同意连接的信息,三次握手结束
注意:握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据
TCP四次挥手
第一次挥手 客户端 > 服务器
客户端向服务器发送请求,通知服务器客户端数据已经发送完毕,请求断开连接
第二次挥手 服务器 > 客户端
服务器收到客户端请求断开连接请求,并返回消息表示已知晓,但此时客户端还不能断开连接,因为不知道服务器是否接收完了客户端发送的信息,可能存在丢包导致消息未接收完毕
第三次挥手 服务器 > 客户端
服务器向客户端返回数据,表示客户端发送的数据已经接收完毕,可以断开连接
第四次挥手 客户端 > 服务器
客户端向服务器发送断开连接的信息,四次挥手结束
TCP和UDP的区别
- TCP是面向连接的,而UDP是面向无连接的
- TCP仅支持单播传输,UDP 提供了单播,多播,广播的功能
- TCP的三次握手保证了连接的可靠性; UDP是无连接的、不可靠的一种数据传输协议,首先不可靠性体现在无连接上,通信都不需要建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收
- UDP的头部开销比TCP的更小,数据传输速率更高,实时性更好。
DNS的查询过程
- 检查浏览器缓存中是否存在该域名与IP地址的映射关系,如果有则解析结束,没有则继续
- 到系统本地查找映射关系,一般在hosts文件中,如果有则解析结束,否则继续
- 到本地域名服务器去查询,有则结束,否则继续
- 本地域名服务器查询根域名服务器,该过程并不会返回映射关系,只会告诉你去下级服务器(顶级域名服务器)查询
- 本地域名服务器查询顶级域名服务器(即com服务器),同样不会返回映射关系,只会引导你去二级域名服务器查询
- 本地域名服务器查询二级域名服务器(即baidu.com服务器),引导去三级域名服务器查询
- 本地域名服务器查询三级域名服务器(即mail.baidu.com服务器),此时已经是最后一级了,如果有则返回映射关系,则本地域名服务器加入自身的映射表中,方便下次查询或其他用户查找,同时返回给该用户的计算机,没有找到则网页报错
- 如果还有下级服务器,则依此方法进行查询,直至返回映射关系或报错
- 进行缓存
浏览器缓存-系统本地-本地域名服务器-根域名服务器-顶级域名服务器-二级域名服务器-三级域名服务器
客户端和服务端长连接的几种方式
- Ajax轮询
实现原理:ajax 轮询指客户端每间隔一段时间向服务端发起请求,保持数据的同步。
优点:可实现基础(指间隔时间较短)的数据更新。
缺点:这种方法也只是尽量的模拟即时传输,但并非真正意义上的即时通讯,很有可能出现客户端请求时,服务端数据并未更新。或者服务端数据已更新,但客户端未发起请求。导致多次请求资源浪费,效率低下。【数据更新不及时,效率低下】
- Long pull长轮询
实现原理:long poll 指的是客户端发送请求之后,如果没有数据返回,服务端会将请求挂起放入队列(不断开连接)处理其他请求,直到有数据返回给客户端。然后客户端再次发起请求,以此轮询。在 HTTP1.0 中客户端可以设置请求头 Connection:keep-alive,服务端收到该请求头之后知道这是一个长连接,在响应报文头中也添加 Connection:keep-alive。客户端收到之后表示长连接建立完成,可以继续发送其他的请求。在 HTTP1.1 中默认使用了 Connection:keep-alive 长连接。
优点:减少客户端的请求,降低无效的网络传输,保证每次请求都有数据返回,不会一直占用线程。
缺点:无法处理高并发,当客户端请求量大,请求频繁时对服务器的处理能力要求较高。服务器一直保持连接会消耗资源,需要同时维护多个线程,服务器所能承载的 TCP 连接数是有上限的,这种轮询很容易把连接数顶满。每次通讯都需要客户端发起,服务端不能主动推送。【无法处理高并发,消耗服务器资源严重,服务端不能主动推送】
- Iframe长连接
实现原理:在网页上嵌入一个 iframe 标签,该标签的 src 属性指向一个长连接请求。这样服务端就可以源源不断地给客户端传输信息。保障信息实时更新。
优点:消息及时传输。
缺点:消耗服务器资源。
- Websocket
实现原理:
Websocket 实现了客户端与服务端的双向通信,只需要连接一次,就可以相互传输数据,很适合实时通讯、数据实时更新等场景。
Websocket 协议与 HTTP 协议没有关系,它是一个建立在 TCP 协议上的全新协议,为了兼容 HTTP 握手规范,在握手阶段依然使用 HTTP 协议,握手完成之后,数据通过 TCP 通道进行传输。
Websoket 数据传输是通过 frame 形式,一个消息可以分成几个片段传输。这样大数据可以分成一些小片段进行传输,不用考虑由于数据量大导致标志位不够的情况。也可以边生成数据边传递消息,提高传输效率。
优点:
双向通信。客户端和服务端双方都可以主动发起通讯。
没有同源限制。客户端可以与任意服务端通信,不存在跨域问题。
数据量轻。第一次连接时需要携带请求头,后面数据通信都不需要带请求头,减少了请求头的负荷。
传输效率高。因为只需要一次连接,所以数据传输效率高。
缺点:
长连接需要后端处理业务的代码更稳定,推送消息相对复杂;
长连接受网络限制比较大,需要处理好重连。
兼容性,WebSocket 只支持 IE10 及其以上版本。
服务器长期维护长连接需要一定的成本,各个浏览器支持程度不一;
成熟的 HTTP 生态下有大量的组件可以复用,WebSocket 则没有,遇到异常问题难以快速定位快速解决。【需要后端代码稳定,受网络限制大,兼容性差,维护成本高,生态圈小】
WebSocket
- 实现了浏览器与服务器全双工通信
- 基于TCP的,是可靠性传输协议
- 是应用层协议
- WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息,HTTP是单向的
- WebSocket是需要浏览器和服务器握手进行建立连接的,而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接
- Web端实现即时通讯的方法
利用Socket建立网络连接的步骤
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
1、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
2、客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3、连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
非对称加密RSA
简介:
- 对称加密算法又称现代加密算法
- 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解
- 非对称加密算法需要两个密钥:公开密钥(publickey) 和私有密钥(privatekey)
- 公开密钥和私有密钥是一对
如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密
如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密
特点:
- 算法强度复杂,安全性依赖于算法与密钥
- 加密解密速度慢
与对称加密算法的对比:
- 对称加密只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥
- 非对称加密有两种密钥,其中一个是公开的
RSA应用场景:
由于RSA算法的加密解密速度要比对称算法速度慢很多,在实际应用中,通常采取数据本身的加密和解密使用对称加密算法(AES)。 用RSA算法加密并传输对称算法所需的密钥
HTTP1、HTTP2和HTTP3之间的关系
HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,虽如此但HTTP/2并非完美的,HTTP/3 就是为了解决 HTTP/2 所存在的一些问题而被推出来的
HTTP1.1的缺陷
- 高延迟 — 队头阻塞(Head-Of-Line Blocking)
队头阻塞是指当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。
针对队头阻塞的解决办法:
- 将同一页面的资源分散到不同域名下,提升连接上限
- 合并小文件减少资源数`,使用精灵图
- 内联(Inlining)资源`是另外一种防止发送很多小图请求的技巧,将图片的原始数据嵌入在CSS文件里面的URL里,减少网络请求次数
- 减少请求数量,合并文件
- 无状态特性 — 阻碍交互
无状态是指协议对于连接状态没有记忆能力`。纯净的 HTTP 是没有 cookie 等机制的,每一个连接都是一个新的连接。
Header里携带的内容过大,在一定程度上增加了传输的成本`。且请求响应报文里有大量字段值都是重复的。
- 明文传输 — 不安全性
HTTP/1.1在传输数据时,所有`传输的内容都是明文`,客户端和服务器端都无法验证对方的身份,无法保证数据的安全性。
- 不支持服务端推送
记忆口诀:队头阻塞高延迟,无状态阻交互,明文传输不安全,服务推送不支持。
HTTP1.1的排队问题
HTTP 1.1多个文件共用一个TCP,这样可以减少tcp握手,这样3个文件就不用握手9次了,不过这样请求文件需要排队,请求和返回都需要排队, 如果第一个文件响应慢,会阻塞后面的文件,这样就产生了对头的等待问题。
有的网站可能会有很多文件,浏览器处于对机器性能的考虑,它不可能让你无限制的发请求建连接,因为建立连接需要占用资源,浏览器不想把用户的网络资源都占用了,所以浏览器最多会建立6个tcp连接;如果有上百个文件可能都需要排队,http2.0正在解决这个问题。
HTTP2简介
HTTP/2是现行HTTP协议(HTTP/1.x)的替代,但它不是重写,HTTP/2基于SPDY,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection)
HTTP2新特性
- 二进制传输
HTTP/2传输数据量的大幅减少,主要有两个原因:以二进制方式传输和Header 压缩。我们先来介绍二进制传输,HTTP/2 采用二进制格式传输数据,而非HTTP/1.x 里纯文本形式的报文 ,二进制协议解析起来更高效。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。
- Header 压缩(首部压缩)
HTTP/2并没有使用传统的压缩算法,而是开发了专门的"HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。
- 多路复用
在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也更容易实现全速传输。
- Server Push(服务端推送)
HTTP2还在一定程度上改变了传统的“请求-应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。减少等待的延迟,这被称为"服务器推送"( Server Push,也叫 Cache push)
- 提高安全性
出于兼容的考虑,HTTP/2延续了HTTP/1的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。
但由于HTTPS已经是大势所趋,而且主流的浏览器Chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以“事实上”的HTTP/2是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用"https”协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识符:“h2"表示加密的HTTP/2,“h2c”表示明文的HTTP/2。
- 防止对头阻塞
http1.1如果第一个文件阻塞,第二个文件也就阻塞了
http2.0的解决,把3个请求打包成一个小块发送过去,即使第一个阻塞了,后面2个也可以回来;相当于3个文件同时请求,就看谁先回来谁后回来,阻塞的可能就后回来,对带宽的利用是最高的;但没有解决TCP的对头阻塞,如果TCP发过去的一个分包发丢了,他会重新发一次;http2.0的解决了大文件的阻塞。
一个分包请求3个文件,即使第一个阻塞了,第二个也能返回
HTTP2的缺陷
虽然 HTTP/2 解决了很多之前旧版本的问题,但它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2的缺点主要有以下几点:
- TCP 以及 TCP+TLS 建立连接时延时
- TCP 的队头阻塞并没有彻底解决
- 多路复用导致服务器压力上升也容易 Timeout(超时)
HTTP3简介
Google 在推SPDY的时候就搞了个基于 UDP 协议的“QUIC”协议,让HTTP跑在QUIC上而不是TCP上。而“HTTP over QUIC”就是HTTP/3,真正“完美”地解决了“队头阻塞”问题。
HTTP3新特性(QUIC新功能)
QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,QUIC基于UDP,而UDP是“无连接”的,根本就不需要“握手”和“挥手”,所以就比TCP来得快。此外QUIC也实现了可靠传输,保证数据一定能够抵达目的地。它还引入了类似HTTP/2的“流”和“多路复用”,单个“流"是有序的,可能会因为丢包而阻塞,但其他“流”不会受到影响。具体来说QUIC协议有以下特点:
- 实现了类似TCP的流量控制、传输可靠性的功能
虽然UDP不提供可靠性的传输,但QUIC在UDP的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性。
- 实现了快速握手功能
由于QUIC是基于UDP的,所以QUIC可以实现使用0-RTT或者1-RTT来建立连接,这意味着QUIC可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。
- 集成了TLS加密功能
- 多路复用,彻底解决TCP中队头阻塞的问题
和TCP不同,QUIC实现了在同一物理连接上可以有多个独立的逻辑数据流。实现了数据流的单独传输,就解决了TCP中队头阻塞的问题。
- 连接迁移
TCP 是按照 4 要素(客户端 IP、端口, 服务器 IP、端口)确定一个连接的。而 QUIC 则是让客户端生成一个 Connection ID (64 位)来区别不同连接。只要 Connection ID 不变,连接就不需要重新建立,即便是客户端的网络发生变化。由于迁移客户端继续使用相同的会话密钥来加密和解密数据包,QUIC 还提供了迁移客户端的自动加密验证。
HTTP1.0和HTTP1.1的对比
HTTP1、HTTP2和HTTP3对比
- HTTP/1有两个主要的缺点:安全不足和性能不高
- HTTP/2完全兼容HTTP/1,是“更安全的HTTP、更快的HTTPS",二进制传输、头部压缩、多路复用、服务器推送等技术可以充分利用带宽,降低延迟,从而大幅度提高上网体验;
- QUIC 基于 UDP 实现,是 HTTP/3 中的底层支撑协议,该协议基于 UDP,又取了 TCP 中的精华,实现了即快又可靠的协议
线程、进程和协程的概念
进程
进程作为拥有资源的基本单位,线程作为调度和分配的基本单位。进程就是一段程序的执行过程例如启动的某个app。进程拥有代码和打开的文件资源、数据资源、独立的内存空间,进程拥有独立的内存空间
线程
有时被称为轻量级进程(LightWeight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。线程共享所在进程中的内存空间
协程
又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制(进程和线程都是由cpu 内核进行调度)。
进程与进程之间完全隔离,互不干扰,一个进程崩溃不会影响其他进程,避免一个进程出错影响整个程序
线程、进程和协程的关系
- 进程与进程之间需要传递某些数据的话,就需要通过进程通信管道IPC来传递
- 一个进程中可以并发多个线程,每个线程并行执行不同的任务
- 一个进程中的任意一个线程执行出错,会导致这个进程崩溃
- 同一进程下的线程之间可以直接通信和共享数据
- 当一个进程关闭之后,操作系统会回收该进程的内存空间
进程间通信的方式
管道通信:
就是操作系统在内核中开辟一段缓冲区,进程1可以将需要交互的数据拷贝到这个缓冲区里,进程2就可以读取了
消息队列通信:
消息队列就是用户可以添加和读取消息的列表,消息队列里提供了一种从一个进程向另一个进程发送数据块的方法,不过和管道通信一样每个数据块有最大长度限制
共享内存通信:
就是映射一段能被其他进程访问的内存,由一个进程创建,但多个进程都可以访问,共享进程最快的是`IPC`方式`信号量通信`:比如信号量初始值是1,进程1来访问一块内存的时候,就把信号量设为0,然后进程2也来访问的时候看到信号量为0,就知道有其他进程在访问了,就不访问了
socket:
其他的都是同一台主机之间的进程通信,而在不同主机的进程通信就要用到socket的通信方式了,比如发起http请求,服务器返回数据
安全问题-SQL注入
原理:
将sql代码伪装到输入参数中,然后传递给服务器进行攻击(利用服务器在执行SQL操作时拼接相应参数)
解决方案:
- 对用户输入进行校验
- 不使用动态拼接SQL
安全问题-XSS跨站脚本攻击(脚本注入)
原理:
攻击者利用表单填写将恶意代码混入其中,在目标网站上注入恶意代码,当用户登录网站时就会执行这些恶意代码,这些脚本可以读取cookie,session tokens,或者其它敏感的网站信息,对用户进行钓鱼欺诈,甚至发起蠕虫攻击等
解决方案:
- url参数使用encodeURIComponent方法转义
- 尽量不使用innerHtml插入HTML内容(避免使用innerHTML,建议使用textContent或innerText替代,textContent可以转义标签)
- 使用特殊符号、标签转义符(将特殊符号进行转义,例如<和>)
安全问题-CSRF跨站请求伪装
原理:
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
简而言之就是用户在A网站成功登录,存在cookie登录信息,然后坏人在B网站(需要诱导用户进入B网站),利用用户在A网站的登录信息(浏览器保存的cookie)向服务器发送请求(利用请求自动携带cookie),例如删除某个数据,服务器收到请求并验证是否存在cookie,因为B网站利用的用户在A网站登录所保存的cookie信息,服务器会默认认为坏人是用户本人,所以浏览器会执行相应操作,当然B网站也是在用户的同一台电脑和浏览器上进行的操作,post请求也可以实现(最简单的就是在网站中设置一个ifream然后将其隐藏,目前大部分浏览器可以识别这种操作)
现在大部分的浏览器的都不会在跨域的情况下自动发送cookie,这个设计就是为了避免csrf的攻击
解决方案:
- 使用referer头来检查请求的来源
- 使用验证码
- 尽量使用post请求(结合token)
安全问题-DDOS
原理:
DDOS又叫分布式拒绝服务,其原理就是利用大量的请求造成资源过载,导致服务不可用。
解决方案:
- 限制单IP请求频率。
- 防火墙等防护设置禁止ICMP包等
- 检查特权端口的开放
Web2.0
Web2.0对应的是移动互联网,用户不再只是内容接收方,可以在线阅读、点评、制造内容,成为内容的提供方,还可以与其他用户进行交流沟通。提供服务的网络平台成为中心和主导,聚集起海量网络数据。
Web3.0
假如说web1.0的本质是联合,那么web2.0的本质就是互动,它让网民更多地参与信息产品的创造、传播和分享,而这个过程是有价值的。web2.0的缺点是没有体现出网民劳动的价值,所以2.0很脆弱,缺乏商业价值。web2.0是脆弱的,纯粹的2.0 会在商业模式上遭遇重大挑战,需要跟具体的产业结合起来才会获得巨大的商业价值和商业成功。web3.0是在web2.0的基础上发展起来的能够更好地体现网民的劳动价值,并且能够实现价值均衡分配的一种互联网方式。(运行在区块链技术上的去中心化互联网),“Web3.0”是对“Web2.0”的改进,在此环境下,用户不必在不同中心化的平台创建多种身份,而是能打造一个去中心化的通用数字身份体系,通行各个平台。
总体而言,web3.0更多的不是仅仅一种技术上的革新。而是以统一的通讯协议,通过更加简洁的方式为用户提供更为个性化的互联网信息资讯定制的一种技术整合。将会是互联网发展中由技术创新走向用户理念创新的关键一步。
DNS只能拿到IP,是如何将IP转为mac地址
通过arp协议,arp维护一个本地的高速缓存表,里面有ip到mac的映射,若没有则广播消息查找
Git部分
Git常见命令
快捷键:
复制:Ctrl+Insert
粘贴:Shift+Insert`
$ git config --global user.name "xxx"
$ git config --global user.email "xxx@163.com"
初始化仓库
$ git init
添加文件到暂存区
// 提交单个文件
$ git add test.txt
// 提交单个目录文件
$ git add js
// 提交所有文件
$ git add .|*|-A // 或. 或* 或-A
提交文件到版本库
// message 提交的备注信息,方便后期查看维护使用
// 将暂存区全部有需要的文件一次性提交到本地版本库中。
$ git commit -m "第一次提交";
查看状态指令
新建、修改的文件通过该指令可以看出对比:
- 红色表示被 新建/修改 的文件,需要执行add操作,该文件目前正处于工作区
- 绿色表示文件已经处于 暂存区,需要执行commit操作 以便添加到版本库
$ git status -s // 简短方式提示日志
$ git status // 正常提示日志
查看操作日志
$ git log
全部撤销最近一次add
$ git reset HEAD
二、分支
创建分支
$ git branch // 查看所有分支
$ git branch cart // 创建名称为cart的分支
切换分支
$ git checkout cart // 切换到名称为cart的分支上
或
$ git checkout -b cart // 创建并切换到cart分支上
合并分支
$ git merge cart
删除分支
$ git branch -d cart
三、远程仓库
获取(复制)仓库
$ git clone "远程仓库地址" // 获取已有仓库的副本到本地
提交操作
把本地版本库的文件push推送到远程仓库
$ git push "远程仓库地址" 分支 // 把某一个分支推送到远程仓库
- 注意只有
版本库
的文件才可以被push推送到远程仓库 - 现在每个文件命运走势:工作区 -- (add) --> 暂存区 -- (commit) ---> 版本库 -- (push) ---> 远程仓库
更新本地
git pull
命令的作用是:取回远程仓库某个分支的更新,再与本地的指定分支合并
// git pull 只从远程仓库把本地没有的文件或内容更新下来,不负责下载.git版本库
git pull <远程主机名> <远程分支名>:<本地分支名>
例:git pull https://xxx.git master// 将远程主机origin的master分支拉取过来,与本地的brantest分支合并
git pull origin master:brantest// 表示将远程origin主机的master分支拉取过来和本地的当前分支进行合并
git pull origin master
注意:
git clone
在一个开发者电脑里边一个项目中执行一次即可git pull
在一个开发者电脑里边一个项目中执行多次即可
四、版本切换操作
查看提交历史
$ git log // 查看目前有效的commit提交日志
$ git log --pretty=oneline // 以非常简单的方式显示文件提交日志信息
$ git reflog // 查看所有commit提交操作的版本号码,包括恢复的git log 给显示当前版本库全部的历史版本,恢复过的版本不给显示,
git reflog 给显示全部的操作日志,包括恢复的操作
把文件 "版本库" 指定的版本恢复到 "工作区":
$ git reset --hard 64ff51e // 把版本号为 64ff51e 的版本恢复到工作区
五、保留本地的修改同时又把远程的合并
pull遇到错误:error: Your local changes to the following files would be overwritten by merge:
注意:
-
如果你
要保存
本地的修改,那按顺序执行以下操作:// 第一步 git stash 进行本地快照
$ git stash
// 第二步
$ git pull origin master
// 第三步 git stash pop会发现发生冲突的本地修改还在
$ git stash pop -
如果你
不保存
本地的修改,远程代码直接覆盖本地,那按顺序执行以下操作:// 第一步
$ git reset --hard
// 第二步
$ git pull origin master