目录
OSI的7层模型:
HTTP特点、HTTPS特点等
UDP 和 TCP 的特点与区别
HTTP1.0/1.1/2.0
对称加密与非对称加密
XSS与CSRF
跨域:
强缓存与协商缓存:
JS数据类型
深拷贝浅拷贝
判断JS数据类型的基本方法:
闭包
this的指向问题
JS运行机制
箭头函数和展开语法...
如何创建BFC
let、var、const
垂直居中方法
CSS选择器及优先级:
浏览器渲染机制:
前端性能优化的手段
尾调用优化:空间的优化
官网优化及性能指标
节流防抖,图片懒/预加载:
Vue
JS中的数组:
async和 await对于promise 的优势
CSS高度坍塌问题的原因以及解决办法
margin合并
position中absolute和fixed的区别
块,行内,行内块级
加载阻塞问题
JavaScript阻止修改对象的三种方式
ES6异步请求并行操作
定义自适应正方形:
javaScript遍历对象总结
原型链
css中单位em和rem的区别
css设置0.5px的border
JS浮点数精度问题
Echarts底层
HTML5新特性
Flex布局主轴及排列方向
关于绝对定位absolute相对于父元素定位的问题
CSS图片旋转
CSS图片裁剪
在Vue.js
盒子模型的设置:
强缓存和协商缓存
Map和forEach区别
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。
URI 是用来标示 一个具体的资源的,我们可以通过 URI 知道一个资源是什么。
URL 则是用来定位具体的资源的,标示了一个具体的资源位置。互联网上的每个文件都有一个唯一的URL。
请求报文构成
响应报文构成
常见请求方法
post和get的区别:
GET提交的数据长度是有限制的,因为URL长度有限制,具体的长度限制视浏览器而定。而POST没有。
状态码分类:
常见状态码:
一般http中存在如下问题:
浏览器在使用HTTPS传输数据的流程是什么?
HTTPS的缺点
总结HTTPS和HTTP的区别
是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。
是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。
TCP应用:
(1)FTP:文件传输协议;
(2)SSH:安全登录、文件传送(SCP)和端口重定向;
(3)Telnet:不安全的文本传送;
(4)SMTP:简单邮件传输协议Simple Mail Transfer Protocol (E-mail);
(5)HTTP:超文本传送协议 (WWW);
UDP应用:
(1)流媒体
采用TCP,一旦发生丢包,TCP会将后续包缓存起来,等前面的包重传并接收到后再继续发送,延迟会越来越大。基于UDP的协议如WebRTC是极佳的选择。
(2)实时游戏
对实时要求较为严格的情况下,采用自定义的可靠UDP协议,比如Enet、RakNet(用户有sony online game、minecraft)等,自定义重传策略,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成的影响。
采用UDP的经典游戏如FPS游戏Quake、CS,著名的游戏引擎Unity3D采用的也是RakNet。
(3)物联网
2014年google旗下的Nest建立Thread Group,推出了物联网通信协议Thread,完善物联网通信。
全球将近50%的人都在使用互联网,人们不断的追求更快、更好的服务,一切都在变化,在越来越多的领域,UDP将会抢占TCP的主导地位。
(4)QQ 文件传输、QQ语音、QQ视频
对于网络通讯质量要求不高的情况下,要求网络通讯速度能尽量快捷方便,就可以使用UDP技术。
HTTP 1.0
缺点: TCP链接无法复用,造成浪费
HTTP 1.1
HTTP 2.0
1. 对称加密
对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密。对称加密只有一个秘钥,作为私钥。
常见的对称加密算法:DES,AES,3DES等等。
更不安全
2. 非对称加密
非对称加密指的是:加密和解密使用不同的秘钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。
常见的非对称加密算法:RSA,ECC
效率更低
XSS
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
XSS的防御措施:
CSRF
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。如:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
CSRF防御措施:
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。
同源策略:一个源指的是主机名、协议和端口号的组合,必须相同
几种解决方案:
1、jsonp的原理就是利用
并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)
2、CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
3、nginx:a网站向b网站请求1.js文件时,向b网站发送一个获取的请求,nginx根据配置文件接收这个请求,代替a网站向b网站来请求这个资源,nginx拿到这个资源后再返回给a网站,以此来解决了跨域问题。
4、postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
强缓存与协商缓存:
1.强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中可以看到该请求返回200的状态码;
2.协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中(请求的资源未更新),则返回304状态码并带上新的response header通知浏览器从缓存中读取资源,如果资源更新则返回资源和状态码200;
两者的共同点是,都是从客户端缓存中读取资源;区别是强缓存不会发请求,协商缓存会发请求。
JS数据类型
基本类型(单类型): String、Number、boolean、null、undefined、symbol。
引用类型:object。里面包含的 function、Array、Date。
检测数组类型的方法
-
- instanceof 操作符
-
- 对象的 constructor 属性
-
- Array.isArray( ) 检验值是否为数组
深拷贝浅拷贝
深拷贝
深拷贝就是 ‘完全'拷贝,拷贝之后新旧数据完全分离,不再共用对象类型的属性值,不会互相影响。JSON.parse(JSON.stringify(Obj))
//实现深拷贝
function deepCopy( target ){
if(typeof target !== 'object') return ;
//判断目标类型,来创建返回值
var newObj = target instanceof Array ? [] : {};
for(var item in target){
//只复制元素自身的属性,不复制原型链上的
if(target.hasOwnProperty(item)){
newObj[item] =(typeof target[item] == 'object' ? deepCopy(target[item]) : target[item]) //判断属性值类型
}
}
return newObj;
}
浅拷贝
当属性值为对象类型时,只拷贝了对象数据的引用,导致新旧数据没有完全分离,还会互相影响。array.concat()或者array.slice() 是特殊的实现数组浅拷贝的方式。
//实现浅拷贝
function shallowCopy( target ){
if(typeof target !== 'object') return ;
//判断目标类型,来创建返回值
var newObj = target instanceof Array ? [] : {};
for(var item in target){
//只复制元素自身的属性,不复制原型链上的
if(target.hasOwnProperty(item)){
newObj[item] = target[item]
}
}
return newObj;
}
判断JS数据类型的基本方法:
typeof
typeof '';// string 有效
typeof 1;// number 有效
typeof Symbol();// symbol 有效
typeof true;//boolean 有效
typeof undefined;//undefined 有效
typeof null;//object 无效
typeof [] ;//object 无效
typeof new Function();// function 有效
typeof new Date();//object 无效
typeof new RegExp();//object 无效
instanceof
判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。
Constructor
toString
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ;// [object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
Object.prototype.toString.call(undefined) ;// [object Undefined]
Object.prototype.toString.call(null) ;// [object Null]
Object.prototype.toString.call(newFunction()) ;// [object Function]
Object.prototype.toString.call(newDate()) ;// [object Date]
Object.prototype.toString.call([]) ;// [object Array]
Object.prototype.toString.call(newRegExp()) ;// [object RegExp]
Object.prototype.toString.call(newError()) ;// [object Error]
Object.prototype.toString.call(document) ;// [object HTMLDocument]
闭包
1.闭包就是子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。归根结底,就是利用js得词法(静态)作用域,即作用域链在函数创建的时候就确定了。
2.子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中。
this的指向问题
- 全局作用域、普通函数以及定时器中的this指向全局对象window
- 方法中的this指向的是调用它的对象
- 构造函数中的this指向构造函数的实例
- 箭头函数中没有绑定this,this为最近外层作用域下有定义的this
- call、apply、bind可改变this指向
JS运行机制
JS是单线程语言
- 好处:单线程的特点能更好的提高运行效率
- JS用法层面:JS的主要用途是与用户互动和dom操作。这决定了它只能是单线程,否则会带来很复杂的同步问题。如:若JS有多个线程,一个操作一个节点,另一个删除那个节点,要先走哪一步?
1. 什么是任务队列
任务队列就相当于一个临时存放异步任务的储存栈,他首先会执行主线程的同步任务,当主线程遇到异步任务,就会先把异步任务先放在任务队列里,等到同步任务全部执行完,再去执行任务队列里面的异步任务。
2. 什么是微任务宏任务
通俗点讲,微任务就是同步任务,宏任务就是异步任务,例如
微任务:Promise(async/await)、简单的console.log
宏任务:定时器、事件绑定、ajax、回调函数
Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:
- 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
- 检查是否存在微任务,如果存在则不停的执行,直至清空微任务队列
- 更新render(每一次事件循环,浏览器都可能会去更新渲染)
- 重复以上步骤
事件循环:
- 将所有任务看成两个队列:执行队列与事件队列。
- 执行队列是同步的,事件队列是异步的,宏任务放入事件列表,微任务放入执行队列之后,事件队列之前。
- 当执行完同步代码之后,就会执行位于执行列表之后的微任务,然后再执行事件列表中的宏任务。
javascript垃圾回收机制原理:
解决内存的泄露,垃圾回收机制会定期(周期性)找出那些不再用到的内存(变量),然后释放其内存。
现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。
标记清除:
js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"。
引用计数:
语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
箭头函数和展开语法...
箭头函数不绑定this,简洁,可作为方法。箭头函数不能用作构造器,和 new一起用会抛出错误。箭头函数没有prototype属性。箭头函数在参数和箭头之间不能换行。但是,可以通过在 ‘=>’ 之后换行,或者用 ‘( )’、'{ }'来实现换行
…
函数调用
function sum(x, y) {
return x * y;
}
const numbers = [1, 2];
console.log(sum(...numbers));
sum(…numbers)相当于 sum.apply(null, numbers)
构造字面量数组
var arr1=[1,2];
var arr2=[...arr1,3,4];
console.log(arr2);//[1,2,3,4]
构造字面量字符串
var arr1=['a','b',...'cdef'];
console.log(arr1);//['a','b','c','d','e','f']
构造字面量对象(克隆对象)
var obj1={a:1};
var obj2={b:2};
var obj3={...obj1,...obj2,c:3};
console.log(obj3);//{a:1,b:2,c:3}
cookie、sessionStorage和localStorage
cookie数据大小不能大于4K;
localStorage和sessionStorage则可以达到5M;
cookie在设置的有效期内一直有效;
localStorage存储持久数据,只要不手动清除则一直存在,无时间限制;
sessionStorage数据在当前浏览器关闭后就会被自动清除
cookie的数据会自动传递到服务器端,服务器端也可以写cookie到客户端
localStorage和sessionStorage不会把数据自动传到服务器端,仅在本地存储
如何创建BFC
body 根元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)
let、var、const
let所声明的变量只在let命令所在的代码块内有效。(块级作用域)
let命令不存在变量提升
let声明变量存在暂时性死区(即变量会绑定某个区域,不受外部影响)
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。
1.var声明的变量会挂载在window上,而let和const声明的变量不会
2.var声明变量存在变量提升,let和const不存在变量提升
3.let和const声明形成块作用域
4.同一作用域下let和const不能声明同名变量,而var可以
5.const一旦声明必须赋值,不能使用null占位;声明后不能再修改 ;如果声明的是复合类型数据,可以修改其属性
垂直居中方法
css:
div{
width: 400px;
height: 400px;
position: relative;
}
img{
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
html:
.container{
width: 500px;
height: 400px;
border: 2px solid #379;
position: relative;
}
.inner{
width: 480px;
height: 380px;
background-color: #746;
position: absolute;
top: 50%;
left: 50%;
margin-top:-190px;
margin-left:-240px;
css:
div{
width: 300px;
height: 300px;
border: 3px solid #555;
display: table-cell;
vertical-align: middle;
text-align: center;
}
img{
vertical-align: middle;
}
还有flex布局
CSS选择器及优先级:
内联样式>内部样式>外部样式>ID选择器>类选择器and伪类选择器>标签(元素)选择器and伪元素选择器>通配符选择器
浏览器渲染机制:
当用户输入一个URL时,浏览器就会向服务器发出一个请求,请求URL对应的资源
· 接受到服务器的响应内容后,浏览器的HTML解析器,会将HTML文件解析成一棵DOM树,DOM树的构建是一个深度遍历的过程,当前节点的所有子节点都构建完成以后,才会去构建当前节点的下一个兄弟节点。
· 将CSS解析成CSSOM树(CSS Rule Tree)
· 根据DOM树和CSSOM树,来构建Render Tree(渲染树),注意渲染树,并不等于DOM树,因为一些像head或display:none的东西,就没有必要放在渲染树中了。
· 有了Render Tree,浏览器已经能知道网页中有哪些节点,各个节点的CSS定义,以及它们的从属关系,下一步操作就是Layout,顾名思义,就是计算出每个节点在屏幕中的位置。
· Layout后,浏览器已经知道哪些节点要显示,每个节点的CSS属性是什么,每个节点在屏幕中的位置是哪里,就进入了最后一步painting,按照算出来的规则,通过显卡,把内容画到屏幕上。
· Repaint重绘 ——改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
· Reflow回流 ——元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。
· reflow 几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显 示与隐藏)等,都将引起浏览器的 reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。
注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
减少回流重绘
·使用class代替style,减少style的使用
·使用resize、scroll时进行防抖和节流处理,这两者会直接导致回流
·使用visibility替换display:none,因为前者只会引起重绘,后者会引发回流
·批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流
·避免触发同步布局事件,我们在获取offsetWidth这类的属性时,可以使用变量将查询结果存起来,避免多次查询,每次对offset / scroll /client等属性进行操作时都会触发回流
·对于复杂动画效果,使用绝对定位让其脱离文档流,复杂动画效果会频繁的触发回流和重绘,我们可以将动画元素设置绝对定位从而脱离文档流避免反复回流重绘。
前端性能优化的手段
·降低请求量:合并资源,减少HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
·加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发(多个地点分配服务器,加载快)。事件委托,防抖节流
·缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。不加载重复资源
·渲染:JS/CSS优化(css在头部,js在底部),加载顺序,服务端渲染,pipeline。
尾调用优化:空间的优化
尾调用之所以与其他调用不同,就在于它的特殊的调用位置。函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。
如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。
如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"(call stack)。
而尾调用时,由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。
只保留内层函数的调用记录。
如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。
官网优化及性能指标
响应时间
交互时间、白屏时间等等
并发数
吞吐量
服务器性能
DNS解析时间
网站设计
关键词优化
图片优化
域名优化
节流防抖,图片懒/预加载:
·防抖:对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行最后一次。
·搜索框搜索输入。只需用户最后一次输入完,再发送请求
·手机号、邮箱验证输入检测
·窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
·节流:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
·滚动加载,加载更多或滚到底部监听
·谷歌搜索框,搜索联想功能
·高频点击提交,表单重复提交
·图片懒加载:懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。
·图片预加载:当图片加载过一次以后,如果再有对该图片的请求时,由于浏览器已经缓存住这张图片了,不会再发起一次新的请求,而是直接从缓存中加载过来。
Vue
-
- vue优点?
答:轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。
2.vue父组件向子组件传递数据?
答:通过props
3.子组件像父组件传递事件?
答:$emit方法
4.v-show和v-if指令的共同点和不同点?
答: 共同点:都能控制元素的显示和隐藏;
不同点:实现本质方法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。
总结:如果要频繁切换某节点,使用v-show(切换开销比较小,初始开销较大)。如果不需要频繁切换某节点使用v-if(初始渲染开销较小,切换开销比较大)。
5.如何让CSS只在当前组件中起作用?
答:在组件中的style前面加上scoped
6. 的作用是什么?
答:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
7.如何获取dom?
答:ref="domName" 用法:this.$refs.domName
8.说出几种vue当中的指令和它的用法?
答:v-model双向数据绑定;
v-for循环;
v-if v-show 显示与隐藏;
v-on事件;v-once: 只绑定一次。
9. vue-loader是什么?使用它的用途有哪些?
答:vue文件的一个加载器,将template/js/style转换成js模块。
用途:js可以写es6、style样式可以scss或less、template可以加jade等
10.为什么使用key?
答:需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。
作用主要是为了高效的更新虚拟DOM。
11.axios及安装?
答:请求后台资源的模块。npm install axios --save装好,
js中使用import进来,然后.get或.post。返回在.then函数中如果成功,失败则是在.catch函数中。
12.v-modal的使用。
答:v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:
v-bind绑定一个value属性;
v-on指令给当前元素绑定input事件。
13.请说出vue.cli项目中src目录每个文件夹和文件的用法?
答:assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置; app.vue是一个应用主组件;main.js是入口文件。
14.分别简述computed和watch的使用场景
答:computed:
当一个属性受多个属性影响的时候就需要用到computed
最典型的栗子: 购物车商品结算的时候
watch:
当一条数据影响多条数据的时候就需要用watch
栗子:搜索数据
15.v-on可以监听多个方法吗?
答:可以,栗子:。
16.$nextTick的使用
答:当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。
17.vue组件中data为什么必须是一个函数?
答:因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
18.渐进式框架的理解
答:主张最少;可以根据不同的需求选择不同的层级;
19.Vue中双向数据绑定是如何实现的?
答:vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。
20.单页面应用和多页面应用区别及优缺点
答:单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
单页面缺点:
不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多。
21.v-if和v-for的优先级
答:当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中。所以,不推荐v-if和v-for同时使用。
如果v-if和v-for一起用的话,vue中的的会自动提示v-if应该放到外层去。
22.assets和static的区别
答:相同点:assets和static两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点
不相同点:assets中存放的静态资源文件在项目打包时,也就是运行npm run build时会将assets中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在static文件中跟着index.html一同上传至服务器。static中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议:将项目中template需要的样式文件js文件等都可以放置在assets中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css等文件可以放置在static中,因为这些引入的第三方文件已经经过处理,我们不再需要处理,直接上传。
23.vue常用的修饰符
答:
.stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡;
.prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
.capture:与事件冒泡的方向相反,事件捕获由外到内;
.self:只会触发自己范围内的事件,不包含子元素;
.once:只会触发一次。
24.vue的两个核心点
答:数据驱动、组件系统
数据驱动:ViewModel,保证数据和视图的一致性。
组件系统:应用类UI可以看作全部是由组件树构成的。
25.vue和jQuery的区别
答:jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:$("lable").val();,它还是依赖DOM元素的值。
Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。
26. 引进组件的步骤
答: 在template中引入组件;
在script的第一行用import引入路径;
用component中写上组件名称。
27.delete和Vue.delete删除数组的区别
答:delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。Vue.delete 直接删除了数组 改变了数组的键值。
28.SPA首屏加载慢如何解决
答:安装动态懒加载所需插件;使用CDN资源。
29.Vue-router跳转和location.href有什么区别
答:使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用history.pushState('/url'),无刷新页面,静态跳转;
引进router,然后使用router.push('/url')来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。
30. vue slot
答:简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。
31.你们vue项目是打包了一个js文件,一个css文件,还是有多个文件?
答:根据vue-cli脚手架规范,一个js文件,一个CSS文件。
32.Vue里面router-link在电脑上有用,在安卓上没反应怎么解决?
答:Vue路由在Android机上有问题,babel问题,安装babel polypill插件解决。
33.Vue2中注册在router-link上事件无效解决方法
答: 使用@click.native。原因:router-link会阻止click事件,.native指直接监听一个原生事件。
34.RouterLink在IE和Firefox中不起作用(路由不跳转)的问题
答: 方法一:只用a标签,不适用button标签;方法二:使用button标签和Router.navigate方法
35.axios的特点有哪些
答:从浏览器中创建XMLHttpRequests;
node.js创建http请求;
支持Promise API;
拦截请求和响应;
转换请求数据和响应数据;
取消请求;
自动换成json。
axios中的发送字段的参数是data跟params两个,两者的区别在于params是跟请求地址一起发送的,data的作为一个请求体进行发送
params一般适用于get请求,data一般适用于post put 请求。
36.请说下封装 vue 组件的过程?
答:1. 建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。(os:思考1小时,码码10分钟,程序猿的准则。)
2. 准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
3. 准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。
4. 封装完毕了,直接调用即可
37.params和query的区别
答:用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是this.$route.query.name和this.$route.params.name。
url地址显示:query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意点:query刷新不会丢失query里面的数据
params刷新 会 丢失 params里面的数据。
38.vue初始化页面闪动问题
答:使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{{message}}的字样,虽然一般情况下这个时间很短暂,但是我们还是有必要让解决这个问题的。
首先:在css里加上[v-cloak] {
display: none;
}。
如果没有彻底解决问题,则在根元素加上style="display: none;" :style="{display: 'block'}"
39.vue更新数组时触发视图更新的方法
答:push();pop();shift();unshift();splice(); sort();reverse()
40.vue常用的UI组件库
答:Mint UI,element,VUX
41.vue修改打包后静态资源路径的修改
答:cli2版本:将 config/index.js 里的 assetsPublicPath 的值改为 './' 。
build: {
...
assetsPublicPath: './',
...
}
cli3版本:在根目录下新建vue.config.js 文件,然后加上以下内容:(如果已经有此文件就直接修改)
module.exports = {
publicPath: '', // 相对于 HTML 页面(目录相同) }
生命周期函数面试题
1.什么是 vue 生命周期?有什么作用?
答:每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做 生命周期钩子 的函数,这给了用户在不同阶段添加自己的代码的机会。(ps:生命周期钩子就是生命周期函数)例如,如果要通过某些插件操作DOM节点,如想在页面渲染完后弹出广告窗, 那我们最早可在mounted 中进行。
2.第一次页面加载会触发哪几个钩子?
答:beforeCreate, created, beforeMount, mounted
3.简述每个周期具体适合哪些场景
答:beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
create:data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
mounted:执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate: 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
destroyed: 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
4.created和mounted的区别
答:created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
5.vue获取数据在哪个周期函数
答:一般 created/beforeMount/mounted 皆可.
比如如果你要操作 DOM , 那肯定 mounted 时候才能操作.
6.请详细说下你对vue生命周期的理解?
答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和**数据对象**data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
vue路由面试题
1.mvvm 框架是什么?
答:vue是实现了双向数据绑定的mvvm框架,当视图改变更新模型层,当模型层改变更新视图层。在vue中,使用了双向绑定技术,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。
2.vue-router 是什么?它有哪些组件
答:vue用来写路由一个插件。router-link、router-view
3.active-class 是哪个组件的属性?
答:vue-router模块的router-link组件。children数组来定义子路由
4.怎么定义 vue-router 的动态路由? 怎么获取传过来的值?
答:在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id。
5.vue-router 有哪几种导航钩子?
答:三种,
第一种:是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
第二种:组件内的钩子
第三种:单独路由独享组件
6.$route 和 $router 的区别
答:$router是VueRouter的实例,在script标签中想要导航到不同的URL,使用$router.push方法。返回上一个历史history用$router.to(-1)
$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。
7.vue-router的两种模式
答:hash模式:即地址栏 URL 中的 # 符号;
history模式:window.history对象打印出来可以看到里边提供的方法和记录长度。利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)。
8.vue-router实现路由懒加载( 动态加载路由 )
答:三种方式
第一种:vue异步组件技术 ==== 异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js文件。
第二种:路由懒加载(使用import)。
第三种:webpack提供的require.ensure(),vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
vuex常见面试题
1.vuex是什么?怎么使用?哪种功能场景使用它?
答:vue框架中状态管理。在main.js引入store,注入。
新建了一个目录store.js,….. export 。
场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
2.vuex有哪几种属性?
答:有五种,分别是 State、 Getter、Mutation 、Action、 Module
导航守卫:
导航守卫就是路由跳转过程中的一些钩子函数。路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机,这就是导航守卫。
事件总线:
EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。
组件通信方式:
props / $emit父组件通过props的方式向子组件传递数据,而通过$emit 子组件可以向父组件通信。
provide/ inject父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
ref / refs:ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。
eventBus又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
Vuex
localStorage / sessionStorage
$attrs与 $listeners $attrs
与$listeners
是两个对象,$attrs
里存放的是父组件中绑定的非 Props 属性,$listeners
里存放的是父组件中绑定的非原生事件。
跨级调用。
缺点:由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
JS中的数组:
数组本来应该是一个连续的内存分配,但是在Javascript中不是连续分配的,而是类似哈希映射的方式存在的。
对于上述的实现方式,熟悉数据结构的同学应该知道,对于读取操作,哈希表的效率并不高,而修改删除的效率比较高。
现在浏览器为了优化其操作,对数组的创建时候的内存分配进行了优化:
对于同构的数组,也就是,数组中元素类型一致,会创建连续的内存分配
对于不同构数组,按照原来的方式创建。
如果你想插入一个异构数据,那么就会重新解构,通过哈希映射的方式创建
1:所有的声明都会提升到作用域的最顶上去。
2:同一个变量只会声明一次,其他的会被忽略掉。
3:函数声明的优先级高于变量声明的优先级,并且函数声明和函数定义的部分一起被提升。
async和 await对于promise 的优势
简洁
await一个promise即可,那么会自动返回这个promise的resolve值,无需在then函数的回调中手动取值,彻底解决了回调。
错误处理
async/await终于使得用同一种构造(古老而好用的try/catch) 处理同步和异步错误成为可能。在下面这段使用promise的代码中,try/catch不能捕获JSON.parse抛出的异常,因为该操作是在promise中进行的。要处理JSON.parse抛出的异常,你需要在promise上调用.catch并重复一遍异常处理的逻辑。通常在生产环境中异常处理逻辑都远比console.log要复杂,因此这会导致大量的冗余代码。async任意一个await出现了异常,await会自动抛出reject,并且程序会被停止,异常统一在try-catch块可以捕获而不会出现捕获链无限延长的问题
CSS高度坍塌问题的原因以及解决办法
在文档流中,父元素的高度默认是被子元素撑开的,也就是子元素多高,父元素就多高。
但是当为子元素设置浮动以后,子元素会完全脱离文档流,此时将会导致子元素无法撑起父元素的高度,导致父元素的高度塌陷。
由于父元素的高度塌陷了,则父元素下的所有元素都会向上移动,这样将会导致页面布局混乱。
给父元素添加 overflow:hidden;在父元素最后添加一个空的div,为这个div设置clear:both;通过伪元素选择器::after清除浮动,缺点代码过多。
margin合并
当两个元素在垂直方向并列,分别设置margin值时会发生一个margin合并的现象
- 给父级盒子设置overflow:hidden
- 给父级盒子设置border
- 给父级盒子设置padding
- 给父元素添加position: absolute;
- 给父元素添加position: fixed;
- 给父元素添加display: inline-block;
position中absolute和fixed的区别
fixed:固定定位
absolute:绝对定位
共同点:都脱离了文档流。
区别: 在有滚动条的情况下,fixed定位不会随滚动条移动而移动,而absolute则会随滚动条移动
常用场合:
1.fixed常用于网站边缘的公司联系方式和快速回到顶部
2.absolute常用于页面
断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况
块,行内,行内块级
块级元素
1.总是从新的一行开始
2.高度、宽度都是可控的
3.宽度没有设置时,默认为100%
4.块级元素中可以包含块级元素和行内元素
5.块级文字元素中不能放入其他块级元素
行内元素
特点:
1.和其他元素都在一行
2.高度、宽度以及内边距都是不可控的
3.宽高就是内容的高度,依靠自身字体大小和图形支撑结构的,不可以改变
4.行内元素只能行内元素,不能包含块级元素
5.行内元素的paddding可以设置
6.margin只能够设置水平方向的边距,即:margin-left和margin-right,设置margin-top和margin-bottom无效
行内块级元素
综合块级元素与行内元素的特性,可设宽高(默认是内容宽高),也可以设置内外边距
加载阻塞问题
CSS 不会阻塞 DOM 的解析,但会阻塞 DOM渲染。
JS 阻塞 DOM 解析,但浏览器会"偷看"DOM,预先下载相关资源。
浏览器遇到 且没有defer或async属性的 标签时,会触发页面渲染,因而如果前面CSS资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。
JS 是 Render blocking resource,JS 的 加载、解析、编译和执行都会阻塞页面渲染。
JavaScript阻止修改对象的三种方式
防止扩展:
禁止为对象添加属性和方法。但已存在的属性和方法可以被修改或删除。
实施操作:Object.preventExtensions()
检测是否应用该操作:Object.isExtensible()
密封:
禁止为对象删除已存在的属性和方法。被密封的对象也是不可扩展的。
实施操作:Object.seal()
检测是否应用该操作:Object.isSealed()
冻结:
禁止为对象修改已存在的属性和方法。所有字段均只读。被冻结的对象也是不可扩展和密封的。
实施操作:Object.freeze()
检测是否应用该操作:Object.isFrozen()
ES6异步请求并行操作
使用Promise.all,async/await, Ajax 指的是 XMLHttpRequest(XHR),Fetch
定义自适应正方形:
margin-top的%值是相对于父元素的宽度的。
利用.CSS3 vw 单位,vw是相对于视口的宽度。视口被均分为100单位的vw。
hello,viewport
.square {
width: 50%;
height: 50vw;
background: #ccc;
}
设置垂直方向的padding撑开容器(padding可以用百分比,百分比是根据它包含块的width来确定的,也就是父级元素的width)
.square2{
width: 50%;
padding-bottom:100%;
height:0;
}
Flex:1
Column-count 设置一行多少个元素
Column-gap 设置元素之间距离
javaScript遍历对象总结
for … in ,Object.keys(),Object.getOwnPropertyNames(obj)
原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
Js实现动画:requestAnimationFrame css实现动画:animation,keyframes
继承的几种方式:
原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承
css中单位em和rem的区别
在css中单位长度用的最多的是px、em、rem,这三个的区别是:
px是固定的像素,一旦设置了就无法因为适应页面大小而改变。
em和rem相对于px更具有灵活性,他们是相对长度单位,意思是长度不是定死了的,更适用于响应式布局。
对于em和rem的区别一句话概括:em相对于父元素,rem相对于根元素。
像素(px):用于元素的边框或定位。
em/rem:用于做响应式页面
css设置0.5px的border
transform:scale(0.5);
JS浮点数精度问题
把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
Echarts底层
一个纯Javascript的图表库,可以流畅的运行在PC和移动设备上,兼容当前绝大部分浏览器(IE6/7/8/9/10/11,chrome,firefox,Safari等),底层依赖轻量级的Canvas类库ZRender,提供直观,生动,可交互,可高度个性化定制的数据可视化图表。
zrender是一个轻量级的Canvas类库
HTML5新特性
一、绘画 canvas
HTML5 标签用于绘制图像(通过脚本,通常是 JavaScript)。
二、用于媒介回放的video和audio元素
HTML5 DOM 为audio和video元素提供了方法、属性和事件。
这些方法、属性和事件允许您使用 JavaScript 来操作audio和video元素。
三、本地离线存储localStorage长期存储数据,浏览器关闭后数据不丢失
localStorage :没有时间限制的数据存储
四、sessionStorage的数据在浏览器关闭后自动删除
sessionStorage :针对一个session的数据存储
五、语意化更好的内容元素,比如 article、footer、header、nav、section
1. 标签定义外部的内容。
2. 标签定义文档或者文档的一部分区域的页眉。 元素应该作为介绍内容或者导航链接栏的容器。
3.Nav元素可以用作页面导航的链接组,在导航链接组里面有很多的链接,点击每个链接可以链接到其他页面或者当前页面的其他部分。
4.section是带有语义的标签。
六、表单控件,calendar、date、time、email、url、search
七、新的技术webworker, websocket, Geolocation
Flex布局主轴及排列方向
flex-direction属性决定主轴的方向:
.box {flex-direction: row | row-reverse | column | column-reverse;}
1、row(默认值):主轴水平方向,起点在start。
2、row-reverse:主轴水平方向,起点在end。
3、column:主轴为垂直方向,起点在start。
4、column-reverse:主轴为垂直方向,起点在end。
交叉轴:
justify-content: center; 控制的是垂直方向的居中
align-items: center; 控制的是水平方向的居中
关于绝对定位absolute相对于父元素定位的问题
当该元素设置为绝对定位absolute时,如果父元素为相对定位relative,则该元素相对于父元素定位;反之则相对于初始包含块即body元素定位。
CSS图片旋转
transform:rotate(119deg);
CSS图片裁剪
clip:rect(0px 50px 200px 0px)
在Vue.js
只更改其中一个data的值,所有vue的指令操作都会重新执行
盒子模型的设置:
强缓存和协商缓存
强缓存: 通过http的response header中的Cache-Control和Expire两个字段控制。
主要看Cache-Control:
private:仅浏览器可以缓存
public:浏览器和代理服务器都可以缓存
max-age=xxx:过期时间
no-cache:不进行强缓存
no-store:不强缓存,也不协商缓存
强缓存步骤:1,第一次请求文件时,缓存中没有该信息,直接请求服务器。
2,服务器返回请求的文件,并且http response header中cache-control为max-age=xxx
3,再次请求该文件时,判断是否过期,如果没有过期,直接读取本地缓存,如果已经过期了,则进行协商缓存。
协商缓存:它的触发条件有两点、
第一点是 Cache-Control 的值为 no-cache,则会促发协商缓存。
第二点是 max-age 过期了,触发协商缓存。
Map和forEach区别
1、map速度比foreach快
2、map会返回一个新数组,不对原数组产生影响,foreach不会产生新数组,foreach返回undefined
3、map因为返回数组所以可以链式操作,foreach不能
4, map里可以用return ,而foreach里用return不起作用,foreach不能用break,
forEach()
返回值是undefined