vue面试题

112 阅读2分钟

什么是MVVM?

在MVVM框架下 视图和模型是不能直接通信 的,只能通过ViewModel进行交互,它能够监 听到数据的变化,然后通知视图进行自动更新,而当用户操作视图时,VM也能监听到视图 的变化,然后通知数据做相应改动,这实际上就实现了数据的 双向绑定 。并且V和VM可以 进行通信。

即模型-视图-视图模型,模型(Model)指

的是后端传递的数据,视图(View)指的是所看到的页面,视图模型(ViewModel)是 mvvm 模式的核

心,它是连接 view 和 model 的桥梁。

2、为什么data是一个函数

组件的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一分新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

Vue组件通讯有哪些方式?

1、props和emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过emit触发事件来做到的。

2.$refs 获取组件实例。

获取dom元素this.$refs.name,获取子组件中的data,

this.$refs.myRef.name,myRef绑定在父组件中的son标签上

3.vuex

Vue的生命周期方法有哪些?一般在哪一步发送请求?

beforeCreate 在实例初始化之后,数据观测(data observe)和 event/watcher 事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。

created 实例已经创建完成之后被调用。在这一步,实例已经完成以下的配置:数据观测(data observe ),属性和方法的运算,watch/event 事件回调。这里没有 el,如果非要想与DOM进行交互,可以通过vm.el,如果非要想与 DOM 进行交互,可以通过vm.nextTick 来访问 DOM。

beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。

mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom节点。

beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁 (patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。(数据修改页面未修改)

updated 发生在更新完成之后,当前阶段组件 Dom 已经完成更新。要注意的是避免在此期间更新数据,因为这个可能导致无限循环的更新,该钩子在服务器渲染期间不被调用。

beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行 善后收尾工作,比如清除定时器。

destroyed Vue实例销毁后调用。调用后,Vue实例指示的东西都会解绑定,所有的事件监听器会被移除,左右的子实例也会被销毁,该钩子在服务器端渲染不被调用。

activated keep-alive 专属,组件被激活时调用

deactivated keep-alive 专属,组件被销毁时调用

设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activateddeactivated

在某些场景下不需要让页面重新加载时我们可以使用keepalive

当我们从首页–>列表页–>商详页–>再返回,这时候列表页应该是需要keep-alive

异步请求在哪一步发起?

可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data已经创建,可以将服务器端返回的数据进行赋值。

如果异步请求不需要依赖 DOM 推荐加载 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

能更快获取到服务端数据,减少页面loading时间; 如果依赖DOM元素:需要再mounted里面进行请求

v-if 和 v-show 的区别

v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。元素销毁和重建控制显示隐藏

v-show 会被编译成指令,条件不满足时控制样式将此节点隐藏(display:none) css样式控制

computed 和 watch 的区别和运用的场景

computed 是计算属性,依赖其它属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容,他可以设置getter和setter。

watch 监听到值的变化就会执行回调,在回调中可以进行一系列的操作。

计算属性一般用在模板渲染中,某个值是依赖其它响应对象甚至是计算属性而来;

而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑。

v-if 和 v-for 为什么不建议一起使用

性能方面的浪费(每次渲染都会先循环再进行条件判断) 这样写:

<template v-if="isShow">
  <p v-for="item in items">
</template>

Vue 2.0 响应式数据的原理(常问)

整体思路是 数据劫持 + 观察者模式 Vue 在初始化数据时 ,会使用 Object.defineProperty 重新定义 data 中的所有属性 ,当页面 使用对 应 属性时,首先会进行 依赖收集 (收集当前组件的 watcher ),如果属性 发生变化 会通知相关 依赖进行 更新操作( 发布订阅 )

Vue2.x 采用 数据劫持结合发布订阅模式 (PubSub 模式)的方式,通过 Object.defineProperty 来劫 持 各个属性 的 setter、getter ,在 数据变动时 发 布消息给订阅者 , 触发相应的监听回 调。

v-model 双向绑定的原理是什么?

v-model 本质 就是 : value + input 方法的语法糖 。可以通过 model 属性的 prop 和 event 属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。

以输入框为例,当用户在输入框输入内容时,会触发 input 事件,从而更新 value。而 value 的改变同样会更新视图,这就是 vue 中的双向绑定。

hash模式和history模式

首先是在 URL 的展示上,hash 模式有“#”,history 模式没有

location.has 的值实际就是 URL 中 # 后面的东西。它的特点在于: hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中 hash监听 window.addEventListener("hashchange",funcRef,false)

history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。

vuex

状态管理器 使用方法

在 main.js 引入 store,注入。只用来读取的状态集中放在 store 中, 改变状态的方式是提交

mutations,这是个同步的事物,异步逻辑应该封装在 action 中。

State:定义了应用状态的数据结构,可以在这里设置默认的初始化状态。

Getter:允许组件从Store中获取数据,mapGetters 辅助函数仅仅是将 store 中的getter 映射到局部计算属性。

Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。

Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步请求。

Module:允许将单一的 Store 拆分更多个 store 且同时保存在单一的状态树中。

store.dispatch(actions中的方,value)store.dispatch('actions中的方法',value)或store.commit('mutations中的方法',value).

nextTick 的作用是什么?他的实现原理是什么

作用 :vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成, nextTick的回调是在下次 DOM 更新循环结束之后执行的延迟回调 。

change() {  
this.msg = '被更新了'  
this.$nextTick(() => {
// DOM更新了 
console.log('$nextTick:' + this.$refs.msgp.innerHTML) 
}) }

原型和原型链

每当定义一个对象(函数也是对象),对象中都会包含一些预定义的属性。其中每个 函数 (对象) 都会有一个 prototype 属性(是一个指针),它指向 一个对象-- 通过该函数创建的实例对象的原型,所以叫原型对象

原型包含了特定类型的所有实例共享的属性和方法,每一个对象都会从原型继承这些属性、方法。所以,原型对象的好处是:可以让所有的实例对象共享它所包含的属性和方法

1689650503682(1).jpg

路由

$router是VueRouter的一个对象,通过Vue.use(VueRouter)和Vue构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由,包含了许多关键的对象和属性。

$route是一个跳转的路由对象,每一个路由都会有一个$route对象,是一个局部的对象,可以获取对应的name,path,params,query等

  • 全局守卫:beforeEach 和 afterEach

  • 独享路由守卫:beforeEnter

  • 组件内路由守卫:beforeRouteEnterbeforeRouteUpdate 和 beforeRouteLeave

全局守卫分为全局前置守卫全局后置守卫

router.beforeEach(async(to, from, next) => {
  nprogress.start()
  if (store.getters.token) {
    // 存在token
    if (to.path === '/login') {
      // 跳转到主页
      next('/') // 中转到主页
      // next(地址)并没有执行后置守卫
      nprogress.done()
    } else {
      // 判断是否获取过资料
      if (!store.getters.userId) {
        const { roles } = await store.dispatch('user/getUserInfo')
        // console.log(roles.menus) // 数组 不确定 可能是8个 1个 0个
        // console.log(asyncRoutes) // 数组 8个
        const filterRoutes = asyncRoutes.filter(item => {
          // return true/false
          return roles.menus.includes(item.name)
        }) // 筛选后的路由
        store.commit('user/setRoutes', filterRoutes)
        router.addRoutes([...filterRoutes, { path: '*', redirect: '/404', hidden: true }]) // 添加动态路由信息到路由表
        // router添加动态路由之后 需要转发一下
        next(to.path) // 目的是让路由拥有信息 router的已知缺陷
      } else {
        next() // 放过
      }
    }
  } else {
    // 没有token
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login') // 中转到登录页
      nprogress.done()
    }
  }
})

vue中对象新增新属性渲染到页面上,$set

使用$set去添加属性,然后赶紧去试了一下this.$set(this.myInfo, 'age', 24),果真可以了。

向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。

它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性

(比如 this.myObject.newProperty = 'hi')