考察点

框架的使用(基本使用,高级特性,周边插件)
框架的原理(基本原理的了解,热门技术的深度,全面性)
框架的实际应用,即设计能力(组件结构、数据结构)

image.png

面试技巧

image.png

荣耀黄金

生命周期

image.png

beforeDestroy处理

及时销毁全局监听事件,避免内存泄露
如even.$off(‘xxx’)

父子组件生命周期顺序

刚开始先创建父,再创建子,后需要保证子组建渲染完,再到父组件
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

更新数据涉及同变量
父beforeUpdate-> 子beforeUpdate->子updated->父updated

$nextTick

Vue的Dom渲染过程是异步执行的
data改变之后,Dom不会立即渲染
$nextTick会在Dom渲染之后被触发,以获取最新Dom节点

正是因为Vue是异步更新Dom,所以当我们修改数据之后,Dom节点的内容不会立即修改,我们这样获取Dom节点的新内容的时候,获取的还是旧的内容

nextTick 是 Vue.js 提供的一个函数,并非浏览器内置。nextTick 函数接收一个回调函数 cb,在下一个 DOM 更新循环之后执行。比如下面的示例:

  1. <template>
  2. <div>
  3. <p v-if="show" ref="node">内容</p>
  4. <button @click="handleShow">显示</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. data () {
  10. return {
  11. show: false
  12. }
  13. },
  14. methods: {
  15. handleShow () {
  16. this.show = true;
  17. console.log(this.$refs.node); // undefined
  18. this.$nextTick(() => {
  19. console.log(this.$refs.node); // <p>内容</p>
  20. });
  21. }
  22. }
  23. }
  24. </script>

show 被置为 true 时,这时 p 节点还未被渲染,因此打印出的是 undefined,而在 nextTick 的回调里,p 已经渲染好了,这时能正确打印出节点。
nextTick 的源码在 github.com/vuejs/vue/b…,可以看到,Vue.js 使用了 PromisesetTimeoutsetImmediate 三种方法来实现 nextTick,在不同环境会使用不同的方法。

v-show 与 v-if 区别及使用场景

(条件渲染)
v-show 只是 CSS 级别的 display: none;display: block; 之间的切换,而 v-if 决定是否会选择代码块的内容(或组件)。

进一步,选型上:什么时候用 v-show,什么时候用 v-if ?

使用场景:
频繁操作时,使用 v-show,一次性渲染完的,使用 v-if

那使用 v-if 在性能优化上有什么经验?

因为当 v-if="false" 时,内部组件是不会渲染的,所以在特定条件才渲染部分组件(或内容)时,可以先将条件设置为 false,需要时(或异步,比如 $nextTick)再设置为 true,这样可以优先渲染重要的其它内容,合理利用,可以进行性能优化。

绑定 class 的数组用法

动态绑定 class 应该不陌生吧,这也是最基本的,但是这个问题却有点绕,什么叫绑定 class 的数组用法?我们看一下,最常用的绑定 class 怎么写:

  1. <template>
  2. <div :class="{show: isShow}">内容</div>
  3. </template>
  4. <script>
  5. export default {
  6. data () {
  7. return {
  8. isShow: true
  9. }
  10. }
  11. }
  12. </script>

