VUE + VUEX 高频面试题合集

目录

1. Vue常见的指令有哪些,有什么用 

2. 双向数据绑定的原理

vue2双向数据绑定原理

vue3双向数据绑定原理

区别

3. 路由的跳转和传参有哪几种方式

声明式

函数式

其他

4. 组件间的通讯方式有哪些

5. 谈一谈对路由守卫的理解

路由守卫

全局前置守卫

1. 三个参数

2.  注意

3.  示例

 路由独享的守卫

6. 谈一谈对Vuex的理解

VUEX

作用 

核心概念

1、state         存放全局初始化变量

2、getter        对象状态进行加工函数

3、mutation   修改state的状态

4、action       执行异步操作,不能修改

5、module     模块

解决vuex刷新状态重置问题

其他

7. 谈一谈对混入的理解

混入

选项合并

全局混入

注意

8. 谈一谈对插槽的理解

插槽

插槽的使用

注意

9. 什么是跨域,如何解决

同源策略CORS

解决跨域

10. 谈一谈封装请求的心得

11. watch和computed的共同点和不同点

共同点

不同点


1. Vue常见的指令有哪些,有什么用 

指令

作用

备注

v-cloak

防止页面闪烁

在vue加载之前v-cloak是存在,vue加载结束之后v-cloak就隐藏了,利用这个特性可以实现:界面防止闪烁

v-text

会替换掉元素里的内容

区别:

        v-html会解析HTML代码和样式(富文本)

        v-text直接展示原始内容

v-html

可以渲染html界面

v-bind

属性绑定

1. 界面元素属性值的绑定

2. 括号里不加引号的都是我们data里的数据读取

3. 如果想使用字符串需要加上引号

4. 里面可以写表达式

5. 里面也可以调用定义好的方法,拿到的是方法的返回值

6. 简写  :

v-on

事件绑定

简写@

v-model

数据双向绑定

1. 作用:数据双向绑定

2. 注意:绑定的是表单控件

3. 双向数据绑定:  数据变化后更新视图;视图变化后更新数据。Model层里的数据和View层上的数据只要有一方变化,另一方随之改变。

4. 原理: 后面有详细说明

v-for      :key

遍历

1. 遍历数组,参数(item,index) in list

2. 遍历对象,参数(value,key,index) in list

3. 遍历数字,num in 10 (1~10)

4. key在使用v-for的时候都需要去设置key

        4-1. 让界面元素和数组里的每个记录进行绑定

        4-2. key只能是字符串或者数字

        4-3. key必须是唯一的

        4-4. “就地复用” 策略

        4-5. key值的作用---提高重拍效率

        4-6. key值的计算方法---diff算法

v-if  

v-else

通过元素的删除和添加控制元素的显示和隐藏

1. 区别

        1-1. v-if删除dom元素

        1-2. v-show设置display:none

2. 应用场景

        2-1. v-if只修改一次的时候可以使用v-if

        2-2. v-show频繁切换的时候可以使用v-show

v-show

通过display属性,控制元素的显示和隐藏

2. 双向数据绑定的原理

vue2双向数据绑定原理

        双向数据绑定v-model的原理:采用“数据劫持”结合“发布者-订阅者”模式的方式,通过“Object.defineProperty()”方法来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变。

 Object.defineProperty()方法有三个参数:
            obj:属性的对象。
            prop:要定义或修改的属性。
            descriptor:一个对象,包括get和set方法

缺点:

根据官方文档,双向数据绑定失效总共有三种情况:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength
  3. 对象中的属性增加或删除时

vue3双向数据绑定原理

         通过Proxy(代理)的方式实现,拦截对data任意属性的操作, 包括属性值的读写、添加、删除等;通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作。

区别

        Object.defineProperty 是 ES5 的方法,Proxy 是 ES6 的方法

        Object.defineProperty 不能监听到数组下标变化和对象新增属性,Proxy可以

        Object.defineProperty是劫持对象属性,Proxy是代理整个对象

       Object.defineProperty局限性大,只能针对单属性监听,所以在一开始就要全部递归监听。Proxy对象嵌套属性运行时递归,用到才代理,也不需要维护特别多的依赖关系,性能提升很大,且首次渲染更快

        Object.defineProperty 会污染源对象,修改时是修改源对象。Proxy是对原对象进行代理并返回一个新的代理对象,修改的是代理对象

