1、对MVVM开发模式的理解

MVVM分为ModelViewViewMode三者

Model: 代表数据模型,数据和业务逻辑都是在Model层中定义

View: 代表UI视图,负责对数据的展示

ViewModel: 负责监听Model中数据的改变并控制视图的更新,处理用户交互操作

ModelView并无直接关联,而是通过ViewModel来进行联系的,ModeIViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

这种模式实现了ModeIView的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作dom

2、组件间的通讯

2.1 父子组件间的通讯

父组件向子组件传值,父组件通过数据绑定,子组件通过props进行接收
举个栗子:

// 父组件
<template>
	<div>
		<child :childmsg="childmsg"></child>
	</div>
</template>
<script>
import Child from "Child.vue的路径";
export default {
  components: { Child },
  data() {
    return {
      childmsg: "this is the msg of child component"
    };
  }
}
</script>
// 子组件
<template>
  <div>{{ childmsg }}</div>
</template>
<script>
export default {
  props: ["childmsg"]
};
</script>

子组件向父组件传值,子组件通过this.$emit(eventName, params),向父组件传递一个事件,还有需要传给父组件的参数(值),父组件通过监听子组件传过来的事件(@eventName = selfMethod),可以接收到子组件传过来的值。
注意:父组件监听子组件传来的事件,在HTML代码不需要带括号,即selfMethod不需要写成selfMethod(param1, param2, ...),只需要在定义方法的时候写出来就好。

// 父组件
<template>
  <div class="home">
    <child @childSend="parentGet"></child>
  </div>
</template>
<script>
import Child from "Child.vue的路径";
export default {
  components: { Child },
  methods: {
  	parentGet(params) {
      console.log(params);
    }
  }
};
</script>
// 子组件
<template>
  <div>
    <button @click="sendData">点击向父组件传递一个参数</button>
  </div>
</template>
<script>
export default {
  methods: {
    sendData() {
      this.$emit("childSend", "childParams");
    },
  },
};
</script>

2.2 祖孙组件之间的传值

a. 可以利用provide inject模式,在一个组件中通过provide给孙子组件传值,孙子组件通过inject进行接收,这个模式是在vue2.5版本之后才开始有的

b. 可以通过 $attrs 传值,借助中间组件的协助

// 祖组件:通过属性绑定将值传给父组件,其中a、b、c为data中的数据
<grandparent :msga="a" :msgb="b" :msgc="c"></grandparent>

// 父组件:通过v-bind="$attrs" (这个v-bind不能简写为:),将祖组件的值传给子组件
<parent v-bind="$attrs"></parent >

// 孙子组件:通过this.$attrs接收这些值
console.log(this.$attrs.msga) ;

2.3 非父子(祖孙)组件间的传值

a. 中央事件总线机制:建立一个空实例进行传值。例如建立一个bus.js空实例,在需要传值的组件中去触发

//原理上就是建立一个公共的js文件, 专门用来传递消息
// bus.js 
import Vue from 'vue'
export default new Vue;

//在需要传递消息的地方引入
import bus from ' . /bus.js '

//传递消息
bus.$emit( 'msg', val) 

//接受消息
bus.$emit( 'msg', val => {
	console.log(val )
})

b. 也可以使用vuex来进行组件间的值传递
使用中央事件总线机制或者vuex都可以处理任意组件间的传值

3、v-if和v-show的区别

1)v-if动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显示或者隐藏

2)编译过程中,v-if是惰性的,如果一开始条件为false,则什么都不做,即不进行编译,到将其条件切换为true时,才进行编译,如果在变为true之后又切换为false,那么这时候需要进行局部卸载;而v-show则不管初始条件是否为true都进行编译,然后缓存起来,而且DOM元素被保留,所以v-if有更高的切换消耗,而v-show有更高的初始渲染消耗

4、vue中computed的特点

计算属性在引用的时候,不要加()去调用,当成普通的变量用就好;

计算属性的求值结果,会被缓存起来,方便下次继续使用;如果计算属性方法中,所依赖的任何数据,都没有发生过变化,则不会重新对计算属性求值。

只要计算属性内部所用到的data中的数据发生了变化,就会立即重新计算这个计算属性的值。

5、Vue路由守卫有哪些,怎么设置,使用场景等

常用的两个路由守卫:router.beforeEach 和 router.afterEach

每个守卫方法接收三个参数:
	to: Route: 即将要进入的目标 路由对象
	from: Route: 当前导航正要离开的路由
	next: Function: 一定要调用该方法来 resolve 这个钩子。

在项目中,一般在beforeEach这个钩子函数中进行路由跳转的一些信息判断。判断是否登录,是否拿到对应的路由权限等等。

6、vue的双向绑定原理

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

以下是详解:

在vue初始化的时候,会调用一个方法initData,这个方法接收一个Component,通过Component.$options.data能够获取到用户传入的数据,然后对数据进行观测;

