目录

一、HTML

1.对 HTML 语义化的理解

2.区别:src 和 href

3.DOCTYPE 的作用

4.HTML5 的新特性

5.script 标签中的 defer 和 async

6. 行内元素 块级元素 空元素

7.meta 标签

8.JPG和PNG的区别

二、CSS

1.CSS 选择器,优先级

2.CSS 动画

3.CSS选择器效率从高到低的排序如下:

4.flex 布局

5.双飞翼布局

6.position 有几个值,absolute 是相对于谁的定位。

三.Javascript

1.原始值和引用值类型及区别

2.JavaScript 数据类型以及 typeof 返回值

3.如何判断一个对象是 Array 还是 Object?

4.类数组与数组的区别:

5.数组常用的api

6.bind、call、apply的区别:

7.new原理

8.闭包

9.原型和原型链

10.实现继承的几种方式以及他们的优缺点

11.浅拷贝与深拷贝

12.作用域

13.防抖与节流

14.Dom节点操作常用方法

15.BOM属性对象方法

16.ajax的请求过程

17.总结JS中string、math、array的常用的方法

18.js中“==”与"==="的区别

19.JS事件绑定(addEventListener)和普通事件(onclick)有什么区别

20.重绘和回流

21.函数柯里化及其通用封装

22.JS中EventLoop事件循环机制

23.前端性能优化

24.Object.create 和new

25.Location 对象

26.跨域解决方案

27.JS 垃圾回收机制

28.前端进阶之setTimeout 倒计时为什么会出现误差?

29.get请求和post请求的区别

30.数组去重的12中方法

四、数据结构

五、计网

1.ios七层模型

​编辑 2.TCP 和 UDP 的区别。

3.HTTP 2.0 新增

六、ES6

1.export和import

2.Promise

3.let、const和var的概念与区别

4.变量提升与暂时性死区

5. Symbol

6.Object.keys()方法

7.Object.assign()

8.set数据结构

9.Map数据结构

10.Proxy

11.Reflect

12.模板字符串

13.箭头函数(=>)

14.for…of 循环

15.jQuery的选择器怎么实现的

八.VUE

1、说说你对 SPA 单页面的理解,它的优缺点分别是什么?

2、v-show 与 v-if 有什么区别?

3、Class 与 Style 如何动态绑定?

4、怎样理解Vue的单向数据流?

5、computed 和 watch 的区别和运用的场景?

6、直接给一个数组项赋值,Vue 能检测到变化吗?

7、谈谈你对 Vue 生命周期的理解?

(1)生命周期是什么?

(2)各个生命周期的作用

8、Vue 的父组件和子组件生命周期钩子函数执行顺序?

9、在哪个生命周期内调用异步请求?

10、在什么阶段才能访问操作DOM?

11、父组件可以监听到子组件的生命周期吗?

12、谈谈你对 keep-alive 的了解?

13、组件中 data 为什么是一个函数?

14、v-model 的原理?

15、Vue 组件间通信有哪几种方式?

16、你使用过 Vuex 吗?

17、使用过 Vue SSR 吗?说说 SSR?

18、vue-router 路由模式有几种?

19、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?

20、什么是 MVVM?

21、Vue 双向绑定原理

22、Vue 框架怎么实现对象和数组的监听?

23、Proxy 与 Object.defineProperty 优劣对比

24、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

25、虚拟 DOM 的优缺点?

26、虚拟 DOM 实现原理?

27、Vue 中的 key 有什么作用?

28、你有对 Vue 项目进行哪些优化?

29、对于即将到来的 vue3.0 特性你有什么了解的吗?



一、HTML

1.对 HTML 语义化的理解

见标签知其意思     header main nav aside footer h1-h6 ul li

优点:结构清晰、方便搜索引擎搜素(seo)、方便理解维护

2.区别:src 和 href

  • src

比如常见的 <img src=""> 这里的 src 所指的是这个图片的所在的位置路径,script 和 iframe 也是,src 被解析的时候,会进行下载并编译,同时会暂停该文档其他资源的下载和处理,所以 js 文件的载入最好在 body 中,而不是在 head 中。

  • href

href被添加,元素会被识别为 css 文件进行处理,且不会停止其它资源的运行。所以建议用 link 加载 css 文件,而不是 @import。

常用的有:

<a href=""></a> 超链接

<link rel="stylesheet" href=""> 引用css样式

3.DOCTYPE 的作用

作用: DOCTYPE 是文档类型 document type 的缩写。 主要作用是告诉浏览器的解析器使用哪种 HTML 规范来解析页面。而如果 DOCTYPE 缺失,或形式不正确,会导致 html 文档或 HTML 文档不是以标准模式(浏览器对页面的渲染具有统一的规范)而是以混杂模式(不同浏览器有不同的的页面渲染)运行。

4.HTML5 的新特性

  • 简化了文档声明,HTML5 的文档声明只需要 <!DOCTYPE HTML>
  • 简化了编码声明,只需要 <meta charset='utf-8'>
  • 删除了一些能用 CSS 代替的就标签,比如 <i>
  • 增加了一些新标签,改善文档结构的有:<header><footer>等。减少插件依赖的 <canvas><audio> 等
  • 增加了一些新的 JavaScript 的 API ,比如地理定位、请求动画帧、离线存储等
  • 配合一些框架,例如 cordova 和 react 等,可以开发基于 HTML5 的移动应用。

5.script 标签中的 defer 和 async

作用:平常的 script 标签如果直接使用, html 会按顺序下载和执行脚本,并阻碍后续 DOM 的渲染。 如果 script 发生延迟,就会阻碍后续的渲染,使得页面白屏。

  1. defer
  • 异步下载文件
  • 不阻碍 dom 的渲染
  • 如果有多个 defer 会按顺序执行
  • 执行顺序:在文档渲染后执行,在 DOMContentLoader 事件调用前执行。

2.async

  • 异步下载文件
  • 不影响 dom
  • 如果有多个 defer 谁快先执行谁

推荐的应用场景:

defer:如果你的脚本代码依赖于页面中的 dom 元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据。

async:如果你的脚本并不关心页面中的 dom 元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据。

6. 行内元素 块级元素 空元素

  1. 行内元素: a \ b \ span \ input \ img \ strong \ br \ em \ big \ small
  2. 块元素: div \ ul \ ol \ li \ h1-h6 \ p \ dl \ dt \ address
  3. 空元素: img \ input \ link \ meta

