这是我的第一篇博客,一起参与掘金新人创作活动,开启写作之路
前言
此篇文章是我汇总了最近面试的一些问题。由于个人原因最近一直在面试,面试的不是很理想,但是觉得有必要把自己遇见的面试问题整理下来,刚好最近遇见新人活动,就来凑个热闹。
特别说明
面试自我介绍
这基本上大家都已经该看的都看了,我也就不多说了。我这边提几个我们大佬教我的几个关键节点
- 工作中遇见的问题,解决问题的办法
- 自己在工作中有没有封装过组件
- 面试的时候尽量展现自己的长处
- 讲述自己的项目,在工作中有没有一些很特别的项目等等等等
基本内容
HTML相关
1 讲述一下html语义化
- 专业的人做专业的事情,同样,
html语义化,就是用正确的标签做正确的事情。 - html语义化就是让页面的内容结构化,以便于浏览器,搜索引擎去解析
- 没有css样式的情况下,也能够以文档的格式西安市,容易阅读
- 搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于SEO
- 使阅读源代码的人对网站更加容易对网站分块,便于理解
2 你有用过哪些前端性能优化的方法?
- 减少http请求次数:
CSS Sprites,JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存,图片服务器。 - 前端模板 JS+数据,减少由于
HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数 - 用
innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。 - 当需要设置的样式很多时设置
className而不是直接操作style - 少用全局变量、缓存
DOM节点查找的结果。减少IO读取操作 - 避免使用
CSS Expression(css表达式)又称Dynamic properties(动态属性) - 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳
- 避免在页面的主体布局中使用
table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢
3 html5新特性,移除了哪些元素
-
新增特性
- 新增选择器
document.querySelector - 地理位置
geolocation - 媒体播放
video和audio - 本地存储
localStorage和sessionStorage - 语义化标签
article,footer,header,nav,section - 表单控件
canlendar,date,time,email,url,search - 全双工通信协议
websocket等等等等(基本上答出几个就行了,没有必要全答完吧)
- 新增选择器
-
移除的元素
- 纯表现的元素:
big,font,tt,u等等 - 对可用性产生负面影响的元素:
frame,frameset,noframes
- 纯表现的元素:
4 请描述一下 cookies,sessionStorage 和 localStorage 的区别?
cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存- 存储大小:
cookie数据大小不能超过4ksessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
- 有期时间:
localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据sessionStorage数据在当前浏览器窗口关闭后自动删除cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
5 Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?
- 页面被加载的时,
link会同时被加载,而@imort页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载import只在IE5以上才能识别,而link是XHTML标签,无兼容问题link方式的样式的权重 高于@import的权重 <!DOCTYPE>声明位于文档中的最前面,处于<html>标签之前。告知浏览器的解析器, 用什么文档类型 规范来解析这个文档- 严格模式的排版和
JS运作模式是 以该浏览器支持的最高标准运行 - 在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。
DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现
css相关
1 介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?
- 标准的css盒子模型有两种,一种是IE盒子模型,一种是w3c盒子模型。盒模型: 内容
content, 填充padding,边界margin, 边框border - 区别:
IE的content部分把border和padding计算进去
2 CSS优先级算法如何计算?
- 优先级就近原则,同权重情况下样式定义最近者为准
- 载入样式以最后载入的定位为准
- 优先级为:
!important > id > class > tag;!important比 内联优先级高
3 对BFC规范的理解?
- 一个页面是由很多个
Box组成的,元素的类型和display属性,决定了这个 Box 的类型 - 不同类型的
Box,会参与不同的Formatting Context(决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染,也就是说BFC内部的元素和外部的元素不会互相影响
4 移动端1px问题的解决方法
- 产⽣原因:主要是根据DPR设备像素⽐,是被默认缩放为100%的情况下,设备像素和css像素的⽐值
- 解决⽅案:
- WWDC对IOS系统给出的⽅案: 在WWDC⼤会上,给出来了1px⽅案,当写0.5px 的时候,就会显示⼀个物理像素宽度的
border,⽽不是css像素的border。所以在 ios 下可以:border:0.5px solid #eee; - 使用边框图片
border:1px solid transparent; border-img:url('******' ) 2 repeat; - 使⽤
box-shadow实现 - 使⽤伪元素
- 设置viewport的scale值
- WWDC对IOS系统给出的⽅案: 在WWDC⼤会上,给出来了1px⽅案,当写0.5px 的时候,就会显示⼀个物理像素宽度的
5 stylus/sass/less区别
- 均具有“变量”、“混合”、“嵌套”、“继承”、“颜色混合”五大基本特性
Scss和LESS语法较为严谨,LESS要求一定要使用大括号 {},Scss和Stylus可以通过缩进表示层次与嵌套关系Scss无全局变量的概念,LESS和Stylus有类似于其它语言的作用域概念Sass是基于Ruby语言的,而LESS和Stylus可以基于NodeJSNPM下载相应库后进行编译;
JS相关
1 介绍一下JavaScript的执行上下文
1 什么是执行上下文
答案: 执行上下文是评估和执行JavaScript代码环境的抽象概念,每当JavaScript代码在运行的时候,它都是在执行上下文中运行
2 执行上下文的类型
1 全局执行上下文
默认或者说的基础的上下⽂,任何不在函数内部 的代码都在全局上下⽂中,它会执⾏两件事:创 建⼀个全局的window对象(浏览器的情况下),并且设置this的值等于这个全局对象,⼀ 个程序中只会有⼀个全局执⾏上下⽂
2 函数执行上下文
每当⼀个函数每调⽤时,都会为还函数创建⼀个新的上下⽂,每个函数都有他⾃⼰的执⾏上下⽂,不过是在岸数被调⽤的时候创建的,函数上下⽂可以有任意多个,每当⼀个新的执⾏上下⽂ 被创建,他会按照定义的顺序执⾏⼀系列步骤
3 Eval函数执行上下文
执⾏在eval函数内部的代码也会有属于他⾃⼰的 执⾏上下⽂,但是由于JavaScript开发者并不经 常使⽤eval
3 执⾏栈
一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文。
当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。
引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文
4 创建执⾏上下⽂
1 创建阶段:
1 this值的决定,(this绑定)
1: 全局执⾏上下⽂: 指向的是全局对象(在浏览器中指向的是 window对象);
2: 函数执⾏上下⽂:this指向的值取决于该函数是如何被调⽤的 若被引⽤对象调⽤,那么this会被设置成那个对 象,否则this的值会被设置为全局对象或者 undefined(严格模式下)
2 创建语法环境组件
1 概念 : 词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用外部词法环境的空值组成。
2 环境记录器 (环境记录器是存储变量和函数声明的实际位置。)
3 外部环境的引用(外部环境的引用意味着它可以访问其父级词法环境(作用域))
4 类型:
1 全局环境((在全局执行上下文中)是没有外部环境引用的词法环境。全局环境的外部环境引用是 null。它拥有内建的 Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且 this的值指向全局对象:
2 函数环境(在函数环境中,函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数):
3 创建变量环境组件
2 执⾏阶段
1 变量赋值
2 函数引用
3 执行其他代码
2 什么是闭包,对闭包的理解(这个算是高频问题了)
闭包就是能够读取其他函数内部变量的函数。
有权访问另一个函数作用域中变量的函数,创建闭包基本常见方法就是函数套函数,通过新包的函数访问函数的局部变量。利用闭包这玩意可以突破作用域链
特性:
- 函数套函数
- 内部函数引用外部函数的参数和变量
- 参数和变量不会被垃圾回收机制回收
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中
闭包的另一个用处,是封装对象的私有属性和私有方法
好处:能够实现封装和缓存等;
坏处:就是消耗内存、不正当使用会造成内存溢出的问题
使用闭包的注意点:
- 由于闭包会使得函数中的变量都被保存在内存中内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏
- 解决方法: 在退出函数之前,将不使用的局部变量全部删除
3 说说你对作用域链的理解
- 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到
window对象即被终止,作用域链向下访问变量是不被允许的 - 简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期
4 解释一下变量的提升
ES5 提升有变量提升和函数提升。
原则:
1、所有申明都会被提升到作用域的最顶上;
2、同一个变量申明只进行一次,并且因此其他申明都会被忽略;
3、函数声明的优先级优于变量申明,且函数声明会连带定义一起被提升。
- 变量提升: 把变量声明提升到函数的顶部,但是变量赋值不会提升。
console.log(a); var a = 9; //输出为undefined /************/ var a; console.log(a); a = 9; //与上例等价 function add(){ y = 9; var y = 1; console.log(y); } add(); //输出1
注意:变量未声明,直接使用,输出变量 is not defined
- 函数提升:函数提升会将函数声明连带定义一起提升。 在JavaScript中函数的创建方式有三种:函数声明、函数表达式(函数字面量)、函数构造法(动态的,匿名的)。只有函数声明创建的函数会执行函数提升,字面量定义的函数(实质为变量+匿名函数)会执行变量提升。
test1();
function test1(){
console.log("可以被提升");
}
test2();
var test2 = function(){
console.log("不可以被提升");
}
console.log(test2);
var test2 = function(){
console.log("不可以被提升");
}
// 函数和变量同时提升
console.log(test3);
function test3(){console.log('func');}
var test3 = 'Mary';
5 js中数据类型的判断
-
基本数据类型: 为简单的数据段保存在栈中。分为:
String,Number,Boolean,Symbol,Undefined,Null -
引用数据类型: 指的是那些保存在堆内存中的对象,所以引用类型的值保存的是一个指针,这个指针指向存储在堆中的一个对象。除了上面的 6 种基本数据类型外,剩下的就是引用类型了,统称为
Object类型。细分的话,有:Object类型、Array类型、Date类型、RegExp类型、Function类型 等。 -
判断数据类型的方法:
- typeof: 是一个操作符。右侧跟一个一元表达式,并且返回这个表达式的数据类型。返回的结果用该类型的字符串形式表示
- instanceof: 是用来判断A是否为B的实例,表达式为 A instanceof B,如果A是B 的实例,则会返回true, 注意:instanceof检测的是原型,只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型
- constructor: 当一个函数F被定义的时候,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并且让其指向F的引用
- to String: 这个是object的原型方法,调用该方法,默认返回当前对象的[Class]。这是一个内部属性,其格式为[objectXxx],其中Xxx就是对象的原型
6 map、forEach区别
-
forEach: 遍历数组中的每一项,没有返回值,对原数组没有影响,不支持IEforEach()方法不会返回执行结果,而是undefined。 也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。 -
map: map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);有返回值,可以return出来
7 JavaScript原型,原型链 ? 有什么特点?(高频)
- 每个对象都会在其内部初始化一个属性,就是
prototype(原型),当我们访问一个对象的属性时 - 如果这个对象内部不存在这个属性,那么他就会去
prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念 - 关系:
instance.constructor.prototype = instance.__proto__ - 特点:
JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变
- 当我们需要一个属性的时,
Javascript引擎会先看当前对象中是否有这个属性, 如果没有的 - 就会查找他的
Prototype对象是否有这个属性,如此递推下去,一直检索到Object内建对象 - 原型:
JavaScript的所有对象中都包含了一个[__proto__]内部属性,这个属性所对应的就是该对象的原型- JavaScript的函数对象,除了原型
[__proto__]之外,还预置了prototype属性 - 当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型
[__proto__]。
- 原型链:
- 当一个对象调用的属性/方法自身不存在时,就会去自己
[__proto__]关联的前辈prototype对象上去找 - 如果没找到,就会去该
prototype原型[__proto__]关联的前辈prototype去找。依次类推,直到找到属性/方法或undefined为止。从而形成了所谓的“原型链”
- 当一个对象调用的属性/方法自身不存在时,就会去自己
- 原型特点:
JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
8 请解释什么是事件代理
- 事件代理(
Event Delegation),又称之为事件委托。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能 - 可以大量节省内存占用,减少事件注册,比如在
table上代理所有td的click事件就非常棒 - 可以实现当新增子对象时无需再次对其绑定
9 call、apply的区别 (懒得继续打字了)
10 防抖和节流(这段代码是我在网上找的栗子)
防抖
在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。可以通过函数防抖动来实现
// 使用 underscore 的源码来解释防抖动
/**
* underscore 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {boolean} immediate 设置为ture时,是否立即调用函数
* @return {function} 返回客户调用函数
*/
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
// 现在和上一次时间戳比较
var last = _.now() - timestamp;
// 如果当前间隔时间少于设定时间且大于0就重新设置定时器
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
// 否则的话就是时间到了执行回调函数
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
// 获得时间戳
timestamp = _.now();
// 如果定时器不存在且立即执行函数
var callNow = immediate && !timeout;
// 如果定时器不存在就创建一个
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
// 如果需要立即执行函数的话 通过 apply 执行
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
整体函数实现
对于按钮防点击来说的实现
- 开始一个定时器,只要我定时器还在,不管你怎么点击都不会执行回调函数。一旦定时器结束并设置为 null,就可以再次点击了
- 对于延时执行函数来说的实现:每次调用防抖动函数都会判断本次调用和之前的时间间隔,如果小于需要的时间间隔,就会重新创建一个定时器,并且定时器的延时为设定时间减去之前的时间间隔。一旦时间到了,就会执行相应的回调函数
节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
/**
* underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。
* 如果想忽略结尾函数的调用,传入{trailing: false}
* 两者不能共存,否则函数不能执行
* @return {function} 返回客户调用函数
*/
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 之前的时间戳
var previous = 0;
// 如果 options 没传则设为空对象
if (!options) options = {};
// 定时器回调函数
var later = function() {
// 如果设置了 leading,就将 previous 设为 0
// 用于下面函数的第一个 if 判断
previous = options.leading === false ? 0 : _.now();
// 置空一是为了防止内存泄漏,二是为了下面的定时器判断
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
// 获得当前时间戳
var now = _.now();
// 首次进入前者肯定为 true
// 如果需要第一次不执行函数
// 就将上次时间戳设为当前的
// 这样在接下来计算 remaining 的值时会大于0
if (!previous && options.leading === false) previous = now;
// 计算剩余时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果当前调用已经大于上次调用时间 + wait
// 或者用户手动调了时间
// 如果设置了 trailing,只会进入这个条件
// 如果没有设置 leading,那么第一次会进入这个条件
// 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
// 其实还是会进入的,因为定时器的延时
// 并不是准确的时间,很可能你设置了2秒
// 但是他需要2.2秒才触发,这时候就会进入这个条件
if (remaining <= 0 || remaining > wait) {
// 如果存在定时器就清理掉否则会调用二次回调
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
// 没有的话就开启一个定时器
// 并且不能不能同时设置 leading 和 trailing
timeout = setTimeout(later, remaining);
}
return result;
};
};
11 垃圾回收机制的理解
- 对于在
JavaScript中的字符串,对象,数组是没有固定大小的,只有当对他们进行动态分配存储时,解释器就会分配内存来存储这些数据,当JavaScript的解释器消耗完系统中所有可用的内存时,就会造成系统崩溃。 - 内存泄漏,在某些情况下,不再使用到的变量所占用内存没有及时释放,导致程序运行中,内存越占越大,极端情况下可以导致系统崩溃,服务器宕机。
JavaScript有自己的一套垃圾回收机制,JavaScript的解释器可以检测到什么时候程序不再使用这个对象了(数据),就会把它所占用的内存释放掉。- 针对
JavaScript的来及回收机制有以下两种方法(常用):标记清除,引用计数 - 标记清除
12 深拷贝和浅拷贝
12 谈谈This对象的理解
this总是指向函数的直接调用者(而非间接调用者)- 如果有
new关键字,this指向new出来的那个对象 - 在事件中,
this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window
13 如何解决回调地狱问题
Vue相关
1 Vue 是如何实现数据双向绑定的?
四个步骤,让你看明白如何实现数据绑定
-
实现一个监听器
Observer:对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty()对属性都加上setter和getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。 -
实现一个解析器
Compile:解析Vue模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。 -
实现一个订阅者
Watcher:Watcher订阅者是Observer和Compile之间通信的桥梁 ,主要的任务是订阅Observer中的属性值变化的消息,当收到属性值变化的消息时,触发解析器Compile中对应的更新函数。 -
实现一个订阅器
Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者Watcher,对监听器Observer和 订阅者Watcher进行统一管理。
2 说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single-page application )就是一个web应用,在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,
SPA相对对服务器压力小; - 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理; 缺点:
- 首屏加载耗时多:为实现单页
Web应用功能及显示效果,需要在加载页面的时候将JavaScript、CSS统一加载,部分页面按需加载; - 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- 不利于
SEO优化
3 v-show 与 v-if 有什么区别?
v-if:真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show:不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的display属性进行切换。v-if适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show则适用于需要非常频繁切换条件的场景
4 谈谈你对 Vue 生命周期的理解
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码:
beforeCreate: 进行数据和方法的初始化created: 已经完成数据和方法的初始化beforeMount: 开始渲染dommounted:可以渲染dombeforeUpdate:data中的数据即将被更新;updated:data中的数据更新完毕;beforeDestroy: 实例即将销毁;destroyed:实例已被销毁;
5 组件之间的传值通信
-
父子组件
- 父传子: 父组件中通过
v-bind绑定一个属性,子组件中通过props接收父组件中的绑定的属性 - 子传父: 子组件通过广播的方式
$emit将值传递给父组件,父组件中通过一个函数去接收子组件中传递过来的值
- 父传子: 父组件中通过
-
爷孙组件:
ref或者eventBus -
兄弟组件: 可以通过中间媒介父组件进行传递值的中转
6 MVVM
- 概念:是将数据模型数据双向绑定的思想作为核心,在
view层和model之间没有联系,通过ViewModel进行交互,而且Model和ViewModel之间的交互是双向的,因此视图的数据变化会同时修改数据源,而数据源的变化也会立即反映view - 和
MVC的区别:MVC是比较直观的架构模式,可以分为三部分,model(用户界面)view(数据保存)control(业务逻辑),View传送指令到Controller,Controller完成业务逻辑后,要求Model改变状态,Model将新的数据发送到View,用户得到反馈,所有通信都是单向的。而MVVM则是数据双向绑定
7 nextTick是如何实现的
nextTick可以让我们在下次DOM更新循环结束之后执行延迟回调,用于获得更新后的DOM
vue用异步队列的方式来控制DOM更新和nextTick回调先后执行
microtask因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
因为浏览器和移动端兼容问题,vue不得不做了microtask向macrotask的兼容(降级)方案
8 Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上
state:存储数据mutations:更新数据的方法actions: 调用mutations方法,更新state数据getters: 对state中的数据进行预处理
9 vue-router
vue-router 有 3 种路由模式:hash、history、abstract
其中,3 种路由模式的说明如下:
hash: 使用URL hash值来作路由。支持所有浏览器,包括不支持HTML5 History Api的浏览器;history: 依赖HTML5 History API和服务器配置。具体可以查看HTML5 History模式;abstract: 支持所有JavaScript运行环境,如Node.js服务器端。如果发现没有浏览器的API,路由会自动强制进入这个模式.
10 虚拟 DOM 的优缺点
- 优点:
- 保证性能下限: 框架的虚拟
DOM需要适配任何上层API可能产生的操作,它的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM操作性能要好很多,因此框架的虚拟DOM至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限; - 无需手动操作
DOM: 我们不再需要手动去操作DOM,只需要写好View-Model的代码逻辑,框架会根据虚拟DOM和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率; - 跨平台: 虚拟
DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、weex开发等等。
- 保证性能下限: 框架的虚拟
- 缺点: 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
11 vue中key值的作用
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
Vue 的 diff 过程可以概括为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。具体有无 key 的 diff 过程。
12 vue中监听和计算的区别
computed用于处理复杂的逻辑运算,主要和methods储存方法来进行区分;methods储存方法,,computed储存需要处理的数据值;methods每次都会调用,computed有缓存机制,只有改变时才执行,性能更佳Wacth用来监听数据变化,可以监听的来源有三部分:prop,data、computed内的数据- 区别
- 相同点:
computed和watch都起到监听/依赖一个数据,并进行处理的作用 - 不同点: 它们其实都是
vue对监听器的实现,只不过computed主要用于对同步数据的处理,watch则主要用于观测某个值的变化去完成一段开销较大的复杂业务逻辑。能用computed的时候优先用computed,避免了多个数据影响其中某个数据时多次调用watch的尴尬情况。
- 相同点:
13 Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题
受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 来实现为对象添加响应式属性,那框架本身是如何实现的呢?
我们查看对应的 Vue 源码:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
我们阅读以上源码可知,vm.$set 的实现原理是:
- 如果目标是数组,直接使用数组的
splice方法触发相应式; - 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用
defineReactive方法进行响应式处理(defineReactive方法就是Vue在初始化对象时,给对象属性采用Object.defineProperty动态添加getter和setter的功能所调用的方法)
14 虚拟DOM 实现原理
虚拟 DOM 的实现原理主要包括以下 3 部分:
- 用
JavaScript对象模拟真实DOM树,对真实DOM进行抽象; diff算法 — 比较两棵虚拟DOM树的差异;pach算法 — 将两个虚拟DOM对象的差异应用到真正的DOM树。
15 介绍一下Vue中的Diff算法
在新老虚拟DOM对比时
- 首先,对比节点本身,判断是否为同一节点,如果不为相同节点,则删除该节点重新创建节点进行替换
- 如果为相同节点,进行
patchVnode,判断如何对该节点的子节点进行处理,先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除) - 比较如果都有子节点,则进行
updateChildren,判断如何对这些新老节点的子节点进行操作(diff核心)。 匹配时,找到相同的子节点,递归比较子节点
在
diff中,只对同层的子节点进行比较,放弃跨级的节点比较,使得时间复杂从O(n^3)降低值O(n),也就是说,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
16 keep-alive的理解
它是vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
- 缓存组件
- 提供
include与exclude属性,允许组件有条件地进行缓存,其中exclude的优先级比include高,max最多可以缓存多少组件实例
17 你有对 Vue 项目进行哪些优化?
- 事件及时的销毁
- 图片资源懒加载
v-if和v-showcomputed和watch- 优化无限列表性能
v-for遍历必须为item添加key,且避免同时使用v-if- 路由懒加载
- 第三方插件的按需引入
- 服务端渲染
SSRor 预渲染
浏览器相关
1. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
- 浏览器地址输入
url回车 - 浏览器查找当前
url是否有缓存,并且比较缓存是否过期 DNS解析URL对应的IP- 根据
IP建立TCP连接(3次握手) - 发送
http请求 - 服务器处理请求,浏览器接受
http响应 - 浏览器解析并且渲染页面
- 关闭
TCP连接(四次握手)
2.讲述一下什么是3次握手四次握手
TCP协议在传输数据的时候,客户端(Client)跟服务端(Server)会建立连接,然后把需要传输的文件进行分段,以及提供可靠传输和流量控制,在数据传输完成后,当前的会话也要断开连接,避免资源浪费。其实TCP的三次握手就是建立连接的过程 ,而四次握手就是连接断开的过程
3.http和https的区别
HTTPS协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。HTTP的连接很简单,是无状态的。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
写在最后
这里面的问题基本上都是自己在面试的时候遇见的,里面的答案我前文也说了,会撞,当然答案不是恒定的,是需要靠自己的理解来回答的。希望大家都能有自己满意的工作哈~
附件是自己补充的思维导图,图片有点大,建议大家下载之后看啊 ps:这个思维导图如果有人觉得眼熟,原创不是我,是我文章开头的大佬汇总的,我只是补充完全而已。