绑定 class 的对象用法能满足大部分业务需求,不过,在复杂的场景下,会用到数组,来看示例:

  1. <template>
  2. <div :class="classes"></div>
  3. </template>
  4. <script>
  5. export default {
  6. computed: {
  7. classes () {
  8. return [
  9. `${prefixCls}`,
  10. `${prefixCls}-${this.type}`,
  11. {
  12. [`${prefixCls}-long`]: this.long,
  13. [`${prefixCls}-${this.shape}`]: !!this.shape,
  14. [`${prefixCls}-${this.size}`]: this.size !== 'default',
  15. [`${prefixCls}-loading`]: this.loading != null && this.loading,
  16. [`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || !!this.customIcon || this.loading),
  17. [`${prefixCls}-ghost`]: this.ghost
  18. }
  19. ];
  20. }
  21. }
  22. }
  23. </script>

示例来自 iView 的 Button 组件,可以看到,数组里,可以是固定的值,还有动态值(对象)的混合。

计算属性和 watch 的区别

这里主要需要回答选型上的区别,而不是api的区别。
这个问题会延伸出几个问题:

  1. computed 是一个对象时,它有哪些选项?
  2. computed 和 methods 有什么区别?
  3. computed 是否能依赖其它组件的数据?
  4. watch 是一个对象时,它有哪些选项?

问题 1,已经在 16 小节介绍过,有 get 和 set 两个选项。
问题 2,methods 是一个方法,它可以接受参数,而 computed 不能;computed 是可以缓存的,methods 不会;一般在 v-for 里,需要根据当前项动态绑定值时,只能用 methods 而不能用 computed,因为 computed 不能传参。
问题 3,computed 可以依赖其它 computed,甚至是其它组件的 data。
问题 4,第 16 小节也有提到,有以下常用的配置:

  • handler 执行的函数
  • deep 是否深度
  • immediate 是否立即执行

《1》计算属性computed 必须要返回一个值哦 通过return来返回的
会缓存,只要数据不发生变化,就使用缓存的数据

《2》 watch的回调函数里面有两个参数,分别是newval和oldval。
不会缓存 只要数据发生变化 就会重新的去计算

另外,需要额外说明的,计算属性可以实现一个值是根据多个值的动态计算结果,而watch偏向与根据一个值得出多个依赖结果,所以watch能做,计算属性不一定能做,但计算属性能做的可以理解为watch都能做,但实现起来会比较麻烦。

事件修饰符

这个问题我会先写一段代码:

  1. <custom-component>内容</custom-component>

然后问:怎样给这个自定义组件 custom-component 绑定一个**原生**的 click 事件?

我一开始并不会问什么是事件修饰符,但是如果候选人说 <custom-component @click="xxx">,就已经错了,说明它对这个没有概念。这里的 @click 是自定义事件 click,并不是原生事件 click。绑定原生的 click 是这样的:
1,给vue组件绑定事件时候,必须加上native ,否则会认为监听的是来自Item组件自定义的事件
2,等同于在子组件中: 子组件内部处理click事件然后向外发送click事件:$emit("click".fn)

  1. <custom-component @click.native="xxx">内容</custom-component>

该问题会引申很多,比如常见的事件修饰符有哪些?如果你能说上 .exact,说明你是个很爱探索的人,会大大加分哦。

.exact 是 Vue.js 2.5.0 新加的,它允许你控制由精确的系统修饰符组合触发的事件,比如:

  1. <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
  2. <button @click.ctrl="onClick">A</button>
  3. <!-- 有且只有 Ctrl 被按下的时候才触发 -->
  4. <button @click.ctrl.exact="onCtrlClick">A</button>
  5. <!-- 没有任何系统修饰符被按下的时候才触发 -->
  6. <button @click.exact="onClick">A</button>

你可能还需要了解常用的几个事件修饰符:

  • .stop 防止冒泡
  • .prevent
  • .capture
  • .self

passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。passive与prevent互斥
而且,事件修饰符在连用时,是有先后顺序的。
image.png

组件中 data 为什么是函数

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
因为组件是用来复用的,JS 里对象是引用关系,若是对象会共享一个数据这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

详解:组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。(也就是说写成函数,会有函数作用域的概念 ,是私有函数,只作用到当前组件中)
参考:https://blog.csdn.net/weixin_42707181/article/details/99638879

递归组件的要求

回答这道题,首先你得知道什么是递归组件。而不到 10% 的人知道递归组件。其实在实际业务中用的确实不多,在独立组件中会经常使用,那回到问题,递归组件的要求是什么?主要有两个:

  • 要给组件设置 name
  • 要有一个明确的结束条件。

应用场景:如菜单递归

Vuex 中 mutations 和 actions 的区别

主要的区别是,actions 可以执行异步。actions 是调用 mutations,而 mutations 来修改 store。

Render 函数

这是比较难的一题了,因为很少有人会去了解 Vue.js 的 Render 函数,因为基本用不到。遇到这个问题,一般可以从这几个方面来回答:

  • 什么是 Render 函数,它的使用场景是什么。
  • createElement 是什么?
  • Render 函数有哪些常用的参数?

说到 Render 函数,就要说到虚拟 DOM(Virtual DOM),Virtual DOM 并不是真正意义上的 DOM,而是一个轻量级的 JavaScript 对象,在状态发生变化时,Virtual DOM 会进行 Diff 运算,来更新只需要被替换的 DOM,而不是全部重绘。
它的使用场景,就是完全发挥 JavaScript 的编程能力,有时需要结合 JSX 来使用。
createElement 是 Render 函数的核心,它构成了 Vue Virtual DOM 的模板,它有 3 个参数:

  1. createElement () {
  2. // {String | Object | Function}
  3. // 一个 HTML 标签,组件选项,或一个函数
  4. // 必须 return 上述其中一个
  5. 'div',
  6. // {Object}
  7. // 一个对应属性的数据对象,可选
  8. // 您可以在 template 中使用
  9. {
  10. // 详细的属性
  11. },
  12. // {String | Array}
  13. // 子节点(VNodes),可选
  14. [
  15. createElement('h1', 'hello world'),
  16. createElement(MyComponent, {
  17. props: {
  18. someProps: 'foo'
  19. }
  20. }),
  21. 'bar'
  22. ]
  23. }

常用的参数,主要是指上面第二个参数里的值了,这个比较多,得去看 Vue.js 的文档。

怎样理解单向数据流

这个概念出现在组件通信。父组件是通过 prop 把数据传递到子组件的,但是这个 prop 只能由父组件修改,子组件不能修改,否则会报错。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
扩展vuex机制就是单项数据流
一般来说,对于子组件想要更改父组件状态的场景,可以有两种方案:

  1. 在子组件的 data 中拷贝一份 prop,data 是可以修改的,但 prop 不能:
  1. export default {
  2. props: {
  3. value: String
  4. },
  5. data () {
  6. return {
  7. currentValue: this.value
  8. }
  9. }
  10. }
  1. 如果是对 prop 值的转换,可以使用计算属性:
  1. export default {
  2. props: ['size'],
  3. computed: {
  4. normalizedSize: function () {
  5. return this.size.trim().toLowerCase();
  6. }
  7. }
  8. }

如果你能提到 v-model 实现数据的双向绑定、.sync 用法,会大大加分的,这些在第 16 节已经详细介绍过。

生命周期

Vue.js 生命周期 主要有 8 个阶段:

  • 创建前 / 后(beforeCreate / created):在 beforeCreate 阶段,Vue 实例的挂载元素 el 和数据对象 data 都为 undefined,还未初始化。在 created 阶段,Vue 实例的数据对象 data 有了,el 还没有。
  • 载入前 / 后(beforeMount / mounted):在 beforeMount 阶段,Vue 实例的 $el 和 data 都初始化了,但还是挂载之前为虚拟的 DOM 节点,data 尚未替换。在 mounted 阶段,Vue 实例挂载完成,data 成功渲染。
  • 更新前 / 后(beforeUpdate / updated):当 data 变化时,会触发 beforeUpdate 和 updated 方法。这两个不常用,且不推荐使用。
  • 销毁前 / 后(beforeDestroy / destroyed):beforeDestroy 是在 Vue 实例销毁前触发,一般在这里要通过 removeEventListener 解除手动绑定的事件。实例销毁后,触发 destroyed。

    组件间通信

这个问题看似简单,却比较大,回答时,可以拆分为几种场景:

  1. 父子通信:
    父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API。
  2. 兄弟通信:
    Bus;Vuex;
  3. 跨级通信:
    Bus;Vuex;provide / inject API。

除了常规的通信方法,本册介绍的 dispatch / broadcast 和 findComponents 系列方法也可以说的,如果能说到这些,说明你对 Vue.js 组件已经有较深入的研究。

路由的跳转方式

一般有两种:

  1. 通过 <router-link to="home">,router-link 标签会渲染为 <a> 标签,在 template 中的跳转都是用这种;
  2. 另一种是编程式导航,也就是通过 JS 跳转,比如 router.push('/home')

Vue.js 2.x 双向绑定原理

这个问题几乎是面试必问的,回答也是有深有浅。基本上要知道核心的 API 是通Object.defineProperty()来劫持各个属性的 setter / getter,在数据变动时发布消息给订阅者,触发相应的监听回调,这也是为什么 Vue.js 2.x 不支持 IE8 的原因(IE 8 不支持此 API,且无法通过 polyfill 实现)。

vue高级用法

自定义组件v-model

image.png

$nextTick

永恒钻石

什么是 MVVM,与 MVC 有什么区别

MVVM 模式是由经典的软件架构 MVC 衍生来的。当 View(视图层)变化时,会自动更新到 ViewModel(视图模型),反之亦然。View 和 ViewModel 之间通过双向绑定(data-binding)建立联系。与 MVC 不同的是,它没有 Controller 层,而是演变为 ViewModel。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作是由 Vue.js 完成的,我们不需要手动操作 DOM,只需要维护好数据状态。

父组件和子组件之间的生命周期执行顺序

为什么v-if和v-for不建议用在同一标签?

在Vue2中,v-for优先级是高于v-if的,咱们来看例子

  1. <div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
  2. {{item}}
  3. </div>

上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:

  1. <div v-for="item in list">
  2. {{item}}
  3. </div>
  4. computed() {
  5. list() {
  6. return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
  7. }
  8. }

为何在v-for中用key

  • 原则是,尽量使key为唯一值,index作为下标也会存在一些问题,比如数组的增删
  • diff算法中通过tag和key来判断,是否是相同的node
  • 减少渲染次数,提升渲染性能

computed和watch有何区别?

  • 1.computed是依赖已有的变量来计算一个目标变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下其会直接读取缓存进行复用,computed不能进行异步操作(需要立即return返回同步目标值数据)
  • 2.watch是监听某一个变量的变化,并执行相应的回调函数,通常是一个变量的变化决定多个变量的变化,watch可以进行异步操作
  • 3.简单记就是:一般情况下computed是多对一,watch是一对多

vue computed的缓存特性
概述:computed的计算属性有缓存机制,只有当其依赖的响应式数据发生变化时才会清空缓存重新计算结果

其缓存机制本质是通过一个dirty属性控制的,只有dirty为true时才会重新计算结果替换缓存。 dirty只有当其响应式数据发送变化时才会设置为true,重新计算后会再次被设置为false

[

](https://blog.csdn.net/SJ1551/article/details/109804232)

vuex的有哪些属性?用处是什么?

Face问题1 - 图7

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

如何用JS实现hash路由

window.onhashchange
image.png
image.png

如何用JS实现H5 history路由

image.png
image.png
image.png
image.png

hash和history如何选择

image.png
对于搜索引起SEO使用history

至尊星耀

不需要响应式的数据应该怎么处理?

在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。

方法一:将数据定义在data的return之外
方法二:Object.freeze()冻结对象
注意点:Object.freeze()冻结的是值,你仍然可以将变量的引用替换掉

  1. // 方法一:将数据定义在data之外
  2. data () {
  3. this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
  4. this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
  5. this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
  6. this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
  7. this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
  8. return {}
  9. }
  10. // 方法二:Object.freeze(), 但是如果在后续方法中直接替换引用
  11. data () {
  12. return {
  13. list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
  14. list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
  15. list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
  16. list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
  17. list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
  18. }
  19. }
  20. created () {
  21. // 界面不会有响应
  22. this.list[0].value = 100;
  23. // 下面两种做法,界面都会响应
  24. this.list = [
  25. { value: 100 },
  26. { value: 200 }
  27. ];
  28. this.list = Object.freeze([
  29. { value: 100 },
  30. { value: 200 }
  31. ]);
  32. }

watch有哪些属性,分别有什么用?

当监听一个基本数据类型时:

  1. watch: {
  2. value () {
  3. // do something
  4. }
  5. }

当监听一个引用数据类型时:

  1. watch: {
  2. obj: {
  3. handler () { // 执行回调
  4. // do something
  5. },
  6. deep: true, // 是否进行深度监听
  7. immediate: true // 是否初始执行handler函数
  8. }
  9. }

对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?

  • 原因:Object.defineProperty没有对对象的新属性进行属性劫持
  • 对象新属性无法更新视图:使用Vue.$set(obj, key, value),组件中this.$set(obj, key, value)
  • 删除属性无法更新视图:使用Vue.$delete(obj, key),组件中this.$delete(obj, key)

直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?

  • 原因:Vue没有对数组进行Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的
  • 使用数组的splice方法,arr.splice(index, 1, item)
  • 使用Vue.$set(arr, index, value)