7.meta 标签

meta 元素被用于规定页面的描述、关键词、文档的作者、最后的修改时间以及其他元数据。标签始终位于 head 元素中。

meta 属性:必选属性 content-进行描述说明的, 相当于键值; 可选属性 http-equiv、name 和 scheme, http-equiv-添加 http 头部内容,name-浏览器解析

包括:

①charset charset定义使用的字符编码

<meta charset="utf-8">

<meta http-euiqv="Content-Type" content="text/html;charset=utf-8">

②SEO

<meta name="keyword" content="csdn"> #网页关键词

<meta name="author" content="LiHua">

<meta name="description" content="we are world">#网页描述

③viewport

<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0">

8.JPG和PNG的区别

1.JPG是有损压缩格式,PNG是无损压缩格式

2.JPG图片没有透明背景,PNG有透明背景

二、CSS

1.CSS 选择器,优先级

Import>内联1000>id100>class==伪类==属性选择器10>元素选择器 伪元素选择器

2.CSS 动画

  • transition和animation的区别

大部分都是相同,都是随着时间改变元素的属性值,

他们的主要区别是transition需要触发一个事件才能改变属性,而animation不需要触发任何事件就可以实行。

transition为两帧,从from…to… 而animation可以一帧一帧的通过keyframes。

3.CSS选择器效率从高到低的排序如下

Id>class>元素选择器>兄弟选择器>子代选择器>后代选择器>属性选择器>伪元素 伪类

ID选择器 比如#header

类选择器 比如.promo

元素选择器 比如 div

兄弟选择器 比如 h2 + p

子选择器 比如 li > ul

后代选择器 比如 ul a 7. 通用选择器 比如 *

属性选择器 比如 type = “text”

伪类/伪元素选择器 比如 a:hover

4.flex 布局

flex是css3新增的一种布局方式,我们可以同时设置一个元素的display属性值设置为flex,
从而使它成为一个flex容器,它的所有子元素都成为它的项目。

一个容器默认有两条轴,一个水平轴,一条是与主轴垂直的交叉轴

flex-direction来指定主轴的方向。

justify-content来指定标签在主轴的排列方式,

使用align-items来指定元素在交叉轴的排序方式。

还可以使用flex-wrap来规定当一行排列不下时的换行方式。

对于一个容器的项目,

使用order属性来指定项目的排列顺序,

flex-grow来指定当前排序空间有剩余的时候,项目放大比例。

flex-shrink来指定当前排序空间不足时, 项目缩小比例。

5.双飞翼布局

6.position 有几个值,absolute 是相对于谁的定位。

absolute :生成绝对定位的元素,相对于最近一级的父元素,且该父元素不能是static,来进行定位。

fixed:(老IE不支持)生成绝对定位的元素,通常相对于浏览器窗口或 frame 进行定位。

relative:生成相对定位的元素,相对于其在普通流中的位置进行定位。

static:默认值。没有定位,元素出现在正常的流中

三.Javascript

1.原始值和引用值类型及区别

原始值(简单数据类型):存储在栈中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
        包含五种简单数据类型:undefined、null、boolean、number 和 string ;可以通过typeof 运算符来判断一个值是否在某种类型的范围内,如果它是原始类型,还可以判断它表示哪种原始类型。

引用值(复杂数据类型):存储在堆中的对象,放在变量的栈空间中的值是该对象存储在堆中的地址,也就是说,存储在变量处的值是一个指针(内存地址),指向存储对象的堆内存中。

包含:Object、function、array等。

2.JavaScript 数据类型以及 typeof 返回值

数据类型:number、string、null、boolean、object、undefined、symbol

typeof x   undefinedtypeof undefined  undefinedtypeof true   booleanfunction A() {}typeof A functionconst arr = []const obj = {}typeof arr objecttypeof obj object

3.如何判断一个对象是 Array 还是 Object?

在 typeof 判断 Array 或者 Object 的时候,其结果都是 object 那么我们怎么知道源对象是 Arrary 还是 Object 的呢?

4.类数组与数组的区别:

相同点:

  • 都可用下标访问每个元素,都有length属性。

不同点:

  • 数组对象的类型是Array,类数组对象的类型是object;
  • 类数组不具有数组所具有的方法,
  • 数组遍历可以用 for in 和 for 循环
  • 类数组只能用 for 循环遍历。

常见的类数组有: 函数的参数 arguments,arguments是一个类数组对象,包含着传入函数中的所有实参集合

5.数组常用的api

  • 字符转换 toString 方法将数组表示为字符串
  • join()方法,将数组里 各元素组合成字符串,但连接符可自己指定
  • unshift方法,是将元素插入数组的首部。
  • shift方法移除数组的第一个元素并将其返回
  • push方法一次可添加单个或多个元素到数组末端,也可以添加数组。
  • pop方法的作用是移除数组末尾的一个元素

6.bind、call、apply的区别

三者都是用于改变函数体内this的指向,但是bind与apply和call的最大的区别是:bind不会立即调用,而是返回一个新函数,称为绑定函数,其内的this指向为创建它时传入bind的第一个参数,而传入bind的第二个及以后的参数作为原函数的参数来调用原函数

apply和call都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部this的指向);apply和call的调用返回函数执行结果;

如果使用apply或call方法,那么this指向他们的第一个参数,apply的第二个参数是一个参数数组,call的第二个及其以后的参数都是数组里面的元素,就是说要全部列举出来;

Bind:返回绑定函数,传入参数数列

Apply:传入参数数组

Call:传入参数数列

7.new原理

mdn上把内部操作大概分为4步:

  • 创建一个空的简单JavaScript对象(即{ } );
  • 链接该对象(即设置该对象的构造函数)到另一个对象 ;(因此this就指向了这个新对象)
  • 执行构造函数中的代码(为这个新对象添加属性);
  • 如果该函数没有返回对象,则返回this。

8.闭包

有权访问另一个函数作用域中的变量的函数;

  • 第一,闭包是一个函数,而且存在于另一个函数当中
  • 第二,闭包可以访问到父级函数的变量,且该变量不会销毁
  • 作用1:隐藏变量,避免全局污染
  • 作用2:可以读取函数内部的变量

9.原型和原型链

在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。

  • 原型链  __proto__ 和 constructor