3. 路由的跳转和传参有哪几种方式

声明式

<router-link to="{path:'/son1' ,query:{ id:'10' }}" >跳转</router-link>

<router-link to="{name:'son1' ,params:{ id:'10' }}" >跳转</router-link>

函数式

this.$router.push({

                       name: "namelogin",

                       params: {

                           userid: 999,

                           name: "wangwu"

                       }

                   })



  this.$router.push({

                       path: "namelogin",

                       query: {

                           userid: 999,



                           name: "wangwu"

                       }

                   })

其他

this.$router.replace()

replace    先移除,再放入,没有历史数据

push        相当于覆盖,有历史数据

go(数字)   返回上个页面

        前进为正数,后退为负数,0就是本页面

back == go(-1)  返回上一页面

4. 组件间的通讯方式有哪些

通讯方式 使用方法 备注
父传子 子组件:设置props属性就可以接受父组件传值

注意:

data和props的区别

data是组件私有的,props是父组件传过来的

data是可以修改的,props是只读的

子传父

父组件:在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的)

子组件:子组件可以触发这个事件$emit('事件名字')

1. $emit方法第二个参数可以定义子组件给父组件传递的内容

2. 在父组件中拿到这内容

        2.1 父组件这个方法没有自定参数,在父组件的方法直接加这个参数就可以拿到

        2.2 父组件有自定义参数,可以传入$event也可以拿到子组件传递的数据。通过$event只能传递第一个参数。
vuex 将数据放在states中,进行统一管理 下面对vuex有详细解释
缓存 localStorage/sessionStorage 保存在本地,需要注意时效性
ref 给dom节点加上ref属性

1. 获取dom节点

1.1 给dom节点记上ref属性,可以理解为给dom节点起了个名字。

1.2 加上ref之后,在$refs属性中多了这个元素的引用。

1.3 通过vue实例的$refs属性拿到这个dom元素。

2. 获取组件

2.1 给组件记上ref属性,可以理解为给组件起了个名字。

2.2 加上ref之后,在$refs属性中多了这个组件的引用。

2.3 通过vue实例的$refs属性拿到这个组件的引用,之后可以通过这个引用调用子组件的方法,或者获取子组件的数据。
事件总线
// 发送消息
EventBus.$emit(channel: string, callback(payload1,…))


// 监听接收消息
EventBus.$on(channel: string, callback(payload1,…))

事件总线可以作为组件沟通的桥梁,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件

但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。

5. 谈一谈对路由守卫的理解

路由守卫

1. vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。简单的说,导航守卫就是路由跳转过程中的一些钩子函数。

2. 全局守卫:是指路由实例上直接操作的钩子函数,特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数

  • beforeEach(to,from, next)路由进入之前
  • beforeResolve(to,from, next)路由解析之前 这个几乎不用
  • afterEach(to,from)路由进入之后

3. 路由独享守卫:是指在单个路由配置的时候也可以设置的钩子函数

  • beforeEnter(to,from,next)

4. 组件守卫:是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。

  • beforeRouteEnter(to,from, next)
  • beforeRouteUpdate(to,from, next)
  • beforeRouteLeave(to,from, next)

5. 记住参数或查询的改变并不会触发进入/离开的导航守卫。可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

6. 分为 全局守卫 路由独享守卫 和 组件守卫

全局守卫

1. 三个参数

to 跳转后的页面

from 跳转前的页面

next 是函数

        直接调用 next() 允许跳转

        next(false) 不允许跳转

        next('/index') 代表要跳转到首页

2.  注意

确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错

3.  示例