调用Observer方法,将数据传给它,然后判断数据是否被观察过了,如果没有被观测过,就new一个Observer,对该数据进行观测;

观测时分两种情况:

1、观测的数据是对象(非数组),则调用walk方法;在该方法中,遍历对象属性,使用defineReactive定义响应式变化,在defineReactive中使用Object.defineProperty重新定义数据,当用户获取数据时会调用get方法,调用时会进行依赖收集,即收集当前的watcher;当数据改变时,触发set,此时先判断当前的value和新的value是否一致,不一致的话调用notify方法,触发数据对应的依赖进行更新。

vuex 如何在modules中拿到其他modules的state_数据


2、如果观测的对象时数组,则先判断当前是否支持原型链,如果支持,将数组的_proto_指向我们改写的数组原型方法arrayMethods,这个方法主要是拦截数组的七个会改变自身数值的方法,(pushpopshiftunshiftsplicesortreverse),当用户调用数组的这些方法时,首先还是会执行原生的方法,还会通知视图更新。如果有新增的数据,还需要对新增的内容进行观测(observeArray),同时还需要对每个数组元素进行观测

vuex 如何在modules中拿到其他modules的state_前端_02

7、vue中mixin是什么,mixin冲突怎么解决

mixin文件是一个对象,可以包含vue组件的任意成分。是分发Vue组件可复用功能的非常灵活的方式,当mixin被组件使用时,所有minxin里的属性/方法会与组件里的属性/方法混合。

在Vue组件中可以有mixins属性,该属性值类型为数组。将mixin引入,作为mixins数组的元素mixins: [mixin]

当mixin中的数据、方法或任何组件选项与组件中的选项具有相同的名称时,可能会发生组件与其mixin之间的命名冲突。如果发生这种情况,则组件本身的属性将优先。

8、vue中使用v-for的key值问题

8.1 为什么需要key

主要是用来提供给DOM的diff算法,因为diff算法会比较节点上的key,如果新的节点在旧的节点中已经存在,即节点的标签和key都相同,但位置发生改变,则将元素复用并进行移动,而不会重新创建或者删除节点;

举个栗子:有ABCD四个旧节点,经过一些操作后变成DABC,此时先将新节点中的第一个节点D取出来和旧节点进行对比,发现在旧节点中有一个节点的key和D相同,那就复用旧节点的D,并将旧节点的D移动到最前面,而不需要去创建新节点;

如果没有key值,那么diff算法在比较节点时,就暴力地按照位置进行比对;

举个栗子:有ABCD四个旧节点,经过一些操作后变成DABC,此时因为没有key值,所以直接拿第一个新节点D与旧节点A对比,如果新节点D与旧节点A的标签一样,但内容不一样,则会操作DOM,将旧节点A修改为新节点D;然后拿第二个新节点A与旧节点B进行比较,如果新节点A与旧节点B标签不一样,则删除旧节点B,新建节点A,以此类推。

所以key值可以提供性能,减少操作DOM节点

8.2 为什么最好使用列表项的id作为key值而不是用index

使用列表项的id作为key值,当列表项发生删除或者增加时,各个列表项的key不会被重新渲染,如果使用index作为列表项的key,当列表项发生删除或者增加时,增加或者删除的列表项后面key都要被重新渲染一遍,所以使用列表项的id作为key值相比于使用index作为列表项的key值,性能有所提升。

举个栗子:有ABCD四个旧节点,删除节点B之后变成ACD,此时如果以index作为key值,那么新节点CD节点的key要从原来的2、3变为1、2,同时,diff算法比较时,会将旧节点的B/C与新节点C/D进行比较,发现旧节点的B©与新节点的C(D)不一样,就会操作DOM,将旧节点B©改为新节点的C(D)。最后旧节点的末尾还有一个D,直接删除;
如果是以列表项id作为key值,那么新节点ACD都可以在旧节点中找到与之具有相同key值的节点,比较第二个和第三个新节点时,旧节点CD会被依次移动到A后面,此时B被挤到最后面,比较完成后直接被删除。
由此可见,使用index作为key值与不使用key值性能是差不多的

9、为什么v-for与v-if不能连用

因为v-for的优先级高于v-if,如果既有v-for,又有v-if,那么先执行v-for,把列表项都生成出来后,再全部执行一遍v-if = false,影响性能,做了许多无用功.

10 、如果对vuex里的state状态改变是非常敏感的,可以怎么做来监听状态的改变

在引用vuex的state的组件中,使用computed计算属性定义一个变量,return Vuex中的state,然后使用watch对变量进行监听,如果state发生变化,watch就可以监听到。

11、虚拟DOM

虚拟DOM其实是一个对象,是使用对象来模拟DOM,如果在一次操作中有多次对DOM节点的更新,每次都立即更新视图会很影响性能,所以虚拟DOM不会立即去操作DOM,而是将多次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性更新到DOM树上,再进行后续操作,避免大量无谓的计算量。