每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。实例原型中有一个属性constructor, 它指向函数对象,即构造函数。

通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果没有则会去原型对象中寻找,直到找到Object对象的原型,Object对象的原型,如果在Object原型中依然没有找到,则返回undefined。

原型链的终点:object.prototype=null

10.实现继承的几种方式以及他们的优缺点

其实js的继承本质上是通过原型链机制实现的扩展。不管是哪种继承方式,都是通过操作父类原型链和子类原型链形成关联关系实现的。只是不同实现中需要考虑不同的问题。在实际项目开发中,建议尽可能使用ES6的class extends实现。其他实现方式主要是理解背后的原理和思想。

  • 原型链继承

通过修改子类的原型为父类的实例,从而实现子类可以访问到父类构造函数以及原型上的属性或者方法。属性没有私有化,原型上属性的改变会作用到所有的实例上。

实现逻辑简单,但是父类构造函数中的引用类型(比如对象/数组),会被所有子类实例共享。其中一个子类实例进行修改,会导致所有其他子类实例的这个值都会改变

function Parent() {this.name = 'fedaily'
}
Parent.prototype.getName = function() {return this.name;
}
function Child() {}
// 这里也可以直接写出Child.prototype = Parent.prototype
// 但是这样就不能访问到父类的构造函数的属性了,即this.name
Child.prototype = new Parent()
var child = new Child()
console.log(child.getName()) // fedaily
  • 构造函数继承

在构造子类构造函数时内部使用call或apply来调用父类的构造函数

实现了属性的私有化,但是子类无法访问父类原型上的属性。

可以实现多继承

  • 组合继承

同时结合原型链继承、构造函数继承就是组合继承了。

同时解决了构造函数引用类型的问题,同时避免了方法会被创建多次的问题,但是父类构造函数被调用了两次。同时子类实例以及子类原型对象上都会存在name属性。虽然根据原型链机制,并不会访问到原型对象上的同名属性,但总归是不美。

  • 寄生继承

  核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点。

function Parent() {this.name = 'fedaily'
}Parent.prototype.getName = function() {return this.name
}function Child() {Parent.call(this)this.topic = 'fe'
}// 仔细看这个函数的实现
inherit(Child, Parent)
function inherit(child, parent) {var prototype = object(parent.prototype)prototype.constructor = childchild.prototype = prototype
}// 这个函数的作用可以理解为复制了一份父类的原型对象
// 如果直接将子类的原型对象赋值为父类原型对象
// 那么修改子类原型对象其实就相当于修改了父类的原型对象
function object(o) {function F() {}F.prototype = o;return new F();
}

这种方式就解决了组合继承中的构造函数调用两次,构造函数引用类型共享,以及原型对象上存在多余属性的问题。是推荐的最合理实现方式(排除ES6的class extends继承哈哈哈)。

  • ES6继承

ES6提供了class语法糖,同时提供了extends用于实现类的继承。这也是项目开发中推荐使用的方式。
使用class继承很简单,也很直观:

11.浅拷贝与深拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

赋值和浅拷贝的区别

当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址

浅拷贝的实现方式

  • Object.assign()

Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。当object只有一层的时候,是深拷贝

  • Array.prototype.concat()

修改新对象会改到源对象

12.作用域

为可访问变量,对象,函数的集合

作用域链是[[Scope]]中所存储的执行期上下文的集合

一个执行期上下文定义了一个函数执行的环境

13.防抖与节流

防抖就类似回城,打断就得重新回。

触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。在限定时间内,总是执行最后一次。

假设设置了五秒, 最后一秒生效(也就是说, 第一次点击和第二次点击之间间隔不到5秒, 则第一次的点击会被作废)

节流就类似技能需要冷却时间到了才能用。

指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。

假设设置了五秒, 第一秒生效, 后面的四秒作废.。

由于节流是第一次生效, 防抖是最后一次生效, 所以防抖获取到最新数据。

14.Dom节点操作常用方法

  • 访问/获取节点

document.getElementById(id);           //返回对拥有指定id的第一个对象进行访问

document.getElementsByName(name);      //返回带有指定名称的节点集合   注意拼写:Elements

document.getElementsByTagName(tagname);   //返回带有指定标签名的对象集合   注意拼写:Elements

document.getElementsByClassName(classname);  //返回带有指定class名称的对象集合 注意拼写:Elements

  • 创建节点/属性

document.createElement(eName);  //创建一个节点

document.createAttribute(attrName); //对某个节点创建属性

document.createTextNode(text);   //创建文本节点

  • 添加节点

document.insertBefore(newNode,referenceNode);  //在某个节点前插入节点

parentNode.appendChild(newNode);        //给某个节点添加子节点

  • 复制节点

cloneNode(true | false);  //复制某个节点  参数:是否复制原节点的所有属性

  • 删除节点

parentNode.removeChild(node);

15.BOM属性对象方法

  • window对象
  • location对象

它提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能

  • history对象

history对象是window对象的属性,它保存着用户上网的记录,从窗口被打开的那一刻算起

16.ajax的请求过程

一、原生JS中的Ajax

1、使用ajax发送数据的步骤

第一步:创建异步对象

var xhr = new XMLHttpRequest();

第二步:设置 请求行 open(请求方式,请求url):

// get请求如果有参数就需要在url后面拼接参数,

// post如果有参数,就在请求体中传递 xhr.open("get","validate.php?username="+name)

xhr.open("post","validate.php");

第三步:设置请求(GET方式忽略此步骤)头:setRequestHeader()

// 1.get不需要设置

// 2.post需要设置请求头:Content-Type:application/x-www-form-urlencoded

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

第四步:设置请求体 send()

// 1.get的参数在url拼接了,所以不需要在这个函数中设置

// 2.post的参数在这个函数中设置(如果有参数)

xhr.send(null) xhr.send("username="+name);

第五步:让异步对象接收服务器的响应数据

17.总结JS中string、math、array的常用的方法

一、String

①charAt()方法用于返回指定索引处的字符。返回的字符是长度为 1 的字符串

②indexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置

③split()方法将字符串分割成字符串数组,并返回此数组

④substring()方法用于提取字符串中介于两个指定下标之间的字符,其内容是从 start 处到 stop-1 处的所有字符,其长度为 stop 减 start。

二、Math

①ceil()方法 对一个数进行向上取整

