Vue2.x常见面试题

对于Vue是一套渐进式框架的理解

渐进式代表的含义是:主张最少。

Vue是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。

vue的两个核心

数据驱动视图和组件化开发

vue的生命周期和生命周期函数

创建:beforeCreate 和created

beforeCreate是将vue组件的对象开始创建

created是将vue组件的对象创建完毕但是还未进行挂载和虚拟DOM的构建

挂载:beforeMount 和 mounted

beforeMount是将组件的虚拟DOM已经创建完毕了但是数据还未进行挂载

mounted是将组件的数据挂载到虚拟DOM上面

更新:beforeUpdate 和 updated

beforeUpdate 是组件数据已经改变但是虚拟dom上的数据还未进行更新

updated 是组件的虚拟dom的数据已经更新完毕时的操作

销毁:beforeDestroy 和 destroyed

beforeDestroy是组件销毁或者说这个组件关闭前处理的函数

destroyed是组件销毁后执行的函数

vue双向绑定的原理

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。具体实现过程:

我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

通俗移动的来讲的话就是:VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。

v-if和v-show的区别

从视图的角度来看亮着区别并不大。都是控制一个元素的显示与隐藏。

但是从DOM的角度来看的话差别其实是非常大的,

v-if如果接受的参数是false那么它将不会创建这个DOM结构,如果应用在显示隐藏切换较频繁的地方的话这个是非常消耗性能和资源的。你让他显示一次的话那么他就要创建一个DOM结构,隐藏一次的话就要移除一次DOM结构。所以说v-if应该应用于切换并不频繁的地方。

v-show则是不管接受的参数是true还是false,这个DOM结构都会进行一个创建,他仅仅是将DOM结构的css样式进行修改来控制这个DOM结构的显示与隐藏。

修饰符

v-model修饰符

.lazy 将文本框的数据绑定事件改为change

.trim 去掉文本框内两边的空格

.number 限制文本框输入的字符为数字

事件修饰符

.stop 防止事件冒泡

.prevent 取消默认事件

.capture 捕获事件

.self 设置事件触发源是本身

.once 设置只能执行一次的事件

键盘修饰符

.enter 回车键

.tab Tab键

.delete

.esc

.space

.up 键盘上键

.down 键盘下键

.left 键盘左键

.right 键盘右键

鼠标修饰符

.left

.right

.middle

修饰键

可以用如下修饰符开启鼠标或键盘事件监听,使在按键按下时发生响应:

.ctrl

.alt

.shift

.meta

vue自定义指令

全局指令

// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
  // 当绑定元素插入到 DOM 中。
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

局部指令

var vm = new Vue({
  el: '#example',
  directives:{
    focus:{
      inserted: function (el) {
        el.focus()
      }      
    }
  }

})

然后可以在模板中任何元素上使用新的 v-focus 属性


vue中key值的作用

用来标记DOM元素做个标识,使其数据更新时效率提高。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,

否则vue只会替换其内部属性而不会触发过渡效果。

vue组件中data为什么是函数

在 new Vue() 中,data 是可以作为一个对象进行操作的,然而在 component 中,data 只能以函数的形式存在,不能直接将对象赋值给它。

当data选项是一个函数的时候,每个实例可以维护一份被返回对象的独立的拷贝,这样各个实例中的data不会相互影响,是独立的。

vue中v-if和v-for的优先级

v-if的优先级小于v-for的优先级

vue子组件调用父组件方法总结

常用的方法总结下

• $emit

• $parent

• prop

• vuex(vuex代码比较麻烦,不写了,说下步骤吧 dispatch:actions=>commit:mutations)

$parent方法

父组件



子组件



$emit方法

父组件



子组件



$prop方法

父组件



子组件



父组件调用子组件的方法

parent.vue



child.vue



兄弟组件之间互相调用

借助父组件作为通信的桥梁:(把一个问题分成两个小问题)

第一问题:父组件如何调用子组件的方法

方案:

利用ref属性,给子组件添加一个ref属性

parent.vue:


然后在子组件的js中定义子组件的方法

tree.js:

appendToTree({param}){
   console.log('这是子组件tree的方法')
},

父组件通过this.$refs.tree.xxxx调用子组件的方法

parent.js:

childTreeFn({param}){
    console.log('参数',param)
    this.$refs.tree.appendToTree({param});
}

第二个问题:子组件如何调用父组件的方法

方案:

子组件用$.emit触发父组件的事件,父组件去监听

child.js

submit(){
    this.$emit('parentFn',{param:this.argument});
},

parent.vue


父组件所监听的childTreeFn函数去调用子组件tree的方法,从而实现兄弟组件方法的互调

组件传值

父向子传值

子组件再父组件调用的时候动态绑定一个自定义属性将数据进行传输

子组件可以通过props属性进行接收

such as:

//parent

// child

export default {
  props: {
    name: {
      type: String,
      default: 'hello world'
    }
  }
}

props可以是对象也可以是数组

type是用来限制传输数据的数据类型的,default则是用来设置未传参时的默认值的,required判断是不是必传

子向父传值

子组件可以通过$emit方法将数据从子组件传递给父组件

// 子组件书写
this.$emit("fn1",'aaaa')

父组件通过绑定事件绑定子组件$emit方法中第一个自定义函数名

