最近要准备面试,现在在准备Vue相关的面试点,下面是我整理的(不全,因为有的知识点适合单独拿出来,所以就没有放在下面)
分为基础版和进阶版, 还有知识适合单独拿出来讲,如vue的模板编译、虚拟dom、diff算法等,我就没有放在这里了
基础版:
1、Vue的优缺点
- 优点:渐进式、组件化、轻量级、虚拟DOM、响应式、单页面路由、数据与视图分开
- 缺点:单页面不利于seo,首屏加载时间比较长
2、为什么说Vue是一个渐进式框架
- 渐进式:通俗点来说,就是,你想用啥就用啥。你想用component就用component,不用也行,你想用vuex就用,不用也行
3、MVVM模型
-
MVVM, 即model View ViewModel
-
model可以理解为数据层,它仅仅关注数据本身,不关注任何行为
-
view指的是我们所看到的页面,MVVM中的view通过使用模板语法来声明式的将数据渲染进DOM
-
ViewModel式MVVM模型的核心,它是连接view和modelh之间的桥梁。它主要有两个方向:
-
- 将model转化为view,即将后端传递的数据转化成所看到的页面,实现的方式是数据绑定:Data banding
-
- 将View转化为Model,即将所看到的页面转化为后端的数据,实现的方式是DOM Listener
-
这两个方向都实现,我们称之为数据的双向绑定
4、生命周期
-
Vue组件的生命周期可以分为四个阶段,create(创建),mount(挂载),update(更新),destroy(销毁)
-
这四个阶段又可以对应八个钩子函数,beforeCreate/created,beforeMount/mounted,beforeUpdate/updated,beforeDestroy/destroyed
-
其大致过程如下:
-
1、实例通过new Vue()创建出来后会初始化事件和生命周期钩子,然后就执行beforeCreate(),这个时候只是一个空壳,是无法访问到data中的数据和methods中的方法,在这一阶段一般不做操作
-
2、实例创建完成之后,即执行created()钩子,此时通过实例是访问到data中的数据、methods中配置的方法。但是它还没有将虚拟DOM挂载到真实DOM上。在这一阶段可以更改数据,而不会触发updated()函数
-
如果我们的ajax请求不依赖于DOM,或者说不改变DOM的话,可以把ajax请求放在created里面。
-
而如果将ajax请求放在mounted()里面的话,由于那个时候DOM已经渲染出来了,有可能会导致页面抖动。而放在created里面请求,不仅请求快,而且还不会造成页面抖动
-
3、接下来会将组件对应的模板编译为虚拟DOM,然后放入render函数中准备进行渲染,这个时候执行beforeMount()钩子函数。这个时候虚拟DOM已经创建完成,但是还没有挂载到真实DOM上,因此在这一阶段我们所有对DOM的操作,最终都不会奏效,这个时候也可以在不触发其他钩子函数的情况下更改数据
-
4、挂载到真实DOM后,即执行mounted()钩子函数,在这一阶段我们可以对真实的DOM节点做一些操作,发送AJAX请求,开启定时器,绑定自定义事件等
-
5、当组件或实例的响应式数据更改之后,就会立刻执行beforeUpdate().这个时候数据式新的,但页面是旧的,也就是说数据和页面尚未保持同步
-
6、然后Vue会重新构建虚拟DOM,然后利用diff算法与上一次的虚拟DOM对比之后重新渲染。渲染完后,执行updated(),这个时候数据和页面保持同步。在这一阶段不建议进行数据操作,避免进入死循环
-
7、当调用$destroy()方法后,会立即执行beforeDestory(),一般在这里做一些善后操作,比如销毁定时器,解绑全局事件,销毁插件对象等
-
8、最后,做一系列的销毁动作,解除各种数据引用,移除事件监听,删除组件_watcher,删除子实例,删除自身self等。同时将实例属性_isDestroyed置为true
5、Vue单页面(SPA)和多页面(MPA)的区别
5.1 SPA
-
概念:
-
SPA应用程序只有一个html文件,在vue中可以通过vue-router来局部切换组件,而非刷新整个页面,来实现无刷新切换页面的技术
-
原理:
-
js会感知到url的变化,通过这一点可以用js监听url中hash值的变化,通过onhashchange事件,由于哈希值的变化并不会引发页面的刷新和跳转,当监听到hash变化,就可以动态切换组件,实现无刷新切换页面的技术
-
优点:
-
1、页面切换快:页面每次切换跳转时,并不需要做html文件的请求,这样就节约了很多http发送延时,这样在切换页面时候速度就很快
-
2、方便了页面间的数据传递:容易进行Vuex或者vue中父子组件通信
-
缺点:
-
1、首屏加载速度很慢:首屏时需要请求一次html,同时还要发送一次js请求,两次请求回来了,首屏才会展示出来。相对于多页应用,首屏时间慢
-
2、不易于SEO:SEO效果很差,因为搜索引擎只认识html里的内容,不认识js的内容,而单页面用的内容都是靠js渲染生成出来的,搜索引擎不识别这部分的内容,也就不会给一个好的排名,会导致SPA应用做出来的网页在百度和谷歌上的排名比较差
5.2、MPA
-
概念:
-
MPA多页面应用,指有多个独立页面的应用,每个页面必须重复加载js、css等相关内容。多页应用跳转,需要整页资源刷新
-
与SPA对比最大的不同即是页面路由切换由原生浏览器跳转控制。页面跳转是返回HTML的
-
优点:
-
1、首屏加载速度快:当我们访问页面的时候,服务器返回一个html,页面就会展示出来,这个过程只经历了一个HTTP请求,所以页面展示的速度非常快
-
2、SEO效果好:搜索引擎在做网页排名的时候,要根据网页的内容才能给网页权重,来进行网页的排名。搜索引擎是可以识别html内容的,而我们每个页面所有的内容都放在html中,所以这种多页应用SEO排名效果好。
-
缺点:
-
1、页面切换慢:因为每次跳转都需要发送一个 HTTP 请求,如果网络状态不好,在页面之间来回跳转时,就会发生明显的卡顿,影响用户体验。
6、为什么data必须是一个函数
- 当我们组件中的data写成一个函数时,数据是以函数返回值形式定义的,这样每次复用一次data,都会返回一份新的data,它拥有自己的作用域,不会产生数据污染
- 当组件中的data写成一个对象时,对象是引用数据类型,它就会共用一个内存地址,在多次使用该组件的时候,改变其中一个组件的只就会影响全部使用该组件的值
7、vue中的修饰符
7.1 v-model的修饰符
-
7.1、lazy修饰符,它的作用是输入框的值时value不会改变,当光标离开输入框时,v-model绑定的value的值才会改变
-
7.2、trim修饰符,它的作用类似于JS中的trim()方法,作用是把v-model绑定的值的首尾空格过滤掉
-
7.3、number修饰符,它的作用是将值转换为数字。它会将字符串中的数字转为数字,并直接忽略掉字母以及字母后后面的字符,如果值是以字母开头,那么numer修饰符就无效,不输出。
7.2 事件修饰符
- 7.4、stop修饰符 阻止事件冒泡。冒泡(由里往外冒泡)
<div @click="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click.stop="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
不加 stop 点击按钮输出 1 2
加了 stop 点击按钮输出 1
console.log(num)
}
}
- 7.5、capture修饰符 使用事件捕获(由外向内捕获)
<div @click.capture="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
不加 capture 点击按钮输出 1 2
加了 capture 点击按钮输出 2 1
console.log(num)
}
}
- 7.6 self修饰符 只有点击事件绑定的本身才会触发事件
<div @click.self="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
不加 self 点击按钮输出 1 2
加了 self 点击按钮输出 1 点击div才会输出 2
console.log(num)
}
}
-
7.7 once修饰符 事件只执行一次
-
7.8 prevent修饰符 阻止默认事件(如a标签的跳转)
-
7.9 native修饰符 这个修饰符是加在自定义组件上的,保证事件能正常执行
<!-- 执行不了 -->
<My-component @click="shout(3)"></My-component>
<!-- 可以执行 -->
<My-component @click.native="shout(3)"></My-component>
- 7.10 passvie修饰符 告诉浏览器你不想阻止事件的默认行为
7.3 鼠标修饰符
- 7.11、left right middle 这三个修饰符是鼠标的左中右按键触发的事件
<button @click.middle="clickEvent(1)" @click.left="clickEvent(2)" @click.right="clickEvent(3)">点我</button>
methods: {
点击中键输出1
点击左键输出2
点击右键输出3
clickEvent(num) {
console.log(num)
}
}
7.4、键盘修饰符
- 7.12 键盘修饰符
//普通键
.enter
.tab
.delete//捕获‘删除’和‘退格’案件
.space
.esc
.up
.down
.left
.right
//系统修饰键
.ctrl
.alt
.meta
.shift
8、Vue的内部指令
-
8.1 v-on 缩写 @
-
用于给元素绑定事件监听器
-
8.2 v-bind 缩写 :
-
用于动态绑定各种变量
-
8.3 v-model v-model:value可直接缩写为v-model
-
用于双向绑定表单类的输入元素
-
8.4 v-for
-
基于原始数据类型多次渲染元素或者模板块
-
8.5 v-text
-
用于更新元素的文本内容
-
8.6 v-html
-
用于元素的innerHTML
-
8.7 v-pre
-
直接跳过该元素及其所有子元素的编译
-
8.8 v-once
-
仅渲染元素和组件一次,并跳过之后的更新
-
8.9 v-cloak
-
用于隐藏尚未完成编译的DOM模板
-
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
-
v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none } 这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。
-
8.10 v-if
-
根据表达式的布尔值,来条件性地渲染元素或者编译片段
-
8.11 v-else-if
-
前一兄弟元素必须要有v-if或者v-else-if 就像js中的else-if
-
8.12 v-else
-
前一兄弟元素必须要有v-fi或者v-else-if ,就像js中的else-if
-
8.13 v-show
-
根据表达式的布尔值,来该改变元素的可见性
-
8.14 v-slot
-
用于声明插槽
9、v-if和v-show的区别
- v-if显示隐藏是将dom元素整个添加或者删除;而v-show则是通过给dom元素添加css样式,display:none,来实现元素的显示和隐藏
- v-if的每一次的显示或者隐藏都会使组件重新跑一遍生命周期,因为显隐决定了组件的生成和销毁,它的切换消耗很高;v-show无论初始状态是什么组件都会渲染,因此v-show有更高的初始渲染消耗
10 为什么v-if和v-for不建议用在同一标签里面
- 在vue2中v-for的优先级是高于v-if。因此当v-for和v-if同时存在时,会先遍历出所有的元素,然后再一个一个判断v-if的表达式的布尔值
<div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
{{item}}
</div>
- 上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作
- 建议使用computed解决上述问题
<div v-for="item in list">
{{item}}
</div>
computed() {
list() {
return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
}
}
进阶版
11、不需要响应式的数据应该怎么处理
- 在vue开发过程中,会有一些数据,从始至终都没有改变过,既然从不改变就不需要再对它做响应式的处理了,不然只会做一些无用功消耗性能(比如写死的下拉框,写死的表格数据)
- 对于这些数据有两种方法处理
- 1:将数据定义在data之外
- 2:Object.freeze()
// 方法一:将数据定义在data之外
data () {
this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
return {}
}
// 方法二:Object.freeze()
data () {
return {
list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
}
}
12 动态设置class、动态设置style
* 动态class对象:<div :class="{ 'is-active': true, 'red': isRed }"></div>
* 动态class数组:<div :class="['is-active', isRed ? 'red' : '' ]"></div>
* 动态style对象:<div :style="{ color: textColor, fontSize: '18px' }"></div>
* 动态style数组:<div :style="[{ color: textColor, fontSize: '18px' }, { fontWeight: '300' }]"></div>
13 虚拟DOM中key的作用
-
1、虚拟DOM中key的作用:
-
- key是虚拟DOM中对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
-
- 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
- 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
-
2、对比规则:
-
(1)、旧虚拟DOM中找到了与新虚拟DOM中相同的key:
-
- 若虚拟DOM中的内容没有变,直接使用之前的真实DOM
-
- 如虚拟DOM中的内容改变了,则生成新的真实DOM,随后替换掉之前页面中的真实DOM
-
(2)、旧虚拟DOM中没有找到与新虚拟DOM相同的key
-
- 创建新的真实DOM,随后渲染到页面
- 创建新的真实DOM,随后渲染到页面
-
3、用inde作为key可能会引发的问题
-
(1)、若对数据进行:逆序添加、逆序删除等破坏顺序的操作:
-
- 会产生没有必要的真实DOM更新 ==> 页面效果没问题,但效率更低
-
(2)、如果结构中还包含了输入类的DOM:
-
- 会产生错误DOM更新 ==> 界面有问题
- 会产生错误DOM更新 ==> 界面有问题
-
4、开发中如何选择key?
-
- 最好使用使用每条数据中的唯一标识作为key,比如id,手机号等唯一值
-
- 如果不存在对数据的逆序添加等破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的