语法:Math.ceil(x)  返回大于等于x 并且与x最接近的整数

②floor()方法 对一个数进行向下取整

语法:Math.floor(x) 返回小于等于x 并且与x最接近的整数

③round()方法 对一个数进行四舍五入取整

语法:Math.round(x) 返回最接近x的整数

④random()方法 返回介于 0 ~ 1 之间的一个随机数

语法:Math.random() 返回0.0 ~ 1.0 之间的一个随机数。

⑤max()方法返回指定的数中带有较大的值的那个数

语法:Math.max(x1,x2,.....) 返回x1,x2,.....中带有最高值的数字  如果有某个参数为 NaN,或是不能转换成数字的非数字值,则返回 NaN。

⑥min()方法返回指定的数中带有较小的值的那个数

三、Array

①concat()方法用于连接两个或多个数组。返回一个新的数组

②join()方法用于把数组中的所有元素放入一个字符串。返回一个字符串,不改变原数组。

③reverse()方法用于颠倒数组中元素的顺序,该方法会改变原来的数组,而不会创建新的数组。

⑤sort()方法使数组中的元素按照一定的顺序进行重新排序,该方法会改变原来的数组。

⑦map()方法按照原始数组元素顺序依次处理元素。map()方法可以方便的遍历数组。返回一个新数组,不会改变原始数组。

语法:arrayObject.map(function(currentValue,index,arr), thisValue)

function(currentValue, index,arr) 必需,函数,数组中的每个元素都会执行这个函数。

currentValue (必选 当前元素的值);

index (可选 当前元素索引)

arr (可选 当前元素属于的数组对象。后两者在回调函数中根据是否需要来决定是否作为参数传入)。

thisValue 可选,网上查到的说法是:对象作为该执行回调时使用,传递给函数,用作 "this" 的值。如果省略了 thisValue ,"this" 的值为 "undefined"。一般用不到该参数。

1 var oldArray=[1,2,3];2  var newArray=oldArray.map(function(val){3    return val+=3;4 });5  alert(newArray);//4,5,6

⑧reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。reduce()方法可以方便的迭代数组。不会改变原数组。

语法:arrayObject.reduce(function(previousValue, currentValue, currentIndex, arr), initialValue)

function(previousValue, currentValue, currentIndex, arr) 必需,函数,

reduce()方法可给该回调函数传入四个值:

previousValue (必选 上一次调用回调返回的值,或者是提供的初始(initialValue));

currentValue (必选 数组中当前被处理的元素);

currentIndex(可选 当前元素在数组中的索引);

arr (可选 调用 reduce 的数组) 。

initialValue 可选,若不设置。则初始值将变成数组中的第一项,而currentValue即从数组中的第二项开始。

1 var arr=[2,4,3,7];2 //数组中的元素进行累加3 var val=arr.reduce(function(n1,n2){4   return n1+n2;5 },0);

18.js中“==”与"==="的区别

== 表示相等 (值相等)

===表示恒等(类型和值都要相等)

js在比较的时候如果是 == 会先做类型转换,再判断值得大小,如果是===类型和值必须都相等。

19.JS事件绑定(addEventListener)和普通事件(onclick)有什么区别

普通事件(onclick)

普通事件就是直接触发事件,同一时间只能指向唯一对象,所以会被覆盖掉。代码如下:

var btn = document.getElementById("btn");btn.onclick = function(){undefinedalert("你好111");}btn.onclick = function(){undefinedalert("你好222");}

只运行你好222

事件绑定(addEventListener)

事件绑定就是对于一个可以绑定的事件对象,进行多次绑定事件都能运行。代码如下:

var btn = document.getElementById("btn");btn.addEventListener("click",function(){undefinedalert("你好111");},false);btn.addEventListener("click",function(){undefinedalert("你好222");},false);

运行结果会依次弹出你好111,你好222的弹出框。

onclick属性不适用以下元素:<base>、<bdo>、<br>、<head>、<html>、<iframe>、<meta>、<param>、<script>、<style> 或 <title>。

20.重绘和回流

我们增删DOM节点,修改一个元素的宽高,页面布局发生变化,DOM树结构发生变化,那么肯定要重新构建DOM树,而DOM树与渲染树是紧密相连的,DOM树构建完,渲染树也会随之对页面进行再次渲染,这个过程就叫回流

当你给一个元素更换颜色,这样的行为是不会影响页面布局的,DOM树不会变化,但颜色变了,渲染树得重新渲染页面,这就是重绘。

21.函数柯里化及其通用封装

函数柯里化,其实就是把多次调用的变量保存在闭包中,每次调用都查看一下变量数和原函数的形参数量是否相等。不相等就继续递归。直到相等为止就处理了。

22.JS中EventLoop事件循环机制

  • JavaScript的事件分两种,宏任务(macro-task)和微任务(micro-task)

宏任务:包括整体代码script,setTimeout,setInterval

微任务:Promise.then(非new Promise),process.nextTick(node中)

    <script>setTimeout(function () {undefinedconsole.log('setTimeout')}, 1000)new Promise(function (resolve) {undefinedconsole.log('promise')}).then(function () {undefinedconsole.log('then')})console.log('console')</script>

首先setTimeout,放入Event Table中,1秒后将回调函数放入宏任务的Event Queue中

new Promise 同步代码,立即执行console.log('promise'),然后看到微任务then,因此将其放入微任务的Event Queue中

接下来执行同步代码console.log('console')

主线程的宏任务,已经执行完毕,接下来要执行微任务,因此会执行Promise.then,到此,第一轮事件循环执行完毕

第二轮事件循环开始,先执行宏任务,即setTimeout的回调函数,然后查找是否有微任务,没有,时间循环结束

总结:

事件循环,先执行宏任务,其中同步任务立即执行,异步任务加载到对应的的Event Queue中(setTimeout等加入宏任务的Event Queue,Promise.then加入微任务的Event Queue),所有同步宏任务执行完毕后,如果发现微任务的Event Queue中有未执行的任务,会先执行其中的任务,这样算是完成了一次事件循环。接下来查看宏任务的Event Queue中是否有未执行的任务,有的话,就开始第二轮事件循环,依此类推。

23.前端性能优化

减少请求数:合并资源(精灵图),减少http请求数。

加快请求速度:预解析DNS,减少域名数,CDN分支。

缓存:http请求协议缓存,离线缓存,离线数据缓存(localStorage)。

