vue 2022最常见的面试题(实践出真理 出场率高得狠)

1.路由的三种模式?

● hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
● history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
● abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

2.vue守卫

全局守卫
beforeEach(to,from, next):全局前置守卫,进入路由之前
router.beforeResolve(to,from, next):全局解析守卫,在beforeRouteEnter调用之后调用
router.afterEach(to,from):全局后置钩子,进入路由之后

路由组件内的守卫
beforeRouteEnter():进入路由前
beforeRouteUpdate():路由复用同一个组件时
beforeRouteLeave():离开当前路由时

独享路由钩子:
beforeEnter(to,from, next);

导航守卫回调参数
to:目标路由对象;
from:即将要离开的路由对象;
next:他是最重要的一个参数,他相当于佛珠的线,把一个一个珠子逐个串起来。以下注意点务必牢记:
1.但凡涉及到有next参数的钩子,必须调用next() 才能继续往下执行下一个钩子,否则路由跳转等会停止。
2.如果要中断当前的导航要调用next(false)。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。(主要用于登录验证不通过的处理)
3.当然next可以这样使用,next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。意思是当前的导航被中断,然后进行一个新的导航。可传递的参数与router.push中选项一致。
4.在beforeRouteEnter钩子中next((vm)=>{})内接收的回调函数参数为当前组件的实例vm,这个回调函数在生命周期mounted之后调用,也就是,他是所有导航守卫和生命周期函数最后执行的那个钩子。
5.next(error): (v2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

当点击切换路由时:
beforeRouterLeave-->beforeEach-->beforeEnter-->beforeRouteEnter-->beforeResolve-->afterEach-->beforeCreate-->created-->beforeMount-->mounted-->beforeRouteEnter的next的回调

3.v-if与v-show区别


v-if   真正的条件渲染    如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。    总结一假为假,
第一次为真,则真
    v-show    简单得多——不管初始条件是什么,元素总是会被渲染    简单地基于 CSS 的 “display” 属性进行切换。
    v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景        v-show 则适用于需要非常频繁切换条件的场景。

4.computed 与watch区别

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;(关于数据计算如购物车加加减减)
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;(监听data数据)

5.Vue组件的生命周期

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

beforeCreate
组件实例被创建之初,组件的属性生效之前
created
组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用
mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate
组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
update
组件数据更新之后
beforeDestory
组件销毁前调用
destoryed
组件销毁后调用

vue自带的缓存组件keep-alive的专属
activited   组件被激活时调用
deactivated 组件被销毁时调用

6.vue组件通信(6种常见方法)

(1)props / $emit 适用 父子组件通信
(2)ref 与 $parent / $children 适用 父子组件通信
(3)eventBus事件总线($emit / $on)
    eventBus事件总线适用于父子组件、非父子组件等之间的通信
(4)$attrs/$listeners 适用于 隔代组件通信
(5)provide / inject 适用于 隔代组件通信
(6)vuex
  依赖注入(provide / inject)
这种方式就是vue中依赖注入,该方法用于 父子组件之间 的通信。当然这里所说的父子不一定是真正的父子,也可以是祖孙组件,在层数很深的情况下,可以使用这种方式来进行传值。就不用一层一层的传递数据了。
provide和inject是vue提供的两个钩子,和data、methods是同级的。并且provide的书写形式和data一样。
provide 钩子用来发送数据或方法。
inject钩子用来接收数据或方法
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

7.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 且同时保存在单一的状态树中。

8.组件中的data为什么是个函数

因为组件是用来复用的,因为js里对象是引用关系,如果data是对象形式,那么data的作用域是没有隔离的,在多个子组件时,会被外部因素影响,如果data是一个函数,那么每个实例可以独自拥有一份返回对象的拷贝,组件实例之间的data属性值不会互相影响

9.v-if和v-for 不能同用的原因?

当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,所以不推荐v-if和v-for同时使用,可以把list放在计算属性中遍历

10.vue指令

11.MVC 与MVVM

MVC :m:model数据模型层   v:view视图层  c:controller控制器
MVVM:m:model数据模型层   v:view视图层  vm:ViewModel

vue中采用的是mvvm模式,这是从mvc衍生过来的

MVVM让视图与viewmodel直接的关系特别的紧密,就是为了解决mvc反馈不及时的问题

m:相当于data()
v:相当于<template></template>

12.Vue父传子的生命周期的执行顺序

 

问题一:父组件中的要传递的 props 属性是通过在created生命周期中发生ajax请求回来的一个数据,子组件的前四个生命周期中都无法拿到这个对象?

⭐原因是:父组件的created函数中调用某个方法而方法会去请求接口,而这个请求是属于异步操作,此时会把异步操作放到消息队列中,等到后面的父子生命周期函数等一系列同步任务执行完成后,会执行异步任务,即读取消息队列的任务,执行回调函数(这点可以去了解一下JS的单线程特点)。此时接口才会返回数据,父组件中的变量才能拿到该数据,但是子组件中的 created 、 mounted 这样只会执行一次的生命周期钩子,已经执行了,所以子组件的前4个生命周期是无法拿到父组件调接口拿来的数据的。

⭐问题二: 子组件加上 ref ,父组件使用 $refs ,在父组件的created函数中无法操作子组件?

⭐原因是: $refs只在组件渲染完成后才填充,并且它是非响应式的,所以只有在子组件的mounted生命周期函数之后即子组件的dom元素渲染完成后,父组件才可以通过 $refs 直接操作子元素,由上图顺序可知父组件的created函数中无法通过 $refs 操作子组件。

13.说一下Vue的双向绑定数据的原理

vue2.0:vue 实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调

vue3.0: 通过proxy

Object.defineProperty 和 Proxy 对比存在哪些优缺点呢?

Object.defineProperty 只能劫持对象的属性,而 Proxy 是直接代理对象。

由于 Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作

14.$route和$router的区别

1.route是路由信息对象,里面主要包含路由的一些基本信息,包含当前的路径,参数,query对象等。(包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom)

2.$router是VueRouter的实例,包含了一些路由的跳转方法,钩子函数

15为什么避免v-if和v-for一起使用

v-if 具有比 v-for 更高的优先级

16.vue中created与mounted区别

1.在created阶段,实例已经被初始化,但是还没有挂载至el上,所以我们无法获取到对应的节点,但是此时我们是可以获取到vue中data与methods中的数据的;


2.在mounted阶段,vue的template成功挂载在$el中,此时一个完整的页面已经能够显示在浏览器中,所以在这个阶段,可以调用节点了

17.vue 插槽

1.默认
<!-- 子组件 hello -->
<template>
  <view>
    <solt></solt>
  </view>
</template>


<!-- 父组件 -->
<hello>
  <view>111</view>
</hello>

2.具名插槽

<!-- 子组件 hello -->
<template>
  <view>
    <solt>我是默认内容</solt>
  </view>
</template>


<!-- 父组件 -->
<hello>
  <view>我能替代默认内容</view>
</hello>

3.作用域插槽
最难理解的是作用域插槽。看了文档说明的朋友可能还会有点晕,大概是说在作用域插槽内,父组件可以拿到子组件的数据。子组件可以在slot标签上绑定属性如nickName,而父组件通过slot-scope绑定的对象下拿到nickName的值。 

// 子组件
<slot :nickName="'Tusi'"></slot>

// 父组件
<template>
    <section>
        <slot-child>
            <template slot-scope="scope">
                <div>{ {scope.nickName}}</div>
            </template>
        </slot-child>
    </section>
</template>
 

18.mixins (混入)的使用

1.是什么

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

2.作用

主要作用是继承和封装,将一些公共的代码抽离,可以减少代码量,提高复用性。

3.使用

// minix.js
export default {
    data () {
        return {
            name: 'minix',
            minixName: 'minixObj',
            flag: false
        }
    },
    mounted() {
        console.log('minixMounted');
    },
    methods: {
        speak() {
            console.log('this is minix');
        },
        getData() {
            return '100';
        }
    }
}
 
// todo.vue
import myMinix from './minix';
 
export default {
    data () {
        return {
            name: 'todo',
            lists: [1, 2, 3, 4]
        }
    },
    mounted() {
        console.log('todoMounted');
    },
    minixs: [myMinix], // todo.vue 中声明minix 进行混合
    methods: {
        speak () {
            console.log('this is todo');
        },
        submit() {
            console.log('submit');
        },
    }
}
 
 
//==========
 
// 最终得到的结果
 
//==========
 
export default {
    data () {
        return {
            name: 'todo', // 共同有的data, 最后保留自己的data
            lists: [1, 2, 3, 4], // 自己独有的,保留
            minixName: 'minixObj', // todo没有的,会被添加进来
            flag: false // todo没有的,会被添加进来
        }
    },
    mounted() {
        // 在钩子函数中的, 会被合并到todo.vue 的钩子函数中, minix中的代码在前,自己的在后
        console.log('minixMounted');
        console.log('todoMounted');
    },
    methods: {
        // 同时有的方法, 会被封装为一个数组, 先执行minix中的,后执行todo自己的
        speak () {
            [
                function() {
                    console.log('this is minix');
                },
                function() {
                    console.log('this is todo');
                }
            ].forEach(item => {
                item();
            })
        },
        // 自己独有的,保留
        submit() {
            console.log('submit');
        },
        // 自己没有的方法会被添加进来
        getData() {
            return '100';
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_53185230/article/details/127527024