前端面试-老朱版本
HTML
src和href的区别
- src用于替换当前元素,指向的内容会嵌入到文档中;浏览器解析到其指向的资源时,会暂停其他资源的下载和处理
- href用于在当前文档和引用资源之间确立联系;浏览器解析到其指向的资源时,会并行下载资源,不会停止对其他资源的下载和处理
doctype的作用
告知浏览器解析器应该以什么样的文档类型定义来解析文档,不同的渲染模式会影响到浏览器对于 CSS代码 甚⾄ JavaScript脚本 的解析。
文档解析类型
- 标准模式
渲染方式和 JS 引擎的解析方式都是以该浏览器支持的最高标准运行 - 怪异模式
向后兼容,模拟老式浏览器的行为以防止站点无法工作
对html语义化的理解
语义化是指根据内容的结构,选择合适的标签。也即用正确的标签做正确的事情。
优点主要有以下两点
- 对于机器友好,更适合搜索引擎爬虫爬取有效信息,利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录。
- 对开发者友好,使用语义类标签增强了可读性,结构也更加清晰,开发者也能清晰的看出网页的结构,便于团队的开发与维护。
常见语义化标签
<header></header>头部
<nav></nav>导航栏
<aside></aside>侧边栏
<main></main>主要区域
<footer></footer>底部
<section></section>区块(有语义化的div)
<artical></artical>主要内容
link与@import导入样式的区别
link
是HTML
标签,不仅可以加载CSS
,还可以定义rel
、type
等属性;@import
属于CSS
范畴,只能加载CSS
link
引用CSS
时,在页面载入时同时加载,@import
则需要在页面完全载入之后才可加载link
是HTML
标签,不存在兼容性问题,@import
是CSS2.1
提供的,低版本浏览器不支持
script标签中defer和async的区别
如果没有defer或async属性,浏览器会立即加载并执行相应的脚本。也就是说在渲染script标签之后的文档之前,不等待后续加载的文档元素,读到就开始加载和执行,这样就会阻塞后续文档的加载。
- defer: 浏览器指示脚本在⽂档被解析后执⾏,script被异步加载后并不会立刻执行,⽽是等待⽂档被解析完毕后执行
- async:同样是异步加载脚本,区别是脚本加载完毕后立即执⾏
PS:多个带async属性的标签,不能保证加载的顺序;多个带defer属性的标签,按照加载顺序执行。
常用的meta标签有哪些
// charset,用来描述HTML文档的编码类型
<meta charset="UTF-8" >
// 页面关键词
<meta name="keywords" content="关键词" />
// 页面描述
<meta name="description" content="页面描述内容" />
// viewport,适配移动端,可以控制视口的大小和比例
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
PS: viewport content参数有以下几种
- width 宽度
- height 高度
- initial-scale 初始缩放比例
- maximum-scale 最大缩放比例
- minimum-scale 最小缩放比例
- user-scalable 是否允许用户缩放
html5新增了哪些特性
-
语义化标签
header、nav、aside、main、footer、section、article
-
媒体标签
audio、vedio -
数据存储
localStorage、sessionStorage -
表单
- 表单类型
url、email、number、time、data、week、month、color、range、search等 - 表单属性
placeholder、autocomplete、autofocus、required等 - 表单事件
oninput、oninvalid
- 表单类型
-
canvas 画布、 geolocation 地理定位、websocket 通信协议
对data-属性的理解
HTML5规定可以为元素添加非标准的属性,但要添加前缀 data-
,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以 data-
开头即添加了自定义属性之后,可以通过元素的 dataset 属性来访问自定义属性的值。
H5自定义属性。若设置的属性中间带间隔符"-",直接读写调用时需改为驼峰命名
示例:<div data-list-name="rem"></div>
写法一:div.dataset.listName
写法二:div.dataset["listName"]
写法三:div.getAttribute("data-list-name")
行内元素、块级元素、可替换元素、空元素分别都有哪些
-
行内元素
a、span、label、img、input、select、textarea、em、strong -
块级元素
div、p、h1...h6、ul、ol、li、table、form -
可替换元素
-
行内替换元素
img、input、select、textarea -
块级替换元素
vedio、iframe
还有些元素仅在特定情况下被作为替换元素处理。
诸如 audio、canvas、option、object、applet
-
-
空元素
br、hr、img、input、link、meta
行内元素和块级元素的区别
-
块级元素独占一行,行内元素与其他行内元素同处一行
-
块级元素可设置行高、宽高、内外边距
行内元素可设置行高、水平方向的内外边距;
不可设置宽高、垂直方向的内外边距。- 行内替换元素可以设置行高、宽高、内外边距
- 行内元素虽无法设置垂直方向上的内外边距,但可看到效果
-
块级元素默认宽度是容器(父级宽度)的100%
行内元素默认宽度与元素内容宽度保存一致
对web worker的理解
在Javascript
单线程执行的基础上,开启一个子线程,进行程序处理,而不影响主线程的执行,当子线程执行完毕之后再回到主线程上,在这个过程中并不影响主线程的执行过程。
如何创建 web worker?
- 检测浏览器对于 web worker 的支持性
- 创建 web worker 文件(js,回调函数等)
- 创建 web worker 对象
对h5离线存储的理解
在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件。
如何使用?
- 在页面头部加入
manifest
属性:
<html manifest="cache.manifest">
- 设置
manifest
文件:
CACHE MANIFEST
# 常常写版本号、时间戳、md5等方便触发缓存更新
# 需要缓存的文件,无论在线与否,都会优先从缓存中获取
CACHE:
js/app.js
css/style.css
# 不缓存的文件,无论缓存是否更新,都会从服务器端获取
NETWORK:
resourse/logo.png
# 资源访问失败时使用备用资源替代
FALLBACK:
/ /offline.html
如何更新缓存?
- 更新 manifest 文件
- 通过 javascript 操作
- 清除浏览器缓存
Canvas和SVG的区别
- Canvas,是一种通过 JS 来绘制 2D 图形的方法。
本身相当于一个画布,没有绘画能力,可通过 JS 进行图像的绘画。 - SVG,可缩放矢量图形,是一种使用 XML 描述 2D 图形的语言。
- 可任意缩放,不会失真
- 文件体积小,下载速度快
CSS
居中
水平居中
-
块级元素
-
margin: 0 auto
-
绝对定位
- 绝对定位 + calc
- 绝对定位 + 负margin
- 绝对定位 + transform
-
flex
-
grid
-
-
行内元素
- text-align: center
垂直居中
-
块级元素
-
绝对定位
- 绝对定位 + calc
- 绝对定位 + 负margin
- 绝对定位 + transform
-
flex
-
grid
-
-
行内元素
- line-height
子元素行高等于父元素高度。只对单行文本有效。
- line-height
水平垂直居中
- 绝对定位 (重点)
- 绝对定位 + calc
- 绝对定位 + 负margin
- 绝对定位 + transform
- 绝对定位 + 上下左右0 + margin: auto
- flex (重点)
- grid (重点)
布局
两栏布局
-
浮动
- float + calc
- float + margin (重点)
- float + BFC (重点)
-
定位
- 绝对定位 + margin
-
inline-block + calc
-
flex (重点)
-
grid (重点)
三栏布局
-
两列定宽一列自适应
待整理 -
两侧定宽中间自适应
-
圣杯布局
-
双飞翼布局
-
浮动
- float + calc
- float + margin (重点)
- float + BFC (重点)
-
定位
- 绝对定位 + margin
-
flex (重点)
-
grid (重点)
-
Flex & Grid
Flex
Flex布局会使项目的 float
、 clear
、vertical-align
属性失效,不包括项目内的子元素。
// 容器属性
flex-direction
flex-wrap
flex-flow
justify-content
align-items
align-content
// 项目属性
order
flex-grow
flex-shrink
flex-basis
flex
align-self
flex属性值
- flex默认值
0 1 auto- flex: 1
1 1 0%- flex: 20%
1 1 20%- flex: auto
1 1 auto- flex: none
0 0 auto
Grid
Grid布局会使项目的float
、display: inline-block
、display: table-cell
、vertical-align
和column-*
等属性失效。不包括项目内的子元素。
// 容器属性
grid-template-rows / grid-template-columns / grid-template-areas
grid-template
grid-auto-rows / grid-auto-columns / grid-auto-flow
grid-row-gap / grid-column-gap
grid-gap
grid
justify-content / align-content
place-content
justify-items / align-items
place-items
// 项目属性
grid-row-start / grid-row-end / grid-column-start / grid-column-end
grid-row / grid-column
grid-area
justify-self / align-self
place-self
最常用:
grid-template-rows / grid-template-columns / grid-template-areas
grid-gap
grid-row / grid-column
grid-area
CSS属性
border-radius
/*值的省略*/
border-radius: 10px 20px 30px 40px; // 四个值: 左上、右上、右下、左下
border-radius: 10px 20px 30px; // 三个值:左上、右上/左下、右下
border-radius: 10px 20px; // 两个值:左上/右下、右上/左下
border-radius: 10px; // 一个值:左上/右上/右下/左下
PS:若使用百分比,相对的则是包含块的宽高
-------------------------------------------
/*值的拆分1.0*/
border-radius: 10px 20px 30px 40px;
border-top-left-radius: 10px;
border-top-right-radius: 20px;
border-bottom-right-radius: 30px;
border-bottom-left-radius: 40px;
/*值的拆分2.0,圆角水平半径和垂直半径*/
border-radius: 10px 20px 30px 40px / 50px 60px 70px 80px;
border-top-left-radius: 10px 50px;
border-top-right-radius: 20px 60px;
border-bottom-right-radius: 30px 70px;
border-bottom-left-radius: 40px 80px;
transform
transform: translate | scale | rotate | skew | matrix
transform: translate(10px, 10px) // 位移
transform: scale(2, 3) // 缩放
transform: rotate(10deg) // 旋转
transform: skew(10deg, 10deg) // 倾斜
transform: matrix() // 待研究
transition
transition: transition-property, transition-duration, // 常用
transition-timing-function, transition-delay // 不常用
transition-timing-function 过渡函数
ease: 先加速,后减速(加速时间短)
linear: 恒速
ease-in: 加速
ease-out: 减速
ease-in-out: 先加速后减速
---------------------------
举个栗子```
.violet{
width: 200px;
height: 100px;
background-color: pink;
transition: width 1s;
}
.violet:hover{
width: 100px;
}
animation
animation: animation-name, animation-duration, // 常用
animation-timing-function, animation-delay, // 不常用
animation-iteration-count, animation-direction // 常用
-----------------------------
举个栗子```
@keyframes rainbow {
0% { background: green; }
50% { background: orange; }
100% { background: red; }
}
.violet:hover {
animation: rainbow 2s infinite reverse;
}
属性继承
-
无继承性的属性
-
display
-
文本属性
text-shadow、text-decoration等 -
背景属性
background、background-color、background-image、background-position、background-repeat、background-attachment
-
定位属性
float、position、top、right、bottom、left、overflow、z-index等 -
盒子模型属性
width、height、margin、border、padding
-
等等。。
-
-
有继承性的属性
- visibility
- 文本属性
text-align、text-indent、text-transform、line-height、color等 - 字体属性
font-size、font-weight、font-style、font-family - 等等。。
小结:text-,font-,line-以及color属性可以被继承
文本、字体、行高、文字颜色可以被继承
选择器优先级
!important > 行内 > id > 类 | 伪类 | 属性 > 标签 | 伪元素 > 通配符 > 继承 > 浏览器默认
对BFC的理解
BFC,块级格式化上下文,是CSS中一种独特的渲染机制。
可将其理解为一个独立的容器,规定了内部元素如何布局,并且容器内部元素与外部元素之间互不影响。简而言之,它是一块独立的区域,让处于BFC内部的元素与外部的元素互相隔离。
产生条件
- 根元素 (html元素)
- 浮动元素(float 不为 none)
- 定位元素(position 为 absolute 或 fixed)
- display(inline-block、table)
- overflow 不为 visible
- flex容器的子元素
- grid容器的子元素
- 等等
渲染规则
- BFC元素内部的浮动元素会参与高度计算 (可用于清除浮动)
- BFC元素内部的父子元素或兄弟元素垂直方向上会产生margin合并 (可用于防止margin塌陷与合并)
- BFC元素不会和浮动元素重叠 (可用于两栏布局)
- BFC元素内部元素与外部元素相互隔离,互不影响
使用场景
- 清除浮动
- 防止margin塌陷和合并
- 防止元素被浮动元素覆盖 (两栏布局)
margin塌陷与margin合并
-
margin塌陷
父子元素嵌套。
父子元素嵌套时,外层整体盒模型的margin-top取两者中margin-top较大的值。
解决方案
- 父元素设置边框
- 将父元素包裹在BFC中
-
margin合并
兄弟元素垂直排列。
兄弟元素垂直排列时,两者相隔的margin取的是两者所设置margin的最大值。
垂直方向外边距合并计算
- 两个相邻的外边距都是 正数 时,折叠外边距是两者中较大的值。
- 两个相邻的外边距都是 负数 时,折叠外边距是两者中绝对值较大的值。
- 两个相邻的外边距是 一正一负 时,折叠外边距是两者相加的和。
解决方案
- 将其中一个兄弟元素包裹在BFC中
对层叠上下文的理解
层叠上下文是用来描述页面中元素在垂直于屏幕方向排列规则而创建出的模型。
层叠上下文中重叠的元素按照一定的规则在垂直方向排列。
产生条件
根元素 (html元素)
定位元素
- relative | absolute (z-index 不为 auto)
- fixed | sticky
CSS3属性
- opacity 属性值小于 1
- flex容器的子元素 (z-index 不为 auto)
- grid容器的子元素 (z-index 不为 auto)
- transform、filter、perspective、clip-path
mask、mask-image、mask-border
属性值不为none- 等等
层叠水平
决定了在同一个层叠上下文中,元素在
Z
轴上的显示顺序层叠顺序
在同一层叠上下文中,层叠顺序自下而上依次是:层叠上下文的根、z-index为负、block、float、inline/inline-block、z-index为0或auto、z-index为正。
层叠准则
- 同一层叠上下文中的元素,按照层叠顺序排列
- 不同层叠上下文中的元素,按层叠水平进行整体排列
- 层叠水平与层叠顺序都相同,在DOM流中处于后面的元素会覆盖前面的元素
display属性值有哪些
none、inline、block、inline-block、table、flex、grid、inherit等
position属性值有哪些
static、relative、absolute、fixed、sticky
清除浮动的方法有哪些
-
额外标签法
-
在浮动元素末尾添加一个空的标签
<div style="clear:both"></div>
-
在浮动元素末尾添加一个br标签
<br clear="all"></br>
-
-
父元素BFC法
能产生BFC的皆可,以下举例两种
-
给父元素添加浮动
可以给浮动元素的父级添加属性 float: left | right;
-
给父元素设置overflow
可以给浮动元素的父级添加属性 overflow: auto | hidden;
-
-
父元素设置高度法
-
父元素伪元素法
-
after单伪元素法
.clearfix::after{ content: "."; display: block; height: 0; visibility: hidden; clear: both; }
-
before/after双伪元素法
.clearfix::before,.clearfix::after { content:""; display:table; } .clearfix::after { clear:both; }
-
隐藏元素的方法有哪些
-
display: none
不占位置 -
visibility: hidden
占位置 -
opacity: 0
占位置 -
transform: scale(0)
占位置
-
clip/clip-path 元素裁剪
占位置
-
H5新增hidden属性
不占位置 -
position: absolute 绝对定位移出可视区域
-
z-index 负值 其他元素覆盖住该元素
雪碧图 / 精灵图
CSSSprites(精灵图),将一个页面涉及到的所有图片都包含到一张大图中去,然后利用CSS的background-image,background-repeat,background-position的组合进行背景定位。
优点:
- 减少HTTP请求数,极大地提高页面加载速度
- 增加图片信息重复度,提高压缩比,减少图片大小
- 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现
缺点:
- 图片合并麻烦
- 维护麻烦,修改一个图片可能需要重新布局整个图片,样式
div {
width: 42px;
height: 34px;
background-image: url(amazon-sprite.png);
background-repeat: no-repeat;
background-position: -8px -335px // 雪碧图中的某个图标的位置
}
响应式布局的方案
- 媒体查询
- 百分比布局
- rem布局
- flex布局
- grid布局
- 视口单位vw/vh
单行、多行文本溢出隐藏
-
单行文本
.textflow { overflow: hidden; white-space: nowrap; // 文本不换行 text-overflow: ellipsis; // 文本超出呈现省略号 }
-
多行文本
.textflow { overflow: hidden; display: -webkit-box; // 盒模型 -webkit-line-clamp: 4; // 控制文本行数 -webkit-box-orient: vertical; // 垂直布置子元素 } PS: 该方法不兼容IE
物理像素、逻辑像素和像素密度分别是什么
对em单位的理解
em相对于当前元素的字号。1em等于当前元素字号。
以下有两个要点:
-
使用em定义字号
<style> div{ font-size: 16px; } p{ font-size: 1.2em; // 1.2*16 = 19.2px } </style> <div> <p>Violet天下第一</p> </div>
-
em同时用于字号和其他属性
<style> div{ font-size: 16px; } p{ font-size: 1.2em; // 1.2*16 = 19.2px padding: 1.2em; // 1.2*19.2 = 23.04px } </style> <div> <p>Violet天下第一</p> </div>
transition和animation的区别
- transition是过渡属性。需要触发,并且只有开始和结束的关键两祯
- animation是动画属性。不需要触发,可以设置好时间自动开始,并可以定义多帧动画
画三角形和扇形
-
三角形
-
常规三角形
// 需要哪个三角形就给它颜色,其余三边透明 // 以向下的三角形为例 div{ width: 0; height: 0; border: 20px solid transparent; border-top: 20px solid red; }
-
直角三角形
// 要直角三角形,则只能设置两条边,上下和左右混搭,一条给颜色,一条透明 // 以靠左向下的直角三角形为例 div{ width: 0; height: 0; border-right: 20px solid transparent; border-top: 20px solid red; }
-
-
扇形
// 在画常规三角形的基础上加上border-radius // 扇形画法还可深究,mark div{ width: 0; height: 0; border: 20px solid transparent; border-top: 20px solid red; border-radius: 50%; }
画一条0.5px的线
-
transform:scale()
transform: scale(0.5);
-
meta
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/> PS: viewport只针对于移动端,只在移动端上才能看到效果
css3新增了哪些特性
border-radius、box-shadow、text-shadow、text-decoration、transform、gradient、animation等
less、scss等css预处理器常用特性
- 变量
- 嵌套
- Mixin 复用
- Extend 继承
- 条件 / 循环语句
- 自定义函数
JS
数据类型
- 基本数据类型
String、Number、BigInt、Symbol、Boolean、Null、Undefined - 引用数据类型
Object、Array、Function
基本数据类型存储在栈中,引用数据类型存储在堆中
数据类型转换
-
转字符串
-
String(x)
-
x.toString(x)
null 和 undefined 没有 toString 方法
-
x + 空字符串
-
-
转数字
-
Number(x)
转换规则
初始类型 转换值 string 若字符串为空,则返回0
若字符串由连续纯数字组成(包括浮点数、十六进制数、正负号),则返回该数字(十进制形式)
若字符串不为空且不由连续纯数字组成,则返回NaNnull 0 undefined NaN boolean 1 / 0 object 先进行原始值转换 (优先级:Symbol.toPrimitive > valueOf > toString)
得到基本数据类型的返回值
然后根据返回值的类型进行上面常规过程的转换 -
parseInt(x, radix)
-
parseFloat(x)
-
+x
转换规则同Number(x)
-
x - 0
-
-
转布尔值
-
Boolean(x)
初始值 转换值 直观上为"空"的值
"",0,NaN,null,undefined
其中空字符串 "" 尤其需要注意false 其他值
其中 "0"、" "、[]、{} 尤其需要注意true -
!!x
-
数据类型检测的方式有哪些(待整理)
- typeof
null、数组会被判别为object,其他判断无误 - instanceof
只能用于判断引用数据类型,不可用于基本数据类型 - constructor
可用于判断数据类型,还可用于访问对象实例的构造函数 - Object.prototype.toString.call()
判断数组的方式有哪些(待整理)
- Object.prototype.toString.call()
- Array.prototype.isPrototypeOf()
- Array.isArray()
- instanceof
- 原型链
obj.__proto__ === Array.prototype
判断空对象的方式有哪些(待整理)
-
JSON.stringify()
JSON.stringify(Obj) == '{}'
-
Object.keys()
Object.keys(Obj).length = 0
isNaN()和Number.isNaN()区别
- isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
- Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,这种方法对于 NaN 的判断更为准确。
console.log(isNaN('aa')); //true
console.log(Number.isNaN('aa')); //false
isNaN()方法接受到参数后,会将其转换成Number类型,由于目前的'aa'是字符串,所以转换Number类型失败,转换的结果为NaN。所以再判断isNaN()之后理所当然的结果是true。
而Number.isNaN()不存在转换的过程,直接比较'aa'是否等于NaN,结果为false。
Object.is() 与比较操作符 == 、=== 的区别
- 使用双等号进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
- 使用三等号进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。
- 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 认定为是相等的。
new操作符调用构造函数,具体做了什么
-
版本一
- 创建一个新的空对象
- 将构造函数的 this 指向这个新对象
- 为这个对象添加属性、方法等
- 返回新对象
-
版本二
- 创建一个新的空对象
- 将新对象的原型设置为构造函数的 prototype 原型对象。
- 将构造函数的 this 指向这个新对象,并为其添加属性、方法等
- 判断构造函数的返回值类型,如果是基本类型,返回创建的对象。如果是引用类型,返回这个引用类型的对象。
继承
-
ES5继承
-
原型链继承
function Parent(name){ this.showAge = function(){ console.log(this.age) } } function Child(age){ this.age = age } Child.prototype = new Parent() var child = new Child(18) child.showAge() // 18
问题: 引用类型的属性被所有实例共享 function Parent(){ this.fruits = ['apple', 'banana'] } function Child(){ } Child.prototype = new Parent() var child1 = new Child() var child2 = new Child() child1.fruits.push('orange') console.log(child1.fruits) // ['apple', 'banana', 'orange'] console.log(child2.fruits) // ['apple', 'banana', 'orange']
-
等等
-
-
ES6继承
class Parent { constructor(name){ this.name = name } showName(){ console.log(this.name) } } class Child extends Parent { constructor(name, age){ super(name) this.age = age } showAge(){ console.log(this.age) } } const child = new Child('Queen', 18) child.showName() // Queen child.showAge() // 18
this指向
- 默认绑定 (直接函数调用)
指向window- 隐式绑定 (对象方法调用)
指向调用的对象- 显式绑定 (call、apply、bind)
指向绑定的对象- new绑定 (构造函数)
指向新创建的实例对象优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
- 箭头函数
当前所在父级执行上下文中的this
对象
深浅拷贝
深拷贝和浅拷贝主要针对对象和数组来说的。
浅拷贝,当复制了一个对象后,一个对象修改,会影响另一个对象。因为拷贝的是对象的引用地址,指向的还是同一片空间。
深拷贝,当复制了一个对象后,一个对象修改,不会影响另一个对象。因为拷贝之后是一个新的对象,拷贝的是原对象的值。
-
浅拷贝
- 扩展运算符
- Object.assign()
- 数组slice()方法
- 数组concat()方法
- 手写方法
let obj = { a: 1, b: { c: 3}} let obj2 = { ...obj } let obj3 = Object.assign({}, obj)
let arr = [1, 2, [3, 4]] let arr1 = arr.slice() let arr2 = arr.concat()
-
深拷贝
- JSON.parse(JSON.stringify())
- jQuery.extend()方法
- 手写递归方法
let obj = { a: 1, b: { c: 3}} let obj1 = JSON.parse(JSON.stringify(obj)) let obj2 = $.extend(true, {}, obj)
事件循环
- JS是单线程的。单线程任务又可分为同步任务和异步任务。
- 同步任务在主线程上排队依次执行。异步任务不进入主线程,而是在其有结果后,再将对应的回调函数推入任务队列中,待到主线程空闲的时候读取执行。
主线程不断循环往复地从任务队列中读取任务,执行任务,这种运行机制称为事件循环。
- 除了广义上的同步和异步任务。还可细分为宏任务和微任务。每当一个宏任务执行完毕后,就会检查微任务队列是否有待执行的任务,若有则依次执行,而后再执行下一个宏任务。如此,循环往复。
- 宏任务 (task / macrotask)
script、setTimeout、setInterval等- 微任务 (microtask)
promise.then、async/await等
对闭包的理解
闭包是指有权访问另一个函数作用域中变量的函数。
简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
作用:延伸变量的作用范围;在函数外部能够访问到函数内部的变量。
对原型链的理解
当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型对象,于是就这样一直找下去,也就是原型链的概念。
对作用域链的理解
在当前作用域查找一个变量时,如果这个作用域内部无法查到这个变量,那么它就会去它的父级作用域查找,若还没有,则依次向上级作用域查找,直到访问到window对象为止。这一层层的关系就是作用域链。
对执行上下文的理解
简单的来说,执行上下文是对Javascript
代码执行环境的一种抽象概念,只要有Javascript
代码运行,那么它就一定是运行在执行上下文中。
执行上下文的三种类型
- 全局执行上下文
- 函数执行上下文
- eval函数执行上下文
执行上下文的三个重要属性
- 变量对象
- 作用域链
- this
call、apply、bind三者异同
-
共同点 : 都可以改变this指向
-
不同点:
- call 和 apply 会调用函数,bind 不会调用函数
- call 、bind 与 apply 传参方式不一样,call或bind传参使用逗号隔开,apply使用数组传递
-
应用场景
-
call 经常做继承
-
apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
-
bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向
-
es6有哪些新特性
- let / const
- class
- promise
- async / await
- 箭头函数
- 解构赋值
- 模板字符串
- 函数默认参数
- 扩展运算符 / 剩余参数运算符
- 等等
节流防抖
- 节流:控制流量,单位时间内事件只能触发一次。
轮播图。类比公交车固定班次,王者荣耀英雄技能冷却。 - 防抖:防止抖动,单位时间内事件触发会被重置。
表单验证。输入框的联想功能。
具体待完善。。
常见排序算法
以从小到大排序为例。
-
冒泡排序
算法步骤
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个
-
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数
-
针对所有的元素重复以上的步骤,除了最后一个
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
function bubbleSort(arr){ let len = arr.lenth; for(let i = 0; i < len - 1; i++){ for(let j = 0; j < len - i - 1; j++){ if(arr[j] > arr[j+1]){ let temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr; }
冒泡排序优化 function bubbleSort(arr){ let len = arr.length; for(let i = 0; i < len - 1; i++){ let flag = true; // 标记变量 for(let j = 0; j < len - i - 1; j++){ if(arr[j] > arr[j+1]){ let temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; flag = false; } } // 若标记变量在某一轮排序中未曾变动,则已排序完成,无须继续比较了 if(flag){ break; } } return arr; }
-
-
选择排序
算法步骤
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
- 重复第二步,直到所有元素均排序完毕
function selectionSort(arr){ let len = arr.length; for(let i = 0; i < len - 1; i++){ let min = i; for(let j = i + 1; j < len; j++){ if(arr[j] < arr[min]){ min = j; } } let temp = arr[i]; arr[i] = arr[min]; arr[min] = temp; } }
-
等等
常见设计模式
两大原则
- 单一职责原则
一个类只负责一个功能领域中的相应职责- 开放封闭原则
对扩展是开放的,而对修改是封闭的
-
单例模式
使用场景
- 网页浮窗
class Singleton { constructor(name,password){ this.name = name this.password = password } static getInstance(name,password){ // 判断对象是否已经被创建,若创建则返回旧对象 // 静态方法中的this指向类本身 if(!this.instance){ this.instance = new SingletonLogin(name,password) } return this.instance } } let obj1 = Singleton.getInstance('CXK','123') let obj2 = Singleton.getInstance('CXK','321') console.log(obj1===obj2) // true console.log(obj1) // {name:CXK,password:123} console.log(obj2) // {name:CXK,password:123}
-
工厂模式
-
等等
常用DOM API
节点有元素节点、属性节点、文本节点等一系列节点,使用DOM API的时候要注意获取到的节点未必都是元素节点。
-
增
document.createElement() document.createTextNode() Node.appendChild()
-
删
parentNode.removeChild() childNode.remove()
-
改
Node.innerText Node.innerHTML
-
查
document.getElementByID() document.getElementsByTagName() document.getElementsByClassName() document.querySelector() document.querySelectorAll() Node.children Node.childNodes Node.firstChild Node.lastChild Node.parentNode Node.previousSibling Node.nextSibling Node.style Node.className Node.classList Node.xxx属性 Node.getAttribute()
Vue
Vue基础
Vue生命周期钩子函数
Vue的生命周期就是Vue实例从创建到销毁的过程。
activated
和 deactivated
是 keep-alive
独有的生命周期。用 keep-alive
包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated
钩子函数,命中缓存渲染后会执行 activated
钩子函数。
- beforeCreate
- created 异步请求
- beforeMount 异步请求
- mounted 异步请求,操作DOM
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestory
- detoryed
Vue父子组件生命周期钩子执行顺序
挂载过程:
- 父组件 beforeCreate
- 父组件 created
- 父组件 beforeMount
- 子组件 beforeCreate
- 子组件 created
- 子组件 beforeMount
- 子组件 mounted
- 父组件 mounted
更新过程:
- 父组件 beforeUpdate
- 子组件 beforeUpdate
- 子组件 updated
- 父组件 updated
卸载过程:
- 父组件 beforeDestroy
- 子组件 beforeDestroy
- 子组件 destroyed
- 父组件 destoryed
Vue父组件如何监听子组件的生命周期钩子
以父组件监听子组件mounted生命周期钩子为例,有以下两种方法
-
第一种
// Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); }
-
第二种
// Parent.vue <Child @hook:mounted="doSomething" ></Child> // Child.vue mounted(){ console.log("mounted"); }
Vue组件data为什么必须是一个函数
Vue组件可以复用,则可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例。
created和mounted区别
- created
在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。 - mounted
在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
在哪个生命周期内调用异步请求
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data已经创建,可以将服务端返回的数据进行赋值。
推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面加载时间,用户体验更好;
- SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。
在哪个生命周期内访问操作DOM
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
常用指令
- v-if
- v-else
- v-show
- v-on
- v-bind
- v-for
- v-model
- v-cloak
v-if和v-show区别
- v-if是动态地向DOM树内添加或者删除DOM元素,性能消耗大,适合单次切换
- v-show是通过设置DOM元素的display样式属性控制显隐,性能消耗低,适合频繁切换
class和style绑定
class绑定
对象语法 :class="{class_A: isA, class_B: isB}" 或 :class="classObject"
数组语法 :class="[class_A, class_B]"
style绑定
对象语法 :style="{color: color, fontSize: fontSize}" 或 :style="styleObject"
数组语法 :style="[styleObjectA, styleObjectB]"
-
class绑定
-
对象语法
<div :class="{ class_A: isA, class_B: isB }"></div> data() { return { isA: true, isB: false } } ----------------------------------------------------------- <div :class="classObject"></div> data() { return { classObject: { class_A: true, class_B: false } } }
-
数组语法
<div :class="[class_A, class_B]"></div> data() { return { class_A: 'class_A', class_B: 'class_B' } }
-
-
style绑定
-
对象语法
<div :style="{ color: color, fontSize: fontSize }"></div> data() { return { color: 'red', fontSize: '30px' } } --------------------------------------------------------- <div :style="styleObject"></div> data() { return { styleObject: { color: 'red', fontSize: '13px' } } }
-
数组语法
<div :style="[styleObjectA, styleObjectB]"></div> data() { return { styleObjectA: { color: 'red', }, styleObjectB: { color: 'red', fontSize: '13px' }, } }
-
v-model实现原理
<input v-model="value" />
<input v-bind:value="value" v-on:input="value = $event.target.value" />
在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件
父组件:
<Child v-model="message"></Child>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
为什么要避免v-if和v-for连用
vue2中v-for优先级高于v-if,vue3中则相反。因而在vue2中同时使用时,v-for会优先作用,造成性能浪费;在vue3中使用时,则由于v-if优先作用,导致其访问不了v-for中的变量。
一般我们在两种常见的情况下会倾向于这样做:
-
为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
<ul> <li v-for="user in activeUsers" :key="user.id"> {{ user.name }} </li> </ul> computed: { activeUsers: function () { return this.users.filter(function (user) { return user.isActive }) } }
-
为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol)。
<ul v-if="shouldShowUsers"> <li v-for="user in users" :key="user.id"> {{ user.name }} </li> </ul>
双向绑定原理、响应式原理
-
Vue 2.x 采用数据劫持结合发布-订阅模式,通过 Object.defineProperty() 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
缺点:
- 无法监测到对象属性的新增或删除
- 无法监测到数组的变化
push()、pop()、shift()、unshift()、splice()、sort()、reverse()除外- 对数组基于下标的修改
- 对数组length的修改
-
Vue 3.0 采用基于代理 proxy 的 observer 实现,提供全语言覆盖的反应性跟踪,消除了 Vue 2.x 基于 Object.defineProperty 实现所存在的诸多限制。
三层架构、MVC、MVVM
-
三层架构
表现层、业务逻辑层、数据访问层
-
MVC
M:模型层、V:视图层、C:控制层
-
MVVM
- Model:代表数据模型,数据和业务逻辑都在Model层中定义
- View:代表UI视图,负责数据的展示
- ViewModel:负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作
<body> <!-- Vue 实例控制的这个元素区域,就是 MVVM 的 V--> <div id ="app"> <p>{{ msg }}</p> </div> <script> // 创建一个Vue的实例。vm 对象,就是 MVVM 中的 VM var vm = new Vue({ el: '#app', // data 就是 MVVM 中的 M data:{ msg: '欢迎学习Vue' } }); </script> </body>
methods、computed、watch的区别
- methods
没有缓存,只要调用,就会执行 - computed
具有缓存性,依赖于其他属性值,只有当属性值发生改变的时候才会重新计算 - watch
没有缓存,监听data中的属性,属性值只要发生变化就会执行
export default{
data(){
return {
firstName: 'Violet',
lastName: 'Evergarden',
msg: ''
}
},
methods:{
getFullName(){
console.log(this.fullNameA, this.fullNameB)
}
},
computed:{
fullNameA(){ // 仅读取
return this.firstName + ' ' + this.lastName
},
fullNameB: { // 读取和设置
get(){
return this.firstName + ' ' + this.lastName
}
set(val){
this.msg = val
}
}
},
watch:{
firstName(newVal, oldVal){
this.fullName = newVal + ' ' + this.lastName
},
lastName: {
handler(newVal, oldVal){
this.fullName = this.firstName + ' ' + newVal
}
}
}
}
slot插槽的用法
插槽可分为三种:默认插槽、具名插槽、作用域插槽。
-
默认插槽
父组件: <Child> <template>默认插槽</template> </Child> 子组件: <div> <slot>默认插槽 - 后备内容</slot> </div> 渲染成: <div> <div>默认插槽</div> </div>
-
具名插槽
父组件: <Child> <template v-slot:violet>具名插槽</template> </Child> 子组件: <div> <slot name="violet">具名插槽 - 后备内容</slot> </div> 渲染成: <div> 具名插槽 </div>
-
作用域插槽
父组件: <Child> <template v-slot:violet="slotProps"> 作用域插槽 - {{slotProps.user.name}} </template> </Child> 子组件: <div> <slot name="violet" :user="user">作用域插槽 - 后备内容</slot> </div> data(){ return { user: { name: 'violet' } } } 渲染为: <div> 作用域插槽 - violet </div>
keep-alive的作用
可以使被包含的组件保留状态,避免重新渲染。
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</kepp-alive>
{
path: '/',
name: 'xxx',
component: ()=>import('../src/views/xxx.vue'),
meta:{
keepAlive: true // 需要被缓存
}
}
// 将缓存name为A或B的组件
<keep-alive include='A, B'>
<router-view/>
</keep-alive>
// 将不缓存name为C的组件
<keep-alive exclude='C'>
<router-view/>
</keep-alive>
对SSR服务端渲染的理解
服务端渲染。将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把HTML直接返回给客户端。
优缺点
- 优点
- 利于SEO
- 首屏加载速度快
- 缺点
- 更多的服务器负载,服务器压力较大
- 更多的开发条件限制,服务器端渲染只支持beforeCreate和created两个钩子
数据更新,视图会立即同步执行重新渲染吗
不会立即同步执行重新渲染。Vue实现响应式并不是数据发生变化之后DOM立即变化,而是按一定的策略进行DOM的更新。Vue在更新DOM时是异步执行的。只要侦听到数据变化, Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中,Vue 刷新队列并执行实际(已去重的)工作。
props用法
父组件:
<Child name='tom' :age='3'></Child>
子组件:
props: ['name', 'age']
props: {
name: String,
age: {
type: Number,
required: true,
default: 5
}
}
$nextTick 的使用
Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
nextTick的回调函数会等到同步任务执行完毕,DOM 更新后才触发。
使用场景示例
div通过v-if由隐藏切换成显示状态之后,由于Vue中数据和DOM渲染是异步的缘故,无法立刻获得div包含的文本内容,这时候就可以用到$nextTick了。
<div id="app"> <div id="div" v-if="showDiv">hello,我是小四</div> <button @click="getText">获取div内容</button> </div> <script> var app = new Vue({ el : "#app", data:{ showDiv : false }, methods:{ getText:function(){ this.showDiv = true; var text = document.getElementById('div').innnerHTML; console.log(text); } } }) </script>
<div id="app"> <div id="div" v-if="showDiv">hello,我是小四</div> <button @click="getText">获取div内容</button> </div> <script> var app = new Vue({ el : "#app", data:{ showDiv : false }, methods:{ getText:function(){ this.showDiv = true; this.$nextTick(function(){ var text = document.getElementById('div').innnerHTML; console.log(text); }); } } }) </script>
Vue组件通信
-
props / $emit
父组件: <Child name="violet" @emit="emitA"></Child> methods: { emitA(msg){ console.log(msg) } } 子组件: <div @click="emitB"></div> props: ['name'], methods: { emitB(){ this.$emit("emit", "Violet天下第一") } }
-
eventBus
// main.js Vue.prototype.eventBus = new Vue() 组件A (发送事件) this.eventBus.$emit('sayHello', "Hello Vue") 组件B (接收事件) this.eventBus.$on('sayHello', (msg) => { console.log(msg) })
-
provide / inject
父组件: provide() { return { num: this.num }; } 子组件: inject: ['num']
-
Vuex
-
等等
Vue常用修饰符
-
.prevent
阻止事件的默认行为 -
.stop
阻止单击事件冒泡 -
.self
事件在该元素自身触发时触发回调
-
.once
事件只触发一次
Vue自定义指令
使用方式:<input v-twice="3">
。待研究。
-
全局注册
Vue.directive('twice', function (el, binding) { el.innerText = binding.value * 2; })
-
局部注册
directives: { twice(el, binding) { el.innerText = binding.value * 2 } }
Vue自定义过滤器
使用方式:{{ msg | reverse}}
或 <span v-text="message | reverse"></span>
-
全局注册
Vue.filter('reverse', function(value){ return value.split('').reverse().join() })
-
局部注册
filters: { reverse(value){ return value.split('').reverse().join() } }
Vue单页面应用优缺点
SPA单页面应用指只有一个页面的应用,仅在页面初始化时加载相应的CSS、JS等资源。所有内容都包含在主页面,每个功能模块都加以组件化。单页应用跳转,即切换相关组件,仅刷新局部资源。
优缺点
- 优点
- 良好的交互体验
- 服务器压力较小
- 前后端分离,结构清晰
- 缺点
- 不利于SEO
- 首屏加载速度慢
Vue Router
路由导航钩子
分三种:全局守卫、路由独享守卫、组件守卫
-
beforeEach
跳转前进行判断拦截,例如登录判定。判断是否登录了,没登录就跳转到登录页。
beforeEach主要有3个参数 to、from、next
-
to:即将进入的目标路由对象
-
from:当前导航正要离开的路由对象
-
next:方法,必须调用来resolve这个钩子函数。执行效果视传参而定。
next()
:进入下一个路由。next(false)
:中断当前的导航。next('/')
或next({ path: '/' })
: 跳转到其他路由,当前导航被中断,进行新的一个导航。
-
-
等等
如何配置404页面
将404页面对应路由配置放到所有路由的配置信息的最后,避免对其他路由path匹配造成影响。
const router = new VueRouter({
mode:"history",
routes: [
{
name: "A",
path: "/a",
component: ()=>import("@/views/A.vue")
},{
name: "B",
path: "/b",
component: ()=>import("@/views/B.vue")
},{
name: "404",
path: "*",
component: ()=>import("@/views/404.vue")
}
]
})
hash和history两种路由模式的区别
-
hash模式
原理:hashchange事件
- url路径中会出现 # 号,# 号及其后面的字符即为hash值
- hash值不包括在http请求中,它是交由前端路由处理,所以hash值改变时不会刷新页面,也不会向服务器发送请求
-
history模式
原理:H5 History API 中新增的 pushState() 和 replaceState() 方法
- url路径中不会出现 # 号
- 依赖后台配置,若后台没给相应配置,页面刷新就会出现404
$route和$router的区别
- $route 路由信息对象,可用于获取name 、 path 、 query 、 params 等路由信息参数
- $router 路由实例对象,可用于路由跳转,想要导航到不同URL,则使用$router.push 方法
路由传参的两种方式及区别
-
params传参
传递参数: <router-link :to="{ name: 'A', params: {id: uid} }">跳转</router-link> this.$router.push({ name: 'A', params: {id: uid} }) this.$router.push({ '/a/' + uid }) 接收参数: this.$route.params.id 还可通过配置props来接收,待试验 路由配置: const router = new VueRouter({ mode: "history", routes: [ { name: "A", path: "/a/:id", component: ()=>import("@/views/A.vue") }, ] })
-
query传参
传递参数: <router-link :to="{ name: 'B', query: {id: uid} }">跳转</router-link> <router-link :to="{ path: '/b', query: {id: uid} }">跳转</router-link> this.$router.push({ name: 'B', query: {id: uid} }) this.$router.push({ path: '/b', query: {id: uid} }) this.$router.push({ '/b?id' + uid }) 接收参数: this.$route.query.id 路由配置: const router = new VueRouter({ mode: "history", routes: [ { name: "B", path: "/b", component: ()=>import("@/views/B.vue") } ] })
-
小结
params传参必须使用name引入路由;query传参则没有限制,path、name皆可
使用params时,如若路由配置中path后面未添加参数名,页面刷新时数据会丢失;query则没有限制
使用params时,如若路由配置中path后面未添加参数名,参数不会在地址栏中显示,而若路由配置中path后面添加了参数名,参数会在地址栏中显示;query所传参数则默认在地址栏中显示。二者链接格式如下。
// params history模式: http://localhost:8080/a/111 hash模式: http://localhost:8080/#/a/111 // query history模式: http://localhost:8080/b?id=222 hash模式: http://localhost:8080/#/b?id=222
声明式导航和编程式导航
-
声明式导航
<router-link to="/home"></router-link> <router-link :to="{name: 'Home'}"></router-link> <router-link :to="{name: 'Home', params: {id: 1}}"></router-link> <router-link :to="{path: '/home', query: {id: 1}}"></router-link>
-
编程式导航
this.$router.push('/home') this.$router.push({name: 'Home'}) this.$router.push({name: 'Home', params: {id: 1}}) this.$router.push({path: '/home', query: {id: 1}}) // 不会记录本次历史 this.$router.replace('/home') // 跳转到历史的某一次,如果是负数就是后退 this.$router.go(-1)
Vue Router示例
import Vue from 'vue'
import VueRouter from 'vue-router'
// 注册路由插件
Vue.use(VueRouter)
const router = new VueRouter({
mode: "history",
routes: [
{
name: "A",
path: "/a",
component: ()=>import("@/views/A.vue")
},{
name: "B",
path: "/b",
redirect: '/b/b1',
component: ()=>import("@/views/B.vue"),
children: [
{
name: "B1",
path: "b1",
component: ()=>import("@/views/B1.vue")
}, {
name: "B2",
path: "b2/:id",
props: true, // 将url中的参数传递给组件,在组件中通过props来接收
component: ()=>import("@/views/B2.vue")
}
]
}
]
})
export default router
Vuex
Vuex有哪些属性
State、Getters、Mutations、Actions、Modules
-
State
基本数据存储 this.$store.state.xxx属性
-
Getters
从基本数据派生出的数据,类似于计算属性,不过不具备缓存性 this.$store.getters.xxx属性
-
Mutations
更改vuex中state数据,只能进行同步操作 this.$store.commit('xxx方法名', '值')
-
Actions
与Mutations差不多,不同之处在于Actions可以包含任意异步操作 this.$store.dispatch('xxx方法名', '值')
-
Modules
将Vuex中的Store分割成模块,每个模块拥有自己的State、Getters、Mutations、Actions
Mutations和Actions的区别
总体而言,两者差不多,区别主要有以下两点:
- Action提交的是Mutation,而不是直接变更状态
- Action可以包含任意异步操作
Vuex示例
import Vue from 'vue'
import Vuex from 'vuex'
// 使用vuex
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 1
},
getters: {
incrementCount(state, getters) {
return state.count + 10;
}
},
mutations: {
incrementSync(state, payload) {
state.count += payload.num;
}
},
actions: {
incrementAsync(context, payload) {
let num = payload.num;
setTimeout(() => {
context.commit('incrementSync', {
num
})
}, 1000);
}
}
})
export defalut store
-------------------------------------
// 调用state
this.$store.state.count
// 调用getters
this.$store.getters.incrementCount
// 调用mutations
this.$store.commit('incrementSync', {
num: 10
})
// 调用actions
this.$store.dispatch('incrementAsync', {
num: 10
})
Vue3.0
有什么变化
-
监测机制的改变
-
组合式API
为什么要使用组合式API?
- Vue2的局限性
- Vue2对TS的支持有限
- 组件逻辑膨胀导致的可读性变差
- 组合式API优点
- 可以按照功能组织代码
- 组件间功能代码可以复用
<template> <button @click="increment"> Count: {{ count }} </button> </template> <script> // Composition API 将组件属性暴露为函数,因此第一步是导入所需的函数 import { ref, computed, onMounted } from 'vue' export default { setup(props, context) { // 对应于Vue2中的data函数 const count = ref(0) // 对应于Vue2中的方法 function increment() { count.value++ } // 对应于Vue2中的计算属性 const twiceCount = computed(() => { count.value * 2 }) watch(count, (newVal, oldVal) => { console.log(newVal) }) // 对应于Vue2中的mounted声明周期 onMounted(() => { console.log('component mounted!') }) return { count, increment, twiceCount } } } </script>
- Vue2的局限性
生命周期钩子函数
Vue2 | Vue3 |
---|---|
beforeCreate | ❌setup(替代) |
created | ❌setup(替代) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestory | onBeforeUnmount |
detoryed | onUnmounted |
React
React基础
React特点、优点以及限制
-
特点
- 虚拟DOM
- 单向数据流
- 支持服务端渲染
-
优点
- 使用虚拟DOM提高了应用性能
- 使用JSX使代码易于读写
- 支持服务端渲染
-
限制
- 只是一个视图库,而不是一个完整的框架
- 学习成本较高,不易上手
React生命周期钩子函数
-
挂载阶段
-
constructor
-
static getDerivedStateFromProps(props, state)
让组件在 Props 变化时更新 State,根据 Props 来派生出新的 State。返回一个对象来更新 State,返回 null 则不做更新。
-
render
-
componentDidMount
-
-
更新阶段
-
static getDerivedStateFromProps(props, state)
-
shouldComponentUpdate(nextProps, nextState)
手动判断是否需要更新。返回false表示丢弃更新,默认返回true。
-
render
-
getSnapshotBeforeUpdate(prevProps, prevState)
读取 DOM 更新前的 DOM 信息,如滚动位置。
-
componentDidUpdate
-
-
卸载阶段
- componentWillUnmount
PS:冷门三生命周期还尚需研究。
React父子组件生命周期钩子执行顺序
挂载过程:
- 父组件 constructor
- 父组件 getDerivedStateFromProps
- 父组件 render
- 子组件 constructor
- 子组件 getDerivedStateFromProps
- 子组件 render
- 子组件 componentDidMount
- 父组件 componentDidMount
更新过程:
- 父组件 getDerivedStateFromProps
- 父组件 shouldComponentUpdate
- 父组件 render
- 子组件 getDerivedStateFromProps
- 子组件 shouldComponentUpdate
- 子组件 render
- 子组件 getSnapShotBeforeUpdate
- 父组件 getSnapShotBeforeUpdate
- 子组件 componentDidUpdate
- 父组件 componentDidUpdate
卸载过程:
- 父组件 componentWillUnmount
- 子组件 componentWillUnmount
React元素和组件的区别
- 元素是不可变的普通对象,用来描述你想要渲染的组件或 DOM 节点。
- 组件可以是类或者函数,它将 props 作为输入然后返回一个元素的树形结构作为输出。
React类组件和函数组件的区别
- 状态。类组件可以有自己的状态State;函数组件之前没有,现在可以使用useState来使用状态。
- 生命周期。类组件有自己的生命周期;函数组件之前没有,现在可以使用useEffect来实现类似功能。
- 性能优化。类组件可以使用shouldComponentUpdate和React.PureComponent;函数组件可以使用React.memo。
- 编程思想。类组件基于面向对象的编程思想;函数组件基于函数式编程思想。
- 内存占用。类组件需要创建并保存实例,需要占用一定内存;函数组件不需要实例,节约内存。
React事件机制与合成事件
React 基于浏览器的事件机制,自身实现了一套事件机制,包括事件注册、事件合成、事件冒泡、事件派发等。在 React中,这套事件机制被称之为合成事件。
合成事件,是 React 模拟原生 DOM 事件所有能力的一个对象,它根据 W3C 标准定义事件,兼容所有浏览器,拥有与原生事件相同的接口。有以下几个优点。
- React 合成事件集中绑定在document上,方便集中管理
- 兼容所有浏览器,跨平台
- 减少事件创建销毁的开销
React合成事件与原生事件的区别
- 对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰
- 对于事件函数处理语法,原生事件使用引号调用函数,react 事件使用大括号调用函数
- 对于阻止浏览器的默认行为,原生事件使用 return false,react 事件采用 event.prenventDefault()
React组件会在什么时候触发Render方法
-
类组件
- 只要执行 setState 方法,就一定会执行渲染,无论 State 值是否有改变
- 只要父组件重新渲染,子组件一定重新渲染
- Props若直接用在Render中,Props发生变化,会重新渲染
-
Hooks函数组件
- 执行useState Hook方法,不一定执行渲染,要看 State 值是否有改变
- 只要父组件重新渲染,子组件一定重新渲染
- Props若直接用在Render中,Props发生变化,会重新渲染
PS:只要父组件重新渲染,子组件一定重新渲染。这句话的前提是子组件没有使用PureComponent、
shouldComponentUpdate、Memo。另外,Props改变与组件是否重新渲染没有必然关系。最后,Render方法的执行并不一定会导致DOM的重新渲染,DOM的重新渲染的条件是Render前后的DOM diff比较的结构不一致。
React引入CSS的四种方式
- 内联样式
- CSS文件
- CSS Modules
- CSS in JS
React类组件事件绑定this的四种方式
-
constructor中使用bind
this.handleClick = this.handleClick.bind(this)
-
定义处使用箭头函数
handleClick = () => { ... }
-
render方法中使用bind
<button onClick={this.handleClick.bind(this)}>点击</button>
-
render方法中使用箭头函数
<button onClick={() => {this.handleClick()}}>点击</button>
React使用refs的四种方式
-
类组件
class MyComponent extends Component { constructor(props) { super(props) this.aRef = React.createRef() this.bRef = React.createRef() this.cRef = React.createRef() } changeText = () => { this.refs.aRef.innerText = 'Hello, A' this.bRef.current.innerText = 'Hello, B' this.cRef.innerText = 'Hello, C' } render() { return ( <div> // 传入字符串 <div ref="aRef">Hi, A</div> // 传入对象 <div ref={this.bRef}>Hi, B</div> // 传入回调函数 <div ref={e => this.cRef = e}>Hi, C</div> <button onClick={this.changeText}>ref内容切换</button> </div> ) } }
-
Hooks函数组件
function MyComponent() { const dRef = useRef() const changeText = () => { dRef.current.innerText = 'Hello, D' } return ( <div> // 传入hooks <div ref={dRef}>Hi, D</div> <button onClick={changeText}>ref内容切换</button> </div> ) }
React代码复用的三种方式
- 高阶组件
- Render Props
- React Hooks
React组件通信
-
props / 回调
父组件: <Child name="rem" callback={this.changeName}></Child> 子组件: <button onClick={this.props.callback.bind(this, 'violet')}>点击</button>
-
eventBus
import { EventEmitter } from 'events' const eventBus = new EventEmitter() 组件A (发送事件) eventBus.emit('sayHello', 'Hello React') 组件B (接收事件) eventBus.addListener('sayHello', (msg) => { console.log(msg) })
-
context
const context = React.createContext() 父组件: <context.Provider value={{name: 'rem', callback: this.changeName}}> <Child /> </context.Provider> 子孙组件 (接收方式二): static contextType = context <div>{this.context.name}</div> <button onClick={this.context.callback.bind(this, 'violet')}>点击</button> 子孙组件 (接收方式二): <context.Consumer> { value => ( <div> <div>{value.name}</div> <button onClick={value.callback.bind(this, 'violet')}>点击</button> </div> ) } </context.Consumer>
-
redux
-
等等
什么是JSX
JSX是 JS 的一种语法扩展,本质是React.createElement()方法的语法糖,用来简化创建虚拟DOM。
class App extends React.Component {
render() {
return(
<div>
<h1>{'Welcome to React world!'}</h1>
</div>
)
}
}
React.createElement(
'div',
null,
React.createElement(
'h1',
null,
'Welcome to React world!')
)
refs转发
Ref 转发允许某些组件接收 ref,并将其向下传递给子组件。
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
))
// 你可以直接获取 DOM button 的 ref
const ref = React.createRef()
<FancyButton ref={ref}>Click me!</FancyButton>
使用refs的几种情况
- 管理焦点、选择文本或媒体播放
- 与第三方 DOM 库集成
- 触发式动画
setState执行机制
-
同步还是异步?是否合并?
-
在组件生命周期或React合成事件中,setState是异步
- 传递对象的 setState 会被合并
- 传递函数的 setState 不会被合并
-
在setTimeout或者原生DOM事件中,setState是同步
- 无论传递对象还是函数,都不会被合并
PS:当对象和函数传递连用时,还需深入研究。
-
使用setState如何更新State中的数组或对象
-
setState更新数组
-
扩展运算符
this.setState(state => ({ list: [...state.list, 20] }))
-
concat
this.setState(state => ({ list: state.list.concat([20]) }))
-
-
setState传递函数
-
扩展运算符
this.setState(state => ({ user: { ...state.user, age: 20 } })
-
Object.assign()
this.setState(state => { user: Object.assign({}, state.user, {age: 20}) })
-
State和Props的区别
- State是在组件内部由组件自己管理的,而Props是从外部传递给组件的
- State在组件内部可以被修改,而Props在组件内部是不可以被修改的
super()和super(props)的区别
- super(),为了继承父类中的this对象
- super(props),为了在constructor中使用this.props
为什么类组件的构造函数仅被调用一次
React 协调算法假设如果自定义组件出现在后续渲染的相同位置,则它与之前的组件相同,因此重用前一个实例而不是创建新实例。
React Diff原理
- 把树形结构按照层级分解,只比较同级元素
- 给列表结构的每个单元添加唯一的 key 属性,方便比较
- React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 方法的时候,React 将其标记为 dirty。到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制。
- 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
React Fiber理解
- React Fiber 是一种基于浏览器的单线程调度算法.
- React 16之前 ,reconcilation 算法实际上是递归,想要中断递归是很困难的,React 16 开始使用了循环来代替之前的递归.
- React Fiber:一种将 recocilation (递归 diff),拆分成无数个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算
React Hooks
使用Hooks要遵循的规范
- 只能在函数式组件和自定义Hook中使用。
- 只能在最顶层使用。不能在条件、循环、嵌套中使用。
useEffect和useLayoutEffect的区别
- useEffect不会阻塞浏览器渲染,而useLayoutEffect会
- useEffect会在浏览器渲染结束之后执行,而useLayoutEffect则是在DOM更新完成之后,浏览器渲染之前执行。
PS:后者总是比前者先执行,后者主要用于处理DOM操作、调整样式、避免屏闪等问题。举个栗子:移动方块。
React Hooks示例
import React, { useState, useEffect, useLayoutEffect, useContext, useRef } from 'react';
const InfoContext = React.createContext()
function Child() {
const info = useContext(InfoContext)
const {name, age} = info
return (
<div>{name} {age}</div>
)
}
export default function Parent() {
const [count, setCount] = useState(0)
const textRef = useRef()
useEffect(() => {
document.title = `You clicked ${count} times`
}, [count])
useLayoutEffect(() => {
textRef.current.innerText = '女友成双'
})
return (
<div>
<span>You clicked {count} times </span>
<button onClick={() => setCount(count + 1)}>Click me</button>
<div ref={textRef}>我们的重制人生</div>
<InfoContext.Provider value={{name: 'Violet', age: 20}}>
<Child/>
</InfoContext.Provider>
</div>
)
}
React Router
路由信息
this.props.history
this.props.history.push()
this.props.history.replace()
this.props.history.go()
this.props.match
this.props.match.params
this.props.location
this.props.location.search
this.props.location.query
this.props.location.state
路由传参的四种方式及区别
- params传参,使用props.match.params接收
- search传参,使用props.location.search接收
- query传参,使用props.location.query接收。刷新会丢失数据。
- state传参,使用props.location.state接收。BrowserRouter中刷新不会丢失数据,HashRouter中刷新会丢失数据。
-
params传参
传递参数: <Link to='/A/996/Violet'>Page A</Link> this.props.history.push("/A/996/Violet") 接收参数: this.props.match.params 路由配置: <Route exact path='/A/:id/:name' component={PageA}></Route>
-
search传参
传递参数: <Link to='/B?id=996&name=Violet'>Page B</Link> this.props.history.push("/B?id=996&name=Violet") 接收参数: this.props.location.search new URLSearchParams(this.props.location.search) queryString.parse(this.props.location.search) 路由配置: <Route exact path='/B' component={PageB}></Route>
-
query传参
传递参数: <Link to={{pathname: '/C', query: {id: 996, name: 'Violet'}}}>Page C</Link> this.props.history.push({pathname: '/C', query: {id: 996, name: 'Violet'}) 接收参数: this.props.location.query 路由配置: <Route exact path='/C' component={PageC}></Route>
-
state传参
传递参数: <Link to={{pathname: '/D', state: {id: 996, name: 'Violet'}}}>Page D</Link> this.props.history.push({pathname: '/D', state: {id: 996, name: 'Violet'}) 接收参数: this.props.location.state 路由配置: <Route exact path='/D' component={PageD}></Route>
Link标签和a标签的区别
a标签在实现页面跳转时,整个页面会重新渲染;而Link标签只会重新渲染更新变化的部分,避免了不必要的重渲染。
如何设置重定向
<Switch>
<Route exact path='/' component={Page}></Route>
// 方式一
<Route path='/A'>
<Redirect to='/'></Redirect>
</Route>
// 方式二
<Redirect from='/A' to='/'></Redirect>
</Switch>
React Router示例
Redux
Redux工作流程
- 首先,用户通过dispatch方法发出Action。
- 然后,Store自动调用Reducer,返回新的State。
- 最后,State发生变化,Store调用监听函数,更新View。
Redux三大原则
-
单一数据源
整个应用的State都被存储到一个状态树里面,并且这个状态树,只存在于唯一的Store中
-
State状态只读
State是只读的,唯一改变State的方法就是触发Action
-
使用纯函数Reducer来修改State
React Redux中UI组件和容器组件的区别
-
UI组件负责UI的呈现,容器组件负责管理数据和逻辑
-
两者通过React Redux中提供的connect方法联系起来
import { connect } from 'react-redux' // UIComponent是UI组件,ContainerComponent是容器组件 const ContainerComponent = connect(mapStateToProps, mapDispatchToProps)(UIComponent)
Redux示例
示例一
import { createStore } from 'redux'
// state
const initialState = {
counter: 0,
}
// actionCreators
const addAction = num => ({ type: "ADD_NUMBER", num })
const subAction = num => ({ type: "SUB_NUMBER", num })
// reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_NUMBER":
return { ...state, counter: state.counter + action.num };
case "SUB_NUMBER":
return { ...state, counter: state.counter - action.num };
default:
return state;
}
}
// store
const store = createStore(reducer)
// store状态变更订阅
store.subscribe(() => {
console.log("counter:", store.getState().counter);
})
// action派发
store.dispatch(addAction(1));
store.dispatch(subAction(1));
示例二
import React, { Component } from 'react'
import ReactDOM from "react-dom"
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'
class Home extends Component {
render() {
return (
<div>
<h2>当前计数: {this.props.counter}</h2>
<button onClick={this.props.addNumber.bind(this, 1)}>加一</button>
<button onClick={this.props.subNumber.bind(this, 1)}>减一</button>
</div>
)
}
}
// state
const initialState = {
counter: 0,
}
// actionCreators
const addAction = num => ({ type: "ADD_NUMBER", num })
const subAction = num => ({ type: "SUB_NUMBER", num })
// reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_NUMBER":
return { ...state, counter: state.counter + action.num };
case "SUB_NUMBER":
return { ...state, counter: state.counter - action.num };
default:
return state;
}
}
// store
const store = createStore(reducer)
// mapState
const mapStateToProps = state => ({
counter: state.counter
})
// mapDispatch
const mapDispatchToProps = dispatch => ({
addNumber(num) {
dispatch(addAction(num))
},
subNumber(num) {
dispatch(subAction(num))
}
})
const App = connect(mapStateToProps, mapDispatchToProps)(Home)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Vue & React
虚拟DOM
-
实现原理
- 用 JS 对象模拟真实 DOM 树,对真实 DOM 进行抽象
- diff 算法 — 比较两棵虚拟 DOM 树的差异
- pach 算法 — 将差异应用到真正的 DOM 树
-
优缺点
-
优点
- 保证性能下限
- 防范XSS攻击
- 无需手动操作DOM
- 跨平台
-
缺点
- 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
- 首次渲染大量DOM时,由于多了一层虚拟DOM的计算,速度比正常稍慢
-
性能优化
-
通用
- key保证唯一
- ssr服务端渲染
- 第三方组件库按需引入
-
Vue
- 路由懒加载
- keep-alive缓存组件
- v-for遍历的同时避免使用v-if
-
React
- 使用 Fragments 避免额外标记
- 使用 shouldComponentUpdate
、
PureComponent、
React.memo 来避免不必要的render - 类组件中事件绑定this方式,选用constructor中绑定bind和定义阶段使用箭头函数这两种方式,可避免随着render多次生成新的方法实例。
key值的作用
key的作用是在虚拟DOM树中标识节点,用来追踪那些元素被修改、添加、移除的辅助标识。可用于管理可复用的元素,更高效地更新虚拟DOM。
Vue和React对比
-
相同点
- 都使用虚拟DOM
- 都支持服务端渲染
- 都是数据驱动视图,提供了响应式和组件化的视图组件
- 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库
-
不同点
- 写法不一样。Vue使用模板语法,React使用JSX语法。
- 数据绑定不一样。Vue实现了数据的双向绑定,而React则是单向的。
- 状态管理不一样。Vue状态数据由data管理,而React中状态数据则由state管理,且不能直接修改state状态,需要通过setState去更新。
- 虚拟DOM不一样。Vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树;而React中每当组件的状态改变时,整个组件树都会被重新渲染。
Vuex和Redux对比
-
相同点
整体流程一致。定义全局State,触发,修改State。
-
不同点
- Vuex 以 mutations 代替 Redux 中的 reducer
- Vue 中的数据是可变的,而Redux使用的是不可变数据
Redux每次都是用新的state替换旧的state,而Vuex是直接修改。 - Vuex 支持异步处理;Redux 本身不支持,异步处理需要借助中间件
Vuex: view —> mutations —> state变化 —> view变化(同步操作)
view —> actions —> mutations —> state变化 —> view变化(异步操作)
// redux流程还待完善,不是特别理解
Redux: view —> actions —> reducer —> state变化 —> view变化(同步异步一样。异步中间件?)
Git
常用Git命令
git init // 新建 git 代码库
git status // 显示有变更的文件
git add // 添加指定文件到暂存区
git commit -m [message] // 提交暂存区到仓库区
git push // 上传及快速合并
git pull // 下载及快速合并
git branch // 列出所有分支
git checkout -b [branch] // 新建一个分支,并切换到该分支
git rm // 删除工作区文件,并且将这次删除放入暂存区
git pull 和 git fetch 的区别
git pull 会将远程仓库的变化下载下来,并和当前分支合并。
git fetch 只是将远程仓库的变化下载下来,并没有和本地分支合并。
Linux
cd 进入对应目录
ls 查看目录中的文件
pwd 显示当前所在目录路径
cat 查看文件中的内容
vi 向文件中插入内容
mkdir 创建对应目录
rm -rf 删除目录及其内的文件
网络
HTTP状态码
- 2XX 成功
- 204
没有内容。服务器成功处理请求,但没有返回任何内容。 - 206
部分内容。服务器成功处理了部分GET请求。
- 204
- 3XX 重定向
- 301
永久性重定向 - 302
临时性重定向 - 303
查看其他。请求的资源可以在另一个URI上找到。 - 304
未修改。请求的资源未修改。
- 301
- 4XX 客户端错误
- 400
请求报文存在语法错误 - 401
请求需要身份验证 - 403
请求被服务器拒绝
- 400
- 5XX 服务器错误
- 500
服务器内部错误 - 503
服务器繁忙
- 500
HTTP1.0和1.1区别
- HTTP/1.1 使用持久连接
- HTTP/1.1 节约带宽
- HTTP/1.1 新增host字段
- HTTP/1.1 新增了很多请求方法
- HTTP/1.1 新增了更多缓存控制策略
HTTP1.1和2.0区别
- HTTP/2 采用二进制格式而非文本格式
- HTTP/2 实现了多路复用,而非有序并阻塞的
- HTTP/2 实现了头信息压缩,降低了开销
- HTTP/2 允许服务器未经请求,主动向客户端发送资源
HTTP和HTTPS协议区别
- HTTP协议端口是80;HTTPS的协议端口是443
- HTTP协议不需要CA证书;HTTPS协议需要,费用较高
- HTTP协议信息是明文传输的;HTTPS使用SSL加密传输
常见HTTP请求方法
- Get:向服务器获取数据
- Post:将实体提交到指定的资源,通常会造成服务器资源的修改
- Put:上传文件,更新数据
- Delete:删除服务器上的对象
- Head:获取报文首部,与GET相比,不返回报文主体部分
- Options:询问支持的请求方法,用来跨域请求
- Connect:要求在与代理服务器通信时建立隧道,使用隧道进行TCP通信
- Trace:回显服务器收到的请求,主要⽤于测试或诊断。
HTTP请求报文是什么样的
请求报文由三部分组成:
-
请求行
由请求方法、URL、协议版本三个字段构成。
形如:GET /index.html HTTP/1.1
-
请求头部
请求头部由关键字/值对组成,每行⼀对,关键字和值⽤英⽂冒号分隔 。
常见请求头属性如下:
- Host:请求的服务器地址
- Connection:指定连接类型
- User-Agent:发生请求的应用程序名称
-
请求体
存放Post、Put等请求携带的数据
HTTP响应报文是什么样的
响应报文由三部分组成:
-
响应行
由协议版本、状态码、描述三个字段组成。
形如:HTTP/1.1 200 OK
-
响应头
响应头部由关键字/值对组成,每行⼀对,关键字和值⽤英⽂冒号分隔 。
常见响应头属性如下:
- Server:服务器应用程序软件的名称和版本
- Content-Type:响应正文的类型
- Content-Length:响应正文的长度
-
响应体
服务器响应的数据
GET和POST请求的区别
-
作用不同
GET通常用来获取数据,POST则用来提交数据
-
传输方式不同
GET通过URL传输数据,POST则通过请求体传输数据 -
安全性不同
POST的数据因为在请求体内,所以有⼀定的安全性保证,⽽GET的数据在URL中,通过历史记录,缓存很容易查到数据信息 -
请求长度不同
GET请求在URL中传送的参数是有长度限制的,POST则没有 -
GET无害
GET在浏览器刷新、后退是⽆害的,POST则会再次提交请求
PUT和POST请求的区别
- PUT请求是向服务器端发送数据,从而修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。(可理解为更新数据)
- POST请求是向服务器端发送数据,该请求会改变数据的种类等资源,它会创建新的内容。(可理解为创建数据)
PS:一个栗子是平时贴吧回复的时候,如果网络不好,回帖失败一直点回帖按钮,刷新后会发现回复了好多条一样的帖子,这就是POST请求。而使用PUT就不会出现这种情况。
TCP和UDP协议区别
TCP和UDP都是传输层协议,都属于TCP/IP协议族:
- TCP,传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议
- UDP,用户数据报协议,是一种无连接的协议
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 无连接 |
是否可靠 | 可靠传输,使用流量控制和拥塞控制 | 不可靠传输,不使用流量控制和拥塞控制 |
连接对象个数 | 只能是一对一通信 | 支持一对一,一对多,多对一和多对多交互通信 |
传输方式 | 面向字节流 | 面向报文 |
首部开销 | 首部最小20字节,最大60字节 | 首部开销小,仅8字节 |
适用场景 | 适用于要求可靠传输的应用,例如文件传输、邮件接收 | 适用于实时应用,例如视频会议、网络直播 |
UDP协议为什么不可靠
UDP在传输数据之前不需要先建立连接,远地主机的运输层在接收到UDP报文后,不需要确认,提供不可靠交付。总结就以下四点:
- 不保证消息交付:不确认,不重传,无超时
- 不保证交付顺序:不设置包序号,不重排,不会发生队首阻塞
- 不跟踪连接状态:不必建立连接或重启状态机
- 不进行拥塞控制:不内置客户端或网络反馈机制
TCP三次握手和四次挥手
三次握手:(交朋友流程替换也可)
client:约不约???!!!(syn=1)
server:约约约!!!(ack=1,syn=1)
client:好的,我来找你!!!(ack=1)
四次挥手:(毕业流程替换也可)
client:老子要离职!!!(FIN=1)
server:OK,你要离职是吧,我马上给你走程序!程序完了我通知你!!(ACK=1)
server:程序走完了,文件发给你了,你最后确认一下(FIN=1)
client:OK,确认完毕,关机跑路(ACK=1)
浏览器输入URL,按下回车后发生了什么
- DNS解析
- 建立TCP连接(三次握手)
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 解析HTML,构建DOM树
- 解析CSS,构建CSSOM规则树
- 合并DOM和CSS规则树,生成Render渲染树
- 布局Render树,包括位置、尺寸信息等等
- 绘制Render树,包括页面像素信息等等
- 断开连接(四次挥手)
浏览器
浏览器内核
-
浏览器内核主要分为两部分:渲染引擎和JS引擎
- 渲染引擎用于获取html、css和图片
- JS引擎用于解析执行JavaScript
以下为常见浏览器内核:
浏览器 内核 CSS前缀 IE/Edge Trident内核 -ms- Firefox Gecko内核 -moz- Safari Webkit内核 -webkit- Chrome Blink内核(原 webkit 内核) -webkit- Opera Blink内核(原 presto 内核) -webkit(原 -o-)
浏览器缓存
- 强缓存
不会向服务器发送请求, 直接从缓存中读取资源 - 协商缓存
强缓存失效后, 浏览器携带缓存标示向服务器发起请求, 由服务器决定是否需要使用缓存
浏览器安全
XSS攻击平+/
XSS攻击是指跨站脚本攻击。通过注入恶意代码来攻击目标网站,使之在用户的浏览器上运行,从而盗取用户的信息。
XSS攻击的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
XSS攻击可分为存储型、反射型和DOM型:
- 存储型:通过将恶意代码存储在服务器上
- 反射型:通过URL传参的方式来攻击
- DOM型:通过插入恶意HTML代码
如何对XSS攻击进行防范:
- 转义字符。对用户输入进行充分转义。
- CSP安全内容策略。建立白名单,告诉浏览器哪些外部资源可以加载和执行。
CSRF攻击
CSRF攻击是指跨站请求伪造攻击。攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
CSRF攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
CSRF攻击可分为GET型、POST型和链接类型:
- GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
- POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
- 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
如何对CSRF攻击进行防范:
- 进行同源检测
- 对Cookie进行双重验证
- 使用CSRF Token进行验证
- 在设置Cookie属性的时候设置Samesite,限制Cookie不能作为被第三方使用
中间人攻击
中间⼈攻击是指攻击者与通讯的两端分别创建独立的联系, 并交换其所收到的数据, 使通讯的两端认为他们正在通过⼀个私密的连接与对方直接对话, 但事实上整个会话都被攻击者完全控制。在中间⼈攻击中,攻击者可以拦截通讯双方的通话并插⼊新的内容。
浏览器存储
- Cookies
- 大小为4K左右
- 每个域名下限制20个左右
- 会随请求发送到服务器
- LocalStorage
- 大小为5M左右
- 永久存储,除非手动清除
- 不会随请求发送到服务器
- SessionStorage
- 大小为5M左右
- 临时存储,仅在当前会话下有效,关闭页面或浏览器后即被清除
- 不会随请求发送到服务器
WebSocket
WebSocket是H5新增的,用于实现浏览器与服务器之间的通信功能。
特点:异步操作、事件驱动、能够实现真正意义上的推送功能
缺点:少部分浏览器不支持
跨域方案
- jsonp
- nginx
- CORS
- vue内置代理
- springboot注解
性能优化
-
使用CDN
-
图片懒加载
懒加载可以加快网页访问速度,减少请求,实现思路就是判断图片元素是否可见来决定是否加载图片。当图片位于浏览器视口 (viewport) 中时,动态设置
<img>
标签的 src 属性,浏览器会根据 src 属性发送请求加载图片。首先不设置 src 属性,将图片真正的 url 放在另外一个属性 data-src 中,在图片即将进入浏览器可视区域之前,将 url 取出放到 src 中。懒加载的关键是如何判断图片处于浏览器可视范围内。
<div class="container"> <img src="loading.gif" data-src="pic.png"> <img src="loading.gif" data-src="pic.png"> <img src="loading.gif" data-src="pic.png"> <img src="loading.gif" data-src="pic.png"> <img src="loading.gif" data-src="pic.png"> <img src="loading.gif" data-src="pic.png"> </div> <script> var imgs = document.querySelectorAll('img'); function lazyLoad(){ var scrollTop = document.body.scrollTop; var clientHeight = window.innerHeight; for(var i=0;i < imgs.length;i++){ if(imgs[i].offsetTop < scrollTop + clientHeight ){ imgs[i].src = imgs[i].getAttribute('data-src'); } } } window.onscroll = lazyLoad(); </script>
-
节流防抖
-
使用雪碧图
-
压缩CSS、JS文件
数据交互
Ajax
Ajax创建步骤
// 创建XMLHttpRequest对象
var ajax = new XMLHttpRequest();
// 使用open方法创建一个Http请求。
ajax.open('GET', url, true);
// 如果是post请求,需要设置请求头
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 发送Http请求(如果不需要参数,就写null)
ajax.send(null);
// 接收服务器响应数据
ajax.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功时
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
var ajax = new XMLHttpRequest();
ajax.open("get||post", url, true||false);
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ajax.send(null);
ajax.onreadystatechange = callBack;
Promise
了解Promise吗
Promise是一种异步编程的解决方案,有三种状态,pending(进行中)、resolved(已完成)、rejected(已失败)。
Promise的特点是只有异步操作的结果,可以决定当前是哪一种状态,其他任何操作都无法改变这个状态。
Promise解决的痛点
- 回调地域问题
- 代码的可读性问题
- 信任问题
Promise如何使用
- 创造一个Promise实例
- 实例生成后,可以用then方法分别指定resolved状态和rejected状态的回调函数
- 可以用try和catch方法预防异常
new Promise((resolve, reject) => {
let num = Math.random()*2
if(num < 1){
resolve('success')
}else{
reject('error')
}
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
Axios
Axios是什么
Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。
Axios的特点
- 支持Promise API
- 可以拦截请求和响应
- 可以转换请求数据和响应数据
- 安全性更高,客户端支持防御CSRF
Axios为什么既能在浏览器环境运行又能在服务器(node)环境运行
- 在浏览器端使用
XMLHttpRequest
对象发送ajax请求 - 在node环境使用
http
对象发送ajax请求
Axios示例
axios.get('/user', {
params: {
ID: 12
}
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
-----------------------------------
axios.post('/user', {
firstName: 'Violet',
lastName: 'Evergarden'
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
// 请求拦截器
// 在请求发送前进行一些操作。例如在每个请求体里加上token。
axios.interceptors.request.use((config) => {
return config;
}, (err) => {
return Promise.reject(error);
})
-----------------------------------------------------
// 响应拦截器
// 在响应接收后进行一些操作。例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页。
axios.interceptors.response.use((res) => {
return response;
}, (err) ={
return Promise.reject(error);
})
1 2
3 4