一、HTML
1、浏览器输入地址后会发生什么?
1⃣️ 浏览器向DNS服务器查找输入URL寻找对应的IP地址
2⃣️ DNS服务器返回网站的IP地址
3⃣️ 浏览器根据IP地址与目标web服务器在80端口上建立TCP连接
4⃣️ 浏览器获取请求页面的html代码
5⃣️ 浏览器在显示窗口内渲染HTML
6⃣️ 窗口关闭时,浏览器终止与服务器的连接
简单来说就是:读取缓存 => dns解析 => tcb链接 => 发送http请求 => 服务器处理请求 => 返回报文 => 渲染 => 结束链接
2、input标签常用的类型有哪些?
input标签的类型 :button、file、checked、radio、password
upload上传文件的底层原理就是利用input标签的file属性
3、常用的伪类选择器有哪些?
1⃣️ a标签必须按顺序的伪类
2⃣️ 结构伪类
3⃣️ 状态伪类
4⃣️ css3 新增伪类 nth-child和nth-of-type
ele:nth-of-type(n)是指父元素下第n个ele元素
ele:nth-child(n)是指父元素下第n个元素且这个元素为ele,若不是,则选择失败
4、五大浏览器内核及兼容前缀?
Internet - Trident - ms
Firefox - Gecko - moz
Chrome - Webkit - webkit
Opera - Presto - o
5、常用的通信方式有哪些?都有什么优缺点?
1⃣️ http协议 : 请求头 请求行 空行 和请求数据四部分组成。是一种面向对象的协议,简单快速灵活,无连接(每次只处理一次,请求返回后即断开释放链接)、无状态(请求返回后,无记忆)
2⃣️ websocket: 浏览器不需要发送请求 服务端数据返回 减少了对服务器端的压力
6、前端常见的设计模式?
1⃣️ 单例模式:这种设计模式的思想是确保一个类只有唯一实例,一般用于全局缓存,比如全局window,唯一登录浮窗等
2⃣️ 工厂模式:工厂模式是创建对象的常用设计模式,为了不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就称为一个工厂
3⃣️ 观察者模式:也叫发布订阅模式,在这种模式中,一个订阅者订阅发布者,当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者
7、响应式布局如何实现?
原因:
在之前使用固定宽高有一定的局限,例如屏幕越来越宽时,因为定得宽是固定的,这样会让页面不美观;屏幕越来越小时,因为定宽的局限会让用户看不到完整的页面内容。在这种情况下,响应式布局就出现了。
实现方式:
1.百分比布局
百分比布局是相对于父元素进行布局,百分比能够对width,height,padding,margin等来进行布局,但是对于border,font-size则不能。百分比布局只适合用于对大面积的地方进行布局,细节之处则不够完美。
2. 媒体查询
媒体查询是可以根据不同屏幕分辨率来设置不同的css样式,通过这种方式来适配不同的屏幕,相对于百分比来说,可以对局部的细节进行调整,@media screen and(min-width/max-width:1200px),screen就是客户端屏幕的信息。不足之处就是代码比较冗余,需要在不同的分辨率下进行不同的css样式处理。
3.rem响应式布局
rem是一个单位,跟px,em类似,1rem就等于根元素font-size的值,有两种方式可以进行设置:
1⃣️ 通过媒体查询来设置,在不同分辨率下给html的font-size进行赋值,比较麻烦
2⃣️ 通过js动态的计算font-size的值,当屏幕尺寸发生改变的时候调用封装的函数
function rem() {
document.documentElement.style.fontSize = document.documentElement.ckientWidth/7.5 + 'px';
}
rem();
window.onresize = rem;
4.弹性盒子布局
通过display:flex来布局,给父元素设置display:flex,在给子元素设置想要变更的属性。
flex:1做了什么操作?flex: 1; === flex: 1 1 auto; 给父元素设置display:flex后,给子元素设置flex: 1 可以实现让弹性盒模型对象根据子元素的个数自动进行空间的均匀分配。
5.vw响应式布局
vw是基于视口的布局方案,在需要使用的时候在meta标签的视口中声明。1vw等于视口宽度的1%,1vh等于视口高度的1%。
二、CSS
1、盒模型怪异盒模型?
标准的盒模型 = width(content) + border + padding + margin
IE怪异盒模型下盒子的大小 = width(content + border + padding)+ margin
回答技巧:平常开发中使用标准盒模型,xxx ui框架底层原理就是这样使用的,这样设置了宽高不会因为border等位置跑偏去做修改
2、DIV如何居中显示?
1、父元素position:relative,子元素宽高已知,但是不需要考虑宽高
div {
width: 200px;
height: 200px;
background-color: orchid;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
2、父元素position:relative,子元素宽高已知
div {
width: 200px;
height: 200px;
position: absolute;
background-color: orchid;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -100px;
}
3、不需要考虑宽高,但是有兼容性
div {
position: absolute;
top: 50%;
left: 50%;
width:100px;
height:100px;
transform: translate(-50%, -50%);
}
4、display:table-cell 文字的居中,需要宽高
div {
display:table-cell;
vertical-align:middle;
text-align:center;
}
5、基于盒模型(Flex:1 自动分配剩余空间)
div {
width: 700px;
height: 600px;
background-color: orange;
display: flex;
align-items: center;
justify-content: center;
}
回答技巧:一开始使用的是xxx,后来在某网站发现flex
3、让元素在窗口内消失?
1⃣️ display:none (不占据位置)
2⃣️ visibility:hidden (占据位置)
3⃣️ z-index:-1
4⃣️ opcity:0
4、对BFC规范(块级格式化上下文)的理解?
BFC 就是“块级格式化上下文”的意思,创建了 BFC 的元素就是一个独立的盒子,不过只有 Block-level box 可以参与创建 BFC, 它规定了内部的 Block-level Box 如何布局,并且与这个独立盒子里的布局不受外部影响,当然它也不会影响到外面的元素。BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。计算 BFC 的高度时,浮动元素也参与计算。
BFC 有以下特性:
1⃣️ 内部的 Box 会在垂直方向,从顶部开始一个接一个地放置
2⃣️ Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin 会发生叠加,每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此
3⃣️ BFC 的区域不会与 float box 叠加
触发条件:
1⃣️ float的值不为none(默认)
2⃣️ overflow的值不为visible(默认)
3⃣️ display的值为inline-block、table-cell、table-caption
4⃣️ position的值为absolute或fixed
三、JS
1、js常见的数据类型有哪些?
基本数据类型:Number、String、Boolean、Null、Nudefined
其他数据类型:Array、Date、Math等
2、数据类型判断?
- 基本类型和引用类型的区别:
基本数据存放在栈中,引用数据类型存放在堆中。
基本数据类型可直接访问,引用数据类型要按引用访问,访问堆中的路径。
基本数据类型赋值后二者完全独立,而引用类型二者指向同一个对象。 - 类型判断的方法
1⃣️ typeof()
它可以检测普通数据类型,比如number,string等基本数据类型,对于引用数据类型都返回object
2⃣️ instanceof
fn instanceof Fn,可以检测引用数据类型,可以判断一个变量是否是某个类型或者实例,由构造类型判断数据类型,返回true或者false,不能判断null和undefined
3⃣️ constructor
fn.constructor == Array,通过对象的construnctor直接判断他是否和他的原型相同,如果构造函数的原型被更改了那么就会检测失败
4⃣️ Object.prototype.toString.call()
目前最完美的解决方案,直接在括号里写入想要判断的类型即可,它可以直接从原型里面找到要判断的类型。
3、数组常用的方法?
Push - 数组最后添加元素
Pop - 数组最后删除元素
Unshift - 数组前面添加元素
Shift - 数组前面删除元素
Slice(start,end) - 截取数组
Concat - 数组合并
instanceOf - 数组元素的索引
Join - 数组转字符串
4、原型和原型链?
- 原型
每个函数都有一个prototype对象,这个对象就是函数的原型,每个函数的原型中都有一个constructor属性,就是构造函数的本身,构造函数被实力化之后,都会有一个__proto__属性,这个属性就是函数的原型,new实例化的对象没有prototype原型,有__proto__。函数的原型有一个优点,就是在原型中添加属性,所有的实例化对象都可以访问到。 - 原型链
当我们访问一个对象的属性的时候,会先从自身去查找,如果没有找到,就会去它的__proto__隐式的原型上查找,如果还没有找到就会在它的构造函数的prototype的__proto__中去查找,这样一层一层向上 查找就会形成一个链式结构,我们称之为原型链。
代码实现原理:先定义一个函数,里面写一些需要的属性,然后new实例化这个函数,通过调用这个实力化出来的新函数来查找属性,就可以实现一个原型链。
5、闭包?
要了解闭包,先要了解变量的作用域,分为全局作用域和局部作用域,js中函数的内部可以访问函数外部的变量,但是在外部无法访问内部的变量。闭包就是定义在一个函数内部的函数,可以实现访问函数内部的变量,闭包的本质就是把函数的内部和外部连接起来。
优点:可以模仿块级作用域,防止全局变量的污染,减少创建全局变量
缺点:防止了全局变量的污染,但是容易导致内存泄漏,会让使用过的变量无法回收,一直占用内存
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
6、js继承?
1⃣️ 构造函数继承
把父函数的this指向改为子函数的this指向,从而实现继承。
优点:可以传递参数,借助call和apply等改变this的指向。call的第二个参数是与改变指向中的方法的参数要对应,apply的第二个参数是一个数组,数组中的元素要与改变的方法中的参数对应,bind方法使用后会返回一个函数,所以第二个参数要写()里写
缺点:只能继承父函数本身的属性,父类原型的方法不能使用,无法实现构造函数的复用,每个新实例都有父类构造函数的副本
2⃣️ 原型链继承
可以把子函数的原型改变为父函数的实例,从而实现继承
优点:子类可以继承父类原型中的属性和方法
缺点:子类无法在继承的过程中给父类传递参数,因为子类的原型都是父类的实例化,引用类型上有一个实例对象修改了,其他实例也会跟着修改,因为他们的原型对象是共用的
3⃣️ 组合式继承
组合式继承就是把构造函数继承和原型链继承结合起来,从而实现继承
优点:它可以使用原型链实现对原型属性和方法的继承,又借助构造函数来实现对实例属性的继承,这样即通过原型上定义的方法实现了函数复用,又保证每个实例都有他自己的属性
缺点:在使用组合式继承的过程中父类的原型对象被调用了两次,这个是没必要的,而且子类实例的构造函数来自父类
4⃣️ 原型式继承:
用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。类似于复制一个对象,用函数来包装
缺点:所有实例都会继承原型上的属性。无法实现复用。(新实例属性都是后面添加的)
5⃣️ 寄生式继承
就是给原型式继承外面套了个壳子
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象
缺点:没有用到原型,无法复用
6⃣️ 寄生组合式继承
在寄生式继承的基础上,将父类的原型传入包裹的函数中,然后定义子类的构造函数,并在子类的构造函数中调用父类的构造函数,然后生成子类的对象,修改子类的构造函数属性,实现理想的继承
优点:效率高,原型链保持不变
7、深拷贝和浅拷贝?
深拷贝和浅拷贝都是针对引用数据类型来说的。深拷贝是A复制了B,当B变化时,A不会改变
浅拷贝是A复制了B,当B变化时,A也会改变
浅拷贝:只复制一层对象的属性或值。
1⃣️ Object.assign({},obj) 和 extend({},obj),第一个参数是一个控对象,第二个参数是要复制的对象,两者都是浅拷贝。extend(true,{},a,b) 第一个参数为true时,可以实现深拷贝。
2⃣️ ES6中的 … 扩展运算符可以实现浅拷贝
深拷贝:递归复制了所有的层级和地址引用,即拷贝多层,对象的每一级都会被拷贝出来。
1⃣️ JSON.parse(JSON.stringify(obj)) ,将对象转换为字符串,再将字符串转换为一个对象,这样来实现一个深拷贝
2⃣️ deep函数来实现,定义一个deep函数,参数是一个obj对象,首先判断obj是是否是一个对象,如果不是返回return false,如果是,定义一个空的数组作为存储空间,然后遍历obj,如果内容是一个对象的话就把循环的当前项添加到上面创建的新数组中,最后在返回这个数组,在需要进行深拷贝的地方可以调用这个函数。
8、对this的理解?
全局中的this指向的是window
bind中的this指向绑定的对象
call和apply中this指向新对象
new中this指向新对象
9、事件捕获?jquery中的事件捕获?
事件类型:键盘事件,鼠标事件,表单事件
事件:js和html之间的交互是通过事件实现的,事件就是文档或浏览器窗口发生的一些特定的交互瞬间
事件流:事件流描述的是页面中接收事件的顺序
- 事件冒泡:就是事件开始时由嵌套最深的元素接收,然后逐级向上传播到最外面的节点
阻止冒泡:JS中event.stopPropagation();vue修饰符 .stop
阻止默认行为:JS中的event.prevent Default();vue中的.prevent - 事件捕获:由嵌套最外面的节点先触发事件,最后是嵌套最深的节点触发事件
设置事件捕获:
1⃣️ JS中的addEventListener(事件名字,function(){},true/false); true/false如果在未指定的情况下,默认是false,向上冒泡的事件不会触发
2⃣️ .captrue 在定义的事件后面写click.captrue - 事件委托:利用事件冒泡的原理,指定某一事件程序来管理某一类型的所有事件,比如子元素的事件交由父元素进行处理
jq:$("#ul").on("click",'li',function(){
js:ul.onclick = function(){
$(this);
var event = event || window.event;
var target = event.target || event.srcElement;
})
}
10、js防抖截流?
1⃣️ 防抖:n秒内事件只执行一次,如果再次执行则重新进行处理
2⃣️ 节流:n秒内事件只执行一次,没有返回结果再次执行的话直接return
11、重绘和回流 ?
1⃣️ 重绘:重绘是指元素的样式,字体等发生变化,但是在浏览器的位置不发生变化的过程
2⃣️ 回流:回流是指元素的位置,大小,属性等发生变化浏览器重新渲染的过程
回流必将引起重绘,重绘不一定会引起回流
12、字符串和数组互转?
字符串转数组:
a = “aaa,bbb,ccc”;
b = a.split(“,”) // [aaa,bbb,ccc]
c = a.split(“”) // [a,a,a,b,b,b,c,c,c]
数组转字符串:
a = [0,1,2,3,4] b = a.join(‘-’) // 0-1-2-3-4
13、foreach和map有什么区别?
foreach返回的是undefined,map返回一个新数组,原数组不会改变
foreach不会终止循环或者跳出循环,除非捕获异常
14、在for循环中,因索引错乱导致删除有漏?
解决:使用倒序,从索引最后一项开始遍历
for (let len = this.correctJson.length, i = len - 1; i >= 0; i--) {
if (this.correctJson[i].paperId == data.paperId) {
this.correctJson.splice(i, 1);
}
}
15、前端常见状态码?
200 - 请求成功
205 - 服务器请求成功,但是没有返回
301 - 重定向
400 - 语法错误,无法被服务器理解,或请求参数有误
403 - 服务器拒绝
404 - 找不到
408 - 请求超时
500 - 服务器端报错
四、AJAX
1、AJAX的原理及过程?
ajax主要是实现客户端和服务端异步通信的效果,实现页面的局部刷新,被大多数主流浏览器支持,不需要插件,提高web程序性能,无需刷新页面就可以获取数据,提高来用户体验。
document.querySelector('#btnAjax').onclick = function () {
// (1)创建异步对象
var ajaxObj = new XMLHttpRequest();
// (2)设置请求的参数。包括:请求的方法、请求的url。
ajaxObj.open('get', '02-ajax.php');
// (3)发送请求
ajaxObj.send();
//(4)注册事件。 onreadystatechange事件,状态改变时就会调用。
//如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
ajaxObj.onreadystatechange = function () {
// 为了保证 数据 完整返回,我们一般会判断 两个值
if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
// 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
// 5.在注册的事件中 获取 返回的 内容 并修改页面的显示
console.log('数据返回成功');
// 数据是保存在 异步对象的 属性中
console.log(ajaxObj.responseText);
// 修改页面的显示
document.querySelector('h1').innerHTML = ajaxObj.responseText;
}
}
}
2、get和post有什么区别?
1⃣️ get传递参数显示在url中,post参数通过http消息发送给服务器
2⃣️ get提交数据有长度限制,原因是特定的浏览器url有长度限制,而post请求一般没有长度限制
3⃣️ get方式请求的数据会被缓存起来,相对来说不太安全,post相对来说安全一些,同等条件下,get传输数据会比post快一些
五、移动端
1、移动端一像素的问题及如何解决?
在移动端,由于屏幕分辨率的不同,现在分为一倍屏、二倍屏、三倍屏。在不同的分辨率上,有可能一像素被渲然成二个像素点或者三个像素点,所以在实际写代码的时候,我们写的border: 1px solid #000; 可能实际被渲然为 2px/3px
解决:border-image来代替border;使用viewport +rem;
2、rem和em有什么区别?是如何计算的?
rem的大小是根据html根目录下的字体大小进行计算的,当我们改变根目录下的字体大小的时候,下面字体都改变,rem不仅可以设置字体的大小,也可以设置元素宽、高等属性
em字体大小是根据父元素字体大小设置的
根元素html的font-size一般为16,那么1rem=16px
如果设计师给的图是750px,那么1rem就是750/16=46.875px,也就是1rem代表46.875px
每一个px都要转换成rem,比较麻烦,可以使用less编译器
@rem:46.875rem; (750/16)
.box {
width: 60/@rem;
}
六、H5,CSS3新特性?
1、H5
1⃣️ 用于绘画的canvas元素
2⃣️ 播放音频视频的video和audio元素
3⃣️ 本地离线存储:
localStorage,sessionStorage ,和Cookie。都是保存在浏览器端的,而且都是同源的。
localStorage :长期存储数据,浏览器关闭后数据不丢失,除非主动删除数据,否则永远不会消失,不参与和浏览器的通信,大小有限制。
方法:localStorage.getItem()/setItem()/removeItem()/clear()
sessionStorage: 只在当前浏览器窗口中有效,即使刷新页面或者进入同源的另一个页面,数据也会存在,数据会在浏览器关闭后消失。
方法:SessionStorage.getItem()/setItem()/removeItem()/clear()
Cookie:可以设置过期的有效时间,存放数据一般为4k左右,数量不超过20个,可以与服务端进行通信,如果保存数据量过多会带来性能问题,需要自己封装。应用场景:用户注册,用户登陆,购物车等
4⃣️ 增加语义化标签:article、footer、header、nav、section
5⃣️ 表单控件:date、time、email、url、search等
2、CSS3
1⃣️ 渐变 background-image: linear-gradient线性渐变、radial-gradient径向渐变
2⃣️ 阴影:box-shadow
3⃣️ 过度动画:transition :all/none。 transition-duration:定义过渡效果花费的时间
4⃣️ transform属性:translate(x,y) 元素移动位置;scale(x,y) 改变元素宽度和高度;rotate() 旋转角度
5⃣️ 盒子模型,flex弹性布局,媒体查询等
七、ES6?
- ES6兼容性:ie10+,火狐,谷歌等,依赖:Node.js / webpack
- 在Node.js环境中运行:Node.js是服务器端的javascript,对ES6的支持度更高
- webpack是一个javascript应用程序的静态模块打包器,它会构建关系依赖,将需要的打包成一个或多个bundle。浏览器只支持部分ES6语法,需要依赖webpack的babel-loader将ES6转换为浏览器都支持的ES5格式。
webpack主要有四部分构成:入口,输出,loader,插件
低版本浏览器编译转换:在线转换和提前编译(browser.js,引入=》type改成type/babel)
1、let和const两个声明变量的关键字
var : 可以重复声明,变量可以修改,全局范围有效,变量会提升
let : 只可声明一次,变量可以修改,具有块级作用域,不存在变量提升
const : 只读,不可修改
2、新增箭头函数
()=> {}
// 如果只有一个参数,()可以省略
// 如果只有一个return {} 可以省略
区别:
1⃣️ 箭头函数和普通函数的写法不同,箭头函数更为简洁;
2⃣️ this指向不同,箭头函数指向的是定义时的对象,普通函数指向的是调用它的对象;
3⃣️ 箭头函数不可以当作构造函数,不可以用new命令,否则会报错;
4⃣️ 箭头函数没有arguments对象,得到的是外层函数的参数
箭头函数最初是为了解决this的指向问题,箭头函数里面的this是定义的时候就确定的,定义好后无法改变。箭头函数一定是匿名函数,适用于没有复杂逻辑和无副作用的纯函数场景中,比如map和filter回调函数中。最好不要在最外层定义箭头函数,可以在箭头函数外部包一层普通函数,将this控制在可见范围,避免全局作用域的污染。
3、新增解构赋值
解构赋值是javascript的表达式,针对数组或者对象进行模式匹配,可以将属性或者值从对象或者数组中取出,赋值给其他变量。本质是模匹配,等号左右两边的模式相同,右边的值(解构源)就会赋值给左边的变量(解构目标),如果匹配不成功就会返回undefined。
应用场景:交换变量的值;函数返回多个属性或值时,如果只想返回一个,可以使用解构赋值返回一个;函数参数的定义;json数据提取等
// 数组解构
1. let [a, b, c] = [1, 2, 3];
// a = 1 b = 2 c = 3
2. let [a, [[b], c]] = [1, [[2], 3]];
// a = 1 b = 2 c = 3
3. let [a, , b] = [1, 2, 3];
// a = 1 b = 3
4. let [a = 1, b] = [];
// a = 1, b = undefined
5. let [a, ...b] = [1, 2, 3];
// a = 1 b = [2, 3]
6. let [a, b, c, d, e] = 'hello';
// a = 'h' b = 'e' c = 'l' d = 'l' e = 'o'
7. let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1]; // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
(当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。)
// 对象解构
1. let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa' bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
2. let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello' // y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
3. let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined // y = 'world'
4. let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10 b = 20 rest = {c: 30, d: 40}
5. let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
传统定义函数:
function student (
name,
age,
) {...}
调用函数:
student ( 'admin', 24);
解构赋值:
student ({
name: 'admin',
age: 24,
})
4、新增模版字符串
传统的字符串拼接繁琐,容易有误,模版字符串以反引号``作为标识,可以当作普通字符串使用,也可以定义多行,也可以在字符串中加入变量,也可以{ }里当作js表达式,代码简洁易懂
5、新增了set和map数据结构
1⃣️ set:类似数组,成员都是唯一的,没有重复的值,可以用来数组去重,本身是一个构造函数,可以new一个实例。set实例的方法分为两类:操作方法和遍历方法。
操作属性:.size:返回Set实例的成员总数
操作方法:add(value):添加某个值,返回Set结构本身;delete(value):删除某个值,返回一个布尔值,表示删除是否成功;has(value):返回一个布尔值,表示该值是否为Set的成员;clear() :清除所有成员,没有返回值;
去重一
let s = new Set();
let s = new Set( [1,2,3,4,4] );
console.log(s); // {1,2,3,4}
console.log(s.size); // 4
去重二
let s = Array.from( new Set( [1,2,3,4,5,5] ) )
console.log(s); // [1,2,3,4,5]
console.log(s.size); // 5
遍历方法:set结构的实例有四个遍历方法,keys() 返回键名的遍历器,values()返回键值的遍历器,entries()返回键值的遍历器,forEach()使用回调函数遍历每一个成员,没有返回值
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item); // red green blue
}
for (let item of set.values()) {
console.log(item); // red green blue
}
for (let item of set.entries()) {
console.log(item); // ["red", "red"] ["green", "green"] ["blue", "blue"]
}
let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value * 2) ) // 2 4 6
2⃣️ map:map数据结构类似于对象,也是键值对的形式,突破了传统的只允许字符串当作键的限制,允许各种类型的值当作键
操作属性:.size:返回 Map 结构的成员总数
const map = new Map([
["name","张三"] , ["title","Author"]
]);
map.size // 2
map.has("name") // true
map.get("name") // "张三"
操作方法:
set(key, value),设置键名key对应的键值为value,然后返回整个 Map 结构,如果key已经有值,则键值会被更新,否则就新生成该键
get()方法读取key对应的键值,如果找不到key,返回undefined
has(key)
delete(key)
clear()
const m = new Map();
m.set('edition', 6); // 键是字符串
m.set(262, 'standard'); // 键是数值
m.set(undefined, 'nah'); // 键是 undefined
m.get('edition') // 6
m.has('edition') // true
m.has('years') // false
m.has(262) // true
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
遍历方法:keys():返回键名的遍历器,values():返回键值的遍历器,entries():返回所有成员的遍历器,forEach():遍历Map的所有成员
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key); // "F" "T"
}
for (let value of map.values()) {
console.log(value); // "no" "yes"
}
for (let item of map.entries()) {
console.log(item[0], item[1]); // "F" "no" // "T" "yes"
}
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value); // "F" "no" // "T" "yes"
}
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value); // "F" "no" // "T" "yes"
}
let numbers = [1,3,45,67];
numbers.forEach(function(item,index,arr){
console.log(item,index); // 1 0,3 1,45 2,67 3
})
6、symbol
symbol是ES6新增的一个数据类型,是js的第七种数据类型,是一种基本数据类型,作为对象属性的标识符,Symbol 函数前不能使用 new 否则会报错,原因在于 Symbol 是一个原始类型的值,不是对象
7、for循环改为for of
for in是ES5的标准,用来遍历Key值,遍历对象和数组,可以遍历所有的可枚举属性,包括原型,所以用来遍历对象更合适。for of是es6的标准,用来遍历value值,遍历数组,不能遍历普通对象。for in 遍历的数组的索引,for of遍历的是数组的元素值
8、新增了模块化
在ES6之前是JavaScript是没有模块系统的,那么他就无法将一个大程序拆分成若干个互相依赖的小文件;如果需要进行模块化操作,就需要从第三方引入;在ES6中就提供了模块化,才让JavaScript第一次支持了module。ES6的模块化分为导出(export)和导入(import)两个模块;
模块可以理解为和函数代码块一样的功能,是封装对象的属性和方法的JavaScript代码,他可以是某单个文件,变量或者函数而且引入模块和引入脚本是有区别的,前者更多的是按需引入加载,后者是无论使用还是不使用,全部一次性引入和加载,类似于通过script标签引入Jquery等,这些都是一次性引入的;
导出:Es6模块导出分为默认导出和指定导出,默认导出的话可以用任意名字来接收,比如导出的是export default module1; 接收的时候可以 import x,如果要一次导出多个,需要用{}包裹,接收时必须与导出时同名
导入:接收用import {name,age} from ‘./module.js’
在导出中除了export关键字,每一个声明必须和脚本中的一样,因为导出的函数和声明需要有一个名称;导出数据,只需要在里边写上数据名就可以export {name,age} 导出函数只需要你定义好函数,然后把函数名导出就可以export sum;
9、新增了扩展运算符
(…)扩展运算符:用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。
数组扩展运算符
1⃣️ 替代函数的apply方法
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
2⃣️ 复制或者合并数组
// ES5 的写法
const a1 = [1, 2];
const a2 = a1.concat();
// ES6 的写法
const a1 = [1, 2];
const a2 = [...a1];
//或
const [...a2] = a1;
// ES5 的写法
[1, 2].concat(more);
arr1.concat(arr2, arr3);
// ES6 的写法
[1, 2, ...more];
[...arr1, ...arr2, ...arr3]
对象扩展运算符
1⃣️ 扩展某个函数的参数,引入其他操作
function baseFunction({ a, b }) {
// ...
}
function wrapperFunction({ x, y, ...restConfig }) {
// 使用 x 和 y 参数进行操作
// 其余参数传给原始函数
return baseFunction(restConfig);
}
2⃣️ 取出参数对象的所有可遍历属性,拷贝到当前对象之中
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
//上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。
// 写法一
const clone1 = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
);
// 写法二
const clone2 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
3⃣️ 合并两个对象
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
//如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同于
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let x = 1, y = 2, aWithOverrides = { ...a, x, y };
// 等同于
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
4⃣️ 修改现有对象的部分属性
let newVersion = {
...previousVersion,
name: 'New Name' // Override the name property
};
10、promise
在ES5中处理异步,都是选择回调函数的形式,在请求成功的函数里面继续写函数,当逻辑复杂需要多个逻辑操作的时候一层层的嵌套就会形成回调地狱,会导致回调函数嵌套复杂,代码不够直观,且难以维护,promise的出现就是解决回调地狱的问题。
promise其实就是一个方法,一个页面后续的js执行,从愈发语法上讲,它是一个对象,可以获取异步操作的消息,也是一个内置的构造函数,通过new来实例化,是异步编程的一种解决方案,采用链式调用的方式且让代码简洁明了容易维护。
promise有两个默认的参数,resolve和reject,返回成功执行resolve,返回失败执行reject。promise有三种状态:pending(进行中),fulfilled/resolved(成功),rejected(失败)。
promise原型上的方法:
1⃣️ then()是成功之后执行的,有两个参数,第一个参数是成功之后执行的,第二个参数是失败之后执行的
2⃣️ catch()是失败之后执行的,可以捕获异常
3⃣️ all()处理多个异步时非常有用,调用多个回调成功后的统一处理
4⃣️ race()在处理数据的时候,哪个结果获得的快就返回哪个,不管结果本身是成功还是失败,可以用来测试接口的响应速度
11、generator(异步编程、yield、next()、await 、async)?
Generator是一个函数,可以在函数内部通过yield返回一个值(此时,Generator函数的执行会暂定,直到下次触发.next());
使用流程:
1⃣️ 创建一个Generator函数的方法是在function关键字后添加*标识
2⃣️ 在调用一个Generator函数后,并不会立即执行其中的代码,函数会返回一个Generator对象,通过调用对象的next函数,可以获得yield/return的返回值
3⃣️ 无论是触发了yield还是return,next()函数总会返回一个带有value和done属性的对象。
4⃣️ value为返回值,done则是一个Boolean对象,用来标识Generator是否还能继续提供返回值
需要注意的是:Generator函数的执行时惰性的,yield后的代码只在触发next时才会执行,next()我们可以在调用next()的时候传递一个参数,可以在上次yield前接收到这个参数
应用场景:
1⃣️ 因为Generator对象是一个迭代器,所以我们可以直接用于for of循环
2⃣️ 模拟实现Promise执行器 async await
async 是 ES7 才有的与异步操作有关的关键字,在定义函数的时候在函数的前面加上async 函数返回的是promise
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。也就是async可以没有await,但是await必须要有async
八、Vue
1、为什么要使用vue?
vue的两大特性。
组件化的开发模式,便于维护结构简单
数据驱动视图,双向绑定,减少dom操作,只需要操作数据即可
简单易上手
2、vue的双向数据绑定原理?
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
实现过程:
1⃣️ 实现一个监听器observer,用来劫持并监听所有属性,如果有变动,就通知订阅者
2⃣️ 实现一个订阅者watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图
3⃣️ 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模版数据以及初始化响应相应的阅读器
3、简述vue的生命周期?
指元素从创建,挂载,更新,销毁的一个过程。
beforeCreate // 挂载元素($el)和数据对象(data)都未初始化,可加loading事件
created // 数据对象已生成,结束loading
beforeMount // 挂载元素和数据对象都已初始化,此时仍是虚拟dom
mounted // vue实例挂载完成,data.filter成功渲染
beforeUpdata // 数据更新时触发
updated // 数据更新
beforeDestory // 实例销毁之前,在这一步,vue 实例仍然完全可用
destoryed // 实例销毁之后,Vue 实例指示的所有东西都会解绑定
4、vue中v-if和v-show的区别?
都是控制元素的显示和隐藏。
v-if真正的条件渲染,支持v-else,true时显示,false时不显示
v-show控制元素的display属性,进行简单的css样式切换
如果需要频繁切换,则使用v-show 较好,如果运行条件不太可能改变,使用v-if较好
5、v-if和v-for的优先级?如何解决?
v-for优先于v-if被解析
在外层嵌套template,在这一层进行v-if判断, 然后在内部进行v-for循环
6、methods,computed,watch的区别?
computed基于它的依赖缓存,只有相关依赖发生改变时才会重新取值
watch每次监听的值变化时,都会执行回调,watch 选项允许我们执行异步操作
- 相同点:都是以函数为基础,本质的话computed依赖于data中的数据;场景的话:methods是应用于逻辑和方法的处理;其余两者都是监听
- 不同点:
computed: 基于它的依赖缓存,只有相关依赖发生改变时才会重新取值,是在html和dom加载后马上执行的
watch:每次监听的值变化时,都会执行回调,watch 选项允许我们执行异步操作
methods:只要调用就会触发,需要有一定的触发条件,比如点击事件
data中的数据:methods可以带参数和返回值,computed必须带返回值不允许带参数,watch不允许带参数和返回值。
7、vue是如何监听数组的?
通过Object.defineProperty()劫持数组为其设置getter和setter后,调用的数组的push、splice、pop等方法改变数组元素时并不会触发数组的setter,这就会造成页面上并不能及时体现这些变化,也就是数组数据变化不是响应式的
在vue开发时,对于响应式数组,其实是重写了数组的push、pop、shift、unshift、splice、sort、reverse七种方法,重写方法在实现时除了将数组方法名对应的原始方法调用一遍并将执行结果返回外,还通过执行ob.dep.notify()将当前数组的变更通知给其订阅者,这样当使用重写后方法改变数组后,数组订阅者会将这边变化更新到页面中,可以做到响应式的变化
8、vue 中 key 的作用?
在生成虚拟dom的过程中,相同的组件产生相同的dom结构,不同的组件产生不同的dom结构,同一层级的dom节点,他们可以通过唯一的id进行区分,这个id就是key。
作用:高效的更新虚拟dom
8、vue第一次加载页面会触发哪几个?一进页面调用接口写在哪个里面?
第一次加载页面触发:beforeCreate, created, beforeMount, mounted 这几个
进入页面加载接口根据情况可以写入created和mounted created的话还没有找到dom节点 不好操作 可写在mounted里
9、data为什么是函数?
这样每个实例可以维护一份被返回对象的独立的拷贝,传进去一个函数,这样每一个实例的data属性都是独立的,不会相互影响。如果传进来是对象,new出来的两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改
10、vue组件之间的传值方式?
1.父组件主动获取子组件的数据和方法
父组件中调用子组件的时候 定义ref
this.$refs.headerChild.属性
this.$refs.headerChild.方法
2.子组件主动获取父组件的数据和方法
子组件使用
this.$parent.属性
this.$parent.方法
3.父组件向子组件传值
通过props传值
4.子组件向父组件传值
子组件发起emit事件,父组件用on去监听
5.兄弟组件传值
兄弟组件:创建一个bus.js文件,在文件内导出一个空的vue对象,在需要传值和接收的两个组件中都引入bus.js文件,在传值的组件通过bus.$
emit(定义的事件,值)来传递值,在需要接收的组件中通过bus.$
on(自定义事件(data)=>{}) 来接收值,data参数就是传递过来的值
6.vuex传值改值
11、v-loader?
基于webpack的一个的loader,解析和转换 .vue 文件,用来打包、转译js或者css文件,就是把代码转换成浏览器能识别的,还有一些打包、压缩的功能等
12、vuex是什么?都有哪些内容?
vuex是vue中对状态管理的一种模式
- state:数据仓库,数据源载体,保存着所有的全局变量
- getters:用于获取state中的数据
- mutations: 唯一可以提交改变state的状态,相当于修改全局变量的事件函数,触发的时候需要事件驱动
- actions:相当于mutation,不同在于action提交的是mutation,而不是直接变更状态,可以包含任意的异步操作,接受一个与store实例具有相同方法和属性的context对象,里面有commit方法和state属性等,触发action通过store.dispatch方法触发
- modules:把状态管理模块化,各自的组件构成各自的模块(mapAction),用来处理全局状态的
13、vuex数据之前是如何传递和接收的?
页面通过mapAction异步提交事件到action,action通过commit把对应参数同步提交到mutation,mutation会修改state中对于的值,最后通过getter把对应值跑出去,在页面的计算属性中,通过mapGetter来动态获取state中的值
14、vuex中action和mutaction的区别?
Action 提交的是 mutation,而不是直接变更状态
Action 可以包含任意异步操作
说简单点就是mutation用于同步执行,action用于异步执行,可以多重分发mutation
15、 vuex中存储的数据刷新页面会消失如何解决?
原因:浏览器刷新页面,vuex会重置state中的数据,所以之前存储的数据会消失
解决一:使用安装vuex-persistedstate(其原理会有一定的内存消耗,可以指定某些数据进行永久存储)
原理:将数据存储在localStorage或sessionStorage或cookie中,在刷新页面的瞬间再从本地存储去获取,然后进行赋值操作
解决二:直接将数据存储在本地存储中,需要的时候从本地存储中获取数据
16、vue-router进行页面跳转的方式?
1、router-link
<router-link to='需要跳转到的页面的路径'>
2、this.$router.push({ path:’/user’})
params方式:
// 对象写法
this.$router.push({
name: 'router name',
params: {
key: value
...
}
})
// 字符串写法
this.$router.push('/index/page1/params1/param2')
// 对象写法
this.$router.push({
name: 'router name',
query: {
key: value
...
}
})
// 字符串写法
this.$router.push('/index/page1?param1=param1¶m2=param2')
params和query的区别在于:query的参数会以字符串拼接的形式(key=value)展示在地址栏
3、this.$router.replace(path:’/’)
17、router和route有什么区别?
router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history对象。经常用的跳转链接 this.$
router.push
route相当于当前正在跳转的路由对象,可获取name,path,params,query等
this.$
route.params. 和this.$route.query. 来获取
18、vue路由传参如何实现?
query:
1⃣️ 视图导航模式: :to="/path"+参数
2⃣️ 编程导航模式:通过this.$
router.push({path:‘路径’,参数}),获取参数的时候使用this.$
router.query.参数名来获取
params:
1⃣️ 视图导航模式: :to=’/路由地址/’+item.id"
2⃣️ 编程导航模式:通过this.$
router.push({name:‘路径’,参数}),获取参数的时候使用this.$
router.params.参数名来获取
区别:
1⃣️ query通过path来引入的,params通过name来引入,参数获取query是用过.query来获取,params是通过.params来获取
2⃣️ query类似于ajax中的get传参,params类似于post传参,前者在浏览器显示参数,后者在浏览器不显示参数
19、路由导航模式有几种,如何实现?
vue-router提供了导航守卫,通俗来讲就是一个拦截器,钩子函数主要有三个:to(即将要进入的路由对象),from(当前导航正要离开的路由),next(进行管道中的下一个钩子,必须调用,参数false为终止执行,true为继续执行)
- 全局守卫:
1⃣️ beforeEach(全局前置守卫):可以在这个函数中进行一些判断,比如用户当前的状态是否可以通过,进入下一个页面,如果条件通过则进入next()
2⃣️ beforeResolve(全局解析守卫):和beforeEach类似,区别是在导航被确认前,同时在所有组件内守卫和异步路由组件解析之后,解析守卫就被调用
3⃣️ afterEach(全局后置钩子):他和守卫不同的是,这些钩子不会接收next函数也不会改变导航本身 - 路由专属守卫:beforeEnter
- 组件内部守卫
1⃣️ beforeRouteEnter():渲染该组件对应的路由被确认前调用,不能获取组件实例this,因为在守卫执行前,组件实例还没有被创建,可以通过next来访问组件实例
2⃣️ beforeRouteUpdate():在当前路由改变,组件被复用的时候调用,可以访问到实例this
3⃣️ beforeRouteLeave():导航离开该组件的对应路由时调用,可以访问this实例
20、vue中的prop
- props:会接手不同的数据类型和设置默认值,有六种数据类型:number,string,boolean,object,function,array
- props数据是单项数据传递,父不影响子,子不影响父,而且在组件中不能直接修改props的值,vue会发出警告。vue中父子组件的关系可以总结为props向下传递,事件向上传递,父组件通过prop给子组件下发数据,子组件通过事件给父组件发送消息。prop是单向绑定的,当父组件的属性发生变化时,将传导给子组件,反之则不会,是为了防止子组件无意间修改了父组件的状态,避免应用的数据流变得难以理解。
- 解决办法:将想要更改的值传递给父组件,在父组件中修改再传递给子组件
- 具体步骤:先将值传递给子组件,子组件通过prop接收并使用,然后通过$emit发起事件给父组件,并将值传递给父组件,父组件接收子组件所发起的事件,定义一个方法,在方法中改变这个值,父组件再将值传递给子组件,形成一个闭环,这样就可以解决问题
21、vue 常用修饰符?
- .prevent: 提交事件不再重载页面
- .stop: 阻止单击事件冒泡
- .self: 当事件发生在该元素本身而不是子元素的时候会触发
- .capture: 事件侦听,事件发生的时候会调用
- .once 只执行一次
- .trim - 输入首尾空格过滤
- .number - 输入字符串转为有效的数字
- .lazy - 取代 input 监听 change 事件
- .passive:addEventListener中的第三个参数,表示 listener 永远不会调用 preventDefault()
- .native:组件绑定当前组件的事件是不会触发的,需要用native才能触发
- .sync:对prop进行双向绑定
22、keep-alive 的作用?
keep-alive是Vue中的内置组件,能在切换过程中把状态保留在内存中,防止重复渲染DOM,包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
prop:
- include: 字符串或正则表达式。只有匹配的组件会被缓存
- exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存
keep-alive生命周期钩子函数:activated、deactivated
场景:Vue中前进刷新,后退缓存用户浏览数据
列表页面 =>点击进入详情页=> 后退到列表页 要缓存列表原来数据=> 重新进入列表页面 => 获取最新的数据
23、vue 自定义指令如何使用?
自定义指令是用来操作DOM的。尽管Vue推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的DOM操作,并且是可复用的。
比如谷歌图片的加载做得非常优雅,在图片未完成加载前,用随机的背景色占位,图片加载完成后才直接渲染出来。用自定义指令可以非常方便的实现这个功能。
24、什么是虚拟dom,和 diff 算法?
可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。将原本需要在真实dom进行的创建节点,删除节点,添加节点等一系列复杂的dom操作全部放到vdom中进行,这样就通过操作vdom来提高直接操作的dom的效率和性能。虚拟DOM的最终目标是将虚拟节点渲染到视图上
diff算法分为一下几个步骤:
- 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
- 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
虚拟DOM就是对于复杂的文档DOM结构提供了一种方便的工具,用于最小化的dom操作本质上是通过JAVAScript来描述DOM之间的元素关系的;作用是能够更高效的渲染和更新视图实现原理是通过Dff算法来实现,就是比如当前页面的数据发生改变时,dff算法会比较同一层级的节点,如果节点类型不同,那么会直接干掉前面的节点,重新创建一个新节点,然后插入这个新节点,而不会再比较这个节点以后的子节点了,如果节点类型相同,那么会直接重新计算该节点的属性,对节点进行更新这就是dff的一个默认执行的操作,通常我们去渲染节点时,需要结合key属性来去使用,作为唯一标识,能够让dff正确识别当前节点,如果数据发生改变,或插入了新的节点时,这个时候dff算法就可以快速的找到当前需要修改的节点位置,去做视图更新,从而减少了dom重复的操作,大大地优化了网页性能
25、Object.defineProperty()方法有何作用?
- 作用:Object.defineProperty方法会直接在对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。在vue中通过getter和setter函数来实现双向绑定
- 语法:他有三个参数object,propName,descriptor
- 参数解释:object:要定义属性的对象,返回的也是;propName:要定义或修改的属性名称。descriptor:要定义或修改的属性描述符
描述符:
1⃣️ value描述符:设置属性值,默认值为undefined。
2⃣️ writable描述符:设置属性的值是否可写,默认值为true。
3⃣️ enumerable枚举描述符:设置属性是否可枚举,就是是否使用for/in语句或Object.keys()函数遍历访问,默认为true。
4⃣️ configurable描述符:设置是否可配置属性特性,默认为true。如果为false,将无法删除该属性,不能够修改属性值,也不能修改属性的属性描述符。
26、Axios 拦截做过哪些?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中
优点:
1⃣️ 可以从浏览器中创建 XMLHttpRequests
2⃣️ 从 node.js 创建 http 请求
3⃣️ 支持 Promise API
4⃣️ 拦截请求和响应
5⃣️ 转换请求数据和响应数据
6⃣️ 自动转换 JSON 数据
可以在请求或响应被 then 或 catch 处理前拦截它们。可以为自定义 axios 实例添加拦截器;请求拦截可以用它做我们的loading 加载和数据的权限验证,包括我们所有的数据预加载也可以实现;
响应拦截:axios 拦截可以配置公用地址
27、vue 中数组中的某个对象的属性发生变化,视图不更新如何解决?
1⃣️ 第一种是数组的值改变,在改变数组的值的时候使用索引值去更改某一项,这样视图不会实时更新,这种情况是因为直接通过索引去改变数组,vue对象监听不到他的变化,所以没有更新;
解决方法:使用vue的变异方法pop(),push(),shift(),unshift(),revese(),sort(),splice()等方法也会触发视图更新
2⃣️ 第二种是改变了对象的某一项,但是其他依赖这个数据的视图没有更新,比如父组件和子组件公用一份数据,数据通过props传到子组件,在子组件中修改数据父组件中不会响应;
解决方法:
(1) 利用vue.set(object,key,val):例:vue.set(vm.obj,‘k1’,‘v1’);
(2) 利用Object.assign({},this.obj)创建新对象;如果是数组就把花括号改为中括号;
(3) 先删除掉那一项,然后再使用set去添加;
28、delete和Vue.delete删除数组的区别?
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。Vue.delete 直接删除了数组 改变了数组的键值。
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、vue2.0和3.0区别?
1⃣️ 默认进行懒观察
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力;3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效
2⃣️ 更精准的变更通知
比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行
3⃣️ 3.0 新加入了 TypeScript 以及 PWA 的支持
4⃣️ 部分命令发生了变化:
1.下载安装 npm install -g vue@cli
2.删除了vue list
3.创建项目 vue create
4.启动项目 npm run serve
5.默认项目目录结构也发生了变化:
(1).移除了配置文件目录,config 和 build 文件夹
(2).移除了 static 文件夹,新增 public 文件夹,并且 index.html 移动到 public 中
(3).在 src 文件夹中新增了 views 文件夹,用于分类 视图组件 和 公共组件
九、其他
1、跨域问题的产生及解决?
同源策略即 协议、域名、端口三者相同。防止受到XSS、CSFR等攻击。跨域是指一个域名下的脚本或者文档去访问另外一个域名下的资源。
1⃣️ jsonp 动态创建script标签,通过src属性,设置带参网址路径,实现跨域请求
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.baidu.com';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
2⃣️ ajax,通过ajax的url路径
$.ajax({
url: 'http://www.baidu.com',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "handleCallback", // 自定义回调函数名
data: {}
});
3⃣️ vue中安装vue-jsonp
this.$http.jsonp('http://www.baidu.com', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
jsonp的缺点只能实现get请求,不能实现post请求,设置post自动解析为get。
jsonp返回的数据格式和普通接口返回的数据格式有什么不同?
JSONP 方式返回来的是一个JS 函数名(数据块),可以理解为回调函数,可以将使用stringify将其序列化一下,便可以使用
4⃣️ websocket 协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
<div>user input:<input type="text"></div>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');
// 连接成功处理
socket.on('connect', function() {
// 监听服务端消息
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});
// 监听服务端关闭
socket.on('disconnect', function() {
console.log('Server socket has closed.');
});
});
document.getElementsByTagName('input')[0].onblur = function() {
socket.send(this.value);
};
</script>
5⃣️ 通过nginx配置一个代理服务器,反向代理访问路径,实现跨域
2、项目开发过程中是如何优化的?
减少http请求次数
利用缓存
图片等压缩
精灵图的使用
封装代码,进行复用
对大数据做文档碎片的处理
3、项目中遇到的困难如何解决?
1.使用layui插件,首次加载页面不显示select下拉框和radio单选
解决:
div添加 lay-filter=“entryForm” 属性
form.render(‘select’, ‘entryForm’); form.render(‘radio’, ‘entryForm’);
2. IE兼容问题
在jquery + layui 项目中 ie不兼容es6的语法 会报语法错误,比如:
1⃣️ 不支持let、const声明的变量和长量,改为用var去声明
2⃣️ 不支持模版字符串
3⃣️ 不支持解构赋值
4⃣️ 不支持箭头函数
5⃣️ 不支持display:flex布局格式,需要写兼容处理
3. 表格
在jquery + layui 项目中,表格不支持横向纵向双滚动,解决将表格整体拆分,拆分为表头部分,侧边部分,表格内容部分用html和css去控制
4.layui 弹窗点击确定按钮检验不通过不关闭
5、登录注册的实现过程?
6、less和sass?
less和sass都是css的预处理器,通过特殊的编写最终被解析成css。
优点:结构清晰,便于扩展,可嵌套编写,减少冗余代码。
区别:Less是基于JavaScript,是在客户端处理的,Sass是基于Ruby的,是在服务器端处理的。关于变量的区别就是Less用@,Sass用$。
常用的less:
1⃣️ 变量的声明
@link-color: #428bca;
@link-color-hover: darken(@link-color, 10%);
// 用法
a,
.link {
color: @link-color;
}
a:hover {
color: @link-color-hover;
}
2⃣️嵌套写法
#header {
color: black;
.navigation {
font-size: 12px;
}
.logo {
width: 300px;
}
}
3⃣️运算
@base: 5%;
@filler: @base * 2; // 10%
@other: @base + @filler; // 15%
@var: 1px + 5; // 6px
4⃣️继承
.a{
color: red;
}
.text {
background-color: #0d94f3;
&:extend(.a);
font-size: 12px;
}
5⃣️Mixins(混合)
.a, #b {
color: red;
}
.mixin-class {
.a(); // 这里也可以用 .a,效果于.a()相同
}
.mixin-id {
#b(); // 同上
}
// 以下是输出:
.a, #b {
color: red;
}
.mixin-class {
color: red;
}
.mixin-id {
color: red;
}
7、node.js了解?
因为npm是Node.js的包管理工具(package manager),npm的应用基于node.js
npm可以根据依赖关系,把所有依赖的包都下载下来并管理起来。否则,靠我们自己手动管理,肯定既麻烦又容易出错
Node.js 就是运行在服务端的 JavaScript
8、webpack?
webpack是一个模块打包工具,在webpack里面一切皆模块
加载 CSS,支持模块化、压缩、文件导入等特性
加载并且压缩图片文件
把 ES6 转换成 ES5
原理:从entry里配置的module开始递归解析entry依赖的所有module,每找到一个module,就会根据配置的loader去找对应的转换规则,对module进行转换后,再解析出当前module依赖的module,这些模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个组Chunk,最后webpack会把所有Chunk转换成文件输出,在整个流程中webpack会在恰当的时机执行plugin里定义的逻辑
9、图片懒加载?
当打开一个有很多图片的页面时,先只加载页面上看到的图片,等滚动到页面下面时,再加载所需的图片。这就是图片懒加载。
作用:减少或延迟请求数,缓解浏览器的压力,增强用户体验
实现方法:
1⃣️ 设置图片src属性为同一张图片,同时自定义一个data-src属性来存储图片的真实地址
2⃣️ 页面初始化显示的时候或者浏览器发生滚动的时候判断图片是否在视野中
3⃣️ 当图片在视野中时,通过js自动改变该区域的图片的src属性为真实地址
1.document.documentElement.clientHeight获取屏幕可视窗口大小;
2.document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
3. 判断当滚动条滚动到一定高度的时候就进行图片懒加载;
10、瀑布流原理?
瀑布流的实现原理就是通过比较每一列的高度,如果这一列高度低的话就在这一列进行添加
实现步骤:
1⃣️ 获取放图片的div和div里面的图片元素
2⃣️ 通过window.οnlοad=function(){}在页面加载的时候执行定义的函数
3⃣️ 定义一个变量columns计算出页面中有多少列=外层盒子的宽度box.offsetWidth/子元素的宽度items[0].offsetWidth最后计算的时候用parseInt除成整数
4⃣️ 循环所有的元素,在循环里面判断如果i小于columns的话就把每一项的top值设置为0;把元素的left值设置为元素的宽度乘以下标值;然后在循环外面定义一个空数组把每一项的高度都添加进去(items[i].offsetHeight);然后就是i>columns的逻辑,在这里面定义一个空变量为minHeight为最小高度,这个最小高度让他默认为0;在定义一个变量为Index为0;然后在写一个循环,循环内容就是每个上面存储每一项高度的数组,然后再通过if判断找出最小的高度值,逻辑就是如果当前循环的高度小于上面定义的minHeight最小高度的话,就让minHeight等于这个高度,然后再让index等于这个元素的下标
5⃣️ 最后一步就是把后面的元素插入到高度最小的元素下面,让循环的当前项的top值为最小高度,让他到这个元素的下面;left值为当前项距离外层盒子的Left值