然后在调用这个函数,函数的默认参数就是传输过来的数据




getData(e){
    console.log(e)
    // e就是传输过来的数据
}
兄弟组件传值

兄弟组件之间可以使用一个bus函数进行传值

实际应用如下

// bus函数写法
// 新建一个bus.js

import Vue from 'vue'
export default new Vue()


// 传值组件写法
// 首先引入这个js文件
// 然后再methods里自定义函数
methods:{
    sendData(){
        bus.$emit('sendDataFn','data')
    }
}
// 接收组件写法
// 同样引入这个js文件
// 再methods里定义函数
methods:{
    getData(){
        bus.$on("sendData",(val)=>{
            // val就是传输过来的数据     
        })
    }
}

vuex

vuex由五大模块组成state mutations getters actions modules

  1. state:state是用来存储数据的,一般将多个页面的公共数据在此进行一个存储。
  2. getters:从state派生的数据,类似于组件中的computed
  3. mutations:用来定义重新设置或者存储state数据的方法,此方法调用时必须是同步的,每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
  4. actions:Actions 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。
  5. moudules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

使用下面这两种方法存储数据:
dispatch:异步操作,写法: this.$store.dispatch(‘mutations方法名’,值)

commit:同步操作,写法:this.$store.commit(‘mutations方法名’,值)

mvvm

MVVM优点编辑
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点

  1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
  4. 可测试。界面素来是比较难于测试的,测试可以针对ViewModel来写。

路由

路由跳转

方法一: this.$router.push({path:'路径')};

方法二:this.$router.push({name:'组件名')};

方法三:

传参

query传参

this.$router.push({
    path:"/",
    query:{a:11}
})

取值 this.$route.query.a

params传参

this.$router.push({
    path:"/",
    params:{a:11}
})

取值 this.$route.params.a

动态路由传参

// 路由配置
 {
     path: '/describe/:id',
     name: 'Describe',
     component: Describe
   }
// 跳转传参

this.$router.push({path: `/describe/${id}`})

取值 this.$route.params.id

watch和computed的区别

计算属性computed :

  1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
  2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
    3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
  3. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
    5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。

侦听属性watch:

  1. 不支持缓存,数据变,直接会触发相应的操作;
    2.watch支持异步;
    3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
  2. 当一个属性发生变化时,需要执行对应的操作;一对多;
  3. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
      immediate:组件加载立即触发回调函数执行,
      deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。

监听的对象也可以写成字符串的形式

watch实现深度监听

watch: {
    obj: {
        handler: function(val) {
        },
        deep: true // 深度监听
    }
}

axios拦截器

对于每次的请求进行拦截,并进行相应的一些处理,例如添加一些公共参数,或者是一些其他的字段。或者是登录超时等等一系列的操作。

在src下的api目录中创建一个js文件
import axios from 'axios'  //引入axios
//下面这两个不一定需要引入,看你项目需要拦截的时候做什么操作,但是一般都需要引入store
import store from '@/store/index'  //引入store
import router from '@/router'  //引入router
创建一个axios实例
let instance = axios.create({
  headers: {
    'content-type': 'application/x-www-form-urlencoded'
  }
})
编写请求拦截器

这个拦截器会在你发送请求之前运行

我的这个请求拦截器的功能是为我每一次请求去判断是否有token,如果token存在则在请求头加上这个token。后台会判断我这个token是否过期。

// http request 拦截器
instance.interceptors.request.use(
  config => {
    const token = sessionStorage.getItem('token')
    if (token ) { // 判断是否存在token,如果存在的话,则每个http header都加上token
      config.headers.authorization = token  //请求头加上token
    }
    return config
  },
  err => {
    return Promise.reject(err)
  })
)
响应拦截器
// http response 拦截器
instance.interceptors.response.use(
  response => {
    //拦截响应,做统一处理 
    if (response.data.code) {
      switch (response.data.code) {
        case 1002:
          store.state.isLogin = false
          router.replace({
            path: 'login',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
      }
    }
    return response
  },
  //接口错误状态处理,也就是说无响应时的处理
  error => {
    return Promise.reject(error.response.status) // 返回接口返回的错误信息
  })
)
最后把实例导出就行了
export default instance
使用
import instance from './axios'

/* 验证登陆 */
export function handleLogin (data) {
  return instance.post('/ds/user/login', data)
}

路由钩子函数

beforeEach

// 跳转前的钩子函数
router.beforeEach((to, from, next) => {
  // to要跳转到的路由,from从哪个路由跳转过来,next执行跳转的函数
})

afterEach

// 跳转后的钩子函数
router.beforeEach(route => { // ...})

组件内的钩子函数

  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能获取组件实例 `this`
    // 因为当钩子执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }

路由拦截

需要在路由中配置meta属性并配置自定义的一个属性作为跳转的开关

router.beforeEach((to, from, next) => {
  let islogin = localStorage.getItem("islogin");
  islogin = Boolean(Number(islogin));

  if(to.path == "/login"){
    if(islogin){
      next("/table");
    }else{
      next();
    }
  }else{
    // requireAuth:可以在路由元信息指定哪些页面需要登录权限
    if(to.meta.requireAuth && islogin) {
      next();
    }else{
      next("/login");
    }
  }
})

你可能感兴趣的:(Vue2.x常见面试题)