渲染:js/css优化,服务器渲染,加快顺序。

24.Object.create 和new 

Object.create是内部定义一个funcition对象f,并且让F.prototype对象 赋值为base,并return出一个新的对象实例。

Object.create =  function (o) {undefinedvar F = function () {};F.prototype = o;return new F();};

new做法是新建一个obj对象o1,并且让o1的__proto__指向了Base.prototype对象。并且使用call 进行强转作用环境。从而实现了实例的创建。

JavaScript 实际上执行的是:

var o1 = new Object();o1.[[Prototype]] = Base.prototype;Base.call(o1);

25.Location 对象

Location 对象包含有关当前 URL 的信息。把用户带到一个新地址

window.location="/index.html"

26.跨域解决方案 

跨域:在同源策略的限制下,会阻止非同源下域的内容进行交互

跨域:同协议、域名、端口

1) JSONP原理

利用 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。

2) JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)

3) JSONP优缺点

JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

4)实现

在a的script标签下,把那个跨域的API数据接口地址,赋值给script的src 传了个test.js

Test.js具体实现一个功能 比如alert

2.cors

在服务端设置

Access-control-allow-Origin 允许哪些域名可以访问

Access-control-allow-methods 允许哪些请求方式

3.第三方服务器代理

实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。

代理服务器,需要做以下几个步骤:

接受客户端请求 。

将请求 转发给服务器。

拿到服务器 响应 数据。

将 响应 转发给客户端。

27.JS 垃圾回收机制 

“可达性” 值就是那些以某种方式可访问或可用的值,它们被保证存储在内存中。

怎么使这些可达的值,变得不可达,特别是在有很多引用的情况下

基本的垃圾回收算法称为“标记-清除”,定期执行以下“垃圾回收”步骤:

垃圾回收器获取根并“标记”(记住)它们。

然后它访问并“标记”所有来自它们的引用。

然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次。

以此类推,直到有未访问的引用(可以从根访问)为止。

除标记的对象外,所有对象都被删除。

28.前端进阶之setTimeout 倒计时为什么会出现误差

定时器是属于 宏任务(macrotask) 。如果当前 执行栈 所花费的时间大于 定时器 时间,那么定时器的回调在 宏任务(macrotask) 里,来不及去调用,所有这个时间会有误差。

29.get请求和post请求的区别

1、请求方式不同:$.get() 方法使用GET方法来进行异步请求的。$.post() 方法使用POST方法来进行异步请求的。

2、携带参数方式不同:get请求可以在路由中携带参数,post不行,get请求是params携带参数,post是data携带参数。get请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给Web服务器的,这种传递是对用户不可见的。

3、数据传输大小不同:get方式传输的数据大小不能超过2KB 而POST要大的多

4、安全问题:

post请求相对安全,无法通过路由查看参数,而且也不可以修改。

ie浏览器会默认缓存get请求,相同请求会从本地获取,而post会从服务器获取

30.数组去重的12中方法 

1、利用ES6 Set去重(ES6中最常用)

不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。

function unique (arr) {return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false,undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

2、利用for嵌套for,然后splice去重(ES5中最常用)

双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。

function unique(arr){            for(var i=0; i<arr.length; i++){for(var j=i+1; j<arr.length; j++){if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个arr.splice(j,1);j--;}}}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];console.log(unique(arr))//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]//NaN和{}没有去重,两个null直接消失了

想快速学习更多常用的ES6语法,可以看这篇文章《学习ES6笔记──工作中常用到的ES6语法》

3、 indexOf去重

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组

function unique(arr) {if (!Array.isArray(arr)) {console.log('type error!')return}var array = [];for (var i = 0; i < arr.length; i++) {if (array .indexOf(arr[i]) === -1) {array .push(arr[i])}}return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]
//NaN、{}没有去重

4、利用sort()

利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对

function unique(arr) {if (!Array.isArray(arr)) {console.log('type error!')return;}arr = arr.sort()var arrry= [arr[0]];for (var i = 1; i < arr.length; i++) {if (arr[i] !== arr[i-1]) {arrry.push(arr[i]);}}return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,
undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]
//NaN、{}没有去重

5、利用对象的属性不能相同的特点进行去重(这种数组去重的方法有问题,不建议用,有待改进)

function unique(arr) {if (!Array.isArray(arr)) {console.log('type error!')return}var arrry= [];var  obj = {};for (var i = 0; i < arr.length; i++) {if (!obj[arr[i]]) {arrry.push(arr[i])obj[arr[i]] = 1} else {obj[arr[i]]++}}return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, null, NaN, 0, "a", {…}]
//两个true直接去掉了,NaN和{}去重

6、利用includes

function unique(arr) {if (!Array.isArray(arr)) {console.log('type error!')return}var array =[];for(var i = 0; i < arr.length; i++) {if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值array.push(arr[i]);}}return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined,
null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
//{}没有去重

7、利用hasOwnProperty

利用hasOwnProperty 判断是否存在对象属性

function unique(arr) {var obj = {};return arr.filter(function(item, index, arr){return obj.hasOwnProperty(typeof item + item) ? false :
(obj[typeof item + item] = true)})
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]
//所有的都去重了

8、利用filter

