天天看点

vue面试二十问

vue面试二十问

这是我自己在准备面试的时候,上网收集汇总的一些知识点,不能说你会这些就能找到自己心仪的工作,但你要想找到心仪的工作就必须会这些!

一、什么是MVVM?

MVVM是Model-View-ViewModel的缩写。Model 层代表数据模型;View 代表DOM,ViewModel 是一个同步View 和 Model的对象。

View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

优点:需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

二、vue的优点是什么?

  1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  2. 组件化,实现html的封装和重用
  3. 虚拟DOM,不在使用原生DOM操作
  4. 运行速度快

三、组件之间的传值?

1.父组件向子组件传值:

使用自定义属性绑定(v-bind:)相应的父组件数据 ,把自定义属性在子组件的props属性里定义一下。

<sonCom v-bind:parentmsg="msg"></sonCom>
//子组件props属性
props:['parentmsg']
           

2.子组件调用父组件方法并传参(变向的子组件向父组件传值)

通过$emit

//父组件方法
methods:{
    show(data){
        console.log(data)
    }
}

//DOM
<sonCom v-on:func = 'show'></sonCom>

//子组件
this.$emit('func',123)//123可以改成子组件data中的数据,完成子组件向父组件传值
           

3.兄弟组件传值

通过event bus ,大项目vuex

1.创建一个空vue实例,并将它发布到全局中(bus.js文件)

import Vue from 'vue'
const bus = new Vue()
export default bus
           

2.在子组件一中引用bus.js,用bus.$emit(‘参数name’,value)方法传参

3.在子组件二中引用bus.js,用bus.$on(‘参数名字’,callback(value))方式接受参数并处理

四、为什么组件中的data必须是function?

组件中的data写成一个函数,数据以函数返回值的形式定义,保证每个组件实例都有自己的私有数据空间,不会造成污染。要不然所有组件实例共用一个data,改一个其他全改

五、v-show、v-if 区别,使用场景

v-show:本质控制css的display:none,只编译一次。切换开销小,初始开销大

适用场景:频繁切换某节点

v-if:本质动态向DOM树添加或者删除DOM元素,不停销毁和创建比较消耗性能。初始渲染开销小,切换开销大

适用场景:不需要频繁切换节点

六、获取DOM

ref=“domName”,this.$refs.domName

七、为什么要使用key?

需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。作用主要是为了高效的更新虚拟DOM

八、双向数据绑定

(大致)

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过

Object.defineProperty()

来劫持各个属性的

setter

getter

,在数据变动时发布消息给订阅者,触发相应的监听回调

核心:Object.defineProperty()(详看红宝书)

手写一个简单的双向数据绑定

let input = document.getElementById("input");
  let text = document.getElementById("text");
  let data = { value: "" };
  Object.defineProperty(data, "value", {
    set: function (val) {
      text.innerHTML = val;
      input.value = val;
    },
    get: function () {
      return input.value;
    }
  });
  input.onkeyup = function (e) {
    data.value = e.target.value;
  };
           

九、computed和watch的区别

computed:

  1. 支持缓存,只有依赖数据发生改变,才会重新进行计算.
  2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化.

适用场景:一个属性受多个属性影响(购物车商品结算)

watch:

  1. 不支持缓存,数据变,直接会触发相应的操作;
  2. watch支持异步;
  3. 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;

适用场景:当需要在数据变化时执行异步或开销较大的操作时(搜索数据)

十、常用事件修饰符

  • .stop():等同于JavaScript中的event.stopPropagation(),防止事件冒泡。
  • .prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为。
  • .capture:与事件冒泡的方向相反,事件捕获由外到内。
  • .self:只会触发自己范围内的事件,不包含子元素
  • .once:只会触发一次

十一、vue-router中的导航钩子

  • 全局 钩子
    const router = new VueRouter({ ... });
    router.beforeEach((to, from, next) => {
        // do someting
    });
    //to: Route,代表要进入的目标,它是一个路由对象
    
    //from: Route,代表当前正要离开的路由,同样也是一个路由对象
    
    //: Function,这是一个必须需要调用的方法,而具体的执行效果则依赖 next 方法调用的参数
               
  • 路由独享钩子
cont router = new VueRouter({
    routes: [
        {
            path: '/file',
            component: File,
            beforeEnter: (to, from ,next) => {
                // do someting
            }
        }
    ]
});
           
  • 组件内的导航钩子
const File = {
    template: `<div>This is file</div>`,
    beforeRouteEnter(to, from, next) {
        // do someting
        // 在渲染该组件的对应路由被 confirm 前调用
    },
    beforeRouteUpdate(to, from, next) {
        // do someting
        // 在当前路由改变,但是依然渲染该组件是调用
    },
    beforeRouteLeave(to, from ,next) {
        // do someting
        // 导航离开该组件的对应路由时被调用
    }
}
           

