2020-10-27 | 面试合集 | UNLOCK | 更新时间:2022-9-5 16:52

前端面试题的收集和总结(Vue 篇)

Vue

Vue 和 React 的区别?

  • react 整体是函数式的思想,把组件设计成纯函数,状态和逻辑通过参数传入,所以在 react 中,是单向数据流,而vue的思想是响应式的,也就是基于是数据可变的,数据双向绑定,通过对每一个属性建立 Watcher 来监听,当属性变化的时候,响应式的更新对应的虚拟dom;
  • react 通过js来操控一切,设计了 jsx,通过 jsx 来生成 htmlcssvue 则把 htmlcssjs 组合到一起,用各自的处理方式,vue 有单文件组件,可以把 htmlcssjs 写到一个文件中,html 提供了模板引擎来处理
  • react 更喜欢规范化用户,搭建一个规范化的架构,然后让用户,社区去做更多,而 vue 则内置了更多API,更为用户考虑更多,vue 提倡开发更简便。

说说你对virtual Dom(虚拟dom)的理解?

虚拟 dom 是对真实 dom 的抽象,本质上是 JavaScript对象 ,通过 JavaScript对象去描述真实DOM,在状态改变时,通过对新旧虚拟 dom 树的差异,在真实 dom 进行局部改变,尽可能的减少对真实 dom 的操作和重绘。它的优点在于保证了性能的下限,同时不需手动操作真实dom,缺点在于无法做到性能的极致优化,同时因为多了一层计算,有时会比直接操作真实dom要慢。

说是你对Vue数据单向流的理解?

数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解

为什么组件的 data 是一个函数?

因为组件是用来复用的,为了防止data复用,将其定义为函数.数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,因为这些实例共用同一个构造函数,就使得所有组件实例共用了一份data,会造成一个组件变化导致全部组件都会变的结果。

vue 怎么让 CSS 只在当前组件生效?

可以在当前组件的style 上添加 scoped; 如果需要影响第三方组件,可以通过在入口文件中配置相关CSS进行样式重写,或者通过 /deep/ 声明进行样式穿透。

Vue中的通信方式有哪些?简介介绍一下。

  • props$emit : 父组件通过props向子组件传递数据,子组件通过$emit方法向父组件传递数据
  • $parent , $children$refs : $parent$children 分别指向父组件和子组件实例,$refs则指向所设置的ref组件实例,可以通过实例直接获取它们的data和方法
  • Vuex : 可以通过Vuex状态管理,各组件通过actionmutationstate进行数据的存取,说偏一点,也可以通过 localStoragesessionStorage 进行数据传递
  • $attrs$listeners : $attrs 会获取父组件通过props传递过来但没被自己使用的值,$listeners 会获取父组件通过v-on 绑定的事件
  • envetBus : 事件总线,通过在main中设置 Vue.prototype.$eventBus = new Vue() ,然后在需要的地方通过对 this.$eventBus 设置 $emit $on 进行事件触发和监听

vue的指令有哪些?讲几个你比较常用的

vue的指令有
v-on:绑定事件      v-bind:绑定属性    v-model:双向绑定   v-once单次渲染 ; 
v-if="条件判断"    v-else-if="条件"   v-else            v-for="i in 循环渲染";
v-text="绑定文本"  v-pre="pre文本"    v-html="innerHtml文本"; 
v-slot:具名插槽使用,只能在template上   v-cloak 可以和CCS规则[v-clock]结合使用

说说你对 v-modal 的理解?

v-modal 在官方文档中的解释是,它是一种语法糖,会自动的在元素或者组件上面解析为 value 属性 和 input 事件. 它的核心实质是通过 object.defineProperty 劫持各个属性,当监听到变化后通过相应的页面元素更新,另一方面通过通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值

vue修饰符你用过哪些,讲一讲