router.beforeEach((to, from, next) => {
    console.log(to):
    console.log(from)
    console.log(next)
})
router.beforeEach( ( to , from , next ) => {
    // 在跳转之前就可以增加逻辑判断了,根据不同的状态判断能否跳转到指定的页面
    // 比如搜索页面需要登录后才可以进入
    if(to.meta.isLogin){ 
    //     判断是否登录 写判断是否登录
        next("/")
    }else {
        // 不需要登录
        next()
    }
    // next(false)
    // 判断 那些页面是需要登录的  那些页面是不需要登录的
} 

 路由独享的守卫

可以在路由配置上直接定义 beforeEnter 守卫:这些守卫与全局前置守卫的方法参数是一样的。

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

6. 谈一谈对Vuex的理解

VUEX

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储,管理应用中所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

作用 

        1、进行统一的状态管理,解决不同组件共享数据的问题。

        2、不同视图需要变更同一状态的问题。

        3、使用vuex之后,状态变化更加清晰。

  1. 核心概念

        state        getter        mutation        action        module

1、state         存放全局初始化变量

        1.state是一个单一状态树,是vuex中为一个的数据源,我们的数据都是放在state中的。

        2. 组件中去取state的值,通过this.$store.state或者可以通过计算属性取得,mapState辅助函数,可以简化操作。

import {mapState} from "vuex";
computed: {
    // 可以写一些其他的计算属性
    ...mapState({
      // title: state => state.title
      // title: "title"
      title: state => {
        return state.title;
      }
    })
  }

2、getter        对象状态进行加工函数

        1. 对state中的数据进行加工(派生)

       2. 取getters中的值,通过this.$store.getters或者通过计算属性获取,getters也有辅助函数mapGetters, 用法和mapState一致。

import { mapGetters} from "vuex";
computed: {
    // 可以写一些其他的计算属性
    ... mapGetters ({
      title: "title"
    })
  }

3、mutation   修改state的状态

        1. 修改state中的值,我们state每次变化,都应该由mutation去修改,方便追踪数据的流转

        2.定义mutation

const store = new Vuex.Store({

        // ....

    mutations: {

        increment(state, count) {

            state.count = count

        },

    }

})

3. 调用mutation

  1. this.$store.commit('mutation类型(函数名)',"参数,参数一般是对象形式")
  2. this.$store.commit({type:'mutation类型(函数名)'},...其他参数)

4. 注意事项

  • 要遵循vue相应式变化的规则,简单说就是对于对象的赋值,要用新对象替换老对象。
  • mutation的type或者说是函数名可以用常量维护。
  • mutation函数必须是同步的。

4、action       执行异步操作,不能修改

1. action类似于mutation,不同的是

        action可以包含异步操作

        action不能直接修改state,如果想修改state的话,需要触发mutation

2. 定义

 actions: {

    // 通过context(上下文环境)可以触发mutation,触发action,获取state、getter等操作

        // 第二个参数就是我们传递过来的参数

        changeTitle(context,payload) {

            setTimeout(() => {

                context.commit(CHANGE_TITLE, payload)

            }, 1000);

        }

    }

3. 触发action

  • this.$store.dispatch('action名字','参数')
  • this.$store.dispatch({type:'action类型(函数名)'},...其他参数)

5、module     模块

1. 由于使用单一的状态树,项目中的状态会集中在一起,导致难以维护,这个时候可以通过module对store进行拆分。

2. 使用module之后,每个模块都有自己的state、mutation等内容,方便维护

3. 定义

const moduleA = {

  state: { ... },

  mutations: { ... },

  actions: { ... },

  getters: { ... }

}



const moduleB = {

  state: { ... },

  mutations: { ... },

  actions: { ... }

}



const store = new Vuex.Store({

  modules: {

    a: moduleA,

    b: moduleB

  }

})

4. 命名空间

  1. 默认state就是有命名空间
  2. 如果想给mutation和action也加上命名空间的话,这里设置模块的namespaced:true
const moduleA = {

  namespaced:true,

  state: { ... },

  mutations: {

    changeName(){}

  },

  actions: { ... },

  getters: { ... }

}

// this.$store.commit('a/changeName')

5. 命名空间之后使用辅助函数createNamespacedHelpers

解决vuex刷新状态重置问题

1、存入缓存

2、在接口请求之后确定状态

其他

单向数据流转

状态:驱动应用的数据源

视图:以声明方式将状态映射到视图

操作:响应在视图上用户输入导致的状态变化

7. 谈一谈对混入的理解

混入

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

选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

全局混入

混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。

var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

注意

谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。推荐将其作为插件发布,以避免重复应用混入。

8. 谈一谈对插槽的理解

插槽

 插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。

插槽的使用

        如果子组件没有使用插槽,父组件如果需要往子组件中填充模板或者html, 是没法做到的

1、具名插槽

具名插槽其实就是给插槽娶个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。

父组件填充内容时,是可以根据这个名字把内容填充到对应插槽中

子组件:在子组件的slot标签中加上name属性

父组件:父组件通过 v-slot:name值  的方式指定到对应的插槽中

2、默认插槽

默认插槽就是指没有名字的插槽,子组件未定义的名字的插槽,父级将会把 未指定插槽的填充的内容填充到默认插槽中。

子组件:在子组件中放一个占位符<slot></slot>标签

父组件:直接给子组件元素填充内容

注意

1.  父级的填充内容如果指定到子组件的没有对应名字插槽,那么该内容不会被填充到默认插槽中。

2.  如果子组件没有默认插槽,而父级的填充内容指定到默认插槽中,那么该内容就“不会”填充到子组件的任何一个插槽中。

3.  如果子组件有多个默认插槽,而父组件所有指定到默认插槽的填充内容,将“” “全都”填充到子组件的每个默认插槽中。

9. 什么是跨域,如何解决

同源策略CORS

        浏览器的一个安全协议(不限制服务),协议主机端口要保持一致,只要有一个不一致就是跨域请求

解决跨域

        1. 后台直接不做限制,放开所有请求。优点:方便;缺点:不安全。

        2. JSONP(和JSON没有太大关系)利用了script标签不受同源策略,需要后端配合(现在不咋用了)

        3. 配置代理(现在90%以上都在用)

        4. 在vue.config.js中进行配置代理(可以配置多个)

10. 谈一谈封装请求的心得

  • 1. 封装请求可以对请求进行统一化的管理,易于维护,对开发人员来说十分简便,此外,可以对请求开始前及过程中进行预处理,提高了请求的效率。
  • 2. axios封装的好处:统一处理,提高效率,便于维护。不用再挂载在Vue实例的原型上,降低了耦合度,两者互不相干了,其中的任何一个出现问题,另一个都能正常运作。

11. watch和computed的共同点和不同点

共同点

        都能监听属性的改变触发函数程序

不同点

  • watch中的函数是不需要调用的
  • computed内部的函数调用的时候不需要加()
  • watch: 属性监听   监听属性的变化
  • computed: 计算属性  通过属性计算而得来的属性
  • watch需要在数据变化时执行异步或开销较大的操作时使用
  • computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用
  • computed中的函数必须用return返回最终的结果,当computed中的函数所依赖的属性如果没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取

从作用机制上:

  1. methods,watch 和 computed 都是以函数为基础的,但各自却都不同
  2. watch 和 computed 都是以 Vue 的依赖追踪机制为基础的,当某一个数据发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动
  3. 对 methods:methods 里面是用来定义函数的,它需要手动调用才能执行。而不像 watch 和 computed 那样,“自动执行”预先定义的函数,相比于 watch / computed,methods 不处理数据逻辑关系,只提供可调用的函数

从性质上:

  1. methods 里面定义的是函数,仍然需要去调用它。
  2. computed 计算属性,事实上和 data 对象里的数据属性是同一类的(使用上)。
  3. watch:类似于监听机制+事件机制

watch 和 computed 区别

  1. 功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
  2. 是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
  3. 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return
  4. watch擅长处理的场景:一个数据影响多个数据 -------搜索框。
  5. computed擅长处理的场景:一个数据受多个数据影响 -- 使用场景:当一个值受多个属性影响的时候--------购物车商品结算
watch: {
    value:{
      handler:function(o,n){},
      immediate: true
    } 
  },

immediate设为true    监听方法会在创建的时候  执行handler里的方法

猜你喜欢

转载自blog.csdn.net/Wr2138/article/details/128009412
今日推荐