十二、vue的两个核心

  1. 数据驱动

​ viewModel,保证数据和视图的一致性

  1. 组件系统

    组件化开发,优点很多,可以很好的降低数据之间的耦合度。将常用的代码封装成组件之后,就能高度的复用,提高代码的可重用性。一个页面/模块可以由多个组件所组成。

十三、Vue生命周期

vue面试二十问
/*创建阶段*/

      //1.第一个生命周期函数
      beforeCreate() {//表示实例完全被创建出来之前,会执行它
        // console.log(this.msg);
        // this.show();

        /*
          在beforeCreate生命周期执行的时候,data和methods中的数据还没有初始化
        */

      },
      //2.第二个生命周期函数
      created() {
        console.log(this.msg + '--->created');
        this.show();
        //在created中,data,和methods 已经被初始化了
      },
      //3.第三个生命周期函数
      beforeMount() {
        //表示 模板已经在内存中编译完成了,但是尚未把模板渲染到页面中
        console.log(document.getElementById("me").innerHTML + '--->beforeMount')
        //在beforeMount 执行的时候,页面中元素还没有被真正替换过来,只是之前写的一些模板字符串
      },
      //4.第四个生命周期函数
      mounted() {
        //表示 模板已经在内存中编译完成了,但是已经把模板渲染到页面中
        console.log(document.getElementById("me").innerHTML + '--->mounted')
        //mounted 执行的时候,页面中元素已经被真正替换过来
        //只要执行完mounted,就表示整个Vue实例已经初始化完毕了,此时组件已经脱离了创建阶段,进入了运行阶段。
      },


      /*运行阶段*/

      //1.第一个运行生命周期函数
      beforeUpdate() { //这时候表示我们的界面还没有被更新,【数据更新了】

        console.log('页面中msg的值' + document.getElementById("me").innerHTML + '--->beforeUpdate')
        console.log("Data中msg值为:" + this.msg)

        //数据是最新的,页面没有同步
      },

      //2.第二个运行生命周期函数
      updated() {

        console.log('页面中msg的值' + document.getElementById("me").innerHTML + '--->updated')
        console.log("Data中msg值为:" + this.msg)
        //数据是最新的,页面已经同步
      },


      /*销毁阶段*/
      //1.第一个销毁生命周期函数
      beforeDestroy() {  //Vue实例从运行到销毁阶段
        //Vue实例上所有的Data和所有的methods,以及过滤器、指令。。。都处于可用状态,此时还没有真正执行销毁过程
      },

      //1.第二个销毁生命周期函数
      destroyed() {
        //Vue实例上所有的Data和所有的methods,以及过滤器、指令。。。都已经销毁。
      },

    });
           

十四、vuex

1.Vuex是什么

