前端面试题总结(Vue篇)(数据驱动,组件系统)
一.Vue的优点
1.轻量级框架:只关注视图层,是一个构建数据的视图集合,基础大小只有几十kb
2.双向数据绑定:更容易操作数据
3.组件化:实现了HTMl的封装和复用,在构建单页面应用方面有独特的优势
4.视图,数据,结构分离:更改数据更为简单,不需要逻辑代码修改
5.虚拟dom:不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式
6.内封装比较完善,有很多方便得指定和过滤器
二.双向数据绑定原理,vue3如何实现双向绑定
1.vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变
2.核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法
3.Object.defineProperty()方法:
1)Object.defineProperty(obj, prop, descriptor) ,这个语法内有三个参数,分别为 obj (要定义其上属性的对象) prop (要定义或修改的属性) descriptor (具体的改变方法)
2)简单地说,就是用这个方法来定义一个值。当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,又用到了它里面的set方法
const obj = {}
Object.defineProperty(obj, 'name', {
get: function() {
console.log('调用了get方法')
}
set: function(newVal) {
console.log('调用了set方法,方法的值是' + newVal)
}
})
obj.name // => 调用了get方法
obj.name = 'hello' // => 调用了set方法,方法的值是hello
Vue3使用proxy来进行数据双向绑定
优点:1.可以动态监听数组的变化
2.可以劫持整个对象,并且返回一个新的对象,不用担心原对象会被操作
三.diff算法
1.首先我们要知道,当数据发生变化时,vue更新节点的过程:
根据真实DOM生成一颗virtual DOM,当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。
2.virtual DOM的概念:virtual DOM是将真实的DOM的数据抽取出来,以对象的形式模拟树形结构
3.diff算法的比较方式:只在同层进行比较
4.patch函数:
function patch (oldVnode, vnode) {
// some code
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
const oEl = oldVnode.el // 当前oldVnode对应的真实元素节点
let parentEle = api.parentNode(oEl) // 父元素
createEle(vnode) // 根据Vnode生成新元素
if (parentEle !== null) {
api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素
api.removeChild(parentEle, oldVnode.el) // 移除以前的旧元素节点
oldVnode = null
}
}
// some code
return vnode
}
先通过sameVnode函数来判断新旧节点是否值得比较,如果有key、tag、data等属性有不同,那就直接替换,如果相同,值得比较则使用patchVnode函数
这个函数做了以下事情:
1)找到对应的真实dom,称为el
2)判断Vnode和oldVnode是否指向同一个对象,如果是,那么直接return
3)如果他们都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
4)如果oldVnode有子节点而Vnode没有,则删除el的子节点
5)如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
6)如果两者都有子节点,则执行updateChildren函数比较子节点
比较子节点其实就是通过遍历oldNode去和newNode做匹配和比较,根据newNode的节点位置更改真实DOM的节点位置,如果有新的节点,就按相应的index添加到真是DOM,如果遍历结束,oldNode存在匹配不到newNode的节点,就将其移除
四.computed以及与watch的区别
computed有缓存,不能异步,有get set方法
watch 没缓存,可以异步
多个数据计算得出的属性推荐使用computed
五.key的作用
key作为虚拟DOM的唯一标示,key的作用主要是为了高效的更新虚拟DOM,方便diff算法进行同层比较
六.vue组件之间通信
1.父组件向子组件传值:props
<p :form='"parentForm"></p>
//父组件写法
props: {
form: Object,
}
//子组件写法
组件中的数据共有三种形式:data、props、computed
2.子组件向父组件传值,通过事件的形式:$emit
change() {
this.$emit('change-parent', data)
}
//子组件写法,data是传递的数据
<p @changeParent="change-parent"></p>
changeParent(data) {
console.log(data)
} //data是子组件传的值
//父组件写法
3.$emit
和$on
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级
4.vuex
:vuex是vue的数据状态统一管理工具,是全局的,相应的,在这不做详细介绍
5.$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性,也就是被props声明过的将被除去attrs列表
6.provide
和inject
:解决了跨级组件间的通信问题,允许一个祖先组件向其所有子孙后代注入一个依赖,由祖先组件用provide
代替data
声明要传递的变量,后代组件用inject
代替props
接受变量,但是这种传值方式不是相应的
7.ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA; // comA就是comA的this
}
}
</script>
$parent
和$children
的使用方法和ref类似,分别访问父组件和子组件
8.bus
:bus是额外的插件,主要用于兄弟组件之间传值
import Vue from 'vue'
const bus = new Vue()
export default bus
// 传值的组件
import bus from '@/utils/bus'
mounted () {
bus.$on('testA', this.testA)
},
testA () {
console.log('由A组件调用')
},
// 接受值的组件
import bus from '@/utils/bus'
mounted () {
bus.$emit('testA')
},
七.vuex的原理
Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。
八.keep-alive的作用
keep-alive的作用是缓存,当页面刷新时,页面的数据和当前状态也会刷新,缓存可以让u页面再次活跃时直接时之前的状态,保留组件状态或避免重新渲染
九.vue.use()原理和作用
安装vue.js的插件,Vue.use() 中的参数必须是一个function函数或者是一个Object对象,如果是对象的话,必须在对象中提供一个install方法。之后会将 Vue 作为参数传入。
1.参数为函数时,函数的参数是Vue对象
2.参数为对象时,它提供的install方法中参数是Vue对象