🍓 作者主页:💖仙女不下凡💖
🍓 学习建议:以下👇 内容不建议死记硬背,而且内容太多也记不住,建议了解链接里面的详情解释之后在理解我总结的面试回答,相信看到这篇文章的小伙伴相当一部分是没有工作经验的初学者,所以我会简单提到这些知识点什么时候使用,因为个人开发经验不算丰富总结内容常有不全,欢迎指正修改!
🍓 欢迎点赞👍 收藏⭐ 留言📝 如有不足与错误敬请指正!
一、CSS部分
🍑 考点 1 — 初始化样式
1.1 什么是CSS初始化?
CSS 初始化是指: 开发者对浏览器的默认样式进行重置。
1.2 为什么要初始化 CSS 样式 ?
浏览器差异
因为浏览器的兼容问题,不同的样式会有默认初始样式,margin
和padding
,下划线等等,浏览器不同, 数值还不一样,如果直接写样式,会出现差异,布局出现错乱,所以要初始化样式,达到统一的布局。
提高编码质量
初始化 CSS
后,可以让开发者省去很多写单独兼容的代码,减少代码体积,节约网页下载时间;还会使得我们开发网页内容时更加方便简洁,开发者就不用考虑太多基础样式的兼容问题了。
🍑 考点 2 — margin重合问题
2.1 有遇到过margin重合问题吗?
- margin重合: 相邻两个盒子垂直方向上的
margin
会发生重叠,只会取比较大的margin
。 - 解决办法: 设置
padding
代替margin
、设置float
、设置overflow
、设置position: absolute
绝对定位、设置display: inline-block
🍑 考点 3 — 关于盒模型
3.1 css盒模型⭐⭐
✨ 重点理解: 盒模型分为标准盒模型与IE
盒模型,元素在网页实际占位为content + padding + border + margin
,标准盒子模型与低版本IE盒子模型区别就是取宽高的时不同,标准盒模型宽高指content
的宽高;低版本IE盒子模型宽高指content + padding + border
。
📢 问题拓展: CSS 如何设置这两种模型? 标准盒模型(默认)box-sizing : content-box;
IE
盒模型box-sizing : border-box
。
🍑 考点 4 — 隐藏元素
4.1 请说说隐藏一个元素的几种方法,以及它们之间的区别⭐
-
第 1 种: 设置元素的
display
为none
是最常用的隐藏元素的方法。
将元素设置为display: none
后,元素在页面上将彻底消失,元素本来占有的空间就会被其他元素占有,也就是说它会导致浏览器的重排和重绘。 -
第 2 种: 设置元素的
visibility: hidden
和display:none
的区别在于,元素在页面消失后,其占据的空间依旧会保留着,所以它只会导致浏览器重绘而不会重排,适用于那些元素隐藏后不希望页面布局会发生变化的场景。 -
第 3 种: 设置元素的
opacity: 0
这种方法和visibility:hidden
的一个共同点是元素隐藏后依旧占据着空间,但我设置透明度为 0 后,元素只是隐身了,它依旧存在页面中。
✨ 重点理解: 拓展到三种隐藏元素的对于点击事件绑定的区别,如果对这 3 中不同隐藏元素的 dom
节点绑定其点击事件,会有下面的结论:
- display:none: 元素彻底消失,很显然不会触发其点击事件
- visibility:hidden: 设置元素的
visibility
后无法触发点击事件,说明这种方法元素也是消失了,只是依然占据着页面空间。 - opacity:0: 可以触发点击事件,原因也很简单,设置元素透明度为 0 后,元素只是相对于人眼不存在而已,对浏览器来说,它还是存在的,所以可以触发点击事件。
🍑 考点 5 — 清除浮动
5.1 请你说出你用过清除浮动的几种办法,以及它们的优缺点⭐⭐⭐
- 第 1 种: 在最后一个浮动标签后,新加一个标签,给其设置
clear: both;
.father {
width: 400px;
}
.big {
width: 200px; height: 200px; float: left;
}
.small {
width: 120px; height: 120px; float: left;
}
.clear {
clear:both;
}
<div class="father">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">clear 标签法</div>
</div>
- 第 2 种: 给父元素添加
overflow: hidden
优点: 代码简洁。
缺点: 如果内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素,因为设置了overflow: hidden
,看具体情况来决定是否使用。
.father {
width: 400px; overflow: hidden;
}
.big {
width: 200px; height: 200px; float: left;
}
.small {
width: 120px; height: 120px; float: left;
}
<div class="father">
<div class="big">big</div>
<div class="small">small</div>
</div>
- 第 3 种: 使用
after
伪元素清除浮动
优点: 符合闭合浮动思想,结构语义化正确
缺点:ie6-7
不支持伪元素:after
,使用zoom:1
触发hasLayout
。整体相对来说,推荐使用after
伪元素来清除浮动
.clearfix:after {/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: ""; display: block; height: 0; clear:both; visibility: hidden;
}
.clearfix{
*zoom: 1; /*ie6 清除浮动的方式 *号只有IE6-IE7 执行,其他浏览器不执行*/
}
<div class="father clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
🍑 考点 6 — CSS3新特性
6.1 请说出你使用过哪些CSS3新特性?
属性 | 作用 |
---|---|
border-radius | 用于实现圆角。ebox-shadow:用于实现阴影。 |
border-image | 用于实现边框图片。text-shadow:用于实现文字阴影。 |
linear-gradient | 用于实现背景线性渐变。 |
transform | 用于实现元素变形,包括旋转 rotate、扭曲 skew、缩放 scale 和移动 |
translate | 以及矩阵变形matrix。 |
transition | 用于在一定的时间区间内,把元素从一种状态平滑地过渡到另一种状态。 |
animation | 结合@keyframes 创建实现动画。 |
6.2 请说出CSS3新增伪类有哪些?分别代表什么含义?
新增伪类 | 作用 |
---|---|
p:first-of-type | 选择属于其父元素的首个元素 |
p:last-of-type | 选择属于其父元素的最后元素 |
p:only-of-type | 选择属于其父元素唯一的元素 |
p:only-child | 选择属于其父元素的唯一子元素 |
p:nth-child(2) | 选择属于其父元素的第二个子元素 |
:enabled :disabled | 表单控件的禁用状态 |
:checked | 单选框或复选框被选中 |
🍑 考点 7 — 样式优先级
7.1 CSS 样式的优先级是怎么样的?⭐
!important
> 内联样式 >ID
选择器 > 伪类 > 属性选择器 > 类选择器 > 标签选择器 > 通配符*
- 如非特殊情况,慎用
!important
。因为使用!important
会扰乱原本层叠和权重产生正常的作用顺序,使后期维护带来麻烦。
🍑 考点 8 — 单位对比
8.1 px em rem vh vw的区别
px
是绝对长度,而em rem vh vw
都是相对长度rem em
区别:rem
只能在html
标签里面设置rem
的依赖的值到底是多少,而em
是可以在自己元素以及父级元素设置em
依赖的值到底是多少。vh
和vw
:就是根据窗口的宽高,分成100等份,100vh
就表示满高,50vh
就表示一半高。
🍑 考点 9 — 技巧题
9.1 用css画一个三角形⭐
width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;
9.2 怎么让 Chrome 支持小于 12px 的文字?
针对 chrome
浏览器,加webkit
前缀,用transform:scale()
这个属性进行放缩。
span {
font-size: 12px;
display: inline-block;
-webkit-transform:scale(0.8);
}
<span>10px 的字体效果</span>
9.3 CSS 如何去除inline-block 元素间的间距?
我们使用 CSS
把非inline-block
的元素改为inline-block
的时候,元素之间就会产生默认的间距。
技巧:使用font-size: 0
。这个方法基本上可以解决大部分浏览器下inline-block
元素之间的间,不过Chrome
浏览器默认有最小字体大小限制,考虑到兼容性,我们还需要添加:-webkit-text-size-adjust
.space {
font-size: 0;
-webkit-text-size-adjust: none;
}
9.4 CSS 如何实现单行文本溢出显示省略号?⭐
用 text-overflow: ellipsis
属性,还需要加宽度 width
属来兼容部分浏览器。
p {
width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
<p>这里再长一点就要变成省略号了</p>
9.5 CSS 如何实现多行文本溢出显示省略号?(注意是多行)
用 box-orient
和line-clamp
属性,还需要加 width
和overflow
。
p {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
width: 200px;
}
<p>这里再长一点就要变成多行文字+省略号了</p>
9.6 请解释一下CSS3的flexbox(弹性盒布局模型)以及适用场景?
该布局模型的目的是提供一种更加高效的方式来对容器中的条目进行布局、对齐和分配空间。在传统的布局方式中,block
布局是把块在垂直方向从上到下依次排列的;而 inline
布局则是在水平方向来排列。弹性盒布局并没有这样内在的方向限制,可以由开发人员自由操作。
试用场景:弹性布局适合于移动前端开发,在Android
和ios
上也完美支持。
9.7 flex弹性布局 ⭐⭐
✨ 总结理解: 先设置主轴(横向或纵向),再设置排列方式。
9.8 行元素与块元素水平居中、垂直居中、水平垂直居中
📢 问题拓展: CSS水平居中+垂直居中+水平/垂直居中的方法总结
二、HTML部分
🍏 考点 1 — 语义化
1.1 说说你对 html 语义化的理解。⭐
✨ 重点理解:html
语义化的三大好处。
结构清晰:
有利于SEO: 可以让搜索引擎更好地获取到更多有效信息,搜索引擎的爬虫依赖于标签来确定上下文和各个关键字的权重,有效提升网页的搜索量。
可维护性: 使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
1.2 前端需要注意哪些 SEO?
- 合理的
title
、description
、keywords
:搜索对着三项的权重逐个减小,title
值强调重点即可,重要关键词出现不要超过 2 次,而且要靠前,不同页面 title 要有所不同;description
把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description
有所不同;keywords
列举出重要关键词即可 - 语义化的
HTML
代码,符合W3C
规范:语义化代码让搜索引擎容易理解网页 - 重要内容
HTML
代码放在最前:搜索引擎抓取HTML
顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取 - 搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
- 重要内容不要用
js
输出:爬虫不会执行 js 获取内容 - 少用
iframe
:搜索引擎不会抓取iframe
中的内容 - 非装饰性图片必须加
alt
- 提高网站速度:网站速度是搜索引擎排序的一个重要指标。
1.3 说说 html 语义化的标签有那些。(记住几个容易的,比如header,nav,section,aside,footer等及其作用)
🍏 考点 2 — 标签类型
2.1 请说说对块元素、行内元素、空元素的理解,它们分别都有哪些常见标签?⭐⭐
- 块级元素: 总是在新行上开始;高度,行高以及外边距和内边距都可控制;宽度缺省是它的容器的 100%, 除非设定一个宽度。它可以容纳内联元素和其他块元素。
常见的块级元素标签有:<h1>
至<h6>
,<div>
,<p>
,<ul>
,<ol>
,<li>
,<dl>
,<dt>
,<dd>
,<table>
,<article>
,<aside>
,<audio>
,<video>
,<footer>
,<header>
,<nav>
,<section>
- 行内元素: 和其他元素都在一行上; 高,行高及外边距和内边距不可改变; 宽度就是它的文字或图片的宽度,不可改变; 内联元素只能容纳文本或者其他内联元素; 设置宽度
width
无效。 设置高度height
无效,可以通过line-height
来设置。 设置margin
与padding
只有左右有效,上下无效。
常见的行内元素标签有:<a>
,<span>
,<strong>
,<i>
,<b>
,<button>
,<textarea>
,<em>
,<label>
,<select>
- 空元素: 没有闭合标签的标签被称作为空标签。
常见的空元素标签有:<input />
,<img />
,<hr/>
,<br>
。
🍏 考点 3 — 标签对比
3.1 请说说<strong>
和<b>
标签的区别
<strong>
标签和<b>
标签都能使得内容有加粗的是视觉效果,区别是:<strong>
有重点强调的作用,<strong>
是“含有语义”的标签,搜索引擎会了解这些语义。其在 HTML 中是特意被设定为表示“强调” 的意思。而<b>
标签则没有“强调”的含义。
3.2 请说说<i>
和<em>
标签的区别
<i>
和<em>
都能使文本变为斜体,区别是<em>
有重点强调的作用,在大多数浏览器里面看起来是斜体,如果单纯为了展示斜体的效果而不加以强调的话,可以使用<i>
标签。如果含有强调的意思的话,需要使用<em>
标签。
🍏 考点 4 — img标签对比
4.1 请你说出<img>
标签支持的图片格式,以及它们的区别?⭐
5.2 请你说出<img>
中 alt
和 title
属性的区别⭐
- alt 属性: ❶如果图像没有下载或者加载失败,会用文字来代替图像显示。 这一作用是为了给加载不出网页图片的用户提供图片信息,方便用户浏览网页,也方便程序猿维护网页。❷搜索引擎可以通过这个属性的文字来抓取图片。
- title 属性: 是当网页上的图片被加载完成后,鼠标移动到上面去,会显示这个图片指定的属性文字,以对图片信息进行补充性说明。
🍏 考点 5 — iframe标签
5.1 为什么要尽量少用<iframe>
标签,请你说出<iframe>
有哪些缺点?
iframe
会阻塞主页面的onload
事件;iframe
和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载,会产生很多页面,不容易管理。- 如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,用户体验度差。
- 代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理
iframe
中的内容,所以使用iframe
会不利于搜索引擎优化(SEO)
。 - 很多的移动设备无法完全显示框架,设备兼容性差。
iframe
框架页面会增加服务器的http
请求,对于大型网站是不可取的。
🍏 考点 6 — label标签
6.1 Label 的作用是什么?是怎么用的?⭐
label
标签来定义表单控制间的关系,当用户选择该标签是,浏览器会自动将焦点转到和标签相关的表单控件上。
🍏 考点 7 — meta标签的viewport
7.1 <mete>
标签的 viewport 的作用和原理是什么?
- 作用: 让当前
viewport
的宽度等于设备的宽度,同时不允许用户进行手动缩放。 - 原理: 移动端浏览器通常都会在一个比移动端屏幕更宽的虚拟窗口中渲染页面,这个虚拟窗口就是
viewport
;目的是正常展示没有做移动端适配的网页,让他们完整的展示给用户。
7.2 viewport 属性值都有哪些?
- width: 设置
layout viewport
的宽度,为一个正整数,或字符串width-device
。 - height: 设置
layout viewport
的高度,这个属性对我们并不重要,很少使用。initial-scale 设置页面的初始缩放值,为一个数字,可以带小数。 - minimum-scale: 允许用户的最小缩放值,为一个数字,可以带小数。
- maximum-scale: 允许用户的最大缩放值,为一个数字,可以带小数。
- User-scalable: 是否允许用户进行缩放,值为"
no
"或"yes
"。no
不允许,yes
允许这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。
<meta name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-sc alable=no">
🍏 考点 8 — HTML5
8.1 请说说你用过哪些 HTML5 的新标签
标签&作用 |
---|
<article> 用来定义独立的内容 |
<audio> 用来定义声音内容 |
<canvas> 用来定义图形 |
<header> 用来定义页眉 |
<footer> 用来定义页脚 |
<nav> 用来 定义导航链接 |
<video> 用来定义视频。 |
8.2 请说出HTML5 的几个新特性
✨ 重点理解: 增加了❶新语义元素(新标签和新属性,如<header></header>
) ❷加强本地存储(增加了sessionStorage
与localStorage
等) ❸表单新增功能(from
) ❹css3
等。
📢 详情文章: h5新特性有哪些
🍏 考点 9 — 增强表单
9.1 你能说说 HTML5 增强表单中新的输入类型属性吗?
属性 | 作用 |
---|---|
search | 用于搜索域,比如站点搜索或 Google 搜索,域显示为常规的文本域 |
url | 用于应该包含 URL 地址的输入域在提交表单时,会自动验证 url 域的值 |
用于应该包含 e-mail 地址的输入域,在提交表单时,会自动验证 email 域的值 | |
datetime | 选取时间、日、月、年(UTC 时间) |
date | 选取日、月、年 |
month | 选取月、年 |
week | 选取周和年 |
time | 选取时间(小时和分钟) |
datetime-local | 选取时间、日、月、年(本地时间) |
number | 用于应该包含数值的输入域,您还能够设定对所接受的数字的限定 |
range | 用于应该包含一定范围内数字值的输入域,类型显示为滑动条 |
📢 详情文章:HTML5新增了哪些input类型及其属性?
🍏 考点 10 — 本地存储
10.1 cookie、localstroage、sessionStorage 的区别?优缺点?⭐⭐⭐
- cookie: 是服务器发给客户端的特殊信息,以文本形式存储在客户端,每次请求都会带上
cookie
。
cookie
的保存时间:设置过期时间,浏览器关闭后不会清除,保存在硬盘中, 过期时间到期后失效。如果不设置过期时间,保存在内存中, 浏览器关闭后消失。
缺点: ❶大小受限,单个cookie
大小不能超过 4kb❷用户可以禁用cookie
, 使功能受限❸安全性较低,有些状态不能保存在客户端❹每次访问都要传送cookie
给服务器,浪费带宽。❺cookie
数据有路径path
的概念,可以限制cookie
只属于某个路径下。 - localStorage 和 sessionStorage: 存储大小都是
5MB
,都保存在客户端不与服务器端进行交互,只能储存字符串类型,对于复杂的json
格式可以进行stringify
和parse
来处理。
区别:localStorage
是永久储存, 除非主动删除, 否则不会消失;而sessionStroage
的有效期只是网页在浏览器打开到关闭的时间段。
10.2 H5 应用程序缓存和浏览器缓存有什么区别?
✨ 重点理解: 应用程序缓存是H5
的重要特性之一,提供了离线使用的功能,提高网站性能,让应用程序可以获取本地的网站内容,例如HTML、CSS、
图片以及JavaScript
。
🍏 考点 11 — 其他问题
11.1 前端页面有哪三层构成,分别是什么?作用是什么?
- 前端页面构成:结构层、表示层、行为层。
- 结构层: 由
HTML
或XHTML
之类的标记语言负责创建。标签,也就是那些出现在尖括号里的单词,对网页内容的语义含义做出了描述,但这些标签不包含任何关于如何显示有关内容的信息。例如,P
标签表达了这样一种语义:“这是一个文本段。”
结构层为页面的骨架,由HTML
或XHTML
标记语言创建,用于搭建文档的结构。 - 表示层: 为页面的样式,由
CSS
(层叠样式表)负责创建,用于设置文档的呈现效果。 - 行为层: 为页面的行为,由
JavaScript
语言创建,用于实现文档的行为
11.2 CSS引入的方式有哪些? link和@import的区别是?
- CSS引入方式: 内联、内嵌 、外链、导入。
- 区别 : 同时加载,前者无兼容性,后者
CSS2.1
以下浏览器不支持,Link
支持使用javascript
改变样式,后者不可。
11.3 script标签中defer和async的区别
defer
浏览器指示脚本在文档被解析后执行,script
被异步加载后并不会立即执行,而是等待文档被解析完毕后执行
<script type="text/javascript" src="x.min.js" defer="defer"></script>
defer
只适用于外联脚本,如果script
标签没有指定src
属性,只是内联脚本,不要使用defer
如果有多个声明了defer
的脚本,则会按顺序下载和执行
defer
脚本会在DOMContentLoaded
和load
事件之前执行
async
同样是异步加载脚本,区别是脚本加载完毕后立即执行,这导致async
属性下的脚本是乱序的,对于script
又先后依赖关系的情况,并不适用
<script type="text/javascript" src="x.min.js" async="async"></script>
只适用于外联脚本,这一点和defer
一致
如果有多个声明了async
的脚本,其下载和执行也是异步的,不能确保彼此的先后顺序
async
会在load
事件之前执行,但并不能确保与DOMContentLoaded
的执行先后顺序
11.4 防抖与节流 ⭐⭐
✨ 重点理解: 关于防抖节流的应用,其实在实际应用中发生滚动事件、input
事件、页面resize
事件等较常遇到,防抖含义是对于短时间内连续触发的事件,让某个时间期限内,事件处理函数只执行一次;节流含义是对于短时间内连续触发的事件,让某个时间期限内,事件处理函数每定期执行一次;
防抖和节流的区别: 防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行。
📢 详情链接:浅谈 JS 防抖和节流
三、JavaScript部分
- 前言: 学习
JavaScript
之前我建议要把他的技术发展的时间线做一个大概的了解。下图是我在之前文章的总结截图,我们知道ES5
是05年版本
的统称,ES6
是15年版本
所有版本的统称,下面的面试题我尽量按照ES5
与ES6
两版不同对比总结。
✨前端学习笔记-javascript,完整版
🍈考点 1 — 数据类型
1.1 JavaScript的数据类型有哪些,分别是什么? ⭐⭐
- 在
ES5
版本中有6种数据类型▶️基本数据类型Undefined、Null、Boolean、Number、String
▶️复杂(引用)数据类型Object、Array
。 ES6
之后引入两种新数据类型Symbol
与bigInt
,Symbol
类型,用于创建一个独一无二的值。
📢 详解文章:js的基本数据类型有哪些?
1.2 null 和undefined 有什么区别?⭐
- undefined: 表示变量声明过但并未赋过值,它是所有未赋值变量默认值;
- null: 主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址。 当使用完一个比较大的对象时,需要对其进行释放内存时,设置为 null。
1.3 判断数据类型的方式有那些?⭐⭐⭐
- typeof: 对于基本类型,除
null
以外,均可以返回正确的结果。对于引用类型,除function
以外,一律返回object
类型。对于null
,返回object
类型。对于function
返回function
类型。 - instanceOf
- Constructor
- Object.prototype.toString.call()
1.4 数据类型检测方法与实现原理 ⭐⭐⭐⭐
✨ 重点理解: typeof()、A instanceof B
这两个方法常用,typeof
除了null
都可以显示正确的类型,instanceof
是ES6
方法原理是判断实例A
是否属于某种类型B
,另外还有object.prototype.toString.call('')
也可以检测数据类型,重点理解实现原理。
📢 详情文章:js中typeof和instanceof原理
1.5 instanceof原理⭐⭐⭐⭐
✨ 重点理解: 该问题可结合5.数据类型转换 理解,instanceof
可以正确判断对象的类型原因是通过判断对象的原型链中是不是能找到类型的prototype
过程:❶首先获取类型的原理❷然后获得对象的原型❸然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为null
,因为原型链最终为null
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while(true) {
if(left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
1.6 数据类型转换 ⭐⭐⭐
✨ 重点理解: 首先明确JS
中类型转换只有三种情况分别是:转换为布尔值、转换为数字、转换为字符串,再去记具体转换的方法和原理。
📢 详情文章:JS的类型转换原理
🍈考点 2 — 传值VS传址
2.1 请说出你对盒模型的理解阅读下面代码,说说运行的结果。⭐
//代码 1: let a = 1; let b = a; b = 3;
console.log(a, b);
//代码 2:
let c = { name:"hello" }; let d = c;
c.name = "hi"; console.log(c, d);
代码 1 运行的结果是:1 3
理由:基本类型的值是通过值复制的方式来赋值或是传递的,所以这里 b 的改变不会影响a 的。基本类型包括有 null、undefined、string、number、boolean 以及ES6 中的symbol。
代码 2 运行的结果是:{ name:"hi" } { name:"hi" }
理由:引用类型赋值并不会真正重新拷贝引用值,而是拷贝引用的地址,也就是传址。c 与 d 是引用同一个地址。所以如果修改了 c,d 的值也会被修改。
引用类型包括有数组、对象、函数等。
🍈考点 3 — 作用域
3.1 执行以下代码的结果是什么?为什么?
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
这段代码的执行结果是 undefined
和 2
。
原因是:变量和函数都被提升到了函数体的顶部。因此,当打印变量 a
时,它虽存在于函数体(因为 a
已经被声明),但仍然是 undefined
。函数 foo
执行后得到的结果是 2
。
✨ 此题考察是的变量提升的知识点,变量提升:可以理解为把变量声明提升到当前执行环境的最顶端。
3.2 执行以下代码的结果是什么?为什么?
var a = 1;
function check() {
return function() {
console.log(a);
console.log(b);
}
}
var func = check();
func();
这段代码的执行结果是:1
和 报错 b is not defined
。
当作用域内找不到 a
,会向上一层一层查找,最后找到了全局下的 a
,输出结果为 1
。同理,向上一层一层查找,最后找不到 b
,所以输出"Uncaught ReferenceError: b is not defined"
。
✨此题考察是的作用域和作用域链的知识点。
🍈考点 4 — 闭包
4.1 什么是闭包? ⭐⭐⭐⭐
✨ 重点理解: 闭包是:指有权访问另一个函数作用域中的变量的函数; 存在一个函数内部引用关系,缺点不自动销毁占内存、存在安全隐患。
稍微详细一点的回答:
在 js
中变量的作用域属于函数作用域,在函数执行完后,作用域就会被清理,内存也会随之被回收。但是由于闭包函数是建立在函数内部的子函数, 由于它可以访问上级作用域,即使上级函数执行完,作用域也不会随之销毁,。
这时的子函数(也就是闭包),便拥有了访问上级作用域中变量的权限,即使上级函数执行完后作用域内的值也不会被销毁。
4.2 如何解决闭包?⭐⭐
(= null
、立即执行函数等)
🍈考点 5 — 原型链和继承
5.1 说一下你对原型链与原型链继承的理解⭐⭐⭐
✨ 原型链: 当对象查找一个属性的时候,如果没有在自身找到,那么就会查找自身的原型,如果原型还没有找到,那么会继续查找原型的原型,直到找到 Object.prototype
的原型时,此时原型为 null
,查找停止。这种通过通过原型链接的逐级向上的查找链被称为原型链。
✨ 原型链继承: 一个对象可以使用另外一个对象的属性或者方法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样根据原型链的规则,如果查找一个对象属性且在自身不存在时,就会查找另外一个对象,相当于一个对象可以使用另外一个对象的属性和方法了。
📢 详情文章:JS面试题-原型及原型链、作用域及作用域链、闭包
继承⭐⭐
✨ 重点理解: 这个概念我其实在实际项目中基本没有用到过,不过也是面试的知识点。
📢 详情链接:前端必知必会ES5、ES6的7种继承
12.__proto__与prototype⭐⭐
✨ 总结理解: 在4.new操作符
中相信有人对于function Base(){放了一堆方法}; obj.__proto__ = Base.prototype;
不理解,其实这部分也是JavaScript
中的原型链知识点、非常重要,一句话总结 prototype是函数才有 的属性实际使用时有一些组件或整个项目常用的方法会放在里面;而 __proto__是每个对象都有的属性 (但是万物皆对象,所以__proto__
谁都有)。
📢 详情文章:__proto__与prototype
🍈考点 6 — BOM与DOM操作
6.1 什么是BOM?什么是DOM?⭐
BOM
是即浏览器对象模型。BOM
提供了独立于内容,与浏览器窗口进行交互的对象; 由于BOM
主要用于管理窗口与窗口之间的通讯,因此其核心对象是window
;BOM
由一系列相关的对象构成,并且每个对象都提供了很多方法与属性;DOM
是文档对象模型。 利用DOM
我们可以操作HTML
中的元素,使得网页被下载到浏览器后,开发者可以根据需求进行页面内容的修改。
6.2 说出几个你比较常用的 BOM 对象和方法?⭐
BOM常见对象/常用方法 | 作用 |
---|---|
window 对象 | 是 JS 的最顶层对象,其他的 BOM 对象都是 window 对象的属性 |
document 对象 | 文档对象 |
location 对象 | 浏览器当前 URL 信息 |
navigator 对象 | 浏览器本身信息 |
screen 对象 | 客户端屏幕信息 |
history 对象 | 浏览器访问历史信息 |
window.alert | window.alert(‘提示信息’) |
window.confirm | window.confirm(“确认信息”) |
window.prompt | window.prompt(“弹出输入框”) |
window.open | window.open(“url 地址”,“打开的方式”,“新窗口的大小”) |
window.close() | 关闭当前的网页 |
window.moveTo() | 移动当前窗口 |
window.resizeTo() | 调整当前窗口的尺寸 |
window.setTimeout | window.setTimeout(fun,时间) 只执行一次 |
window.setInterval | window.setInterval(fun,时间) 无限执行 |
13.节点操作⭐
✨ 重点理解: 一些基本的方法比较基础知道就行
🍈考点 7 — Ajax与跨域
7.1 原生 js ajax 请求有几个步骤?分别是什么?
✨ 一共有 5 个步骤,如下:
// 1.创建 XMLHttpRequest 对象
var ajax = new XMLHttpRequest();
//2. 规定请求的类型、URL 以及是否异步处理请求。
ajax.open('GET', url, true);
//3. 发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//4. 发送请求
ajax.send(null);
//5. 接受服务器响应数据
ajax.onreadystatechange = function () {
if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) {}
};
🍈考点 8 — 事件模型
8.1 请说说什么是事件流?
事件是文档或者浏览器窗口中发生的,特定的交互瞬间。而事件流,描述的是从页面中接收事件的顺序。
8.2 什么是事件冒泡?什么是事件捕获?
事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target
)。
事件冒泡阶段:事件从事件目标(target
)开始,往上冒泡直到页面的最上一级标签。
8.3 请说出阻止事件冒泡的几个办法?
event.stopPropagation()
: 事件处理过程中,阻止了事件冒泡,但不会阻击默认行为,比如:点击事件绑定在 a
标签的话,会执行了超链接的跳转。
return false
: 事件处理过程中,阻止了事件冒泡,也阻止了默认行为。比如:点击事件绑定在 a
标签的话,不会执行超链接的跳转。
🍈考点 9 — this 关键字
9.1 this ⭐⭐⭐⭐
✨ 重点理解: 箭头函数没有this
(扩展面试题箭头函数特点:箭头函数没有原型对象prototype
这个属性、没有arguments
对象)看下面详情文章中的两个经典案例代码理解this
指向的原理与应用。
📢 详情文章:this详解以及相关7道面试题
9.2 如何改变this指向 ⭐⭐⭐⭐
✨ 重点理解: ⑴ 改变this
的几种方法:❶使用ES6
箭头函数❷在函数内部使用const _this = this
;❸使用call() apply() bind()
❹new
实例化一个对象。⑵ 改变this
指向apply、call、bind
三种方法的区别?⑶ 哪些实际应用:其实在实际开发中并不算是常用与常用的数据操作方法使用频率相差很多,但是比如在某个方法中想调用其他方法或者数据会使用。
📢 详情文章: apply、call、bind区别
🍈考点 10 — es6篇
10.1 说说你知道那些es6特性(面试)
主要看文档啃熟悉,与技术官面试才能游刃有余,可以先按上面的9点来啃熟悉,其他稳定里的可以认识就行,因为这9点是重点考察对象
let
、const
- 变量的解构赋值
symbol
set
和map
的数据结构(set
主要用于去重)proxy
(vue3
的重点)promise
对象(如何实现,与async
,await
的区别)async
await
(返回值是promise
)generator
(react
使用比较多)class
、class
的继承(node
,react
主要使用)
10.2 介绍下 Set、Map的区别(比较少被问道,基本问的是set)⭐
Set
数据容器: 是一个能够存储无重复值的有序列表。
let set = new Set([1,2,3,3,3,3]);
console.log(set.size); /执行结果为3, 因为重复的值去除了, 所以set数组中只有3个变量长度为3/
Map
数据结构: 类似于对象,也是键值对的集合。“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说Object
结构提供了“字符串—值”的对应,Map
结构提供了“值—值”的对应,是一种更完善的Hash
结构实现。如果需要“键值对”的数据结构,Map
比Object
更合适。Map
的键是跟内存地址绑定的,只要内存地址不一样,就可视为两个键。这样解决了同名属性碰撞(clash)的问题,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
📢 详情链接: 深入理解ES6–7.Set和Map
📢 详情链接: JavaScript中Map的用途是什么?
10.3 如何手动创建一个Promise? ⭐⭐
✨ 重点理解: 可以使用Promise
的构造函数来手动创建一个Promise
。构造函数接收一个执行器函数作为参数,该函数会在Promise
被创建时立即执行。执行器函数有两个参数:resolve
和reject
,分别用于将Promise
转为fulfilled
状态并返回结果,或将Promise
转为rejected
状态并返回原因。
const myPromise = new Promise((resolve, reject) => {
// 异步操作
// 当操作成功时调用 resolve(value)
// 当操作失败时调用 reject(reason)
});
10.4 介绍下promise,有用过all方法吗?(重要,百分之90会问到,除非到不了这里就被pass)⭐⭐⭐⭐⭐
Promise
是异步编程的一种解决方案。Promise
对象有以下两个特点,对象的状态不受外界影响和一旦状态改变,就不会再变,任何时候都可以得到这个结果,promise
对象有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。Promise
对象是一个构造函数,用来生成Promise
实例,Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。promise
的方法(有些人叫api
)有then
, catch
,finally
,resolve
,reject
等。
Promise.all()
方法可以将多个Promise
实例包装成一个新的Promise
实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject
失败状态的值
需要特别注意的是,Promise.all
获得的成功结果的数组里面的数据顺序和Promise.all
接收到的数组顺序是一致的,即p1
的结果在前,即便p1
的结果获取的比p2
要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all
毫无疑问可以解决这个问题。
let pros = []
data.forEach(element => {
pros.push( this.getJsonFile(element.json,element.fileName) )
});
Promise.all(pros).then(val => {
this.fontsList = [...val]
})
10.5 如何去重?(重要,百分之70) ⭐⭐⭐⭐
✨[…new set(arr)]
10.6 for…in和for…of区别⭐
✨ 重点理解: 这个问题很简单for…in
和for…of
都是循环数据使用,不同的是for…in
用于遍历数组或者对象的属性,for...of
和forEach
一样,是直接得到值for of
不能对象用。
const arr = ['a', 'b', 'c']
for (let i in arr) {
console.log(i)
}; /执行结果为0 1 2/
for (let i of arr) {
console.log(i)
}; /执行结果为a b c/
但是for...in
有一种特殊情况
const arr = ['a', 'b', name:'qiqingfu']
for (let i in arr) {
console.log(i)
}; /执行结果为a b name/
10.7 常见数组操作方式 ⭐⭐⭐⭐
📢 详情文章:js中数组操作方法整理
✨ 重点理解: 遍历数组的方式与数组去重的方法。
异步加载的方式有那些(重要,笔试,面试)
数组:
1.Defer
2.HTML5为<script>
元素定义的async属性
3.动态创建<script>
标签
23.Proxy对象ES6⭐
✨ 重点理解: Proxy
对象是ES6
引入的新知识点,建议结合Object.defineproperty()
与vue
框架的双向数据绑定一起理解。
📢 详情链接:详解es6中Proxy代理对象的作用
📢 详情链接:面试官: 实现双向绑定Proxy比defineproperty优劣如何?
🍈考点 11 — 其它问题
22.Object.defineProperty()⭐
✨ 重点理解: 我在刚刚接触这个方法的时候一直有个疑问,为什么要用这个方法,这不是更加不方便了么,后来慢慢理解了,这个方法可以限制加入的数据是否可以遍历、是否可以删除等。
📢 详情链接:深入浅出Object.defineProperty()
8.new操作符 ⭐⭐⭐
✨ 总结理解: 调用new
构造函数例var obj = new Base();
这样代码的结果是什么?new
操作符具体干了什么呢?其实很简单就干了三件事情。❶ var obj = {};
创建一个新对象 ❷obj.__proto__ = Base.prototype;
❸Base.call(obj);
新对象的_proto_
属性指向构造函数的原型对象,同时返回新对象obj
,所以构造函数中不用return
值。
📢 详情文章:js中的new关键字都干了些什么?
10.深拷贝与浅拷贝⭐⭐⭐⭐
✨ 总结理解: 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的,不针对基础数据类型。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
额外记住深拷贝浅拷贝的几种方法:
浅拷贝方法object.assign
、...
、=
。
深拷贝方法 JSON.parse(JSON.stringify(obj))
、
📢 详情文章: 深拷贝和浅拷贝
17.JavaScript的三种弹出⭐
📢 详情链接:JavaScript中常用的三种弹窗
19.Event Loop⭐⭐⭐⭐
✨ 重点理解:
- 线程与进程:
JS
是单线程执行,这里引出“线程”与“进程”的区别?(⼀个进程中可以有多个线程,所以JS
运行的时候阻止UI
渲染,两个线程是互斥的,原因也是因为JS
可以修改DOM
) - 执行栈: 可以认为是一个存储函数的调用栈结构,遵循“先进后出”的原则
- Event Loop事件循环: 微任务优先级高(
process.nextTick
、promise
、Object.observe
、MutationObserver
、async/await
),宏任务(setTimeout
、setInterval
、XHR
)
21.事件机制⭐
🔥涉及面试题: 事件的触发过程是怎样的?知道什么是事件代理吗?
- 事件触发的三个阶段: ❶
window
往事件触发处传播,遇到注册的捕获事件会触发❷传播到事件触发处时触发注册的事件❸从事件触发处往 window 传播, 遇到注册的冒泡事件会触发。 - 注册事件: 通常使用
addEventListener
注册事件。
node.addEventListener(
'click',
event => {
event.stopImmediatePropagation()
console.log( '冒泡 ')
},
false
)
// 点击 node 只会执行上面的函数,该函数不会执行
- 事件代理: 如果⼀个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注
册在父节点上。优点:节省内存;不需要给子节点注销事件。
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector( '#ul')
ul.addEventListener( 'click', (event) => {
console.log(event.target);
})
</script>
四、网络基础http☃️
前言: 既然我们是前端开发那就势必要与后端配合,来请求后台的服务器的数据,这时候就是网络http
出场的时间了,个人建议在看该面试题之前建议先将http
基础复习一遍,方便了解下面的面试题以及总结的答案。前端工程师必须要知道的HTTP部分。
1.跨域⭐⭐⭐
❄️涉及面试题: 什么是跨域?为什么浏览器要使用同源策略?你有几种方法可以解决跨域问题?了解预检请求吗?
出于安全考虑(防止CSRF
攻击,当然跨域也并不能完全解决安全问题,因为拦截是浏览器拦截的请求还是已经发送出去了)有同源政策,意思就是协议、域名或端口有一个不用就是跨域,Ajax
请求会失败。
你可能会疑问明明通过表单的方式可以发起跨域请求, 为什么Ajax
就不会 。因为归根结底,跨域是为了阻止用户读取到另⼀个域名下的内容,Ajax
可以获取响应, 浏览器认为这不安全,所以拦截了响应 。但是表单并不会获取新的内容,所以可以发起跨域请求 。同时也说明了跨域并不能完全阻止CSRF
, 因为请求毕竟是发出去了。
- JSONP: 原理就是利用
<script>
标签没有跨域限制的漏洞。但是只限于get
请求。
<script src= " http://domain/api?param1=a¶m2=b&callback=jsonp" > </script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
📢 详情链接:最常见的六种跨域解决方案
2.存储Cookies与Session,SessionStore,LocalStore区别及使用⭐⭐
📢 详情链接:Cookie、session和localStorage、以及sessionStorage之间的区别
3.浏览器缓存机制⭐⭐⭐
- 缓存位置
- 缓存策略
- 实际场景应用缓存策略
HTTP缓存
4.浏览器渲染原理⭐⭐⭐⭐
- 渲染过程: ❶浏览器接收到
HTML
文件并转换为DOM
树❷将CSS
文件转换为CSSOM
树❸生成渲染树。 - 为什么操作DOM慢: 因为
DOM
是渲染引擎的东西,JS
属于JS
引擎的东西,当我们通过JS
操作DOM
的时候就涉及到这两个线程之间的通信那么势必会带来一些性能上的损耗,并且操作DOM
可能还会带来重绘与回流的问题。 - 插入几万个DOM,如何实现页面不卡顿?: 虚拟滚动,就是只渲染可见区域的内容,非可见区域完全不渲染了。
5.GET与POST请求方式的区别⭐
✨ 重点理解: 我们在前言复习时了解到,http
请求方式不止GET
与POST
,还有PUT
等其他请求方式,但是不作为重点了解。
✨ 面试时回答3点我认为足够了!!!
❶GET
比POST
不安全,因为GET
提交的数据放在URL
中,POST
则不是(POST
也不安全,因为HTTP
是明文传输抓包就能获取数据内容,要想安全还得加密)。
❷GET
回退浏览器无害,POST
需要再次提交请求(GET
有缓存中拿结果,POST
没缓存)。
❸GET
和POST
还有一个重大区别,简单的说:GET
产生一个TCP
数据包,POST
产生两个TCP
数据包。换句话说:对于GET
方式的请求,浏览器会把http header
和data
一并发送出去,服务器响应200(返回数据),而对于POST
,浏览器先发送header
,服务器响应100 continue
,浏览器再发送data
,服务器响应200 ok
(返回数据)。也就是网络请求的两次握手,我记得在哪见过解释。
📢 详情链接:你敢在post和get上刁难我,就别怪我装逼了
https 的握手过程
五、ajax前后端交互
🎃1.ajax的代码书写步骤
2.ajax中get和post请求的区别
3.如何解析json数据:JSON.parse()
ajax模块化
六、Webpack部分
💎1.有哪些常见的Loader?你用过哪些Loader?
Loader | 作用 |
---|---|
raw-loader | 加载文件原始内容(utf-8 ) |
file-loader | 把文件输出到一个文件夹中,代码中通过相对 URL 去引用输出的文件 (处理图片和字体) |
url-loader | 与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值时返回其 publicPath ,小于阈值时返回文件 base64 形式编码 (处理图片和字体) |
source-map-loader | 加载额外的 Source Map 文件,以方便断点调试 |
svg-inline-loader | 将压缩后的 SVG 内容注入代码中 |
image-loader | 加载并且压缩图片文件 |
json-loader | 加载 JSON 文件(默认包含) |
handlebars-loader | 将 Handlebars 模版编译成函数并返回 |
babel-loader | 把 ES6 转换成 ES5 |
ts-loader | 将 TypeScript 转换成 JavaScript |
awesome-typescript-loader | 将 TypeScript 转换成 JavaScript ,性能优于 ts-loader |
sass-loader | 将SCSS/SASS 代码转换成CSS |
css-loader | 加载 CSS ,支持模块化、压缩、文件导入等特性 |
style-loader | 把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS |
postcss-loader | 扩展 CSS 语法,使用下一代 CSS ,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 |
eslint-loader | 通过 ESLint 检查 JavaScript 代码 |
tslint-loader | 通过 TSLint 检查 TypeScript 代码 |
mocha-loader | 加载 Mocha 测试用例的代码 |
coverjs-loader | 计算测试的覆盖率 |
vue-loader | 加载 Vue.js 单文件组件 |
i18n-loader | 国际化 |
cache-loader | 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里 |
… | 更多 Loader 请参考官网 |
💎2.有哪些常见的Plugin?你用过哪些Plugin?
Plugin | 作用 |
---|---|
define-plugin | 定义环境变量 (Webpack4 之后指定 mode 会自动配置) |
ignore-plugin | 忽略部分文件 |
html-webpack-plugin | 简化 HTML 文件创建 (依赖于 html-loader) |
web-webpack-plugin | 可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用 |
uglifyjs-webpack-plugin | 不支持 ES6 压缩 (Webpack4 以前) |
terser-webpack-plugin | 支持压缩 ES6 (Webpack4) |
webpack-parallel-uglify-plugin | 多进程执行代码压缩,提升构建速度 |
mini-css-extract-plugin | 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin) |
serviceworker-webpack-plugin | 为网页应用增加离线缓存功能 |
clean-webpack-plugin | 目录清理 |
ModuleConcatenationPlugin | 开启 Scope Hoisting |
speed-measure-webpack-plugin | 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时) |
webpack-bundle-analyzer | 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块) |
… | 更多 Plugin 请参考官网 |
七、Vue 部分面试题
再看这些面试题之前,希望了解一些基本概念,什么是Vue实例,什么是Vue组件,什么是$el,路由,基本的javascript语法等。如果时间充足建议花两个星期的时候研读下面的笔记已经对应的视频,很多问题就可以迎刃而解了。
前端学习笔记____Vue2+Vue3全家桶
浅谈Vue构造、Vue实例、Vue组件的区别
🍀考点 1 — Vue的理解
1.1 Vue优缺点⭐
- 优点: 渐进式,组件化,轻量级,虚拟
dom
,响应式,单页面路由,数据与视图分开。 - 缺点: 单页面不利于
seo
,不支持IE8以下,首屏加载时间长
1.2 谈谈对MVVM模型的理解?⭐⭐⭐
- M模型(Model): 对应
data
中的数据,数据模型定义数据操作逻辑 - V视图(View): 模板代表
UI
组件,负责将数据模型转化成UI
展现出来 - VM视图模型(ViewModel):
Vue
实例对象。通过双向绑定把View
和Model
进行同步交互,不需要手动操作DOM
的一种设计思想。
✨总结理解:Vue
参考了该MVVM
模型,在Vue
之前就有MVVM
的模型理念。一句话就是:数据改变视图,视图影响数据。
1.3 Vue是如何实现数据双向绑定的?⭐⭐⭐⭐
Vue
双向绑定2.X与3.X的原理不同,Vue2.x
底层语法是Object.defineProperty()
,Vue3.1
是Proxy
,衍生面试题2.X与3.X
的区别?3.X
版本有什么优点?可以自行思考一下。通过这个话题其实可以衍生出Proxy
与Object.defineProperty
优劣对比(ES6)语法?与JavaScript
部分呼应。
✨总结理解: 采用数据劫持结合发布者- 订阅者模式的方式, 通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通Javascript
对象传给Vue
实例来作为它的data
选项时,Vue
将遍历它的属性, 用Object.defineProperty
将它们转为getter/setter
。用户看不到getter/setter
,但是在内部它们让Vue
追踪依赖, 在属性被访问和修改时通知变化。
📢详解文章:Vue 是如何实现数据双向绑定的?
1.4 请你说说对“渐进式框架”的理解⭐
✨总结理解: 所说的“渐进式”,其实就是vue
的使用方式,同时也体现了vue
的设计理念,渐进式代表的含义是:主张最少。
📢详解文章: vue渐进式框架的理解
1.5 请说说什么是虚拟DOM?虚拟DOM实现原理?⭐⭐
📢详解文章:vue虚拟Dom详解
✨总结理解:虚拟Dom
是一个JS
对象,通过对象的方式来表示DOM
结构。将页面的状态抽象为JS
对象的形式,配合不同的渲染工具,使跨平台渲染成为可能。
通过事务处理机制,将多次DOM
修改的结果一次性的更新到页面上,从而有效的减少页面渲染的次数,减少修改DOM
的重绘重排次数,提高渲染性能。
在代码渲染到页面之前,vue
会把代码转换成一个对象(虚拟DOM
)。以对象的形式来描述真实DOM
结构,最终渲染到页面。在每次数据发生变化前,虚拟Dom
都会缓存一份,变化之时现在的虚Dom
会与缓存的虚拟Dom
进行比较。
在vue
内部封装了diff
算法,通过这个算法来进行比较,渲染时修改改变的变化,原先没有发生改变的通过原先的数据进行渲染。 另外现代前端框架的一个基本要求就是无需手动操作DOM
,一方面是因为手动操作DOM
无法保证程序性能,省略手动DOM
操作可以大大提升开发效率。
1.6 Vue中的this指向⭐
✨总结理解: 关于this
指向:❶组件配置中data
函数、methods
中的函数、computed
中的函数它们的this
指向均是VueComponent
实例对象 ❷new Vue()
配置中data
函数、methods
中的函数、computed
中的函数它们的this
指向均是Vue
实例对象。
🍀考点 2 — Vue生命周期相关
2.1 说说对Vue生命周期理解⭐⭐⭐⭐⭐
✨总结理解: Vue
实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom
->渲染、更新->渲染、卸载等一系列过程,即Vue
的生命周期。我们要知道Vue
实例生命周期有8
个,但是keep-alive
有两个专属的生命周期,详解如下图。
生命周期 | 详解 |
---|---|
beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
created | 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用 |
mounted | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子 |
beforeUpdate | 组件或实例数据更新之前调用,发生在虚拟 DOM 打补丁之前 |
update | 组件或实例数据更新之后 |
activited | keep-alive 专属,组件被激活时调用 |
deactivated | keep-alive 专属,组件被销毁时调用 |
beforeDestory | 组件或实例销毁前调用 |
destoryed | 组件或实例销毁后调用 |
errorCaptured | 在捕获一个来自后代组件的错误时被调用 |
2.2 说说Vue实例生命周期钩子函数具体适用的场景?⭐⭐
- beforeCreate: 可以在这个
loading
事件,在加载实例时触发。 - created: 初始化完成时的事件写在这里,如在这结束
loading
事件,异步请求也适宜在这里调用。 - mounted: 挂载元素,获取到
DOM
节点。 - update: 如果对数据统一处理,在这里写上相应函数。
- beforeDestory: 可以做一个确认停止事件的确认框。
2.3 Vue的父组件和子组件生命周期钩子函数执行顺序?⭐⭐⭐
- 加载渲染过程:
父beforeCreate => 父created => 父beforeMount => 子beforeCreate => created => 子beforeMount => 子mounted => 父mounted
。 - 子组件更新过程:
父beforeUpdate => 子beforeUpdate => 子updated => 父updated
- 父组件更新过程:
父beforeUpdate => 父updated
- 销毁过程:
父beforeDestroy => 子beforeDestroy => 子destroyed => 父destroyed
2.4 在哪个生命周期内调用异步请求?
✨总结理解:
📢详解文章:
2.5 在什么阶段才能访问操作DOM?⭐
在钩子函数mounted
被调用前,Vue
已经将编译好的模板挂载到页面上,所以在mounted
中可以访问操作DOM
。vue
具体的生命周期示意图可以参见如下,理解了整个生命周期各个阶段的操作,关于生命周期相关的面试题就难不倒你了。
2.6 父组件可以监听到子组件的生命周期吗?
✨总结理解: 可以。有两种方法:1.在父组件中定义一个事件,在子组件中用$emit
去触发。2.父组件引用子组件时,给父组件添加@hook
来监听。其它的生命周期事件,例如:created,updated 等都可以监听。
📢详解文章:父组件可以监听到子组件的生命周期吗?
🍀考点 3 — 计算属性
3.1 请说说computed和methods、watch的区别?⭐⭐⭐
- computed: 计算属性,依赖其他属性的值。具有缓存,只有他依赖的值发生变化,下一次取当前属性时才会重新计算,这样的好处是避免了每次取值都重新计算。
- watch: 侦听器,当侦听的值发生改变时,其他变化会跟着改变有些操作会被触发,当需要在数据变化时执行异步或开销比较大的操作时,使用
watch
是最合理的。 - methods: 方法页面刚刚加载时调用一次,结果不会缓存。
methods
里面是用来定义函数的,他需要手动调用才能执行。而不像computed
与watch
那样,“自动执行”预先定义的函数。
3.2 说computed和watch有何区别?
Computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当
computed
内有异步操作时无效,无法监听数据的变化 computed
属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data
中声明过或者父组件传递的props
中的数据通过计算得到的值- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用
computed
Watch
- 不支持缓存,数据变,直接会触发相应的操作;
watch
支持异步;- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是
data
中声明过或者父组件传递过来的props
中的数据
🎨考点 4 — Vuex
4.1 Vuex 是什么?怎么使用?哪种功能场景使用它?⭐
- Vuex: 是一个专为
Vue.js
应用程序开发的状态管理器, 采用集中式存储管理应用的所有组件的状态,主要是为了多页面、多组件之间的通信。 - Vuex的五个属性:
state
、getter
、mutation
、action
、module
。
✨总结理解: 由view
层发起一个Action
给Mutation
, 在Mutation
中修改状态, 返回新的状态, 通过Getter
暴露给view
层的组件或者页面,页面监测到状态改变于是更新页面。
4.2 Vuex 的五个属性分别如何使用⭐
Vuex的State存储数据
Mutation是更改 Vuex 的 store 中的状态的唯一方法。在action中调用tore.commit来触发Mutation
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。通过store.dispatch来Action
4.3 请描述一下 Vuex 和 localStorage 的区别是什么?⭐
vuex
存储在内存,而localstorage
以文件的方式存储在本地localstorage
只能存储字符串类型的数据,存储对象需要JSON
的stringify
和parse
方法进行处理。读取内存比读取硬盘速度要快vuex
是一个转为为Vue.js
应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex
用域组件之间的传值,而localstorage
是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用。vuex
能做到数据的响应式,localstorage
不能做到- 刷新页面时
vuex
存储的值会丢失,localstorage
是永久性,不会丢失。
🍀考点 5 — vue-router
5.1 vue-router是什么?它有哪些组件?⭐⭐⭐
📢详解文章: vue-router是什么?有哪些组件?
- vue-router: 传统页面切换是用超链接
a
标签进行切换,vue-router
是vue.js
官方路由管理器。vue
的单页应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。 - 组件:
router-link
、router-view
组件。
5.2 vue-router实现原理⭐⭐⭐
单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。
单页面应用(SPA)
的核心之一是:更新视图而不重新请求页面。
vue-router
在实现单页面前端路由时, 提供了两种方式Hash
模式和History
模式;根据mode
参数来决定采用哪一种方式 。
- Hash 模式:
vue-router
默认hash
模式,使用url
的hash
来模拟一个完整的url
,于是当url
改变时,页面不会重新加载。hash(#)
是url
的锚点,代表的是网页中的一个位置,单单改#
后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说#
是用来指导浏览器动作的,同时每一次改变#
后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一 个位置;所以说Hash
模式通过锚点值的改变,根据不同的值,渲染指定DOM
位置的不同数据 - History 模式: 由于
hash
模式会在url
中自带#
,如果不想要很丑的hash
,我们可以用路由的history
模式,只需要在配置路由规则时,加入mode: 'history'
,这种模式充分利用history.pushState API
来完成url
跳转而无须重新加载页面。
5.3 能说下vue-router中常用的hash和history路由模式实现原理吗?⭐⭐⭐
- hash模式: 背后的原理是
onhashchange
事件,可以在window
对象上监听这个事件,并通过window.hash
获取url
后面的hash
值。
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1);
// 获取hash后可以用来进行下一步的操作
document.body.style.color = hash;
}
- history模式: 利用了
HTML5 History Interface
中新增的pushState()
和replaceState()
方法。需要后台配置支持。如果刷新时服务器没有响应的资源,会刷出404。
📢详解文章: hash 和 history 路由模式实现原理吗?
5.4 请说出使用路由模块来实现页面跳转的三种方式。⭐
✨总结理解: ❶直接修改地址栏❷this.$router.push('路由地址')
❸<router-link to="路由地址"></router-link>
5.5 $router与 $route的区别⭐⭐⭐
router
是VueRouter
的实例,是一个全局的对象。通过Vue.use(VueRouter)
和VueRouter
构造函数得到一个router
的实例对象,它包含了路由跳转的方法、钩子函数等。route
是路由信息对象|跳转的路由对象,每一个路由都会有一个route
对象,是一个局部的对象,可以获取对应的name
、path
、params
、query
等。
📢详解文章: vue2.0中的$router 和 $route的区别
5.6 vue-router路由模式有几种?⭐
✨总结理解: vue-router 有 3 种路由模式:hash
、history
、abstract
📢详解文章:vue-router路由模式有几种?
5.7 Router的传参方式有哪些,有什么区别?⭐⭐
📢详解文章: Vue-router路由传参三种方法及区别
5.8 Router的懒加载是如何实现的⭐⭐
把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应的组件即为路由的懒加载component:() = import('../views/home')
5.9 Router的导航钩子有哪些?说说导航守卫?⭐⭐⭐⭐
- 全局守卫:
router.beforeEach
- 全局解析守卫:
router.beforeResolve
- 全局后置钩子:
router.afterEach
- 路由独享的守卫:
beforeEnter
- 组件内的守卫:
beforeRouteEnter
、beforeRouteUpdate(2.2 新增)
、beforeRouteLeave
📢详解文章: vue-router有哪几种导航守卫
5.8 谈谈你对keep-alive的了解?⭐⭐⭐
📢详解文章: 谈谈你对keep-alive的了解?
✨总结理解: keep-alive 其实就是缓存路由组件,可以不使用,使用之后的优点是:让不展示的路由组件保持挂载,不被销毁,使用方法如下
<!--如不写include属性,代表所有的router-view中所有层级都缓存不销毁 -->
<keep-alive include="['组件名1', '组件名2']">
<router-view></router-view>
</keep-alive>
5.9 相同的路由组件如何重新渲染?⭐⭐
开发人员经常遇到的情况是,多个路由解析为同一个Vue组件。问题是,Vue出于性能原因,默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化
const routes = [{
path: "/a",
component: MyComponent
},{
path: "/b",
component: MyComponent
}];
如果依然想重新渲染,怎么办呢?可以使用key
<template>
<router-view :key="$route.path"></router-view>
</template>
🍀考点 6 — 组件通信
6.1 Vue 组件间通信有哪几种方式?⭐⭐⭐⭐
✨总结理解: 组件之间通信包括三种:父组件传子组件,子组件传父组件,兄弟组件之间。
6. 怎样理解Vue的单向数据流?⭐
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个 prop 的情形 :
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
- 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
📢详解文章:什么是vue单向数据流(详解)
🍀考点 7 — vue-cli
7.1 构建的vue-cli工程都到了哪些技术,它们的作用分别是什么?
1.vue.js:vue-cli 工程的核心,主要特点是双向数据绑定和组件系统
2.Sass,Less
3.vue-router:vue 官方推荐使用的路由框架。
4.vuex:专为 Vue.js 应用项目开发的状态管理器,主要用于维护 vue 组件间共用的一些 变量 和方法。
5.axios( 或者 fetch 、 ajax ): 用于发起 GET 、 或 POST 等 http 请求,基于 Promise 设计。
6.vux 等:一个专为 vue 设计的移动端 UI 组件库。
7.创建一个 emit.js 文件,用于 vue 事件机制的管理。
8.webpack:模块加载和 vue-cli 工程打包器。
🍀考点 8 — 常用指令
8.1 说说你在日常开发中常用的指令有哪些?⭐
8.2 Vue中的key有什么作用? (key的内部原理)⭐⭐
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
🌼 虚拟DOM中key的作用:key是虚拟DOM对象的标示,当数据发生变化时,Vue会3据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较。比较规则如下:
🌼 对比规则:❶旧虚拟DOM中找到了与新虚拟DOM相同的key;若虚拟DOM中内容没变,直接使用之前的真实DOM;若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。❷旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到页面。
🌼 用index作为key可能会引发的问题:❶若对数据进行:逆序添加、逆序删除破坏操作( 会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低 )。⑵如果结构中还包含输入类的DOM( 会产生错误DOM更新 ==> 界面有问题 )。
🌼 开发中如何选择key?
❶最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。❷如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
8.3 v-if 与 v-show 的区别⭐⭐⭐
✨总结理解: v-if
是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件。v-show
不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS
的“display”
属性进行切换。所以v-if
适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show
则适用于需要非常频繁切换条件的场景。
8.4 为什么Vue中的v-if和v-for不建议一起用?⭐⭐⭐
当v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。这意味着v-if
将分别重复运行于每个v-for
循环中。即先运行v-for
的循环,然后在每一个v-for
的循环中, 再进行v-if
的条件对比。会造成性能问题,影响速度。
为了避免出现这种情况,乐意在外层嵌套 template
(页面渲染不生成dom
节点),在这一层进行
<template v-if="isShow"><p v-for="item in items"></template>
v-if
判断,然后在内部进行 v-for
循环。
如果条件出现在循环内部,可通过计算属性computed
提前过滤掉那些不需要显示的项。
computed: {
items: function() {
return this.list.filter(function (item){
return item.isShow
})
}
}
8.5 为什么v-if和v-for不建议用在同一标签?⭐⭐⭐
v-for
优先级是高于v-if
,v-for
和v-if
同时存在,会先把元素都遍历出来,然后再一个个判断,并隐藏掉,这样的坏处就是,渲染了无用的节点,增加无用的dom
操作
8.6 使用过哪些Vue的修饰符呢?
8.7 v-model的原理?
✨总结理解: 一句话概括就是,绑定数据并且监听数据改变。v-model
是:value="msg" @input="msg=$event.target.value"
的语法糖,其中:value="msg"
是绑定了数据,value
就是 input
输入框里的值;@input="msg=$event.target.value"
就是监听 input
输入框里值的变化,然后改变值。
📢详解文章: 你知道v-model的原理吗?说说看
8.8 自定义指令的使用(directives)
示例代码如下
/需求1: 定义一个v-big指令,与v-text类似但是会把绑定的数值放大10倍/
/需求2: 定义一个v-fbind指令,与v-bind类似但可以让其所绑定的input元素默认获取焦点/
<body>
<div id="root">
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button> <hr/>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: { n:1 },
/局部自定义指令/
directives: {
/两种方式,第一写成对象big:{}(完整),第二种写成函数big(){}/
big(element,binding){ /element是真实DOM元素,binding是一个对象存各种信息/
element.innerText = binding.value * 10;
},
fbind:{ /包含3个钩子函数/
console.log(this); /注意此处的this指向window/
bind(element,value){ element.value = binding.value }, /指令与元素成功绑定时调用/
inserted(element,value){ element.focus() }, /指令所在元素被插入页面时调用/
upddate(element,value){ element.value = binding.value } /指令所在的模板被重新解析时调用/
}
},
})
</script>
🌼 局部指令 new Vue({ directives:{指令名:配置对象} })
或 new Vue({ directivea(){} })
🌼全局指令Vue.directive(指令名, 配置对象)
或 Vue.directive(指令名, 回调函数)
。
🌼 配置对象中常用的3个钩子函数:
❶ bind 指令与元素成功绑定时调用
❷ inserted 指令所在元素被插入页面时调用
❸ update 指令所在模板结构被重新解析时调用。
⚠️指令定义时不加 v-,但使用时要加 v- ;指令名如果是多个单词,要使用 kebab-case 命名方式,不要用 camelCase 命名。
🍀考点 9 — 其他问题
9.1 解决Vue页面加载时出现{{message}}闪退⭐
使用v-cloak
解决,它是是一个特殊属性,Vue
实例创建完毕并接管容器后,会散删掉v-cloak
属性;使用css
配合v-cloak
可以解决网速慢时页面展示出{{xxx}}
的问题。
<h2 v-cloak>{{name}}</h2>
<!-- 如果不加v-cloak页面会显示{{name}},5秒之后插值表达式才会解析 -->
9.2 el与data的两种写法
🌼 el
的两种写法
<head>
<script text="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">容器</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root', /第一种写法,直接与容器关联/
data: {},
})
</script>
<head>
<script text="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">容器</div>
<script type="text/javascript">
const vm = new Vue({
data: {},
});
vm.$mount('#root'); /mount挂载,写法灵活,比如可以加定时器控制执行时间/
</script>
</body>
🌼 data
的两种写法
<head>
<script text="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">容器</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {}, /第一种写法,对象式/
})
</script>
<head>
<script text="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">容器</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: function(){ /第二种 函数式,function可以省略,组件必须使用函数式/
return: 'xxx',
},
})
</script>
🌼 思考:为什么组件中 data 必须使用函数式写法?这个也是一个面试题。
9.3 为什么组件中data必须使用函数式写法?⭐⭐⭐
🌼 其实将组件中的data
配置成对象也并不会报错,但是因为组件会被多次使用,如果不配置成函数配置成对象的话,数据data
就会互相污染。
9.4 谈谈对SAP单页面的理解,它的优缺点分别是什么?
📢详解文章:
✨总结理解: 先了解什么是 SAP 单页面应用,用一句话总结就是整个应用只有一个完整页面,点击页面中的导航只做局部刷新。
- 优点: ①用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;②基于上面一点,SPA 相对对服务器压力小;③前后端职责分离,架构清晰。
- 缺点: ①初次加载耗时多;②前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;③SEO 难度较大。
9.5 Vue中如何动态绑定Class与Style?
/绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定/
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><dr/>
/绑定class样式--数组写法,适用于:要绑定的样式个数和名字不确定/
<div class="basic" :class="classArr">{{name}}</div><dr/>
/绑定class样式--对象写法,适用于:要绑定的样式个数和名字确定,但是要动态决定用不用/
<div class="basic" :class="classObj">{{name}}</div><dr/>
<div class="basic" :style="{fontSize: fsize+'px'}">{{name}}</div>
/绑定style样式--数组写法/
<div class="basic" :style="styleObj">{{name}}</div>
/绑定style样式--对象写法/
<div class="basic" :style="styleArr">{{name}}</div>
9.6 直接 arr[index] = xxx 无法更新视图怎么办?为什么?⭐⭐
Vue
没有对数组进行Object.defineProperty
的属性劫持,所以直接arr[index] = xxx
是无法更新视图的使用数组的splice
方法,arr.splice(index, 1, item)
使用Vue.$set(arr, index, value)
###9. 你有对 Vue 项目进行哪些优化?
###9.Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
###9.Vue框架怎么实现对象和数组的监听?
###9. 使用过Vue SSR吗?说说SSR?
###9. 直接给一个数组项赋值,Vue能检测到变化吗?
内置关系VueComponent.prototype.proto === Vue.prototype