function unique(arr) {return arr.filter(function(item, index, arr) {//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素return arr.indexOf(item, 0) === index;});
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

9、利用递归去重

function unique(arr) {var array= arr;var len = array.length;array.sort(function(a,b){   //排序后更加方便去重return a - b;})function loop(index){if(index >= 1){if(array[index] === array[index-1]){array.splice(index,1);}loop(index - 1);    //递归loop,然后数组去重}}loop(len-1);return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

10、利用Map数据结构去重

创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。

function arrayNonRepeatfy(arr) {let map = new Map();let array = new Array();  // 数组用于返回结果for (let i = 0; i < arr.length; i++) {if(map .has(arr[i])) {  // 如果有该key值map .set(arr[i], true); } else { map .set(arr[i], false);   // 如果没有该key值array .push(arr[i]);}} return array ;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

11、 利用reduce+includes

function unique(arr){return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

12、[...new Set(arr)]

[...new Set(arr)]
//代码就是这么少----(其实,严格来说并不算是一种,相对于第一种方法来说只是简化了代码)

有些文章提到了foreach+indexOf数组去重的方法,个人觉得都是大同小异

四、数据结构

  • 1.树和图的遍历方式

数的遍历方式

前中后序遍历 递归||非递归 非递归用栈

层次遍历(层次遍历可以设一个队列,把元素放在队列里,每次输出队头元素。)

图的遍历方式

DFS BFS

  • 2.数组和链表的区别

数组是将元素在内存中连续存放,

由于每个元素占用内存相同,可以通过下标迅速访问任何元素

链表中的元素在内存中不是顺序存储的,而是通过元素中的指针联系到一起。

数组 遍历方便 插入删除麻烦 链表恰好相反

五、计网

1.ios七层模型

 2.TCP 和 UDP 的区别。

3.HTTP 2.0 新增

二进制协议,多路复用(共享连接),数据流,信息头压缩,客户端推送

4.HTTP状态码
200 请求成功

301 永久重定向

302 临时重定向

304 not Modified 未修改

400 客户端错误

401 当前身份验证

403 服务器已经得到请求,但是拒绝执行

404 not found

500 客户端在执行时发生错误,无法完成请求。

六、ES6

1.export和import

模块化开发

  • Export

如果希望从外界获取某个文件内部的变量,必须通过export输出,

  • Import命令

export定义了模块的对外接口后,其他JS文件就可以通过import来加载这个模块,

2.Promise

Promise回顾
首先来回顾一下Promise是什么。

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

(1)Promise的实例有三个状态:

Pending(进行中)
Resolved(已完成)
Rejected(已拒绝)
当把一件事情交给promise时,它的状态就是Pending,任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。

(2)Promise的实例有两个过程:

pending -> fulfilled : Resolved(已完成)
pending -> rejected:Rejected(已拒绝)
需要注意:一旦从进行状态变成为其他状态就永远不能更改状态了。

特点:

将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。流程更加清晰,代码更加优雅。
Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:

无法取消Promise,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

3.let、const和var的概念与区别

区别

let有块级作用域 不存在变量提升 不存在重复定义 不可能提前使用。

const固定的,一开始就要声明好,后面不能更改

4.变量提升与暂时性死区

变量提升:var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。

暂时性死区:只要块级作用域内存在let声明,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;if (true) {undefinedtmp = 'abc'; // ReferenceErrorlet tmp;}

5. Symbol

Symbol是ES6中新增的一种数据类型, 被划分到了基本数据类型中

基本数据类型: 字符串、数值、布尔、undefined、null、Symbol

引用数据类型: Object

Symbol的作用

用来表示一个独一无二的值   let xxx = Symbol();

6.Object.keys()方法

获取对象的所有属性名或方法名(不包括原形的内容),返回一个数组。

7.Object.assign()

Object.assign()方法将多个原对象的属性和方法都合并到了目标对象上面。可以接收多个参数,第一个参数是目标对象,后面的都是源对象。

如果在这个过程中出现同名的属性(方法)后面的会覆盖之前的。

var o1 = { a: 1, b: 1, c: 1 };  //目标对象
var o2 = { b: 2, c: 2 };        //源对象1
var o3 = { c: 3 };              //源对象2
var obj = Object.assign(o1, o2, o3);console.log(obj); // { a: 1, b: 2, c: 3 }//assign的合并的目标对象会被修改
console.log(o1); // { a: 1, b: 2, c: 3 }
var o1 = 'abc';       //字符串 string
var o2 = true;        //布尔型 boolean
var o3 = 10;          //数值   numbervar obj = Object.assign(o1, o2, o3);//字符串会转换对象属性 index作为键 字符串作为内容
//布尔型和数值 不转
console.log(obj); // { 0: a, 1: b, 2: c }//assign的合并的目标对象会被修改
console.log(o1); // { a: 1, b: 2, c: 3 }

8.set数据结构

Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。

9.Map数据结构

它类似与对象,也是键值对集合,但是”键”的范围不限于字符串,对象也可以当作键

10.Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

const p = new Proxy(target, handler)

targe:t要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

11.Reflect

设计目的:

①将Object属于语言内部的方法放到Reflect上

let obj = {}let newVal = ''Reflect.defineProperty(obj, 'name', {undefinedget() {undefinedreturn newVal},set(val) {undefinedconsole.log('set')// this.name = valnewVal = val}})obj.name = 'es'console.log(obj.name)

②修改某些Object方法的返回结果,让其变得更合理

③让Object操作变成函数行为

// 老写法

'assign' in Object // true

// 新写法

Reflect.has(Object, 'assign') // true

④Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法

12.模板字符串

用一对反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,也可以在字符串中嵌入变量,js表达式或函数,变量、js表达式或函数需要写在${ }中。即完成拼接

13.箭头函数(=>)

箭头函数中this的使用跟普通函数也不一样,在JavaScript的普通函数中,都会有一个自己的this值

普通函数:
1、函数作为全局函数被调用时,this指向全局对象
2、函数作为对象中的方法被调用时,this指向该对象
3、函数作为构造函数的时候,this指向构造函数new出来的新对象
4、还可以通过call,apply,bind改变this的指向
箭头函数:
1、箭头函数没有this,函数内部的this来自于父级最近的非箭头函数,并且不能改变this的指向。
2、箭头函数没有super
3、箭头函数没有arguments
4、箭头函数没有new.target绑定。
5、不能使用new
6、没有原型
7、不支持重复的命名参数。

箭头函数的简单理解

参数=》函数实体

1、箭头函数的左边表示输入的参数,右边表示输出的结果。

const s = a => a
console.log(s(2)) // 2

2、在箭头函数中,this属于词法作用域,直接由上下文确定,对于普通函数中指向不定的this,箭头函数中处理this无疑更加简单,如下:

//ES5普通函数
function Man(){this.age=22;return function(){this.age+1;}
}
var cala=new Man();
console.log(cala())//undefined//ES6箭头函数
function Man(){this.age=22;return () => this.age+1;
}
var cala=new Man();
console.log(cala())//23

3、箭头函数中没有arguments(我们可以用rest参数替代),也没有原型,也不能使用new 关键字,例如:

//没有arguments
var foo=(a,b)=>{return arguments[0]*arguments[1]}
console.log(foo(3,5))
//arguments is not defined//没有原型
var Obj = () => {};
console.log(Obj.prototype);
// undefined//不能使用new 关键字
var Obj = () => {"hello world"};
var o = new Obj();
// TypeError: Obj is not a constructor

4、箭头函数给数组排序

const arr = [10, 50, 30, 40, 20]
const s = arr.sort((a, b) => a - b)
console.log(s) // [10,20,30,40,50]

14.for…of 循环

用于对象遍历

for (variable of iterable) {undefined

statement

}

15.jQuery的选择器怎么实现的

jquery原型里面有一个init初始化的方法,将传入的值进行解析,比如传入的id还是class还是标签名。然后通过相应的方法返回数组型对象。既可以通过对象直接调用方法,也可以使用数组的length。

八.VUE

1、说说你对 SPA 单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

优点:

用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;

基于上面一点,SPA 相对对服务器压力小;

前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;

前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;

SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

2、v-show 与 v-if 有什么区别?

答: 共同点:都能控制元素的显示和隐藏;
不同点:实现本质方法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。
总结:如果要频繁切换某节点,使用v-show(切换开销比较小,初始开销较大)。如果不需要频繁切换某节点使用v-if(初始渲染开销较小,切换开销比较大)。

3、Class 与 Style 如何动态绑定?

Class 可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
  • 数组语法:<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

Style 也可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  • 数组语法:<div v-bind:style="[styleColor, styleSize]"></div>

4、怎样理解Vue的单向数据流?

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

有两种常见的试图改变一个 prop 的情形 :

  • 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

props: ['initialCounter'],

  • 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性

props: ['size'],

5、computed 和 watch 的区别和运用的场景?

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;

当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

【web前端面试必问1】vue中 methods、computed、watch的区别_studyer网的博客-CSDN博客

6、直接给一个数组项赋值,Vue 能检测到变化吗?

由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一个问题,Vue 提供了以下操作方法:

// Vue.set

为了解决第二个问题,Vue 提供了以下操作方法:

// Array.prototype.splice

7、谈谈你对 Vue 生命周期的理解?

(1)生命周期是什么?

Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

(2)各个生命周期的作用

8、Vue 的父组件和子组件生命周期钩子函数执行顺序?

Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

加载渲染过程

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

子组件更新过程

父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

父组件更新过程

父 beforeUpdate -> 父 updated

销毁过程

父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

9、在哪个生命周期内调用异步请求?

可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

能更快获取到服务端数据,减少页面 loading 时间;

ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

10、在什么阶段才能访问操作DOM?

在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。vue 具体的生命周期示意图可以参见如下,理解了整个生命周期各个阶段的操作,关于生命周期相关的面试题就难不倒你了。

11、父组件可以监听到子组件的生命周期吗?

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

// Parent.vue
以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:

//  Parent.vue
当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

12、谈谈你对 keep-alive 的了解?

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

一般结合路由和动态组件一起使用,用于缓存组件;

提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;

对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

13、组件中 data 为什么是一个函数?

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

// data
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

14、v-model 的原理?

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

text 和 textarea 元素使用 value 属性和 input 事件;

checkbox 和 radio 使用 checked 属性和 change 事件;

select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'>
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

15、Vue 组件间通信有哪几种方式?

Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。

(1)props / $emit 适用 父子组件通信

这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。

(2)ref 与 children 适用 父子组件通信

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

children:访问父 / 子实例

(3)EventBus (on) 适用于 父子、隔代、兄弟组件通信

这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。

(4)listeners 适用于 隔代组件通信

attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

listeners" 传入内部组件

(5)provide / inject 适用于 隔代组件通信

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

(6)Vuex 适用于 父子、隔代、兄弟组件通信

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

16、你使用过 Vuex 吗?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

主要包括以下几个模块:

State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。

Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。

Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。

Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。

Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

17、使用过 Vue SSR 吗?说说 SSR?

Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。

即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。

服务端渲染 SSR 的优缺点如下:

(1)服务端渲染的优点:

更好的 SEO:因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;

更快的内容到达时间(首屏加载更快):SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;

(2) 服务端渲染的缺点:

更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;

更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

如果没有 SSR 开发经验的同学,可以参考本文作者的另一篇 SSR 的实践文章《Vue SSR 踩坑之旅》,里面 SSR 项目搭建以及附有项目源码。

18、vue-router 路由模式有几种?

vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:

switch (mode) {
其中,3 种路由模式的说明如下:

hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;

history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;

abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

19、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?

(1)hash 模式的实现原理

早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':

https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性:

URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;

hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;

可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;

我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path);
history 路由模式的实现主要基于存在下面几个特性:

pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;

我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);

history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

20、什么是 MVVM?

Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表

MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示:

图片

(1)View 层

View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。

(2)Model 层

Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。

(3)ViewModel 层

ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。

需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。

MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。

这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

我们以下通过一个 Vue 实例来说明 MVVM 的具体实现,有 Vue 开发经验的同学应该一目了然:

(1)View 层

<div id="app">
(2)ViewModel 层

var app = new Vue({
(3) Model 层

21、Vue 双向绑定原理

Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据,如下图所示:

即:

输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。

Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。

其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。

Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。

实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。

实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

以上四个步骤的流程图表示如下,如果有同学理解不大清晰的,可以查看作者专门介绍数据双向绑定的文章《0 到 1 掌握:Vue 核心之数据双向绑定》,有进行详细的讲解、以及代码 demo 示例。

22、Vue 框架怎么实现对象和数组的监听?

如果被问到 Vue 怎么实现数据双向绑定,大家肯定都会回答 通过 Object.defineProperty() 对数据进行劫持,但是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,同理无法对数组进行劫持,但是我们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操作)的变化,那它是怎么实现的呢?我们查看相关代码如下:

通过以上 Vue 源码部分查看,我们就能知道 Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。

23、Proxy 与 Object.defineProperty 优劣对比

Proxy 的优势如下:

Proxy 可以直接监听对象而非属性;

Proxy 可以直接监听数组的变化;

Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;

Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;

Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

Object.defineProperty 的优势如下:

兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

24、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 的功能所调用的方法)

25、虚拟 DOM 的优缺点?

优点:

保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;

无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;

跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

26、虚拟 DOM 实现原理?

虚拟 DOM 的实现原理主要包括以下 3 部分:

用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;

diff 算法 — 比较两棵虚拟 DOM 树的差异;

pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

如果对以上 3 个部分还不是很了解的同学,可以查看本文作者写的另一篇详解虚拟 DOM 的文章《深入剖析:Vue核心之虚拟DOM》

27、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 过程,可以查看作者写的另一篇详解虚拟 DOM 的文章《深入剖析:Vue核心之虚拟DOM》

所以 Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。

更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:

function createKeyToOldIdx (children, beginIdx, endIdx) {

28、你有对 Vue 项目进行哪些优化?

如果没有对 Vue 项目没有进行过优化总结的同学,可以参考本文作者的另一篇文章《 Vue 项目性能优化 — 实践指南 》,文章主要介绍从 3 个大方面,22 个小方面详细讲解如何进行 Vue 项目的优化。

(1)代码层面的优化

v-if 和 v-show 区分使用场景

computed 和 watch 区分使用场景

v-for 遍历必须为 item 添加 key,且避免同时使用 v-if

长列表性能优化

事件的销毁

图片资源懒加载

路由懒加载

第三方插件的按需引入

优化无限列表性能

服务端渲染 SSR or 预渲染

(2)Webpack 层面的优化

Webpack 对图片进行压缩

减少 ES6 转为 ES5 的冗余代码

提取公共代码

模板预编译

提取组件的 CSS

优化 SourceMap

构建结果输出分析

Vue 项目的编译优化

(3)基础的 Web 技术的优化

开启 gzip 压缩

浏览器缓存

CDN 的使用

使用 Chrome Performance 查找性能瓶颈

29、对于即将到来的 vue3.0 特性你有什么了解的吗?

Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:

(1)监测机制的改变

3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:

只能监测属性,不能监测对象

检测属性的添加和删除;

检测数组索引和长度的变更;

支持 Map、Set、WeakMap 和 WeakSet。

新的 observer 还提供了以下特性:

用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。

默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。

更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。

不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。

更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。

(2)模板

模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。

同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。

(3)对象式的组件声明方式

vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。

3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。

此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。

现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。

(4)其它方面的更改

vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:

支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。

支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。

Web 前端基础知识面试大全相关推荐

  1. Web前端工程师知识体系大全,Web前端入门基础体系

    Web前端工程师知识体系大全 什么?你上面说的太多太乱了,好吧下面的知识体系只要能掌握个六七成,就能找到个前端的碗了! Web前端基础知识体系大全

  2. Web前端基础知识整理

    1. 前端基础知识 文件分类 XML(扩展标记语言) 装载有格式的数据信息,用于各个框架和技术的配置文件描述 特点: 扩展名为.xml 内容区分大小写 标签要成对出现,形成容器,只能有一个 标签按正确 ...

  3. html clear属性值,【Web前端基础知识】clear使用方法

    [摘要] 前端即网站前台部分,运行在PC端,移动端等浏览器上展现给用户浏览的网页.下面是[Web前端基础知识]clear使用方法,小编建议有准备参加考试的备考生一定要合理规划时间,仔细阅读相关规定,提 ...

  4. WEB前端 基础知识汇总

    javascript: JavaScript中如何检测一个变量是一个String类型?请写出函数实现 typeof(obj) === "string" typeof obj === ...

  5. Web前端基础知识总结

    一.HTML和CSS 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? IE: trident内核 Firefox:gecko内核 Safari:webkit内核 Opera:以前是pres ...

  6. web前端基础知识:html布局如何应用?

    网站布局 大多数网站会把内容安排到多个列中(就像杂志或报纸那样). 可以使用 或者 元素来创建多列.CSS 用于对元素进行定位,或者为页面创建背景以及色彩丰富的外观. 提示即使可以使用 HTML 表格 ...

  7. web前端基础知识查漏补缺,JavaScript面试题,赶紧收藏

    1介绍js的基本数据类型 js一共六种基本数据类型,分别是 undefined null boolean number string ,还有es6新增的symbol 和 es10新增的bigint. ...

  8. web前端基础知识-(六)jQuery-补

    一.JS正则 1.定义正则表达式 JavaScript种正则表达式有两种定义方式,定义一个匹配类似 <%XXX%> 的字符串: 1)构造函数 var reg=new RegExp('< ...

  9. Web前端基础知识:ES5及ES6this详解

    今天,我们学习一下JavaScript中的this.我们从什么是this,ES5及ES6中this的几种情况进行学习.让this变的so easy,我们这里说的都是非严格模式下. 什么是this th ...

最新文章

  1. 安全攻防技能——Web安全——XSS
  2. NDK 交叉编译常用变量
  3. boost::graph_as_tree用法的测试程序
  4. python 之 前端初识 html
  5. Java for LeetCode 061 Rotate List
  6. ASP.NET 连接MySql数据库
  7. 会计电算化算不算计算机专业,成都会计学校会计电算化专业介绍
  8. Zookeeper-入门-安装
  9. 简易天气java论文_【Java小项目】简单的天气预报
  10. Python基础笔记(四)
  11. DDR3 Vivado 仿真测试成功
  12. AutoJs学习-QQ批量加好友
  13. python如何识别图片中的文字_如何利用Python识别图片中的文字
  14. 服务器启动端口被占用,解决Nginx启动出现端口被占用的问题
  15. php 连接 mysql 8.0
  16. gephi和python_python+nlp+Gephi 分析电视剧【人民的名义】
  17. 蓝牙电话/耳机和蓝牙音乐profile
  18. 有道云笔记 markdown html,你不可不知的有道云笔记Markdown指南【进阶版】
  19. 最新公布!“中国开发者大调查”第二批中奖名单出炉啦
  20. TMB:肿瘤突变负荷简介

热门文章

  1. 获取对话框当前cfont_MFC设置对话框、字体对话框、颜色对话框(转)
  2. 分享个变形金刚地球之战挂机辅助
  3. 记一次艰难的SQL注入(过安全狗)
  4. MacBook替换登录界面壁纸
  5. 推荐Mac壁纸App
  6. 成功解决:计算交叉熵lossFunction报错“1D target tensor expected, multi-target not supported”的解决办法
  7. Commvault蔡报永:点亮你的暗数据
  8. 刚开始接触YOLO,记录一下Windows系统下载yolov5与初步进行训练的过程
  9. 圆桌会回顾 | SecureBoost:挑战千万级别样本训练之性能提升篇
  10. 程序员,不要让自己做兔子(updated) 网上最近流传的一个笑话,关于兔子,狼还有一只老虎的,故事 我就是想打你了,还需要什么理由吗?谁让你是兔子 项目经理是这样当的...