Vuex是实现组件全局状态(数据)管理的一种机制,可以方便实现组价之间的数据的共享

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28jCMlgP-1599554454926)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1598779618877.png)]

  1. Vuex优点
  • 能够在Vuex中集中管理共享的数据,易于开发和后期维护
  • 能够高效的实现组件之间的数据共享,提高开发效率
  • 储存在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
  1. Vuex四个核心概念
    • State

      State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新

      组件中访问:

      //方式一:
      this.$store.state.myDate
      //方式二:
      import { mapState } from 'vuex'
      
      computed:{
          ...mapState(['myData'])
      }
                 
    • Mutation

      Mutation用于变更Store中的数据,可以集中监控数据变化(不能写异步代码)

      //store.js中
      mutation:{
          func(state,argument){
              fn()
          }
      }
      //组件中调用
       this.$store.commit('func',argument)
      
      
      //第二种方法
      import { mapMutations } from 'vuex'
      methods:{
          ...mapMutations(['func']),
              myfunc(){
              this.func(argument)
          }
      }
                 
    • Action

      如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action 中还是要通过触发Mutation的方式间接变更数据

      //store.js中
      actions:{
          func(context,argument){
              setTimeout(()=>{
                  context.commit('mutation里的function',argument )
              },wait)
          }
      }
      
      //组件中
      this.$store.dispatch('func',argument)
      
      //方式二:
      import { mapActions } from 'vuex'
      methods:{
          ...mapActions(['func']),
              myfunc(){
              this.func(argument)
          }
      }
                 
    • Getter

      用于对Store中的数据进行加工处理形成新的数据。Store数据改变,Getter的数据也会跟着变化(像vue里的计算属性)

      getters:{
          newData(state){
              return `新值为${state.myData}`
          }
      }
      //方式一:
      this.$store.getters.newData
      
      //方式二:
      import { mapGetters } from 'vuex'
      
      computed:{
          ...mapGetters(['newData'])
      }
                 
    • modules

      在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理。

      module:可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

      const moduleA = {
       state: { ... },
       mutations: { ... },
       actions: { ... },
       getters: { ... }
       }
      const moduleB = {
       state: { ... },
       mutations: { ... },
       actions: { ... }
       }
       
      const store = new Vuex.Store({
       modules: {
        a: moduleA,
        b: moduleB})
                 

十五、单页面应用(SPA)

SPA->就是只有一个主页面的应用,浏览器一开始要加载所有必须的 html、js、css。交互时候由路由程序动态载入,单页面的页面跳转仅刷新局部资源,多用于pc端

优点:

  • 良好的交互体验:单页应用的内容的改变不需要重新加载整个页面。
  • 减轻服务器压力:服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。
  • 良好的前后端分离:后端不用负责模板渲染,只需要进行数据的存储与计算

缺点:

  • 对SEO不太友好,不利于seo的搜索
  • 需要自行实现导航的前进后退;
  • 页面复杂度高、初次加载耗时多

十六、虚拟DOM

  1. 什么是虚拟DOM?

​ 虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。

​ 这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树,那么每次 dom 的更改就变成了 js 对象的属性的更改,这样一来就能查找 js 对象的属性变化要比查询 dom 树的性能开销小。

  1. 为什么DOM性能开销大?

​ 其实并不是查询 dom 树性能开销大而是 dom 树的实现模块和 js 模块是分开的这些跨模块的通讯增加了成本,以及 dom 操作引起的浏览器的回流和重绘,使得性能开销巨大,原本在 pc 端是没有性能问题的,因为 pc 的计算能力强,但是随着移动端的发展,越来越多的网页在智能手机上运行,而手机的性能参差不齐,会有性能问题。

  1. vue大致解决方法

​ 每次更新 dom 都尽量避免刷新整个页面,而是有针对性的去刷新那被更改的一部分、也就是diff算法

  1. diff算法的大致思路

​ 我们先根据真实DOM生成一颗

virtual DOM

,当

virtual DOM

某个节点的数据改变后会生成一个新的

Vnode

,然后

Vnode

oldVnode

作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使

oldVnode

的值为

Vnode

。diff的过程就是调用名为

patch

的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。

具体实现可以参考https://www.cnblogs.com/wind-lanyan/p/9061684.html

十七、vue-router的两种模式

1、hash ——即地址栏URL中的#符号(此hsah 不是密码学里的散列运算)。 比如这个URL:http://www.abc.com/#/hello, hash 的值为#/hello。它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。

2、history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。(需要特定浏览器支持) 这两个方法应用于浏览器的历史记录站,在当前已有的back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改是,虽然改变了当前的URL,但你浏览器不会立即向后端发送请求。history模式,会出现404 的情况,需要后台配置。—>利于seo

十八、Keep-alive组件

组件切换的时,保持这些组件的状态,以避免反复重渲染导致的性能问题(Vue 会创建了一个新的实例)

十九、Slot插槽

插槽含义:就是引入子组件后,在插入子组件元素中添加信息或者标签,使得子组件的指定位置插入信息或者标签

插槽有三种:默认插槽、具名插槽、作用域插槽

二十、nextTick

在Vue生命周期的

created()

钩子函数进行的DOM操作一定要放在

Vue.nextTick()

的回调函数中

changeMsg() {
      this.msg = "Hello world."
      this.msg1 = this.$refs.msgDiv.innerHTML //原来内容
      this.$nextTick(() => {
        this.msg2 = this.$refs.msgDiv.innerHTML //Hello world
      })
      this.msg3 = this.$refs.msgDiv.innerHTML//原来内容
    }

//其根本原因是因为Vue中DOM更新是异步的
           

十九、Slot插槽

插槽含义:就是引入子组件后,在插入子组件元素中添加信息或者标签,使得子组件的指定位置插入信息或者标签

插槽有三种:默认插槽、具名插槽、作用域插槽

二十、nextTick

在Vue生命周期的

created()

钩子函数进行的DOM操作一定要放在

Vue.nextTick()

的回调函数中

changeMsg() {
      this.msg = "Hello world."
      this.msg1 = this.$refs.msgDiv.innerHTML //原来内容
      this.$nextTick(() => {
        this.msg2 = this.$refs.msgDiv.innerHTML //Hello world
      })
      this.msg3 = this.$refs.msgDiv.innerHTML//原来内容
    }

//其根本原因是因为Vue中DOM更新是异步的
           

继续阅读