目录
17什么是promise?promise有哪些常用方法?有哪些异步方案?
39、暂停性死区、let和const有什么区别、const定义可修改吗,那const一个对象呢?
44vue双向绑定2.0和3.0它有什么更新你知道吗45HTML语义化
55async、promise、setTimeout执行顺序
56setTimeout后面的时间是0代表什么意思,那如果setTimeout后面的时间设为6秒那它一定会是6秒之后执行吗?
58http和https的区别,浏览器如何保证CA证书是可靠的
60 vue框架中@符号和:符号分别代表v-on和v-bind
1、跨域及解决方法
简单来说,跨域就是一个域通过某种方式请求到另一个域的数据
跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
同源策略限制了以下行为:
cookie、localstorage和indexDB无法读取
DOM和js对象无法获取
ajax请求发送不出去
2、常见的跨域场景
所谓同源是指域名、协议、端口均相同
http://www.nealyang.cn/index.html 调用 http://www.nealyang.cn/server.php 非跨域
http://www.nealyang.cn/index.html 调用 http://www.neal.cn/server.php 跨域,主域不同
http://abc.nealyang.cn/index.html 调用 http://def.neal.cn/server.php 跨域,子域名不同
http://www.nealyang.cn:8080/index.html 调用 http://www.nealyang.cn/server.php 跨域,端口不同
https://www.nealyang.cn/index.html 调用 http://www.nealyang.cn/server.php 跨域,协议不同
localhost 调用 127.0.0.1 跨域
3、跨域的解决办法
3.1jsonp跨域
jsonp跨域其实也是JavaScript设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的,所以我们可以通过这个“犯罪漏洞”来进行跨域。一般,我们可以动态的创建script标签,再去请求一个带参网址来实现跨域通信
//原生的实现方式
let script = document.createElement('script');
script.src = 'http://www.nealyang.cn/login?username=Nealyang&callback=callback';
document.body.appendChild(script);
function callback(res) {
console.log(res);
}
当然,jQuery也支持jsonp的实现方式
$.ajax({
url:'http://www.nealyang.cn/login',
type:'GET',
dataType:'jsonp',//请求方式为jsonp
jsonpCallback:'callback',
data:{
"username":"Nealyang"
}
})
虽然这种方式非常好用,但是有一个最大的缺陷是,只能实现get请求
2、CORS
目前主流跨域解决方案
CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发送XMLHTTPRequest请求,从而克服了AJax只能用同源使用的限制。
CORS需要浏览器和服务器同时支持,目前,所有的浏览器都支持该功能,IE浏览器不能低于IE10。IE8+:IE8/9需要使用XDomainRequest对象来支持CORS。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS通信与同源的AJAX请求没有差别,代码完全一样,浏览器一旦发现AJAX请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以实现跨域通信。
两种请求
一种简单请求,另一种非简单请求
简单请求:请求方式为HEAD、POST或者GET
http头信息不超出一下字段:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
为什么要分为简单请求和非简单请求,因为浏览器对这两种请求方式的处理方式是不同的。
简单请求
CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据
2、POST和GET的区别
GET方式是从服务器获取数据,在做数据查询时建议使用GET;POST方式是向服务器传送数据,在做数据添加、修改和删除的时候,建议使用POST方式
GET请求只是简单的获取数据,不会修改请求的资源,而post请求会修改请求的资源,导致的后果是相同的get请求会获取到相同的资源,而相同的post请求不一样能保证相同的资源
get请求的参数是在HTTP中是通过URL传递的,POST请求的数据是通过requestbody体传递的
GET请求的参数的数据长度是有限制的,而POST没有
GET请求无法传递二进制数据到服务器,POST可以
get比post更不安全,因为参数直接暴露在URL中,所以不能用来传递敏感信息
get请求只能进行URL编码,而post支持多种编码方式
get请求参数会被完整保留在浏览历史记录里,而POST中的参数不会被保留
关于GET请求长度的误区,HTTP协议未规定GET和POST的长度限制,GET的最大长度显示是因为浏览器和web服务器限制了URI的长度,不同的浏览器和WEB服务器,限制的最大长度不一样
3、CSS盒子模型从内到外
content、padding、border、margin
两个标准:标准模型和IE模型
标准模型width=content
所以盒子总宽度为:width+border+padding
IE盒模型盒子总宽度为width
如何设置?boxsizing
4、绝对定位和相对定位
相对定位:参照属性为static即默认位置时的坐标
绝对定位:脱离文档流,不为元素预留空间,元素相对于最近的非static定位的祖先元素发生偏移。当元素没有非static定位的祖先时,会相对HTML发生偏移
5、什么是文档流
什么是文档流 - sunmarvell - 博客园 (cnblogs.com)
网页在解析时,遵循从上到下,从左到右的顺序
文档流也称为普通流
脱离文档流:本来这个标签时属于文档流管理的,那么它应该按照文档流的正常布局方式从左至右从上至下,并且符合标签本身的含义。脱离文档流是指标签脱离了文档流的管理,不受文档流的布局约束了,并且更重要的一点是,这个标签在原文档流中所占的空间也被清除了。脱离文档流的元素处于浮动状态(可以理解为漂浮在文档流的上方),当一个元素脱离文档流后,依然在文档流中的其他元素将忽略该元素并填补其原先的空间。
几种脱离文档流的机制:
float:使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在该元素的周围。
absolute:相对于第一个position不是static的父级元素定位,当父级元素的position全是static的时候,absolute是相对于html来进行定位的。
fixed:完全脱离文档流,相对于浏览器窗口进行定位。(相对于浏览器窗口就是相对于html)。
6、脱离文档流会造成什么问题
高度塌陷
7、怎么解决父元素高度塌陷
四种
1、额外标签法
在最后一个标签后面添加一个标签,并设置clear:both(不推荐,因为产生了无意义标签)
2、父元素添加overflow:hidden,触发BFC方式
3、使用after伪元素
4、使用before和after双伪元素
8、CSS有哪些伪类
不同的状态,使用不同的样式。E:link
E:visited
E:active
E:hover
E:focus
用来匹配页面的URI中某个标识符的目标元素。E:target
用来匹配使用指定语言的元素。E:lang(language)
当元素处于某种状态下时,才起作用,在默认状态下不起作用。E:checked、
E:enabled、
E:disabled
结构伪类选择器,这个就比较多了,平时用的也比较频繁。:nth-child,
:nth-last-child,
:nth-of-type,
:nth-last-of-type,
:first-child,
:last-child,
:only-child,
:first-of-type,
:last-of-type,
:only-of-type,
:root
匹配元素所有在文档的根元素,:empty
选择没有子元素的元素,且不包含节点
否定伪类选择器,E:not(F)
匹配所有除F外的E元素
9、js有哪些基本数据类型和引用数据类型,区别
基本数据类型:undefined、null、Boolean、number、string。基本数据类型的访问是按值访问的。基本类型的值是不可变的,任何方法都不能改变基本类型的值,比如对一个字符串调用投UpperCase方法,原始的字符串并不会发生改变,而会返回一个新的字符串。
基本类型的比较是值的比较
基本类型的变量是存放在栈区的,栈区包括了变量的标识符和变量的值
引用类型:Object、Array、function、date
引用类型的值是可变的,可以为引用类型添加属性和方法,也可以删除其属性和方法
引用类型的存储需要内存的栈区和堆区(堆区指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址。
引用类型的比较时引用的比较,就是比较两个对象的堆内存中的地址是否相同
10、js正则表达式里?、*、+代表什么含义
*匹配前面的子表达式零次或多次
+匹配前面的子表达式一次或多次
?匹配前面的子表达式零次或一次
11、什么是事件冒泡和事件委托
事件冒泡:当一个元素接收到事件的时候,会把他接收到的事件传给父级,一直传到window (注意这里传递的仅仅是事件 并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。)
冒泡阶段就是事件的触发响应会从最底层目标一层层地向外到最外层(根节点),事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;
事件委托是通过事件的冒泡原理来实现的,比如我们平时在给ul中的li添加事件的时候,我们是通过for循环一个个添加,如果li很多个的话,其实就有点占内存了。
通俗地讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素;一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
事件委托的时候如何知道是哪个子元素
target是触发事件的对象,点击的对象,而currentTarget是触发绑定的对象,也就是向上冒泡触发的对象
12、数组、字符串常见的API
数组:连接两个或多个数组:concat
push、unshift添加元素,push末尾加,unshift开头加
shift、pop删除元素,shift删第一个,pop删最后一个
sort数组排序
reverse反转数组
slice截取数组
indexOf()&lastIndexOf()索引方法,不改变原数组,两个方法都是返回要查找的项所在数组中首次出现的位置,没找到的话就返回-1
forEach遍历
字符串
截取:slice() 和 substring() 用法类似,只是slice支持负数,而substring的负数会被转换成0,然后截取0和start之间字符。
如果 substring() 的 start 比 end 大,这个方法将先交换两个参数的值,然后返回它们之间的子串。
split把字符串以分隔符为界点分割成数组
str.indexOf(value [,from]) 或 str.lastIndexOf(value [,from]),在指定字符串中寻找一个字符或子串。只查找第一个,找不到返回 -1。
str.concat(value),把value拼接到str上,推荐使用 str + value 代替。
str.charAt(index) 和 str[index],获取指定位置字符
str.trim(),去掉字符串前后空格。
str.charCodeAt(index),指定位置的字符转ASCII码
str.toUpperCase(),转成大写。 str.toLowerCase(),转成小写。
13、map、filter、forEach之间的区别
map可以指定函数处理数组的每个元素,不改变原数组并返回处理后的数组。
forEach不会生成新的数组,也无法通过return或者break这样的语句跳出本次循环。
filter:不会改变原数组,返回符合条件的新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
14、数组去重的方法
1、利用ES6set去重
2、利用for嵌套,然后splice去重,双层循环,外层循环元素,内存循环时比较值,值相同时,则删去这个值
3、利用indexOf去重,新建一个空的数组,for循环原数组,判断结果数组是否存在当前元素,如果相同则跳过,不同则push进数组
4、利用includes,新建一个空的数组,for循环原数组,判断结果数组是否存在当前元素,如果相同则跳过,不同则push进数组
5、利用filter,filter指定的函数测试所有元素,并创建一个包含所有通过测试元素的新数组
15、除了px外,还有哪些常见的单位
rem是相对于根元素html的font-size来计算的,比如你设置了1.2rem,根元素的font-size是100px,那么这个元素动态算出来的px就是120px
em是相对于当前对象内文字的字体尺寸,如果当前文本的字体尺寸未被人设置,则相对于浏览器的默认字体尺寸
viewpoint height,视窗高度,1vh等于视窗高度的1%。如果浏览器的高是800px,1vh为8px
viewpoint width,1vw等于视窗宽度的1%。如果浏览器的宽是500px, 1vw为5px
vmin and vmax
视口高度和宽度两者的最小值或者最大值
比如,浏览器的宽度设置为1200px,高度设置为800px,1vmax = 1200/100px = 12px,1vmin = 800/100px = 8px。如果宽度设置为600px,高度设置为1000px, 1vmin就等于6px, 1vmax则等于10px。
16、如果给你字符串,怎么找到我想要对应字符的位置?
indexof
17什么是promise?promise有哪些常用方法?有哪些异步方案?
什么是Promise,我们用它来做什么?_danielyulu的博客-优快云博客_什么是promise
promise是异步编程的一种解决方案,从语法上将,promise是一个对象,从它可以获取异步操作的消息;从本意上将,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态:pending(等状态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行
在某些情况下,回调嵌套很多,代码很繁琐,这种情况速成回调地狱
promise是用来解决两个问题的:
回调地狱、代码难以维护,常常第一个的函数的输出是第二个函数的输入的这种现象
promise可以支持多个并发的请求,获取并发请求中的数据
promise可以解决异步的问题,本身不能说promise是异步的
JS 异步编程都有哪些方案? - 掘金 (juejin.cn)
我们都知道JavaScript是单线程的,如果js都是同步代码执行意味着什么?这样可能会造成阻塞,如果当前我们有一段代码需要执行时,如果使用同步的方式,那么就会阻塞后面的代码执行;而如果使用异步则不会阻塞,我们不需要等待异步代码执行的返回结果,可以继续执行该异步任务之后的代码逻辑。因此在JS编程中,会大量使用异步来进行编程。
js异步编程的实现方式
1回调函数--->常见问题:回调地狱
2为了解决回调地狱的问题,提出了Promise的解决方案。
3Generator也是一种异步编程解决方案,它最大的特点
4、async/await
18、v-if和v-show的区别
v-if与v-show的用法与区别 - 简书 (jianshu.com)
vue中显隐方法常用两种,v-show和v-if
实现本质方法区别:
v-show本质就是标签display设置为none,控制隐藏,节点会一直存在
不管初始条件是什么,元素总是会被渲染,并且只是简单地基于元素的display属性进行切换。
v-if是动态的向DOM树内添加或者删除DOM元素,根据表达式的值有条件的渲染元素,在切换时元素及它的数据绑定/组件被销毁并重建
如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
- v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
- v-show 则适用于需要非常频繁切换条件的场景
19组件间的传递方式
Vue组件之间传递数据的五种方式_SiQianglove的博客-优快云博客
1、父组件向子组件传递数据,使用props属性;子组件向父组件中传递数据,在子组件中使用$emit派发事件,父组件中使用v-on监听事件;缺点:组件嵌套层次多的,传递数据比较麻烦
2、祖先组件通过依赖注入(inject/provide)的方式,向其所有的子孙后代传递数据;缺点:无法监听数据修改的来源,不支持响应式
3、通过属性$root/$parent/$children/ref,访问根组件、父级组件、子组件中的数据;缺点:要求组件之间要用传递性
4、通过事件总线(event bus)的方式,可以实现任意两个组件间进行数据传递;缺点:不支持响应式,这个概念是vue1.0版本中的,现在已经废弃
5、通过vus.js的状态管理模式vuex,实现多个组件进行数据共享,推荐使用这种方式进行项目中各组件间的数据传递
总结:
父组件和子组件间通信:
- 父向子传递数据是通过props,子向父传递数据是通过event($emit);
- 通过父链/子链进行数据传递($parent / $children);
- 通过 ref 也可以访问组件实例;
- 依赖注入:provide / inject;
兄弟组件间通信: - event bus
- Vuex
跨级组件间通信: - event bus;
- Vuex;
- 依赖注入:provide / inject;
19、推栈的区别
堆与栈的区别_Dablelv的博客专栏-优快云博客_堆和栈的区别
(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。
(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。
(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者BSS段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。
20居中布局
水平居中
行内元素:如果父元素是块级元素,则给他的父元素设置text-align:center,如果不是,则先把它的父元素设置为块级元素,再给父元素设置text-align:center
块级元素:一、如果宽度确定,给其设置margin0,auto;不限宽度,这时候默认子元素和父元素一样,这时需要设置子元素为display:inline-block;或者display:inline;即将其转换为行内块级/行内元素,给父元素设置text-align:center;二、使用定位属性;首先设置父元素为相对定位,再设置子元素为绝对定位,设置子元素的left:50%,即让子元素的左上角居中,定宽度,设置绝对子元素的margin-left:-元素宽度的一半px,或者设置transform:translateX:-50%;不限宽度:transform:translateX(-50%)三、利用flex布局实现:宽度定不定都可以。只需要给待处理的块级元素的父元素添加display:flex;justify-content:center
垂直居中
单行的行内元素,使其行高等于盒子的高度
定位:top:50%,margin-top:-元素高度的一半px,transform:translateY-50%
flex:align-items:center
水平垂直居中:一、top:0;right:0;bottom:0;left:0;margin:auto;
方案二:设置父元素为相对定位,给子元素设置绝对定位,left:50%;top:50%;margin-left:-元素宽度的一半px;margin-top:-元素高度的一半px;
给子元素设置绝对定位,left:50%;top:50%;transform:translateX(-50%)translateY(-50%);
设置父元素为flex定位,justify-content:center;align-items:center
21、回流和重绘
重绘:当页面元素样式改变不影响元素在文档流中的位置时(如background-color,border-color,visibility),浏览器只会将新样式赋予元素并进行重新绘制操作。
回流:当渲染树render tree中的一部分或全部因为元素的规模尺寸、布局、隐藏等改变时,浏览器重新渲染部分DOM或全部DOM的过程。
回流必定引起重绘,重绘不一定会引起回流
什么时候会触发回流或重绘?
删除或添加可见的DOM元素
元素的位置发生改变
元素的尺寸发生改变(包括外边距、内边距、边框大小、高度和宽度等)
内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
页面渲染初始化
浏览器的窗口resize尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
最复杂的一种,获取某些属性,引起回流
offset(top/left/width/height)
scroll(top/left/width/height)
client(top/left/width/height)
width height
调用了getComputedStyle()或者IE的currentStyle
避免回流重排
22JSOP工作原理
(7 封私信 / 80 条消息) JSONP 的工作原理是什么? - 知乎 (zhihu.com)
利用script标签没有跨域限制的“漏洞”
当需要通讯时,本站脚本创建一个script元素,地址指向第三方的API网址,形如<script src="http://www.example.net/api?param1=1¶m2=2"></script>并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。第三方产生的响应为json数据的包装(故称之为jsop,即json padding),形如:callback({"name":"hax","gender":"Male"}),这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
23除了script还有哪些标签不受跨域影响
img、link
24cookie、localstorage、sessionstorage有什么区别
共同点都保存在浏览器端
cookie数据始终在同源的http请求中携带,即cookie在浏览器和服务器间来回传递,并且存储大小限制也不同,cookie数据不能超过4k,同时每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。而sessionstorage和localstorage不会自动把数据发送给服务器,仅在本地保存,sessionstorage和localstorage虽然也有存储大小的限制,但比cookie大得多,可以达到5
m或更大
数据有效期不同,sessionstorage仅在当前浏览器窗口关闭前有效,自然不能长久保存,localstorage始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据,cookie只在设置的cookie期限之前一直有效,即使窗口或浏览器关闭
作用域不同,sessionstorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口都是共享的,cookie也是在所有同源窗口都是共享的
25css选择器优先级
!import>行内样式>ID选择器>类选择器>标签>通配符>继承>浏览器默认属性
26画正方形、三角形
边框的均分原理
正方形的边框设置粗一点,width,height=0,把其他三角形设置成透明色transparent
27vuex
vuex理解_ss515896745的博客-优快云博客_vuex的理解
Vuex是一个专为Vue.js应用程序开发的状态管理模式
什么是状态管理
先了解下单向数据流帮助我们更好地理解状态管理。平时我们写的简单vue文件中,包含data、template、methods这三个部分可以分别把
data看作state,驱动应用的数据源,也就是定义的变量;
template看作view以声明方式将state映射到视图,显示变量;
methods看作actions,响应在view上的状态变化;
这就是下图状态管理包含的几个部分:
因为上图状态管理只在一个vue文件中使用,所以就形成了“单向数据流”
但是如果我们遇到多个视图依赖于同一个状态和来自不同视图的行为需要变更同一状态,这种单向数据流虽然也能通过组件兄弟/父子通信传值来实现,但是代码会变得复杂不好维护。因此,
1、把组件的共享状态抽取出来,以一个全局单例模式管理
在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为(这句话的意思简单理解:vuex把项目中共享状态一般会把抽离出来放在store文件夹中,这时项目中所有的vue文件template都是视图,而store中的状态可以通过this.$store去获取你定义的状态,能做到不管哪个vue文件都能获取到,也都能变更和触发store的行为)
2、通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护(这句话不好理解就简单理解成:vuex中定义了state,getters,mutations,action四个属性,不同属性做不同的事情就是用来维护状态、视图、行为的独立性)
vuex
简单来说,vuex就是使用一个store对象来包含所有的应用层级状态,也就是数据的来源。当然如果应用比较庞大,我们可以将store模块化,也就是每个模块都有自己的store
如果还是不能理解,就先将vuex四个属性记下来:state,getters,mutations,actions,然后再分析四个属性的特点,什么地方会用到,是怎样连接在一起的
1、state
state上存放的,说的简单一些就是一些变量,也就是所谓的状态。没有state的时候,我们都是直接在data中进行初始化的,但是有了state之后,我们就把data上的数据转移到state上去了。当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余,为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性,让你少按几次键
其实就是把state上保存的变量转移到计算属性上。当映射的计算属性的名称与state的子节点名称相同时,我们也可以给mapstate传一个字符串数组
computed: mapState([
// 映射 this.count 为 store.state.count
‘count’
])
2、Getters
getters简单来说就是存放一些公共函数供组件调用。getters会暴露为store.getters对象,也就是说可以通过store.getters[属性]来进行相应的调用。mapGetters辅助函仅仅是将store中的getters映射到局部计算属性,其实也就是从getters中获取对应的属性,跟解构类似。具体如下
这样我们就可以将getters中的eventOrOdd属性值传给对应组件中的eventOdd上,Getters接受state作为其第一个参数,Getters 也可以接受其他 getters 作为第二个参数。
3、Mutations
mutations与事件类似,更改Vues的store中的状态的唯一方法是是提交mutation。所以mutations上存放的一般就是我们要改变的state的一些方法
我们不能直接调用一个mutation handler。这个选项更像是事件注册“当触发一个类型为increment的mutation时,调用次函数。
”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
store.commit(‘increment’)
当 mutation 事件类型比较多的时候,我们可以使用常量替代 mutation
事件类型。同时把这些常量放在单独的文件中可以让我们的代码合作者对整个 app 包含的 mutation 一目了然:
一条重要的原则就是要记住 mutation 必须是同步函数。
前面说了,mutation 像事件注册,需要相应的触发条件。而 Action 就那个管理触发条件的。
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action
可以包含任意异步操作。
28 介绍一下vue-router路由传参
vue-router(路由)详细教程_YinXiaobo-优快云博客_vue 路由
前端面试vue-router路由跳转方式(传参数)_卡卡西:一个擅长拷贝的前端程序员-优快云博客_vue路由传参面试
由于Vue开发时对路由支持的不足,于是官方补充了vue-router插件。vue的单页面应用是基于路由和组件的。路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,实际上就是组件的切换。路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是我们webAPP的链接路径管理系统。
为什么不能像原来一样直接用a标签编写链接呢?因为我们一般用Vue做的都是单页应用,只有一个主页面index.html,所以你写的a标签是不起作用的,用使用vue-router来进行管理
1、router-link
1.1不传参
<router-link :to="/home">跳转</router-link>
1.2传参
<router-link :to="{name:'home', params: {id:1}}"></router-link>
params传参数(类似post)
路由配置path:"home/:id"或者path:"/home:id"
不配置path,第一次可请求,刷新页面id会消失,配置path,刷新页面id会保留
取参数值:
html取参 $route.params.id
script取参 this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}"></router-link>
query传参数(类似get,url后面会显示参数)
路由不可配置
取参数值
html取参$route.query.id
script取参this.$route.query.id
2this.$router.push()编程式
2.1不传参数
this.$router.push('/home');
this.$router.push({name:'home'});
this.$router.push({path:'/home'});
2.2query传参数
this.$router.push({name:'home',query: {id:'1'}});
this.$router.push({path:'/home',query: {id:'1'}});
取参数同1、2
html 取参 $route.query.id
script 取参 this.$route.query.id
2.3params传参数
this.$router.push({name:'home',params: {id:'1'}}); // 只能用 name
路由配置
path: "/home/:id" 或者 path: "/home:id" ,
不配置path,第一次可请求,刷新页面id会消失
配置path,刷新页面id会保留
取参数值
html 取参 $route.params.id
script 取参 this.$route.params.id
2.4query和params区别
query类似get,跳转之后页面url后面会拼接参数,类似?id=1,非重要性的可以这样传,
密码之类还是用params,刷新页面id还在
params类似post,跳转之后页面url后面不会拼接参数,但是刷新页面id会消失
3.this.$router.replace() (用法同上,push)
4. this.$router.go(n)
区别:
1this.$router.push
跳转到指定url路径,并向history栈中添加一个记录,点击后退会返回到上一个页面
2this.$router.replace
跳转到指定url路径,但是history中不会有记录,点击返回会跳转到上上个页面(直接替换了当前页面)
3this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
Vue前端面试总结(二十九)Vue路由传参如何实现 详解_Rick_and_mode的博客-优快云博客
vue路由传参
路由传参可分为两种
query和params来实现
query如何实现传参
通过在router-link或者this.$router.push()传递。在地址栏传递拼接?传递参数
然后再页面通过this.$router.query.id来接收
优点,通用性比较好,刷新数据不会消失
params如何实现传参
也是通过router-link里面或者this.$router.push()传递
在地址栏传递使用/来进行拼接
然后在页面通过this.$route.params.id 接收
29、token在登录中如何使用
token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。为了避免客户端频繁向服务器请求数据(如重复的表单提交操作),以及用户的身份验证(令牌)。
1、主要使用token的基本流程如下
客户端使用用户名和密码进行登录
服务端收到请求,去验证用户名和密码,验证成功后,服务端会签发一个token,再把这个token发送给客户端
客户端将收到的token存储起来,可以存放在cookie或者localstorage
客户端每次需要登录验证的请求都需要带着这个token发送给服务端
服务端收到请求后先验证token,如果验证成功,就向客户端返回数据
vue获取token 实现token登录_小菜鸟的博客-优快云博客_vue获取token
1、在第一次登录的时候前端调用后端的接口,把用户名和密码传给后端。
2、后端收到请求,验证用户名和密码,验证成功后,返回给前端一个token值。
3、前端收到后端传给的token值,将token存储在本地 loaclStorage和vuex中。(本次项目用的是vue框架,使用了vuex全局状态管理)
4、前端每次路由跳转,就判断localStorage中是否有token,如果没有就跳转登录页面,如果有就跳转到相应的页面。
5、分装一公用的请求接口方法,每次请求调用后端接口,都在请求头中带上token
6、后端判断请求头中是否有token,如果有token就拿到token并且验证token,验证成功返回数据,验证失败(例如token过期),就返回给前端一个状态码,一般是401,请求头中没有token也返回401 (第6步是后端做,前端只要根据后端返回都状态做相应都处理就行了)
7、如果前端拿到后台返回都状态码是401,就清除token并跳转登录页面。
30、项目在移动端上面如何做到移动端适配
31flex布局
在flex容器中有两条轴,水平主轴和垂直的交叉轴,在容器中的每个默认单元块被称之为flex item占据的主轴空间为(main size),占据的交叉轴空间为(cross size)
flex容器
首先,实现flex布局需要先指定一个容器,任何一个容器都可以被指定为flex布局,这样容器内部的元素就可以用flex来进行布局
简单来说,如果使用块元素,就是用display:flex,如果使用行内元素,就使用display:inline-flex
有以下六种属性可以设置在容器上:
1、flex-direction,2、flex-wrap,3、flex-flow,4、justify-content,5、align-items,6、align-content
flex-direction决定主轴的方向,即项目的排列方向,默认值:row,水平方向,起点在左端
flex-wrap:决定容器内项目是否可以换行,默认情况下不换行,flex空间不足时,项目尺寸随之调整并不会被挤到下一行
flex-flow:flex-direction和flex-wrap的简写形式
justify-content:定义了项目在主轴上的对齐方式
align-items定义了项目在交叉轴上的对齐方式
align-content定义了多跟轴线的对齐方式,如果只有一根轴线,那么该属性不起作用,即flex-wrap为nowrap的时候,项目不换行,不会产生多条轴线
有六种属性可以用在item项目上
1、order
2、flex-basis
3、flex-grow
4、flex-shrink
5、flex
6、align-self
1、order,定义项目在容器中的排列顺序,数值越小,排列越靠前
2、flex-basis:定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间,默认值:auto,即项目本来的大小,这时候item的宽高度取决于width和height的值,当主轴为水平方向的时候,当设置了flex-basis,项目的宽度设置值会失效,flex-basis需要跟flex-grow和flex-shrink配合使用才能发挥效果
当flex-basis值为0%时,是把该项目视为0尺寸的,故即使声明该尺寸为140px,也没什么用
当flex-basis值为auto时,则根据尺寸的设定值(假设为100px),则这100px不会纳入剩余空间
3、flex-grow:定义项目的放大比例
默认值为0,即使存在剩余空间也不放大
当所有的项目都以flex-basis的值进行排列后,仍有剩余空间,如果所有项目的flex-grow都为1,则它们将等分剩余空间,如果一个项目的flex-grow为2,其他为1,那么前者占据的剩余空间比其他项多一倍
4、flex-shrink定义了缩小比例
5、flex:简写
6、align-self,允许单个项目有与其他项目不一样的对齐方式
32、一个div盒子,不知道宽高,怎么垂直居中
首先设置父级元素为相对定位,再设置子元素为绝对定位,然后设置子元素的top:50%,然后设置transform:translateY-50%
flex布局,alight-items:center
33 vue的computed和watch的区别
前端面试题vue——computed、methods、watch的区别_Lniuniu的博客-优快云博客
computed是计算属性,依赖其他属性值,并且computed的值有缓存,只有它依赖的属性值发生变化,下一次获取computed的值时才会重新计算computed的值,不支持异步
watch使用watch选项允许我们执行异步操作(访问一个API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的,不支持缓存,数据变,直接触发响应的操作,支持异步
watch擅长处理一个数据影响多个数据
computed擅长处理一个数据受多个数据影响
34webpack
一个打包工具
35forEach返回什么
无返回值,返回undefined
36forEach和map的区别
forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回
37ES6
1、let const
块级作用域,不具备变量提升,不能重复声明
const块级作用域,变量声明不提升,在声明时必须被赋值,声明时要大写,不可修改
2、箭头函数
不需要function关键字来创建函数,省略return关键字,继承当前上下文的this关键字
3、允许对函数参数设置默认值
4、支持二进制和八进制的字面量
5、对象和数组的解构
6、对象超类
7、for...in遍历一个迭代器(例如一个数组,输出数组的值“和for...of遍历对象属性
38、箭头函数和普通函数区别
普通函数this指向window对象(严格模式下undefined),作为某个对象的方法调用时,this指向该对象,被间接调用时,指向call/apply/bind的第一个参数,若第一个参数为null,参考第一条,普通函数作为构造函数,用new关键字构造实例时,this指向新创建的实例
箭头函数由外部非箭头函数的this决定,继承当前上下文的this关键字
普通函数可以作为构造函数来使用,用new关键字来创建对象实例
箭头函数不能作为构造函数
箭头函数不绑定arguments
39、暂停性死区、let和const有什么区别、const定义可修改吗,那const一个对象呢?
在代码块内,使用let、const命令声明命令之前,该变量都是不可用的,这在语法上称之为暂停性死区
不可以,对象可以修改,因为const中保存的只是指针,指针不发生改变,修改对象的属性不会该项对象的指针,所以被允许
40路由守卫,怎么使用
Vue Router 路由(路由守卫)---route_yiyueqinghui的博客-优快云博客
1、路由守卫是什么?
简单来说,导航守卫就是路由跳转前、中、后过程中的一些钩子函数,这个函数能让你操作一些其他的事,这就是路由守卫
官方解释,vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航
2、路由守卫分类
导航守卫分为:全局的、组件内的、单个路由独享三种
2.1全局的
指路由实例上直接操作的钩子函数,它的特点是所有配置路由的组件都会触发
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
全局路由的钩子函数包括
beforeEach()
在路由跳转前触发,参数包括to、from、next三个,这个钩子作用主要是用于登录验证
beforeResolve(2.5+)
这个钩子和beforeEach类似,也是路由跳转前触发,参数也是to、from、next三个,与beforeEach的区别参见官网
afterEach
是在路由跳转完成后触发,参数包括to、from,它发生在beforeEach和beforeResolve之后,beforeRouterEnter(组件内守卫)之前
2.2路由独享的
指在单个路由配置的时候也可以设置的钩子函数
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
路由独享的钩子函数包括
beforeEnter
与全局的beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数to、from、next
2.3组件内的
指在组件内执行的钩子函数,类似于组件内的生命周期
<template>
...
</template>
export default{
data(){
//...
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
组件内的路由钩子函数包括
beforeRouterEnter
路由进入组件之前调用,参数包括to、from、next。该钩子在全局守卫beforeEach和独享守卫beforeEnter之后,全局全局beforeResolve和全局afterEach之前调用,要注意的是该守卫内访问不到组件的实例,也就是this为undefined,也就是它在beforeCreate生命周期前触发
beforeRouteUpdate
在当前路由改变时,并且该组件被重复调用时调用,可以通过this访问实例。参数包括to,from,next
beforeRouteLeave
导航离开该组件的对应路由时调用,可以访问组件实例this,参数包括to,from,next
3、路由守卫回调参数
to:目标路由对象
from:即将离开的路由对象
next:它是最重要的一个参数,它相当于佛珠的线,把一个个珠子逐个串起来
vue-router中beforeEach全局守卫,实现路由跳转登录验证_bob的博客-优快云博客
41防抖的原理
所谓防抖,就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又出发了事件,则会重新计算函数执行时间
防抖是优化高频率执行js代码的一种手段
你尽管触发事件,但是我一定在事件触发n秒后才执行,如果你在一个事件触发的n秒内又触发了这个事件,那我就以新的事件的时间为准,n秒后才执行,总之,就是等你触发完事件n秒内不再触发事件,我才执行。
42、防抖和节流的区别
防抖:一定时间内只执行一次,如果再次出发则重新计算时间,即执行的都是最后一次触发的函数;节流:虽然也是一定时间只执行一次但它执行的是第一次触发的函数;我又说了防抖节流的应用场景
总结:
函数防抖:将多次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否有延迟调用函数未执行。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
防抖应用场景:登录发短信按钮,避免用户多次点击发起多次请求
调整浏览器大小resize次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
节流应用场景,scroll事件,每隔一秒计算一次位置信息等个
43vue双向数据绑定的原理
Vue数据双向绑定(面试必备) 极简版 - 前端婴幼儿 - 博客园 (cnblogs.com)
1、什么是setter、getter?
答:定义:对象有两种属性:(1)数据属性,就是我们经常使用的属性,(2)访问器属性,也称存取器属性(存取器属性就是一组获取和设置值的函数)
log打印出来的如下:
数据属性就是a和b
get和set就是关键字,它们后面各自对应一个函数,这个函数就是上面说的存储器属性,get对应的方法称为getter,负责获取值,它不带任何参数。set对应的方法为setter,负责设置值,在它的函数体中,一切的return都是无效的。
2、什么是Object.defineProperty()
定义:对象是由多个名值对组成的无序的集合,对象中每个属性对应任意类型的值
定义对象可以使用构造函数或字面量的形式:
除了以上添加属性的方式,当然还可以使用Object.defineProperty定义新属性或修改原有的属性
语法:
Object.defineProperty(obj, prop, descriptor)
参数:obj:必须,目标对象;
prop:必须,需定义或修改的属性的名字
的scriptor:必须,目标属性所拥有的特性
返回值:传入函数的对象,即第一个参数obj;
存取器描述:
如何理解Vue的双向数据绑定?
定义:
Vue是采用数据劫持结合发布/订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
上面那些大概就是下面Observer的部分,对每个vue中的data中定义的属性循环使用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,实现对视图进行更新
Dep,它就像一个依赖管理一样,什么是依赖管理?
在vue中v-model,v-name,{{}}等都可以对数据进行展示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这三个指令的html视图也必须改变
于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者(addSub),其订阅者只是更新自己的指令对应的数据,也就是v-model=‘name’和{{name}}有两个对应的订阅者,各自管理自己的地方;
每当属性的set方法触发,就循环更新Dep中的订阅者(notify)
compile主要做的事情是解析模板指令,将模板中的变量替换成数据
什么时候工作:
1)初始化,init的时候,初始化渲染页面视图
2)将每个指令对应的节点绑定更新函数,添加监听数据的订阅者
Dep负责维护依赖,而订阅者则来自于compile,一旦数据变动,则会绑定更新函数,此时也就是产生了订阅者,这个时候Dep内就增加了一个订阅者,而一旦数据变动,则会收到通知,更新视图;
Dep负责维护依赖,而订阅者则来自于compile,一旦数据变动,则会通过watcher绑定更新函数,此时watcher也向Dep中添加了订阅者,一旦Dep接到Observer的通知,它就会再去通知watcher,watcher则会调用自身的update()方法,并触发compile中绑定的回调,更新视图
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;
然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会;
接着为input会添加监听事件,修改值就等于为该属性赋值,则会触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
双向绑定原理
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
因此接下去我们执行以下3个步骤,实现数据的双向绑定:
- (1)实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- (2)实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。
- (3)实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。
44vue双向绑定2.0和3.0它有什么更新你知道吗
45HTML语义化
语义化标签就是让标签有自己的含义,利用本身传达它所包含内容的一些信息,使浏览器和搜索引擎直观的认识标签和属性的用途和作用
过去我们常常采用DIV和CSS的方式布局页面,但是DIV标签本身没有独特的含义,这样做的结果就是文档结构不够清晰,不利于浏览器对页面读取,在分离CSS样式后,用户体验不好
所以HTML5新增了很多语义化标签,使文档更具可读性,页面结构更具清晰
好处:
代码结构清晰,可读性高,减少差异性,便于团队开发和维护
在页面没有加载CSS的情况下,也能呈现较好的页面结构,增加用户体验
对搜索引擎优化,良好的结构和语义,有助于爬虫抓取更多的有效信息
有哪些?
header,nav,mian,section....
46说说定位,哪个会脱离文档流
static:默认属性(不)
relative:相对于position属性取static事盒子的位置(不脱离文档流)
absolute:相对于最近的非static定位的祖先元素偏移,如果没有,就相对于HTML发生偏移(脱离文档流)
fixed:相对于屏幕视口发生偏移,元素的位置在屏幕滚动时不会改变(脱离文档流)
sticky:relative和fixted的混合,
也就是说sticky会让元素在页面滚动时如同在正常流中(relative定位),但当滚动到特定位置时就会固定在屏幕上如同fixed,这个特定位置就是指定的top、right、bottom、left四个阈值其中之一。
sticky定位的阈值是相对它的最近滚动祖先来定义的,而sticky的作用区域也是它的第一个非static父元素内,也就是说粘性布局的效果只在该父元素内表现出来。
47BFC
常见的定位方案,定位方案是控制元素的布局,有三种常见的定位方案:
普通流
在普通流中,元素按照其在HTML中的先后位置自上而下布局,在这个过程中,行内元素水平排列,知道当行被占满后换行,块级元素则会被渲染为完整的一个新行,除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在HTML文档中的位置决定
float(float)
在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动方向尽可能的向左边或右边偏移,其效果与印刷排版中的文体环绕相似
绝对定位(absolute positioning)
在绝对定位布局中,元素会整体脱离文档流,因此绝对定位不会对其兄弟元素造成影响,而元素的具体的位置由绝对定位的坐标决定
BFC属于上诉定位方案的普通流
具有BFC特性的元素可以看做是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且BFC具有普通容器所没有的一些特性
通俗一点讲,可以把BFC理解成一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部
只要满足下面任何一个条件都可以触发BFC
body根元素
浮动元素:float除none以为的值
绝对定位元素:position(absolute,fixed)
display为inline-block、table-cell、flex
overflow除了visible以为的值(hidden,auto,scroll)
四BFC特性以及应用
1、同一个BFC下边距会发生折叠,要想避免折叠,可以将其放在不同的BFC容器里
2、BFC可以清除浮动
3、BFC可以阻止元素被浮动元素覆盖,左浮动,第二个元素被覆盖,要想避免元素被覆盖,可以触发第二个元素的BFC特性,在第二个元素中加入overflow:hidden
48 flex:1代表什么
flex属性是flex-grow,flex-shrink、flex-basis三个属性的缩写。
flex-grow:定义项目的放大比例:默认是0,即即使存在剩余空间也不会放大,所有项目的flex-grow为1,等分剩余空间(自动放大占位);flex-grow为n的项目,占据的空间(放大的比例)是flex-grow为1的n倍
flex-shrink:定义项目的缩写比例:默认为1,即空间不足,项目将缩小,所有项目的flex-shrink为1,当空间不足时,缩小的比例相同;flex-shrink为0,空间不足时,该项目不会缩小,flex-shrink为n的项目,空间不足时缩小的比例是flex-shrink为1的n倍
flex-basis:定义在分配多余空间之前,项目占据的主轴空间,浏览器根据此属性计算主轴是否有多余空间,默认值auto,即项目原本大小,设置后项目将占据固定空间
所以flex属性的默认值:0 1 auto(不放大会缩小)
flex为none:0 0 auto(不放大也不缩小)
flex为auto: 1 1 auto(放大且缩小)
flex为一个非负数字n:该数字为flex-grow的值,
flex:n;= flex-grow:n;
flex-shrink:1;
flex-basis:0%;
flex为两个非负数字n1,n2: 分别为flex-grow和flex-shrink的值,
flex:n1 n2; = flex-grow:n1;
flex-shrink:n2;
flex-basis:0%;
flex为一个长度或百分比L:视为flex-basis的值,
flex: L; = flex-grow:1;
flex-shrink:1;
flex-basis:L;
flex为一个非负数字n和一个长度或百分比L:分别为flex-grow和flex-basis的值,
flex:n L;= flex-grow:n;
flex-shrink:1;
flex-basis:L;
可以发现,flex-grow和flex-shrink在flex属性中不规定则为1,flex-basis为0%
flex:1即为flex-grow:1,经常用作自适应布局,将父元素的display:flex,侧边栏大小固定后,将内容区flex:1,内容区则会自动放大占满剩余空间
flex属性-flex:1到底是什么 - LangZ- - 博客园 (cnblogs.com)
达到等分布局的效果
49 js的数据类型
JS的基本类型共有七种:bigInt(bigInt是一种内置对象,是处symbol外的第二个内置类型)、number、string、boolen、symbol、undefined、null。复杂数据类型有对象(object)包括基本的对象、函数(Function)、数组(Array)和内置对象(Data等)。
50如何判断一个数组是数组
typeof基本类型除了null以外都返回对应的字符串,null返回object
复杂数据类型除了function返回function外,其余均返回object
object.property.toString.call方法,基本数据类型都返回对应的类型,复杂数据类型也能返回对应的类型
instanceof,只能用来检查复杂数据类型,因为instanceof用于检测构造函数(右边)的prototype属性是否出现在某个实例对象(左边)的原型链上
51instanceof原理
只要右边变量的 prototype 在左边变量的原型链上即可
52forEach和for
forEach适合只进行集合或数组遍历,for则在较复杂的循环中效率更高,在不确定循环次数,或者循环次数需要计算的情况下,使用forEach比较方便
foreach不能对数组或集合进行修改(添加删除操作),如果想要修改就要用for循环
for循环可以中断循环(利用break或return)但forEach不可用中断循环
53Promise和async的区别
Async/Await是一种新的编写异步代码的方式。其他方式是回调或者Promise
Async/Await实质上是构建在Promise之上,它不能用于纯的回调或者Node.js的回调中
和Promise一样,Async/Await是非阻塞的
Async/Await很大的特点是,它可以让异步代码看起来就像同步代码那样,大大提高了异步代码的可读性
语法
假设函数getJSON()返回一个Promise,基于Promise的调用示例如下
const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()
getJSON()返回Promise之后,在then()函数里输出结果,并返回done
使用Async/Await改写如下:
const makeRequest = async () => {
console.log(await getJSON())
return "done"
}
makeRequest()
1、在函数前使用关键字async来标记一下这是一个异步函数,它隐含着表示函数会返回一个Promise,当函数返回值时就表示Promise被处理(resolve)处理了
2await关键字只能用在async标记的函数内,换句话说它是不能用在代码的最顶层。await的意思是等待getJSON()返回的promise被处理了才会执行。
与Promise对比简洁干净,与promise需要使用then()函数来处理Promise返回的结果,而asyn/await则直接在代码按顺序上处理结果,代码量减少的同时,显得更简洁
错误处理
async/await让我们可以同时捕获异步和同步跑出的异常。Promise如果在then()函数里出现异常,在promise的外面的try/catch是捕获不到的,这种情况我们需要使用Promise的catch()函数。如:
const makeRequest = () => {
try {
getJSON()
.then(result => {
// this parse may fail
const data = JSON.parse(result)
console.log(data)
})
.catch((err) => {
console.log(err)
})
} catch (err) {
console.log(err)
}
}
async/await异步代码和同步代码共用try/catch
const makeRequest = async () => {
try {
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
条件语句
有一种情况是我们会对返回的Promise数据做判断,如果符合某种条件则需要发起另外一个异步请求使用Promise示例如下:
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}
这种嵌套的Promise读起来很容易迷糊。
async/await改写如下:
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
看起来像同步代码,大大增强了异步代码的可读性。
中间值
有一种情况是需要通过多个嵌套的请求,其中前面的请求返回的是一个中间值,后面的请求需要使用中间值来发起请求。使用Promise如下:
const makeRequest = () => {
return promise1()
.then(value1 => {
// do something
return promise2(value1)
.then(value2 => {
// do something
return promise3(value1, value2)
})
})
}
async/await改写就很简单:
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
另外,Async/Await也很方便我们调试代码
54浏览器的事件循环机制、宏任务、微任务
浏览器中的事件循环机制【看完就懂】 - 小土豆biubiubiu - 博客园 (cnblogs.com)
js单线程
步骤一、从script代码开始,到script代码结束,按顺序执行所有代码
步骤二、在步骤一顺序执行代码的过程中,如果遇到同步任务,立即执行,然后继续执行后续代码,如果遇到异步任务,将异步任务交给对应的模块处理(事件交给事件处理线程,ajax交给异步HTTP请求线程),当异步任务到达触发条件以后将异步任务的回调函数推入任务队列(宏任务推入宏任务队列,微任务推入微任务队列)。
步骤三、步骤一结束后,说明同步代码执行完毕。此时读取并执行微任务队列中保存的所有微任务
步骤四、步骤三完成后读取并执行宏任务队列中的宏任务,每执行完一个宏任务,就去查看微任务队列中是否有新增的微任务,如果存在则重复执行步骤三;如果不存在继续执行下一个宏任务,
宏任务:settimeout、settimerval
微任务:promise await
55async、promise、setTimeout执行顺序
promise、async、await、settimeout异步原理与执行顺序 - Cristina_Guan - 博客园 (cnblogs.com)
使用async定义的函数,当它被调用时,它返回的其实是一个Promise对象。(当这个async函数返回一个值时,Promise的Resolve方法会负责传递这个值,当async函数抛出异常时,Promise的reject方法也会传递这个异常值。)
await是一个让出线程的标志。await后面的函数先执行一遍,然后就会调出整个async函数来执行后面js栈的代码,等本轮事件循环完了之后又会跳回到async函数中等待await后面表达式的返回值,如果返回值为Promise则继续执行async函数后面的代码,否则将返回的promise放入promise队列。
56setTimeout后面的时间是0代表什么意思,那如果setTimeout后面的时间设为6秒那它一定会是6秒之后执行吗?
setTimeout的作用是将代码推迟到指定时间执行,如果指定时间为0,即setTimeout(f,0),那么会立刻执行吗? 答案是不会。因为setTimeout运行机制说过,必须要等到当前脚本的同步任务和“任务队列”中已有的事件,全部处理完以后,才会执行setTimeout指定的任务。也就是说,setTimeout的真正作用是,在“任务队列”的现有事件的后面再添加一个事件,规定在指定时间执行某段代码。setTimeout添加的事件,会在下一次Event Loop执行。 setTimeout(f,0)将第二个参数设为0,作用是让f在现有的任务(脚本的同步任务和“任务队列”中已有的事件)一结束就立刻执行。也就是说,setTimeout(f,0)的作用是,尽可能早地执行指定的任务。
关于setTimeout()你所不知道的地方,详解setTimeout()-前端开发博客 (caibaojian.com)
57http1.0和2.0的区别
一、HTTP1.0
HTTP协议的第二个版本,第一个在通讯中指定版本号的HTTP协议版本
HTTP 1.0浏览器与服务器只保持短暂的连接,每次请求都需要与服务器建立一个TCP连接。
服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求
简单来讲,每次与服务器交互,都需要新开一个连接
例如,解析html文件,当发现文件中存在资源文件的时候,这时候又创建单独的连接。最终导致,一个HTML文件的访问包含了多次的请求和响应,每次请求都需要创建连接,关闭连接
这种形式明显造成了性能上的缺陷
如果需要建立长连接,需要设置一个非标准的Connection字段 COnnection:keep-alive
二、HTTP1.1
在HTTP1.1中,默认支持长连接(connection:keep-alive),即在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟
建立一次连接,多次请求均由这个连接完成
这样,在加载html文件的时候,文件中多个请求和响应就可以在一个连接中传输
同时,HTTP1.1还允许客户端不用等待上一次请求的结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次返回响应结果,以保证客户端能够区分出每次的响应内容,这样也显著地减少了整个下载过程所需要的时间
同时,HTTP1.1在HTTP1.0的基础上,增加了更多的请求头和响应头来完善功能,如下:
引入了更多的缓存控制策略,如f-UNmodified-Since,if-Match,if-None-Match等缓存头来控制缓存策略
引入range,允许值请求资源某个部分
引入host,实现了在一台WEB服务器上可以在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟WEB站点
并且还添加了其他的请求方法:put、delete、options...
三、HTTP2.0
HTTP2.0在相比之前版本,性能上有很大的提升,如添加了一个特性:
多路复用
二进制分帧
首部压缩
服务器推送
多路复用
HTTP2.0复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了“队头堵塞”
上图中,可以看到第四步中css、js资源是同时发送到服务端
二进制分帧
帧是HTTP2通信中最小单位信息
HTTP2采用二进制格式传输数据,而非HTTP1.x的文本格式,解析起来更高效,将请求和响应数据分割成更小的帧,并且它们采用二进制编码
HTTP2中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流
每个数据流都以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装,这也是多路复用同时发送数据的实现条件
首部压缩
HTTP/2
在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送
首部表在HTTP/2
的连接存续期内始终存在,由客户端和服务器共同渐进地更新
例如:下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销
#服务器推送
HTTP2
引入服务器推送,允许服务端推送资源给客户端
服务器会顺便把一些客户端需要的资源一起推送到客户端,如在响应一个页面请求中,就可以随同页面的其它资源
免得客户端再次创建连接发送请求到服务器端获取
这种方式非常合适加载静态资源
四、总结
HTTP1.0:
- 浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接
HTTP1.1:
- 引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用
- 在同一个TCP连接里面,客户端可以同时发送多个请求
- 虽然允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的,服务器只有处理完一个请求,才会接着处理下一个请求。如果前面的处理特别慢,后面就会有许多请求排队等着
- 新增了一些请求方法
- 新增了一些请求头和响应头
HTTP2.0:
- 采用二进制格式而非文本格式
- 完全多路复用,而非有序并阻塞的、只需一个连接即可实现并行
- 使用报头压缩,降低开销
- 服务器推送
58http和https的区别,浏览器如何保证CA证书是可靠的
HTTPS协议需要到CA(Certificate Authority,证书颁发机构)申请证书,一般免费证书比较少,因而需要一定的费用。
HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议
HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
HTTP的连接是简单的,无状态的,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输。身份验证的网络协议,比HTTP协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的,无连接的意思是通信双方都不长久的维持对方的任何信息)。
1.浏览器首先解析网站的数字证书,如果该证书的签发机构能够在可信任签发机构中找到,则为可信任证书,否则进行2
2.从授权信息访问信息段中获得该签发机构的数字证书,并判断该授权机构的数字证书的签发机构是否能够在可信任签发机构中找到,若果找到则该授权机构是可信的,并从授权机构的数字证书中获得授权机构的公钥公钥,如果不在则重复2步骤。如果最终都没有找到可信机构,则为不可信证书。
59浏览器缓存
浏览器缓存是浏览器将用户请求过的静态资源(HTML,css,js),存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载了,不需要再去服务端请求了。
但是如果处理不当可能会导致服务端代码更新了,但是用户却还是老页面。缓存的优点是减少了冗余的数据传输,节省网费,减少服务器的负担,提升网站性能,加快客户端加载网页的速度
缓存流程
我们可以认为,浏览器里有一个专门存放缓存规则的一个数据库,也可以说是一个映射表,把缓存资源信息,同电脑磁盘中的实际文件的地址,对应起来。
浏览器第一次请求资源时
上面所说的缓存规则,就是声明所请求的资源,要采取哪种缓存策略,缓存多长时间等等,而这个规则,是在http的header中返回来的
3缓存规则
强缓存和协商缓存
强缓存
简单粗暴,如果资源没有过期,就取缓存,如果过期了,则请求服务器
看cache-control的值,图中max-age=31xxxx,就是说在这些秒内,都直接使用缓存,超过了就继续请求服务器。
cache-control的几个值含义:
max-age=xxx过期时间
no-cache不进行强缓存
所以,判断该资源是否命中强缓存,就看response中的cache-control的值,如果max-age=xxx秒,则命中强缓存。如果cache-control的值是no-cache,说明没命中强缓存,走协商缓存
强缓存的步骤:
1、第一次请求a.js缓存表中没有信息,直接请求后端服务器
2、后端服务器返回了a.js且http response header中cache-control为max-age=xxx,所以是强缓存规则,存入缓存表中
3、第二次请求a.js,缓存表中是max-age,那么命中强缓存,然后判断是否过期,如果没过期,直接读缓存的a.js,如果过期了,则执行协商缓存的步骤了。
协商缓存
触发条件:
1、cache-control的值为no-cache(不强缓存)
2、或者max-age过期了(强缓存,但总有过期的时候)
也就是说,不管怎样,都可能最后要进行协商缓存(no-store除外)
这个图,虽然强缓存命中,但是也有ETag和last-modified,这两个就是协商缓存的相关规则,
ETag:每个文件有一个,改动文件就变了
last-modified:文件修改时间
也就是说,每次http返回来response header中的ETag和Last-modified,在下次请求时request header就把这两个带上(但是名字改变了ETag->if-None-Match,Last-modified->if-Modified-Since),服务端把你带来的标识,资源目前的标识,进行对比,然后判断资源是否更改了
这个过程是循环往复的,即缓存表每次请求成功后都会更新规则
1第n次请求成功时:
2缓存表中更新该资源的ETag值
3第n+1次请求
从缓存表中取该资源最新的ETag,然后加在request header中,注意变名字了,由ETag-->If-None-Match
图
所以协商缓存步骤总结
1、请求资源时,把用户本地资源的ETag同时带到服务端,服务端和最新资源做对比
2、如果资源未改动,返回304,浏览器读取本地缓存
2、如果资源有改动,返回200,读取最新资源
59常用的布局方式
圣杯布局及原理详解之浮动,负margin,百分比_Sallywa的博客-优快云博客_圣杯布局原理
圣杯布局、双飞翼布局具体实现原理和实现方法_hyis Yoo的博客-优快云博客_双飞翼布局原理
弹性布局(flex),定位(absolute),浮动(float),静态布局(最基本的)
grid布局
CSSGrid布局由两个核心部分组成是Wrapper(父元素)和items(子元素)。wrappers是实际的grid(网格),items是grid(网格)内的内容
圣杯布局和双飞翼布局
作用:圣杯布局和双飞翼布局解决的问题是相同的,就是两边定宽,中间自适应的三栏布局,中间栏要放在文档流前面以优先渲染
区别:
圣杯布局:为了让中间div内容不被遮挡,将中间div设置了左右padding-left和padding-right后,将左右两个div用相对布局position:relative并分别配合right和left属性,以便左右两栏div移动后不遮挡中间div
双飞翼布局:为了让中间div内容不被遮挡,直接在中间div内部创建子div用于放置内容,在该div里用margin-left和margin-right为左右两栏div留出位置
圣杯布局:
优点:不需要添加dom节点
缺点:圣杯布局的缺点:正常情况下是没有问题的,但是特殊情况下就会暴露此方案的弊端,如果将浏览器无线放大时,「圣杯」将会「破碎」掉。如图:当middle部分的宽小于left部分时就会发生布局混乱。(middle<left即会变形)
当middle的宽度为大于left宽度时:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>圣杯布局</title>
<style>
#bd{
padding: 0 200px 0 180px;
height: 100px;
}
#middle{
float: left;
width: 100%;
height: 500px;
background:blue;
}
#left{
float:left;
width:180px;
height:500px;
margin-left:-100%;
background: #0c9;
position: relative;
left: -180px;
}
#right{
float: left;
width: 200px;
height: 500px;
margin-left: -200px;
background: #0c9;
position: relative;
right: -200px;
}
</style>
</head>
<body>
<div id="bd">
<div id="middle">middle</div>
<div id="left">left</div>
<div id="right">right</div>
</div>
</body>
</html>
其中:
左右栏通过添加负的margin放到正确的位置了,此段代码是为了摆正中间栏的位置
#bd{
padding: 0 200px 0 180px;
height: 100px;
}
中间栏的位置摆正之后,左栏的位置也相应右移,通过相对定位的left恢复到正确位置
#left{
position: relative;
left: -180px;
}
中间栏的位置摆正之后,右栏的位置也相应左移,通过相对定位的right恢复到正确位置
#right{
position: relative;
right: -200px;
}
双飞翼布局:
优点:不会像圣杯布局那样变形
缺点是:多加了一层dom节点
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <title>双飞翼布局</title>
6 <style>
7
8 #center{
9 float:left;
10 width:100%;/*左栏上去到第一行*/
11 height:100px;
12 background:blue;
13 }
14 #left{
15 float:left;
16 width:180px;
17 height:100px;
18 margin-left:-100%;
19 background:#0c9;
20 }
21 #right{
22 float:left;
23 width:200px;
24 height:100px;
25 margin-left:-200px;
26 background:#0c9;
27 }
28
29 /*给内部div添加margin,把内容放到中间栏,其实整个背景还是100%*/
30 #inside{
31 margin:0 200px 0 180px;
32 height:100px;
33 }
34 40 </style>
41 </head>
42 <body>
43 <div id="center">
44 <div id="inside">middle</div>
45 </div>
46 <div id="left">left</div>
47 <div id="right">right</div>
48 </body>
49 </html>