highlight: gruvbox-dark theme: qklhk-chocolate
1.css盒子模型
当我们对页面进行布局的时候 浏览器的渲染引擎会根据标准之一的CSS基础盒子模型(CSS basic box model) 将所有的元素都表示为一个矩形的盒子(box)
一个盒子由content、padding、border、margin组成
content,即实际内容,显示文本和图像
boreder,即边框,围绕元素内容的内边距的一条或多条线,由粗细、样式、颜色三部分组成
padding,即内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background属性影响
margin,即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域
盒子模型分为W3C标准盒子模型和IE怪异盒子模型
W3C盒子设置的width和height 只代表内容宽高 不包含padding和border的值
盒子的宽等于width+padding+border+margin
IE盒子模型设置宽高会包含padding和border 盒子总宽高等于 width+margin
Box-sizing属性可以设置当前盒子遵循那个盒子模型的规范
content-box 默认值,元素的 width/height 不包含padding,border,与标准盒子模型表现一致
border-box 元素的 width/height 包含 padding,border,与怪异盒子模型表现一致
inherit 指定 box-sizing 属性的值,应该从父元素继承
2. display: none 和 visibility:hidden 的区别
Display:none和visibility:hidden都会将元素隐藏 但是visibility:hidden将元素隐藏后元素所占的为位置还会在 而display:none不会
3.W3C 规范是什么?
W3C规范是万维网联盟制定的web标准,网页主要有三部分组成: 结构,行为,表现,对应的标准也分三方面:结构化标准语言主要包括XHTML和XML,表现标准语言主要包括CSS,行为标准主要包括对象模型(如W3C DOM)、ECMAScript等
4.less和sass的区别
Sass和less的主要区别就是less是基于JavaScript的客户端处理所以安装的时候要通过npm,而sass是基于ruby,所以在服务端处理 less的变量是@ sass的变量是$定义
5. 元素水平垂直居中的方法有哪些?如果元素不定宽高呢?
1.第一种方法可以使用定位+margin:auto的方式实现
通过给父元素设置相对定位,而给子元素设置绝对定位,并且四个定位属性的值都设置了0,再给子元素设置宽高,宽高这时候再给它一个margin:auto它就可以上下左右都居中了
2.第二种方法 通过flex布局
给父元素设置display:flex,justify-content:设置为center 实现元素的左右居中 设置align-items:center实现垂直居中
3.第三种方法 通过grid布局 用法同flex布局一样
4.第三种方法 table布局
设置父元素为display:table-cell,子元素设置 display: inline-block。利用vertical和text-align可以让所有的行内块级元素水平垂直居中
5.使用margin:50% 50% 实现上下左右都居中
6.利用绝对定位和transform
将元素的 position 设置为 absolute,top 和 left 设置为 50%,然后使用 transform 属性的 translate(-50%, -50%) 来使元素在垂直和水平方向上居中。
.element {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
6.什么是回流和重绘
在HTML中,每个元素都可以被视为一个盒子在浏览器解析过程中,会涉及到回流和重绘
回流(也叫重排):在对页面的布局或者盒子的大小进行修改的时候 就会引起回流
重绘:只修改元素的颜色、背景等不影响页面布局的情况下会引起重绘
回流必定会引发重绘,在页面初次加载的时候、必定会引发一次回流和重绘
7.响应式布局的方案
1.可以通过rem进行响应式布局
给html设置font-size,页面中的元素单位全部使用rem,在页面大小变化的时候,根据窗口大小改变html的字体大小
2.百分比
所有的元素单位都使用百分比来进行布局
3.vh/vw
使用vh和vw,100vh等于一屏的高度
4.媒体查询
概念:为不同尺寸的屏幕设置不同的 CSS 样式
当屏幕符合时,会使用里面的样式,如果外部设置过,则会替换里面相同的样式,不同的样式会继承
@media 常用参数
| 属性名 | 作用 |
|---|---|
| width、height | 浏览器可视宽度、高度 |
| device-width | 设备屏幕的宽度 |
| device-height | 设备屏幕的高度 |
用法一
/* 屏幕小于 300 px */
@media screen and (max-width: 300px) {
div {
background-color:red;
}
}
/* 屏幕大于 300 px */
@media screen and (min-width: 300px) {
div {
background-color:yellow;
}
}
/* 屏幕小于 300px 大于 200px */
@media screen and (min-width: 200px) and (max-width: 300px){
div {
background-color:blue;
}
}
用法二 内联样式
<style media="(min-device-width:200px) and (max-device-width:300px)">
div {
background-color:blue;
}
</style>
<style media="(min-device-width:300px) and (max-device-width:400px)">
div {
background-color:yellow;
}
</style>
用法三 行内样式
<link href="css/test1.css" rel="stylesheet"></link>
// 当浏览器宽度大于 200px 时引入 test2.css
<link href="css/test2.css" rel="stylesheet" media="(min-width:200px)"></link>
5.自适应布局
不同的设备使用不同的页面
8.CSS相关的优化
1.放在顶部
2.不使用@import
3.异步加载CSS
4.利用webpack将css压缩为一个css文件
5.使用内联加载关键css
9.DOM、BOM对象
BOM:浏览器对象模型,客户端和浏览器端窗口操作的基础(操作浏览器窗口的),可以使用BOM对浏览器窗口进行访问和操作,比如移动窗口位置、返回历史记录等等,但是BOM没有一个成型的规范,但是所有的浏览器都支持。
DOM:文档模型对象,提供了一系列的应用程序接口(api),供我们开发者对DOM的添加删除修改等
10.BFC是什么?
BFC是css布局时的一个概念,它是一个块级格式化上下文,是页面中的一块渲染区域,它内部的元素不会影响到外部的元素,是一个完全独立的空间,可以用来清除浮动 开启BFC模式 overflow:hidden
11.Position每个属性的区别
默认值是static,没有定位,遵循默认的文档流,忽略left、top、right、bottom的作用
fixed 固定,脱离文档流不占据空间
absolute 绝对定位,相对于页面根元素body,元素会脱离文档流
relative 相对定位 相对于父元素进行定位
sticky 基于用户的滚动位置来定位,在position和fixed之间来回切换;
当页面滚出目标区域时,它会像fixed一样固定在目标位置;
不滚动时,其行为与相对定位相同;
滚动后,top,left,bottom,right生效,但margin会失效;
12.rgba和opaity的区别
rgba和opacity都可以让元素消失 rgba作用与元素背景色的透明度 而opaity作用于整个元素的透明度
13.常见的行内元素有哪些
1.span标签
2.a标签
3.b标签
4.img
5.input
6.select
14.常见块级元素
1.div
2.ul li
3.ol li
4.p标签
5.h1-h6
15.检测页面渲染-前端性能监控
window.performance 参考(www.jianshu.com/p/1355232d5…)
16.实现文字环绕效果
17.两栏布局
18.三栏布局
19.浮动和定位的区别
float浮动会导致元素脱离文档流,准确的说,float浮动属于半脱离文档流
float浮动跟position:absolute一样拥有脱离文档流的功能,但是float虽然脱离了文档流但是仍然会占据位置,其他的文本内容会按照顺序继续排列——如果你对所有的元素都设置了浮动,你会看到这几个div并不会重叠,而是会顺序排列。可以参考设置display:none,跟visible:hidden的效果
浮动和定位的区别:
- 浮动还会占据原来的位置
- 定位会脱离文档流,不占据原来的位置
如果三个元素均设置了浮动,则按顺序位置并排在一排,产生的问题是会使父元素(没设置宽高情况下)高度坍塌从而使边框合并,解决的方法就是清除浮动。 若第二个元素设置了宽高,则第三个元素浮动会漂浮在宽高之外。
20.清除浮动的方法
为什么要清除浮动?
清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题
1. 父级添加overflow属性(父元素添加overflow:hidden)通过触发BFC方式,实现清除浮动
.fahter{
width: 400px;
border: 1px solid deeppink;
overflow: hidden;
}
优点:代码简洁
缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素
2.使用after伪元素
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
<body>
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
<!--<div class="clear">额外标签法</div>-->
</div>
<div class="footer"></div>
</body>
优点:符合闭合浮动思想,结构语义化正确
缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout.
3.使用before和after双伪元素清除浮动
.clearfix:after,.clearfix:before{
content: "";
display: table;
}
.clearfix:after{
clear: both;
}
.clearfix{
*zoom: 1;
}
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
<div class="footer"></div>
优点:代码更简洁
缺点:用zoom:1触发hasLayout.
4.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.fahter{
width: 400px;
border: 1px solid deeppink;
}
.big{
width: 200px;
height: 200px;
background: darkorange;
float: left;
}
.small{
width: 120px;
height: 120px;
background: darkmagenta;
float: left;
}
.footer{
width: 900px;
height: 100px;
background: darkslateblue;
}
.clear{
clear:both;
}
</style>
</head>
<body>
<div class="fahter">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">额外标签法</div>
</div>
<div class="footer"></div>
</body>
</html>
优点:通俗易懂
缺点:无意义的额外标签,影响语义化
21.HTML5新增了那些新特性?
1. 增了一些语义化标签,例如:header、footer、section、article等
2. 增媒体元素,audio、video、audio和video标签能够很容易的输出音频或视频流,提供便利的获取文件信息的API
3. 新增了canvas API 提供了一个通过JavaScript 和 HTML的canvas元素来绘制图形的方式。它可以用 于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
4. 强型的表单,input支持更多的类型,例如:color、number、date、video、url等
22.F5刷新和ctrl+F5刷新的区别?
F5刷新只是将当前界面进行重新渲染,相当于回流
ctrl+F5则会将当前页面的缓存清除,相当于第一次进入页面
23.文本溢出省略号怎么做?
.text{
overflow:hidden,
text-overflow:ellipsis,
line-clamp:2,
}
24.CSS有哪些选择器
1.id选择器 (#box) 选择所有id为box的元素
2.类选择器 (.box) 选择所有类名为box的元素
3.标签选择器 (div) 选择标签为div的所有元素
4.子代选择器 (div > p) 选择标签为div中的所有p元素
5.相邻同胞选择器(.one+.two),选择紧接在.one之后的所有.two元素
6.群组选择器(div,p),选择div、p的所有元素
25.伪元素和伪类的区别
伪类(pseudo-class) 是CSS选择器的一种,用于选择元素的某个状态,如:hover可以用来选择鼠标悬停在元素上的状态,:active可以用来选择元素被激活的状态等。
伪元素(pseudo-element) 是用来表示HTML元素的某个部分的特殊的CSS选择器,它们以::开头,通常被用来向某个元素添加特殊的样式和内容,如在某个元素的前面或后面插入一些内容,或者为某个元素的第一个字母设置样式等。 因此,伪元素和伪类的主要区别在于,伪元素表示元素的某个部分,而伪类表示元素的某个状态。
伪类常用的包括:
- :hover 鼠标悬停状态。
- :active 元素被激活状态。
- :focus 元素获取焦点状态。
- :visited 已访问过的链接状态。
- :nth-child(n) 选择第n个子元素。
- :first-child 选择第一个子元素。
- :last-child 选择最后一个子元素。
- :not(selector) 选择所有不匹配给定选择器的元素。
伪元素常用的包括:
- ::before 在元素内部的前面添加内容。
- ::after 在元素内部的后面添加内容。
- ::first-line 选择元素的第一行文本。
- ::first-letter 选择元素的第一个字母。
- ::selection 选择被用户选中的部分。
在CSS3中还新增了一些伪类和伪元素,如:
- :nth-of-type(n) 选择同类型兄弟元素中的第n个元素。
- :last-of-type 选择同类型兄弟元素中的最后一个元素。
- :empty 选择没有子元素的元素。
- ::placeholder 为表单元素的placeholder设置样式。
- ::backdrop 为元素内部的背景设置样式。
26.clear属性的作用
clear : none | left | right | both
对于CSS的清除浮动(clear),一定要牢记:这个规则只能影响使用清除的元素本身,不能影响其他元素。
27.如何画一条0.5px的线
用css样式实现一个0.5像素的线条,主要考察了transform 中scale的缩放属性 ,其中scale可以延申为scaleX和scaleY
width: 100px;
height: 1px;
background: red;
transform: scaleY(0.5);
28.如何画一个三角形
1.使用 border 实现三角形应该是大部分人都掌握的,也是各种面经中经常出现的,利用了高宽为零的容器及透明的 border 实现。
div {
border-top: 50px solid yellowgreen;
border-bottom: 50px solid deeppink;
border-left: 50px solid bisque;
border-right: 50px solid chocolate;
}
这样,让任何三边的边框的颜色为 transparent,则非常容易得到各种角度的三角形:
2.使用 linear-gradient 绘制三角形
接着,我们使用线性渐变 linear-gradient 实现三角形。
它的原理也非常简单,我们实现一个 45° 的渐变:
div {
width: 100px;
height: 100px;
background: linear-gradient(45deg, deeppink, yellowgreen);
}
让它的颜色从渐变色变为两种固定的颜色:
div {
width: 100px;
height: 100px;
background: linear-gradient(45deg, deeppink, deeppink 50%, yellowgreen 50%, yellowgreen 100%);
}
再让其中一个颜色透明即可:
div {
background: linear-gradient(45deg, deeppink, deeppink 50%, transparent 50%, transparent 100%);
}
3. transform: rotate 配合 overflow: hidden 绘制三角形
这种方法还是比较常规的,使用 transform: rotate 配合 overflow: hidden。一看就懂,一学就会,简单的动画示意图如下:
设置图形的旋转中心在左下角 left bottom,进行旋转,配合 overflow: hidden。
.triangle {
width: 141px;
height: 100px;
position: relative;
overflow: hidden;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: deeppink;
transform-origin: left bottom;
transform: rotate(45deg);
}
}
JS部分
1.JS有哪些数据类型?
基本数据类型
Number
String
null
undefined
Boolean
Symbol
Bigint
引用数据类型
标准普通对象 Object
标准特殊对象 Array Date Math RegExp Error...
非标准特殊对象 Number String Boolean
可调用/可执行 function
2.数据类型的检测方式有哪些?优缺点有哪些?
1.typeof 检测数据类型最常用的方式
缺点:没有办法细分对象,只要是引用数据类型(除了function),检测结果都是Object,检测null结果也是Object 这是因为JS中的对象都是以二进制存储的,而null存储为二进制前三位都是0,JS会判断为是对象所以会出现这样的情况。
2.instanceof 用于检测一个对象是否在一个对象的原型链上
缺点:只能用于引用数据类型
3.constructor 指向当前对象的构造函数本身
缺点:constructor可以被更改
4.Object.prototype.toString.call() 检测数据类型最好的方式
缺点:无法细分谁是谁的实例
3.instanceof的原理?
function myIntanceof(L, R) {
let RP = R.prototype,
LP = L.__proto__;
while (true) {
if (LP == null) {
return false
} else if (LP == RP) {
return true
}
LP = LP.__proto__;
}
}
4.==和=== 有什么区别?
== 简单比较,两边类型相同,比较大小 两边类型不同,根据下方表格,再进一步进行比较
- Null == Undefined ->true
- String == Number ->先将String转为Number,在比较大小
- Boolean == Number ->现将Boolean转为Number,在进行比较
- Object == String 会将对象转为字符串 ===是严格比较 会比较等号两边的数据类型和值 有一项不相等就会为false
5.NaN === NaN吗?
不等于,因为NaN不与任何值相等 包括他自己
6.手写call、apply、bind
call 临时改变this指向 只能接收一个参数 不能接收数组
let obj = {
name: 'leizai',
age: 20
}
function fn() {
console.log(this);
}
Function.prototype.myCall = function (target) {
/* 判断当前调用这个函数的this是否是一个函数 */
if (typeof this != 'function') throw new TypeError('Not a Function');
/* 不传参数的话默认为window */
target = target || window;
/* 保存当前this */
let fn = this
/* 在target上挂载当前this */
target.fn = fn;
/* 接收返回值并传参 此时是target调用的fn 此时的this就是target*/
let res = target.fn(arguments[1]);
/* 只需要改变一次this指向 调用完后删除 */
delete target.fn
return res
}
fn.myCall(obj, 1);
apply 临时改变this指向 与call的唯一区别就是接收的参数是一个数组
Function.prototype.myApply = function (target) {
/* 判断当前调用这个函数的this是否是一个函数 */
if (typeof this != 'function') throw new TypeError('Not a Function');
if(!Array.isArray(arguments[1])) throw new TypeError('Parameter is not an array')
target = target || window;
let fn = this
target.fn = fn;
let res = target.fn(arguments[1]);
delete target.fn
return res
}
bind 永久改变this指向 返回一个新函数
Function.prototype.myBind = function (target, ...arg) {
let fn = this;
return function (...arg1) {
return fn.call(target, ...arg, ...arg1);
}
}
7.字面量创建的对象和new创建的有什么区别?new内部做了些什么
let obj = {};
简单易懂,不需要作用域解析,速度更快
new内部
首先会在堆内存中开辟一块内存
将这个对象的__proto__隐式原型指向所属类的原型
将this指向这个新创建的对象
判断执行的返回结果,如果返回值是引用数据类型,则返回这个引用数据类型,如果是基本数据类型,则返回新创建的这个对象
手写new
function myNew(target, ...arg) {
let obj = {};
obj.__proto__ = target.prototype;
let res = target.call(obj, ...arg);
return typeof res == "object" ? res : obj;
}
function Person(...value) {
console.log(value);
this.name = 'hello'
this.age = '20'
}
console.log(myNew(Person, 1, 2, 3, 4));
8.什么是作用域?什么是作用域链?
规定变量和函数可以作用的范围就是作用域, JS中有全局作用域和局部作用域 当我们在使用一个变量的时候,会先在当前作用域查找,当前作用域没有就会向上级作用域查找,直到找到window上,这个机制就是作用域链
9.谈谈你对面向对象的理解
面向对象简称OO,是现在广泛使用的一种编程方式,简单来说就是将程序封装成一个个独立的模块或单元,最后组成一个完整的程序,这种方式使得程序的耦合度变低。代码更好维护。
**举个栗子:**就像一台电脑就是由不同的配件组成(显卡、CPU、内存...),当其中的一个配件损坏了以后,只需要更换坏掉的配件即可,不会影响其他配件。
10.堆栈内存
在浏览器打开页面的时候,会开辟堆栈内存两个空间,栈中的内存是自动分配的,而堆内存则是动态分配的
在堆内存(Heap)中浏览器会在堆内存的顶部创建一个全局对象GO(Global Object)用来存储浏览器自带的API和属性,这个对象就是window。在全局用var声明的变量也会放在window中,而在堆内存的底部就是用来存放引用数据类型的
栈内存(stack) 执行上下文又分为全局执行上下文和函数执行上下文
在栈内存的底部永远是EC(g),EC(g)是全局执行上下文,在EC(g)中有一个VO对象用来存储在全局作用域下用let、const声明的变量,VO里会默认有一个变量window执行指向堆内存中的GO对象
而在栈顶是函数执行上下文EC(fn),函数每次执行都会创建一个函数执行上下文,在EC(fn)中有一个AO对象,用来存储函数中定义的私有变量和形参
栈内存的特点: 函数执行先进后出
11.js文件过大导致首屏加载慢怎么解决?
1.把JS文件进行压缩 compression-webpack-plugin,开启gzip压缩
2.通过cdn引入
3.通过路由懒加载将每个组件的js文件进行分割
12.css兼容性
通过postcss-loader postcss-preset-env自动添加浏览器对应的私有前缀
2.通过CDN进行引入
13.项目开发流程?几个人员?
1.产品设计
产品经理收集用户的客户、领导的需求,完成产品设计
2.技术选型
选择适合该项目的技术栈
3.项目分工
UI 部门开始准备产品设计图
开发部门商讨开发计划并整理出接口文档
4.项目开发
5.项目测试
测试部门对产品进行测试,整理出bug文档,开发部门各自认领自己负责的功能点的bug并修改
6.项目构建
测试完成后,项目提交到生产环境,通过构建工具,webpack,Gulp等对项目进行压缩合并等操作
7.项目上线
将打包好的dist文件给后端,后端进行部署,运维负责上线任务
项目人员安排
一般一个项目会由一名产品经理、UI设计师、前端、后端、测试来组成(五人组);如果是做APP的话,就需要iOS、Android工程师。
12.如何判断当前是浏览器环境还是node环境?
判断window对象是否存在
13.如何判断当前是哪个浏览器
window.navigator.userAgent
14.es6module与commonJS的区别
1.nodeJS遵循的是conmmonJS的规范,因为CommonJS模块的require语法是同步的,导致CommonJS模块规范只适合用在服务端。因为如果在浏览器端使用同步的方式加载一个模块,需要有网络来决定快慢,响应时间可能会很长,而es6Module无论是在浏览器端还是服务端都是可以使用的,但是在服务端使用需要遵循特殊规则
2.输出方式的不同,commonJS的require输出的是模块的拷贝,而ES6Module输出的则是值的引用,模块内部发生变化的时候,commonJS不会改变已经引用的模块,而ES6Modlue在加载时遇到import就会生成一个只读引用,等加载时再去找对应的模块
3.commonJS是在运行时加载,ES6Module是在编译时加载
4.ES6Module支持引用commonJS模块,commonJS不支持引用ES6Module模块
15.浅拷贝和深拷贝
浅拷贝: 浅拷贝就是只拷贝当前对象的第一层,更深层次的对象只进行引用。会出现值被修改的问题。
浅拷贝的方法
1.Object.assing() ES6新增 浅拷贝对象
2.利用for in循环
let obj = {
name:'小明',
age:18,
}
/*拷贝给当前对象*/
let newObj = {}
for(var key in obj){
newObj[key] = obj[key]
}
实现深拷贝的方法
深拷贝: 深拷贝则是将一个对象进行完全拷贝,无论层级。等于复制出了一个一模一样的对象,但是这两个对象之间不会互相影响。
1. JSON.parse(JSON.stringify(对象))
但是这种方法也有不少坏处,比如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
2.直接使用var newObj = Object.create(oldObj),也可以达到深拷贝的效果。
3.使用递归进行处理
const deepCopy = function deepCopy(ObjectOrArray) {
let newObj = null;
/* 当前是对象的情况 */
if (Object.prototype.toString.call(ObjectOrArray) == '[object Object]') {
newObj = {};
for (const key in ObjectOrArray) {
newObj[key] = deepCopy(ObjectOrArray[key])
}
/* 当前是数组的情况 */
} else if (Object.prototype.toString.call(ObjectOrArray) == '[object Array]') {
newObj = [];
ObjectOrArray.forEach(key => {
/* 递归向数组中添加数据 */
newObj.push(deepCopy(key))
});
} else {
newObj = ObjectOrArray
}
return newObj
}
let obj = {
a: [1, 2, 3],
b: {
d: [5, 6, 7],
c: {
e: 3,
f: 8,
},
}
},
arr = [675, 453, 543, 543, 423, 423]
let res = deepCopy(obj)
console.log(res);
16.判断是否是数组的方法有那些?
1.isArray
2.instanceof
3.object.prototype.tostring.call()
17.正则捕获url
let str = '<a htef="http://www.baidu.com">',
reg = /(http:\/\/[A-Za-z\.]+)/g
str.replace(reg, ($1, $2) => {
console.log($1,$2);
})
18.session和cookie的区别
cookie保存在客户端,session保存在服务端 当你登陆一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上,客户端每次请求服务器的时候会发送当前会话sessionid,服务器根据当前sessionid判断相应的用户数据标志,以确定用户是否登陆或具有某种权限。由于数据是存储在服务器上面,所以你不能伪造。
sessionid是服务器和客户端连接时候随机分配的,如果浏览器使用的是cookie,那么所有数据都保存在浏览器端,比如你登陆以后,服务器设置了cookie用户名,那么当你再次请求服务器的时候,浏览器会将用户名一块发送给服务器,这些变量有一定的特殊标记。服务器会解释为cookie变量,所以只要不关闭浏览器,那么cookie变量一直是有效的,所以能够保证长时间不掉线
区别
(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性 能方面,应当使用COOKIE
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中
19.ES6有哪些新特性
1.let、const的增加,让开发过程中变量的使用变得更加完善和规范
2.ES6Module模块化导入
3.原始值类型中新增了symbol(唯一值)和Bigint(大数)
4.增加了Class类
5.函数被允许为参数设置默认值
6.对象的结构赋值语法
7.模板字符串在使用 ``反引号 包裹的字符串中用 ${} 符号使用变量
8.展开运算符 ...[] 可以将数组进行展开
9.新增了set和map数据结构
20.JS中的同步异步
进程和线程
每启动一个应用就相当于一个进程,一个进程可以包含多个线程。所以浏览器是多线程的,但是浏览器只分配一个线程去解析JS代码,所以JS是单线程的,所以JS中大部分是同步编程,比如循环、函数执行、递归等 所以,一旦写了死循环就会导致代码停止向下执行,JS引擎就会一直被占用。 抛出错误也会导致程序停止执行(所以遇到不稳定的代码一般使用try/catch包裹起来,防止程序停止运行)
但是JS中也存在 “异步编程” :依托于浏览器多线程,在基于EventLoop事件循环机制
异步任务分为异步宏任务和异步微任务
异步宏任务:
- 定时器:setTimeOut、setInterval
- 事件绑定
- ajax/fetch
- ...
异步微任务:
1.async/await
2.requestAnimationFrame(动画)
3.Promise.then/catch/finally
4.IntersectionObserver(获取元素与窗口的交叉状态)
5....
EventLoop事件循环机制
//浏览器打开一个页面,除了开辟堆栈内存,还会默认创建两个队列
//+webAPI队列:监测异步任务是否可以执行
//+EventQueue队列:存储所有可执行的异步任务,在这个队列中排队等待执行
//@1 JS代码执行过程中,遇到异步任务,会把其放在webAPI中去监听:监听啥时候可以执行
//+ 此过程需要浏览器分配其他线程帮助我们进行监测才可以
//@2 如果webAPI监测的某个异步任务可以执行了,也不会立即拿到栈中去执行;而是先把其放在EventQueue中排队等着
//@3 当栈中所有"同步任务"都执行完,主线程空闲下来的时候,再去EventQueue中,把正在排队的异步任务拿出来执行
//+微任务优先级高于宏任务:如果排队的微任务,则先执行微任务,不论它是啥时候进来的!!
//+同类型的任务,优先执行队列开始的
//特殊:异步任务也是拿到栈中,交给主线程执行.所以,如果执行中出现死循环,主线程一样会被占用,其他异步任务也无法再执行了
21. [1,2,3].map(parseInt)返回什么?
答:返回[1,NaN,NaN]
解答:
MDN对parseInt的解释:developer.mozilla.org/en-US/docs/…
parseInt是JS中的内置对象:
语法:parseInt(string, radix)
作用: 解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。
参数:
string:
要被解析的值。如果参数不是一个字符串,则将其转换为字符串 (使用 ToString抽象操作)。字符串开头的空白符将会被忽略。
radix(可选):
从 2 到 36 的整数,表示进制的基数。例如指定 16 表示被解析值是十六进制数。如果超出这个范围,将返回 NaN。假如指定 0 或未指定,基数将会根据字符串的值进行推算。注意,推算的结果不会永远是默认值 10
第一项: 从上面就可以得知数组中的第一项是1,索引为0。radix传0的话会自己进行推算或者使用默认值10,所以会返回1
第二项: 而第二项值为2,索引为1。由于不存在1进制,所以会将数字转为NaN
第二项: 而第二项值为3,索引为2。
22.防抖和节流
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
function debounce(func, wait) {
let timeout;
return function () {
let context = this; // 保存this指向
let args = arguments; // 拿到event对象
clearTimeout(timeout)// 清除定时器
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
时间戳写法
function throttled1(fn, delay = 500) {
/*外部声明变量-获取进入函数时的时间*/
let oldtime = Date.now()
return function (...args) {
/*内部声明变量-获取执行回调函数的时间*/
let newtime = Date.now()
/*两个时间戳相减如果等于传入函数的时间,则使用apply执行函数*/
if (newtime - oldtime >= delay) {
fn.apply(null, args)
oldtime = Date.now()
}
}
}
定时器写法
function throttled2(fn, delay = 500) {
/*定义变量-用于接收定时器*/
let timer = null
return function (...args) {
/*判断timer是否为null*/
if (!timer) {
/*将定时器赋值给timer并执行函数,执行完毕后重新将timer赋值为null方便下次执行*/
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay);
}
}
}
区别:
防抖是在指定时间内重新触发函数则重新开始计时,防止了函数和接口被频繁触发,更适合用于表单提交、登录验证等功能上
节流是在指定时间内只将函数执行一遍,达到减少执行函数执行次数的目的,所以跟适合用于像onscroll、onmove这类触发频繁的事件
23.if(a==1&&a==2&&a==3){} 如何才会成立
方法一 重写valueOf
var a = {
value:1,
valueOf:()=>{
return ++a.value
}
}
方法二 构建对象的私有属性方法toString()
var a = [1,2,3];
a.toString = a.shift;
//此时的a.toString()调用的不再是Object.prototype.toString(),而是自己私有的属性方法toString();
if(a == 1 && a == 2 && a ==3) {
console.log("OK")
}
方法三 利用Object.defineProperty
Object.defineProperty(window,'a',{
get:function() {
//this指向window.a
this.value ? this.value++ : this.value = 1;
return this.value;
}
})
if(a == 1 && a == 2 && a ==3) {
console.log("OK")
}
24.++在前与++在后的区别
++在前会先进行累加再进行计算,++在后会先进行计算再进行累加
25.map与Object的区别
不同点
在Object中的key只能是基本数据类(string、Bigint、number)
而在map中的key可以是Javascript支持的任何数据类型
元素顺序
Map 元素的顺序遵循插入的顺序,而 Object 的则没有这一特性。
继承
Map 继承自 Object 对象。
创建实例
Object
let obj = {}
let obj1 = new Object({})
let obj2 = Object.create(null)
map仅可以通过构造函数
let mapCase = new Map([[1, 2], [2, 3]]); // map = {1 => 2, 2 => 3}
map有自己的方法进行增删改查
map.get(访问数据)、map.set(添加数据)、map.has(判断数据是否存在)、map.delete(删除数据)、map.size(获取数据大小)、map.cler(全部删除)
object通过 . 和 [] 访问数据,通过delete删除数据(Object删除数据仅是解除绑定,map则是真正的删除) Object新增
obj['key'] = value;
obj.key = value;
map可以被迭代,Object不可以
console.log(typeof obj[Symbol.iterator]); // undefined
console.log(typeof map[Symbol.iterator]); // function
何时使用 Map ,何时使用 Object?
虽然Map 在很多时候优于 Object,但是作为 JavaScript 最基础的数据类型,还是有很多情景更适合使用 Object。
- 当所要存储的是简单数据类型,并且 key 都为字符串或者整数或者 Symbol 的时候,优先使用 Object ,因为Object可以使用 字符变量 的方式创建,更加高效。
- 当需要在单独的逻辑中访问属性或者元素的时候,应该使用 Object,例如:
var obj = {
id: 1,
name: "It's Me!",
print: function(){
return `Object Id: ${this.id}, with Name: ${this.name}`;
}
}
console.log(obj.print());//Object Id: 1, with Name: It's Me.
// 以上操作不能用 Map 实现
- JSON 直接支持 Object,但不支持 Map
- Map 是纯粹的 hash, 而 Object 还存在一些其他内在逻辑,所以在执行 delete 的时候会有性能问题。所以写入删除密集的情况应该使用 Map。
- Map 会按照插入顺序保持元素的顺序,而Object做不到。
- Map 在存储大量元素的时候性能表现更好,特别是在代码执行时不能确定 key 的类型的情况。
26.数组去重的方法
1.利用ES6新增的数据结构进行去重
缺点:set去重会将数组顺序打乱,因为set是无序的,set在处理数据量特别大的情况下会占用较多的内存, 会导致加载缓慢,慎用
let arr = [1,1,3,5,6,6,7,7]
arr = Array.form(new Set(arr)) 或者 [...new Set(arr)]
2.利用对象去重
let arr = [1,1,3,5,6,6,7,7]
function NoRepeat(array) { //封装代码
var obj = {};
for (var i = 0; i < array.length; i++) {
var item = array[i]; //先把每一项的值拿出来
if (typeof obj[item] == 'undefined') { //使用typeof检测表达式的结果的数据类型 结果为字符串
obj[item] = item; //在对象中属性名与属性值是一样的
} else {
array.splice(i, 1); //splice() 删除数据 第一参数:要删除的数据位置 第二参数:要删除的个数
i--; //解决数组下标塌陷
}
}
return array
}
3.利用数组去重
let ayy = [5, 4, 5, 1, 1, 5, 4, 6, 8, 6, 8, 8, 8, 9, 9, 9, ];
let aya = [];
for (var i = 0; i < arr.length; i++) {
var res = arr[i];
if (!ary.includes(res)) {
ary.unshift(res);
} else {
arr.splice(i, 1);
i--;
}
}
27.循环对象的方法有哪些?
- for...in 循环:用于遍历对象的属性,可以获取对象的所有可枚举属性。
- Object.keys()[配forEach使用] 方法:返回一个数组,包含对象的所有可枚举属性的名称。
- Object.values() 方法:返回一个数组,包含对象的所有可枚举属性的值。
- Object.entries() 方法:返回一个数组,包含对象的所有可枚举属性的键值对。
28.找出一个字符串中出现次数最多的字母
let str = 'abdgdbcaethbganmugthaesqszaphfdvwd'
const StringCount = function StringCount(params) {
let obj = {}
for (let i = 0; i < params.length; i++) {
/*chat方法返回对应的字母*/
let itemStr = params.chat(i)
if (obj[itemStr]) {
obj[itemStr] += 1
} else {
obj[itemStr] = 1
}
} /*循环找出最大值*/
let max = 0,
value = '';
for (let k in obj) {
if (obj[k] > max) {
max = obj[k]
value = k
}
}
return `出现次数最多的字母为${value},次数为${max}`
}
29.for in 和for or的区别
1.for-of 无法遍历 不可迭代对象
可迭代对象包括: Array,Map,Set,String,TypedArray,arguments等等
但for in 可以
2.for-of 遍历的是键值,for-in遍历的是键名(key)
30.forEach和map的区别
forEach能否修改原数组?
1.如果数组是原始数据类型(不可以被修改)
let numArr = [1, 2, 3];
numArr.forEach((item) => {
item = item * 2;
});
console.log(numArr); // 打印结果:[1, 2, 3]
2.如果是引用数据类型(直接修改整个元素对象时,无法改变原数组)
let objArr = [
{ name: '云牧', age: 20 },
{ name: '许嵩', age: 30 },
];
objArr.forEach((item) => {
item = {
name: '邓紫棋',
age: '29',
};
});
console.log(JSON.stringify(objArr));
// 打印结果:[{"name": "云牧","age": 20},{"name": "许嵩","age": 30}]
3.数组的元素是引用数据类型(修改元素对象里的某个属性时,可以改变原数组)
let objArr = [
{ name: '云牧', age: 28 },
{ name: '许嵩', age: 30 },
];
objArr.forEach((item) => {
item.name = '邓紫棋';
});
console.log(JSON.stringify(objArr));
// 打印结果:[{"name":"邓紫棋","age":28},{"name":"邓紫棋","age":30}]
Map方法一定不会修改原数组
是否有返回值
forEach没有返回值或者说返回值是undefined
Map有返回值,会映射出一个新的数组
前端页面优化
页面加载时的优化
1.使用服务器渲染,使页面加载更快配合骨架屏使用
2.使用缓存,缓存静态资源文件
3.减少http请求
合并css/js文件并进行压缩
图片进行懒加载/图片的压缩
4.异步加载js
5.静态资源使用cdn
页面运行时的优化
1.防抖和节流
2.减少DOM的回流和重绘
3.使用事件委托绑定事件
4.使用requestAnimationFrame制作动画
VUE
1.v-model的原理
v-model可以实现双向绑定,它的原理:通过父传子、子传父来实现双向数据绑定,是个语法糖。
2.实现v-model
通过v-bind:value + v-on:input
3.组件之间传参的方式
1.父组件通过v-bind:xxx=data向子组件传参,子组件通过props接收,父组件通过v-on:xxx=event向子组件传递事件,子组件通过 $emit() 触发(可以传参)。
2.通过ref获取组件的实例也可以进行传参
3.provite/inject 通过上下文的方式,父组件的所有子组件都可以接收到数据
4.通过### $listeners/$attrs一般用于组件的二次封装
$listeners里边存储的是父组件传进来的事件$attrs里边存储的是没有被props接收的哪些父组件传进来的属性
非父子组件通过vuex解决
4.谈谈你对vue-router的理解
Vue-router主要适用于单页面应用组件之间切换,
vue-router有两种模式,hash模式和history模式
hash模式的原理是hashchange事件
history模式的原理是 popstate事件+pushstate和replacestate方法
history在用户刷新页面后可能会出现页面404的情况
为什么会出现这样的情况?
因为vue-router中的页面实际上是一个个的组件,刷新页面就会去服务器请求对应的页面,但是页面是前端制作的,而后台没有对应的页面,所以就会出现404的情况
怎么避免这样的情况?
在刷新页面后请求页面的时候后台给我们返回项目对应的html而不是404
在vue-router3中我们通过new vueRouter创建路由 而在vue-router4中通过createRouter创建
在页面中我们通过router-link实现路由跳转
在router-view展示当前路由对应的组件
路由的传参方式
问号传参 在路径后面拼接参数
Params传参 通过router-link的to属性传参
Query传参 通过router-link的to属性传参对应的路由表必须提供name属性
在页面中我们通过this.$route获取当前路由的信息和上个页面传递过来的参数(vue-router4使用useRoute获取)
使用this.$router中的push、replace、go、back实现页面跳转
Push和replace有什么区别?
Push是在当前页面的事件池中添加一个页面 而replace是将当前页面进行替换
什么是动态路由?
动态路由就是项目中只有最基本的页面路由表信息(例如:首页、登录页),其余的路由表信息由后台返回给我们,拿到路由表数据我们可以进行权限校验。
怎么进行权限校验?
我一般在项目中经常使用有两种方案
1. 都是用户登录后,后台收到登陆者信息后将符合该用户权限的路由表返回给我们。
2. 后台给我们返回该用户对应的权限代码和路由表,我会根据路由表只显示符合该用户权限的页面,在导航守卫中禁止用户自行跳转没有权限的页面
导航守卫函数有哪些?
在项目中我一般使用的有
BeforeEach: 路由前置守卫 在导航被触发时执行 我一般在这里进行登录态校验和权限校验
afterEach: 路由后置守卫 在每次导航后执行 我一般在这个函数中更改页面的title
其余的导航守卫函数不是很常用
beforeResolve: 全局解析守卫 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
beforeEnter: 路由独享守卫 在路由表中进行使用
组件内置守卫
beforeRouteEnter: 在渲染该组件的对应路由被 confirm 前调用
beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave: 导航离开该组件的对应路由时调用
5.vuex的理解
vuex的诞生是为了解决复合组件之间的数据交互而诞生的,vuex就像一个全局的一个数据仓库
vuex的核心概念
state:存储的数据
getters:获取从state中衍生出来的数据(类似于计算属性)
mutations:同步修改state中的数据 在组件中通过commit方法调用指定的方法
actions:异步修改state中的数据(无法直接修改state中的数据,需要通过commit调用mutations中的方法修改)组件中使用dispath方法调用actions中的方法
modules:模块化,当项目变大的时候vuex就会变得特别臃肿或者分理出复杂的模块,所以通过分模块的方法来解决
在组建中批量使用mutations、actions、getters、state
通过map辅助函数getters、state要写在计算属性中,mutations、actions写在methods中,
如果是使用别的模块中数据和方法,则需要将map辅助函数的第一个参数改为模块名
6.vue中的优化
1.不需要修改/仅展示的数据通过 Object.freeze 进行冻结,冻结之后vue就无法对数据进行劫持,展示的速度就会很快
2.使用v-show复用组件
3.keep-alive复用组件
4.v-for和v-if避免一起使用
5.路由懒加载
6.befordestroy中的定时器和事件清除
7.通过functional将组件标记为无状态的组件
7.父子组件传参的方法
8.Vue2和Vue3的区别?
(Vue3与Vue2的区别 - 掘金 (juejin.cn)
9.vue2的watch和computed的区别?
Vue中的watch和computed都是响应式的属性,但是它们的用途和使用方式有所不同。
watch是一个观察者,用来监听某个属性的变化,并且在这个属性变化时执行一些操作。watch可以监听一个属性或一组属性,当监听的属性发生变化时,就会执行watch中定义的函数。watch可以监听到深度嵌套的属性,并且可以处理异步操作。使用watch通常是因为需要在属性变化时,执行副作用的操作。
computed是一个计算属性,用来计算并返回新的属性值。computed会缓存计算结果,在计算结果不变时直接返回缓存的结果,避免重复计算。computed只能依赖于已经存在的属性,并且不会监听属性的变化。使用computed通常是因为需要计算出一个新的属性值。
所以,watch和computed的区别在于:
- watch是用来监听属性变化的,computed是用来计算新的属性值的。
- watch可以处理异步操作,computed不能。
- watch可以监听深度嵌套的属性,computed不行。
- computed会缓存计算结果,watch不会。
10.Watch如何进行深度监听?
在要监听的对象中太添加deep属性为true
watch: {
obj: {
deep: true,
handler(val) {
console.log('obj has changed', val)
}
}
}
11.vue的生命周期
12.Vue的自定义指令
简单两个例子v-focus(获取焦点)、v-color(设置字体颜色)
<body>
<div id="app">
<h2 v-color>{{msg}}</h2>
<input type="text" v-focus />
</div>
<script src="vue.js"></script>
<script>
//注册一个全局自定义指令 `v-focus`
Vue.directive("focus", {
inserted(el) {
el.focus();
},
});
//注册一个全局自定义指令 `v-color`
Vue.directive("color", {
bind(el) {
el.style.color = "red";
},
});
const vm = new Vue({
el: "#app",
data: {
msg: "自定义指令",
},
});
</script>
</body>
13.自定义指令的钩子函数有哪些
指令对象中的钩子函数(五个):
bind: 只调用一次,指令第一次绑定到元素(还没有渲染到页面)时调用,比较常用。(自定义获取焦点无效)
inserted: 元素渲染好了,插入到元素中。在表单获取焦点focus时使用。
update: 当前元素渲染完成,绑定的值发生改变的时候触发。
componentUpdated: 当前元素所在的区域全部渲染完成,绑定的额值发生改变时触发。
unbind: 只调用一次,指令与元素解绑时调用。
uni-app
uniapp的生命周期
oninit
页面初始化
onload
页面加载
onshow
页面显示
onReady
页面初次加载完成
onHide
页面隐藏
onUnload
页面卸载
onResize
页面尺寸发生变化
onPullDownRefresh
用户下拉动作
onReachBottom
页面触底
授权登录流程
准备工作: 1.申请小程序账号
1.uni.getUserProfile调起授权登录接口,success就是表示用户点击了授权登录,success回调可以获取用户的用户名、头像、性别等
2.调用uni.login获取用户的code值,将code值和appid以及app_secret(app秘钥)传给后台
3.后台返回openid以及登录过期时间,调用登录接口将这两个参数以及用户信息传给后台
小程序冷启动与热启动
小程序启动会有两种情况,一种是「冷启动」,一种是「热启动」。 假如用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台,这个过程就是热启动;冷启动指的是用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动。
Hybrid App 混合开发
利用app提供的壳子
小程序实现的原理
HTTP网络知识
1.http和https的区别
http(超文本传输协议): 默认端口号是80,http的安全性要比https低
通信使用明文(不加密),内容可能被窃听
https(http + TSL/SSL加密证书): 默认端口号是443,安全性比http高很多
HTTPS主要作用是:
- 对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;
- 对网站服务器进行真实身份认证。
使用https是需要购买证书的,要进行HTTPS通信,证书是必不可少的。而使用的证书必须向认证机构(CA)购买
2.http缓存,如何优化缓存
http缓存是浏览器为了保存资源副本并在下次请求直接使用的技术,http缓存可以有效得提升网页或应用的性能,是我们性能优化的必要手段。
http缓存分为强缓存和协商缓存
强缓存 强缓存不会向服务器发送请求,直接从缓存中读取资源,在 chrome 控制台的 network 选项中可以看到该请求返回 200 的状态码,并且size显示from disk cache或from memory cache;
协商缓存 协商缓存会先向服务器发送一个请求,服务器会根据这个请求的 request header 的一些参数来判断是否命中协商缓存,如果命中,则返回 304 状态码并带上新的 response header 通知浏览器从缓存中读取资源。
3.GET和POST有什么区别?
1.传参方式
-
GET传参方式是在地址栏进行query传参,所以GET请求传参是受URL长度限制的,URL 的最大长度是 2048 个字符
-
POST的传参方式是在请求体中进行传参,POST传参长度没有限制
2.安全性
-
GET的参数由于是在地址栏传参所以安全性比较低
-
POST更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中
3.缓存
- GET请求可以被缓存,而POST请求不可以被缓存
4.参数是否被保存
-
GET:参数保留在浏览器历史中。
-
POST:参数不会保存在浏览器历史
5.对数据类型的限制
-
GET:只允许 ASCII 字符。
-
POST:没有限制。也允许二进制数据。
6.请求次数
-
GET:发送一次http请求。
-
POST:先发送请求头再发送请求体,相当于两次请求。
7.编码类型
-
GET:application/x-www-form-urlencoded
-
POST:application/x-www-form-urlenc
4.常见的状态码以及含义
- 200:成功
- 206:断点续传
- 301:永久转移
- 302:临时转移
- 304:协商缓存
- 307:临时重定向
40系列(客户端错误)
- 400:请求参数错误
- 401:无权限访问
- 403:请求出现问题,但是没有错误提示
- 404:请求地址错误
- 405:请求方式错误
- 408:请求超时
50系列(服务端错误)
- 500:服务器错误
- 503:服务器超负荷
5.跨域解决方案
1.proxy代理
利用服务器与服务器之间没有跨域限制的特点,使用中间服务器进行代理
2.jsonp
利用script标签的src没有同源限制,向后台请求数据,后台返回一个函数,只能进行get请求
3.cors资源共享
后端设置白名单,允许前端跨域
4.location.hash + iframe
6.http常用的请求方法有哪些?
Get:常用于请求数据
Post:常用于提交表单
Delete:常用于删除数据
Put:常用于传输文件
Cnnect:用于建立特殊通道
Head:获取资源源信息
7.浏览器建立连接的三挥四握
三次挥手
-
TC稳定可靠的传输协议[经历三次握手]
-
UDP:快速的传输协议[不需要三次握手 直接传输即可 但是不一定稳定]
-
三次握手,客户端先向服务端发起一个SYN包,进入SYN_SENT状态,服务端收到SYN后,给客户端返回一个ACK+SYN包,表示已收到SYN,并进入SYN_RECEIVE状态,最后客户端再向服务端发送一个ACK包表示确认,双方进入establish状态。
-
之所以是三次握手而不是两次,是因为如果只有两次,在服务端收到SYN后,向客户端返回一个ACK确认就进入establish状态,万一这个请求中间遇到网络情况而没有传给客户端,客户端一直是等待状态,后面服务端发送的信息客户端也接受不到了。
四次握手
-
四次挥手,首先客户端向服务端发送一个FIN包,进入FIN_WAIT1状态,服务端收到后,向客户端发送ACK确认包,进入CLOSE_WAIT状态,然后客户端收到ACK包后进入FIN_WAIT2状态,然后服务端再把自己剩余没传完的数据发送给客户端,发送完毕后在发送一个FIN+ACK包,进入LAST_ACK(最后确认)状态,客户端收到FIN+ACK包后,再向服务端发送ACK包,在等待两个周期后在关闭连接
-
之所以等待两个周期是因为最后客户端发送的ACK包可能会丢失,如果不等待2个周期的话,服务端在没收收到ACK包之前,会不停的重复发送FIN包而不关闭,所以得等待两个周期
WebPack
1.loader的原理是什么?
loader(又称为格式转换器)是webpack的核心机制,但是原理却非常简单。
loader内部会会导出一个函数,但是这个函数不能是箭头函数,否则会导致this指向错误,
这个函数就是我们这个 Loader 对资源的处理过程,它的输入就是加载到的资源文件内容,输出就是我们加工后的结果。我们通过 source 参数接收输入,通过返回值输出。
对于返回的输出,有两种思路:
- 直接在这个 Loader 的最后返回一段 JS 代码字符串;
- 再找一个合适的加载器,在后面接着处理我们这里得到的结果
Webpack 加载资源文件的过程类似于一个工作管道,你可以在这个过程中依次使用多个 Loader,但是最终这个管道结束过后的结果必须是一段标准的 JS 代码字符串。