vue修饰符的话分 事件修饰符,按键修饰符,鼠标修饰符和表单修饰符。
事件修饰符的话有:.stop(阻止冒泡事件) .prevent(阻止默认事件) .capture(点击子元素,在冒泡前触发) .self(只接受当前元素,不接受冒泡) .native(监听组件根元素的事件) .once(只执行一次) .passive(滚动事情会不停触发,不会阻止默认事件)
按键修饰符的话有:.enter .tab .esc .space .up .down .ctrl .alt .shift .meta(系统按键) .exact 等等,
鼠标修饰符的话有:.left .right .middle
表单修饰符的话有:.lazy(只有失去焦点,触发change事件后才会触发) .trim(清除输入框里的前后空白字符串
.number(对数据进行parseFloat,既前面字符串为数字则解释为数字,否则不解析)

单向数据绑定和双向数据绑定的区别?

单向数据绑定就是简单的将modal数据绑定在view视图上,当数据改变的时候,view视图也跟着改变,但用户输入的时候,modal中的数据却不会跟着改变。而双向数据绑定则会跟着用户的输入,改变modal中的数据

VUE数据双向绑定的原理?

Vue 采用数据劫持加发布者-订阅者模式的方式,遍历 data 中的所有属性,通过 Object.defineProperty() 来劫持各个属性的 settergetter ,在数据变动时通过Dep算法 遍历所有订阅者,发布消息给订阅者,触发相应的监听回调,完成订阅视图的改变

Vue事件绑定的原理,讲一下?

Vue中通过v-on或其语法糖@指令来给元素绑定事件,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNode,VNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定

Vue可以数据劫持知道数据变化,为什么还要虚拟dom进行diff检测?

vue是通过watcher知道数据发生了变化,每绑定一个数据,就会产生一个watcher,一旦绑定颗粒度过细就会产生大量的watcher,这对内存和依赖开销打来很大的负担,所以vue在组件级进行push检测,当检测到组件发生改变,通过diff算法,获取组件的差异变化,然后进行视图的改变

VUE的网络请求放哪?

一般看网络请求的涉及情况,如果网络请求涉及到dom节点加载后处理,那就放在 mounted 生命周期,如果未涉及到,则可以放在 created 周期,至于为什么不放 beforeCreate 和 beforeMounte,则是因为beforecreate 部署了方法运算和watch监测,beforeMounte 则比created 晚

VUE的生命周期有哪些?

vue的生命周期有:
beforeCreate (在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用)
created      (在实例创建完成后被立即同步调用,数据侦听和事件已经配置完成)
beforeMount  (挂载开始之前,相关的 `render` 函数被首次调用)
mounted      (实例挂载后调用)
beforeUpdate (数据更新时调用,虚拟DOM渲染之前,状态更改但未被渲染)
updated      (数据导致虚拟daom重新渲染后调用) 
beforeDestroy(实例销毁之前,实例任然可用)
destroyed    (实例销毁后,子实例也被销毁)

activated    (在缓存组件 keep-alive 激活时触发)
deactivated  (在缓存组件 keep-alive 停用时触发) 
errorCaptured(捕获到子组件错误的时候触发)

扩展:updated 生命周期不保证子组件的重新绘制,可以在updated里调用$nextTick() 保证视图已全重新渲染。

Vue的生命钩子如何实现?

Vue 的生命周期钩子核心实现是利用发布订阅模式先把用户传入的生命周期钩子订阅好(内部采用数组的方法存储)然后在创建组件实例的过程中会一次执行对应的钩子方法(发布)

函数式组件和普通组件的区别?

不需要实例化,没有生命周期钩子函数,不能使用计算属性,watch;没有普通组件里的this调用;props不需要显示声明,结构简单,代码清晰。

Vue中 key的作用和原理?

为了更高效的更新虚拟dom key 主要用在 Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。作用是为了更快速定位出相同的新旧节点,尽量减少 DOM 的创建和销毁的操作,基于key的变化重新排列元素顺序,并且会移除key不存在的元素; 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试再利用相同类型元素的算法。
有相同父元素的子元素必须有独特的key。重复的key会造成渲染错误

watch、methods 和 computed 的区别?

watch 是用于监听某些数据的回调。每当所监听的数据发生变化时才能执行回调处理,适用于复杂的业务逻辑
methods 一般是用于书写业务逻辑的方法,methods会将这些方法混入vue实例中,然后这些方法可以通过vm实例进行访问
computed 是计算属性,在监听到依赖值的变化后,通过计算从而动态返回内容,主要目的是简化模板内的复杂运算,它会被缓存,只有依赖发生变化的时候,它才会执行运算

v-show和v-if指令的共同点和不同点?

共同点在于都能控制元素的显示和隐藏;不同点在于实现本质方法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。所以v-if比较适合不会频繁切换状态的元素,可以有效降低首次加载的编译时间,v-show则适合频繁切换状态的元素,可以防止频繁切换导致编译问题;

为什么Vue中的v-if 和v-for不建议一起用?

在Vue早期版本中可以一起使用,现在已经不能一起使用了,必须分开使用。早期版本中当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。这意味着 v-if 将分别重复运行于 每个 v-for 循环中,即先运行 v-for 的循环,然后在每一个 v-for 的循环中,再进行 v-if 的条件对比,会造成性能问题,影响速度。

delete和vue.delete的区别?

delete仅仅只会删除值,内存占位还在。而vue.delete会删除其在内存中的占位,例如数组,delete仅删除改位置上的值,数组长度不变,而vue.delete则会删除其的占位,数组长度变短

Vue的有用的属性和方法?

  • Vue.nextTick(()=>{ }) 会等下次dom渲染后执行函数;可以用于created生命周期执行副作用操作
  • keep-alive 缓存组件;在包裹其他组件的时候;会缓存不活动的组件,而不是销毁它,但还是会删除dom

说说 vue-router

vue-router通过hash 与History interface两种方式实现前端路由,更新视图但不重新请求页面”是前端路由原理的核心之一;hash模式,在url后面会添加 # ,路由会添加在 # 后面。通过监听hashChange事件来重新渲染页面但不刷新,而 history 模式在url的表现上则和正常路由基本一致,通过调用 history.pushState方法并监听popstate事件来实现,但在刷新的时候会因为没有这个路径的文件导致404。需要在Nginx配置将页面执向项目的index文件。

route 和 router 的区别是什么?

route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数
router是“路由实例对象”,包括了路由的跳转方法(push、replace),钩子函数等。

说一下你用过的的路由函数钩子(路由导航守卫)?

vue-router 的话有

  • 全局守卫

beforeEach (全局前置守卫),它是路由转跳之前触发的,一般用于路由权限检测,判断登录转跳等,
beforeResolve (全局解析守卫)和前置守卫类似,可以用于注册全局守卫,但它是在导航确认之前,组件和异步解析之后。
afterEach (全局后置钩子)和守卫不同,钩子不接受next函数,也不改变导航本身,一般用来处理转跳执行的操作,但是不改变导航的操作

  • 独享守卫

beforeEnter (路由独享守卫):在路由配置中单独给某个路由配置的,用法和beforeEchart 一样

  • 组件守卫

**beforeRouteEnter **:在组件实例创建之前执行的钩子,它不能访问this
**beforeRouteUpdate **:在路由改变且组件更新的时候进行访问
beforeRouterLeave:在路由离开组件时调用

有使用过Vue的动态路由,讲一下

很多时候,我们有list的详情页需要查询,因为文件名或者用户是不确定,有时候需要动态在路由里配置文件名或者其他动态ID之类的路由。这时候一般直接在Vue-router中配置就好了,以冒号配置需要动态变化的路径

讲一下你对VUEX的理解?

vuex是专门给vue开发出来的一个状态管理模式;

  • 采用集中式存储管理应用的所有组件的状态;Vuex 通过单一状态树管理所有数据,通过store.state 来访问组件状态;
  • Vuex通过Getter 来派生一些状态,类似计算属性,可以通过store.getter 来访问派生属性;
  • 更改Vuex中状态的唯一办法就是commit Mutation;必须是同步函数;mutation可以通过判断传入的type,完成不同的对state的修改
  • Vuex通过action 来commit mutation来修改 state,action可以用来作异步处理,可以通过store.dispatch action访问执行
  • vuex 通过modules对 store 进行拆分,每个module都有自己的action,mmutation 等,再通过modules合并到同一个store

如果把数据请求放在vuex中,你放在那里?

可以把数据请求放在放在action里面,结合ES6的 Promise()异步返回数据,然后通过dispatch这个action来访问请求,再通过then()方法对返回的数据进行处理。

actions:{
  getName({commit}){
    return new promise((res,rej)=>{
      axios().then(()=>{
        res('ok')   //返回异步成功
      }).catch(()=>{
        rej('no')   //返回异步失败
      })
      commit('get') //通过commit触发mutation,间接改变state
    })
  }
}

Vuex的属性有哪些?

Vuex的属性有 state getter mutation action modules, state用于存储vue的基本数据;getter用于派生一些状态类似计算属性。mutation用于提交数据的方法,是同步;action也用于提交数据,不过是触发的是mutation,modules模块化vue,让每个模块都有自己的属性。

如果要优化一个Vue项目,怎么做?

图片的优化,大型图片的懒加载和缩略显示,以及一些小图的雪碧图合并。请求链接的合并,减少请求链接数,降低对象层级,对象层级太深会导致性能变差。异步组件的的加载,路由的懒加载和第三方组件的按需加载。防抖和节流的应用等等

讲一下Vue的自定义指令,你怎么写的?

除了默认的指令,Vue允许我们注册自定义指令,我们可以通过Vue.directives全局自定义指令,也可以通过directives选项注册局部自定义指令。同时Vue会提供几个钩子函数以供你使用编辑。像 bind inserted update componentUpdated unbind。