Vue面试题汇总

126 阅读13分钟

                                         

  • v-ifv-for****的优先级 1.显然v-for优先于v-if被解析。
    2.如果同时出现,渲染函数中先执行循环再判断条件,return符合条件的数据,这样无可避免的执行了循环,浪费了性能。源码codeGen/index.js中64行先处理for再处理if。
    3.要避免这种情况:①在外层判断条件,内部进行循环。②先根据条件过滤数据,再循环。

  • Vue组件中的data为什么是个函数,而Vue****根实例没有限制? 1.Vue组件可能存在多个实例,如果使用对象形式定义,则会导致他们公用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的。
    2.采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有校规避多实例之间状态污染问题。
    3.在Vue根实例创建过程中则不存在该限制,因为根实例只能有一个,不需要担心这种情况。
    4.源码里,数据初始化的代码中,会校验data的形式,执行对应的方式。而根实例可以跳过data校验。

  • Vuekey****的作用和工作原理        1.key的主要作用是为了高效的更新虚拟DOM。其原理是vue在patch的过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新元素,使整个patch过程更加高效,减少DOM的操作量,提高性能。
    2.另外,若不设置key可能在列表更新时引发一些隐藏bug,比如说transition过度效果不触发。

  • **怎样理解Vue中的diff算法**        1.diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM做对比(即diff),将变化的地方更新在真实的DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为O(n)。        2.Vue2.0中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精准找到发生变化的地方。        3.Vue中diff执行的时刻是组件实例执行其更新函数时,他会对比oldVnode和newVnode,此过程称为patch。       4.diff过程整体遵循深度优先、同层比较的策略;两个节点比较的时候先判断他们是否都拥有子节点或文本节点;比较两组子节点是diff算法的重点:首先尝试比较两组节点的首尾四个元素,找出相同节点,一般情况下都是有相同点的,这样可以减少循环次数,如果没有相同节点,就只能循环查找,当找到相同节点时,执行patch。如果找不到就剩下来,做批量新增或删除处理。过程中借助key通常可以非常精准的找到相同节点,因此整个patch过程非常高效。
  • 谈一谈对****Vue组件化的理解        1.组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一,他使开发者使用小型、独立和通常可复用的组件构建大型应用。
    2.组件化发开能大幅提高应用开发效率、测试性、复用性。
    3.组件使用按分类有:页面组件、业务组件、通用组件。
    4.Vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,他们基于VueComponent,扩展与Vue。
    5.Vue中常见的组件话技术有:属性prop,自定义事件,插槽等,他们主要用于组件通信、扩展等
    6.合理的划分组件,有助于提升应用性能。
    7.组件应该是高内聚、低耦合的。
    8.遵循单向数据流原则。

  • 谈一谈对Vue设计原则的理解

           1.易用性:Vue提供数据响应式、声明式模板语法和基于配置的组件系统等核心特性。这些使我们只需要关注应用的核心业务即可,只要会写js、html和css就能轻松编写Vue应用。        2.灵活性:渐进式框架的最大优点就是灵活性,如果应用足够小,我们可能仅需要Vue核心特性即可完成功能;随着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是学习难度都是一个逐渐增加的平和曲线。        3.高效性:高性能的虚拟DOM和diff算法是我们的应用拥有最佳的性能表现。Vue追求高效的过程还在继续,Vue3中引入Proxy对数据响应式改进以及编辑器中对于静态内容编译的改进都会让Vue更加高效。
  • 谈一谈你对MVC、MVP、MVVM的理解****        1.三者都是框架模式,他们设计的目的都是为了解决Model和View层的耦合问题。
    2.MVC模式出现较早,主要应用于后端,如Spring MVC、ASP.NET MVC等,在前端领域的早期也有应用,如backbone.js,他的优点是分层清晰,缺点是数据流混乱,灵活性带来的维护性问题。
    3.MVP模式是MVC的进化形式,Presenter作为中间层负责MV通信,解决了两者耦合问题,但是P层过于臃肿会导致维护问题。
    4.MVVM模式在前端领域有广泛应用,他不仅解决MV耦合问题,还同时解决了维护两者映射关系的大量繁杂代码和DOM操作代码,在提高开发效率、可读性同时还保持了优越的性能表现。

  • 你了解哪些Vue性能优化方法吗?        1.路由懒加载:使用的时候按需加载,打包的时候整体体积大幅缩减。

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

           2.keep-alive缓存页面。

    <template>
      <div id="app">
        <keep-alive>
          <router-view>
        </keep-alive>
      </div>
    </template>
    

           3.条件合适时使用v-show控制DOM。
    4.v-for遍历时避免同时使用v-if
    5.长列表性能优化:如果列表是纯粹的数据展示,不会有任何变化,就不需要做响应式。

    const users = axios.get('./user')
    this.users = Object.freeze(users)
    

           6.事件的销毁:Vue组件销毁时,会自动解绑他的全部指令及事件监听器,但是仅限于组件本身的事件。

    created () {
      this.timer = setInterval(() => {
        this.refresh()
      }, 2000)
    },
    beforeDestroy () {
      clearInterval(this.timer)
    }
    

           7.图片懒加载:对于图片过多的页面,为了加速页面加载速度,所以很多时间我们需要将页面内未出现在可视区域内的图片先不做加载,等到滚动到可视区域后再去加载。

    <img v-lazy="./static/img/1.png">
    

           8.第三方插件按需引入:像element-ui这样的第三方组件库可以按需引入,避免体积太大。
    9.无状态的组件标记为函数式组件。

    <template functional>
      <div class="cell">
        <div v-if="value"></div>
      </div>
    </template>
    
    <script>
    export default {
      props: ['value']
    }
    </script>
    

           10.子组件分割:有动态内容的部分,把他切割为独立组件,数据改变后不会影响其它地方,否则整个页面都需要重新渲染,因为每个组件只有一个watcher。
    11.变量本地化:例如代码逻辑中多次用到computed中的属性,把他存为本地变量后再使用,不要频繁引用。
    12.SSR

  • **你对Vue3.0的新特性有哪些了解** 1.运行速度更快 2.打包体积小 3.维护性 4.跨平台
  • **简单说一说Vuex的使用及理解**        1.Vuex是Vue专用的状态管理库。他以全局方式集中管理应用的状态,并且可以保证状态变更的可预测性。        2.Vuex主要解决的问题是组件之间状态共享问题。利用组件的通信方式,虽然能够做到状态共享,但是往往需要在多个组件之间保持状态的一致性,这种模式很容易出现问题,也会使程序逻辑变得复杂。Vuex通过把组件的共享状态抽取出来,以全局单例模式管理,这样任何组件都能用一致的方式获取和修改状态,响应式的数据也能够保证简洁的单项数据流动,我们的代码将变得更结构化并且易维护。        3.Vuex并非必须,它能帮助我们管理共享状态,但却带来更多的概念和框架。如果我们不打算开发大型单页应用,或者我们的应用并没有大量全局的状态需要维护,完全没有使用
    Vuex的必要。        4.我在使用vuex过程中有以下理解:首先是对核心概念的理解和运用,将全局状态放入state对象中,它本身是一颗状态树,组件中使用store实例的state访问这些状态,然后有配套的mutation方法修改这些状态,并且只能用mutation修改,在组件中调用commit
    方法提交mutation;如果应用中有异步操作或者复杂逻辑组合,我们需要编写action,执行结果如果有状态修改仍然需要提交mutation,组件中调用这些action使用dispatch方法派发。最后是模块化,通过modules选项组织拆分出去的各个子模块,在访问状态时注意添加子模块的名称,如果子模块有设置namespace,那么在提交mutation和派发dispatch时还需要额外的命名空间前缀。        5.Vuex在实现单向数据流时需要做到数据的响应式,通过源码的学习发现是借用了vue的数据响应化特性实现的,他会利用Vue将state作为data对其进行响应化处理,从而使得这些状态发生变化时,能够导致组件重新渲染。
  • **Vue组件之间的通信方式** 1.props 2.$emit/$on 3.$parent/$children 4.$attrs/$listeners 5.ref 6.$root 7.eventbus 8.vuex 9.provide/inject 父子组件:props、$emit/$on、$parent/$children、$attrs/$listeners、ref 兄弟组件:$parent、$root、eventbus、vuex 跨层级组件:eventbus、vuex、provide/inject
  • Vue路由中如何保证指定路由的安全        1.阐述vue-router中路由保护策略:vue-router中保护路由安全通常使用路由守卫来做,通过设置路由守卫钩子函数的方式添加路由守卫函数,在里面判断用户的登录状态和权限,从而达到保护指定路由的目的。
    2.描述具体实现方式:具体实现有几个层级:全局守卫beforeEach、beforeResolve、afterEach,单个路由守卫beforeEnter,组件内守卫beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。以全局守卫为例,可以使用router.beforeEach((to,from,next=>{}))方式设置守卫,每次路由导航时,都会执行该守卫,从而检查当前用户是否可以继续导航。通过给next函数传递多种参数达到不同的目的,比如如果禁止用户继续导航可以传递next(false),正常放行可以不传递参数,传递

    path字符串可以重定向到一个新的地址等等。        3.简单说一下他们是怎么生效的:这些钩子函数之所以能够生效,也和vue-router工作式有关,像beforeEach只是注册了一个hook(钩子),当路由发生变化,router准备导航之前会批量执行这些hooks,并且把目标路由to,当前路由from,以及后续处理函数next传递给我们设置的hook。

    可能的追问:
    1.全局守卫、单个路由守卫、组件内守卫的区别?
    ①作用范围
    ②只有组件内实例可以获取到组件实例

    beforeRouteEnter((to, from, next) => {
      next(vm => {
        //vm
      })
    })
    

    ③名称、数量、顺序
    全局:beforeEach beforeResolve afterEach
    单个路由:beforeEnter
    组件内路由:beforeRouteEnter beforeRouteUpdate beforeRouteLeave
    2.前端路由是用什么方式实现的?
    答:前端路由的目标是根据不同的路径显示不同的内容,基于hash或history,监听hashChange或popstate事件,这些事件触发时,调用路由的api实现跳转,实际上页面是不刷新的,仅仅是为了获取到对应的内容并渲染出来,替换之前的内容。我们可能会实现一个router-view组件,使用响应式的方式监控url的变化,一旦url变化之后,动态调用router-view的渲染函数,在函数中获取到对应的组件,把他渲染出来展示在页面上。

  • 你知道nextTick****吗,他是干什么的,实现原理是什么? 1.下定义:
    Vue.nextTick([callback,context])
    nextTick是全局API,由于vue的异步更新策略导致我们对数据的修改不会立即体现在dom上,此时如果想要立即获取更新后的dom状态,就需要使用这个方法。

    2.原理解释:       vue在更新dom时是异步执行的,只要监听到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用。 3.在什么地方用它:       当我们想在修改数据后立即看到dom执行结果就需要用到nextTick方法。 4.如何使用: 5.源码实现:       源码中有callback数组,把传入的函数放到这个数组里,然后用timerFunc异步方式调用他们,首选异步方式会是Promise,用循环的方式执行callback。
  • 说一说你对Vue响应式的理解        1.所谓数据响应式就是能够使数据变化可以被检测并对这种变化做出响应的机制。
    2.mvvm框架中要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。
    3.以vue为例说明,通过数据响应式感知到数据变化,通知当前组件相关watcher入队,并且执行更新,更新时实际做的是把组件转换成虚拟DOM,使用patch算法(把虚拟DOM

    转换成真实DOM)。这样可以使我们只操作数据,不用频繁的DOM操作,从而大大提升开发效率,降低开发难度。        4.vue2中的响应式会根据数据类型的不同来做不同处理。通过学习源码,在Observer这个类中会判断,当前对象如果是纯对象,则采用Object.defineProperty()的方式遍历拦截里面的所有key,当这些数据发生变化时有所相应;如果是数组,先用当前数据的副本把当前数据的实例覆盖,封装操作数组的7个方法,使这些方法可以额外的做更新通知,从而做出响应。这种机制很好的解决了数据响应化的问题,但在实际使用中也存在一些缺点:比如初始化时递归遍历对象会造成性能损失;使用7个方法之外的操作,比如索引操作、新增或删除属性时需要用户使用Vue.set/Vue.delete这样特殊api才能生效;对于es6中新产生的Map、Set这些数据结构不支持等问题。        5.为了解决这些问题,vue3重新编写了这一部分的实现,利用ES6的Proxy机制代理要响应化的数据,他有很多好处,编程体验是一致的,不需要使用特殊的api,初始化性能和内存消耗都得到了大幅改善,另外由于响应化的实现代码抽取到独立的reactivity包,使我们可以更灵活的使用它,甚至不需要引入vue就可以体验。