前端知识梳理与总结(六)--面试常见问题之VUE

  1. MVVM原理
  • 在 MVVM 中,最核心的也就是数据双向绑定,Vue 中的数据劫持。
    Vue 内部使用了 Object.defineProperty() 来实现双向绑定,通过这个函数可以监听到 set 和 get 的事件。

2.vue双向数据绑定的原理

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

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

  • 需要一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者。
  • 需要一个指令解析器Compile,根据模板替换数据,获取和解析每一个节点及指令,以及绑定相应的更新函数,添加监听数据的订阅者(将相关指令对应初始化成一个订阅者Watcher)。
  • 需要 一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图。
    在这里插入图片描述

3.vue生命周期

vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期。
beforeCreate/created、beforeMount/mounted、beforeUpdate/updated、beforeDestroy/destroyed

  • beforeCreate:在这个生命周期函数执行的时候,data和methods中的数据都还没有初始化
  • created:data和methods中的数据已经初始化完成,调用methods中的方法或者操作data中的数据最早只能在created中操作,挂载阶段还没开始,$el 属性目前不可见。
  • beforeMount:此函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中
  • mounted:el 被新创建的 vm.$el 替换,并挂载到实例上
  • beforeUpdate:此函数执行的时候,页面中显示的数据还是旧的,此时,data中的数据是新的,页面尚未和最新的数据保持同步。
  • updated:事件执行时,页面和data数据已经保持同步,都是最新的。
  • beforeDestroy:vue实例进入到了销毁阶段,但是实例身上的所有data和methods以及指令等都处于可用状态,此时还没有真正执行销毁的过程。
  • destroyed:当执行到此函数时,组件已经被完全销毁了,所有data和methods以及指令等都已经不可用状态
    在这里插入图片描述

4.axios底层实现(ajax)

原理:基于Promise的异步Ajax请求库
axios 原理还是属于 XMLHttpRequest, 因此需要实现一个ajax。
还需要promise对象来对结果进行处理。

  • axios是createInstance函数传入了defaultConfig参数返回的值。
  • createInstance底层根据默认设置 新建一个Axios对象, axios中所有的请求[axios, axios.get, axios.post等…]内部调用的都是Axios.prototype.request,将Axios.prototype.request的内部this绑定到新建的Axios对象上,从而形成一个axios实例。新建一个Axios对象时,会有两个拦截器,request拦截器,response拦截器。

5.组件传值
父传子:通过v-on绑定一个变量名称,然后再子组件里面用props接收。
子传父:子组件里面绑定一个方法,用$emit(函数名’func’,需要传递的参数)把参数传递给父组件。父组件里面定义好这个函数。<child @func="getMsgFormSon"></child>

6.虚拟DOM树/抽象节点VNode

Vue.js将DOM抽象成一个以JavaScript对象为节点的虚拟DOM树,以VNode节点模拟真实DOM,可以对这颗抽象树进行创建节点、删除节点以及修改节点等操作,在这过程中都不需要操作真实DOM,只需要操作JavaScript对象后只对差异修改。

7.v-if和v-show的区别

举例:
v-show指令,元素隐藏时,只是增加了display:none的样式,而 v-if 指令则直接通过把该行删除来实现了隐藏效果。

  • 相同点:都能动态的控制dom元素的显示与隐藏
  • 异同点:v-if在每次完成显示和隐藏功能时,需要不断的在dom树上完成节点的创建和删除操作,v-show则直接通过修改display样式的属性值来完成。
  • 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗
  • 使用场景:当一个元素会被频繁的显示和影藏时,使用v-show。如果在运行时条件很少改变,则使用 v-if 较好。

8.v-for中key 值的作用

key的作用主要是为了高效的更新虚拟DOM
为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素
在用v-for更新已渲染的元素列表时,会使用就地复用的策略,其实就是说列表数据修改的时候,它会根据key值去判断某个项是否修改,如果修改了就重新渲染,否则就复用之前的元素。

9.vue中插槽的作用

插槽就是Vue实现的一套内容分发的API,将元素作为承载分发内容的出口。
作用域插槽:作用域插槽其实就是带数据的插槽。可以把组件上的属性/值,在组件元素上使用!<template slot-scope="soped"></template>

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。
具名插槽:就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。父组件通过 v-slot:[name] 的方式指定到对应的插槽中。

10.vuex的功能
在这里插入图片描述

11.Vue-router 实现原理(Hash模式和History模式)

vue-router通过hash与History interface两种方式实现前端路由
更新视图但不重新请求页面,是前端路由原理的核心

  • hash(#)是URL 的锚点,代表的是网页中的一个位置,改变#后面的部分,浏览器会滚动到相应位置,不会重新加载网页。
  • HTML5 History API提供了一种功能,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;
在vue-router中,它提供mode参数来决定采用哪一种方式。
当你选择mode类型之后,程序会根据你选择的mode 类型创建不同的history对象(HashHistory或HTML5History或AbstractHistory)

更新页面流程如下:
1.HashHistory.push() 将新路由添加到浏览器访问历史的栈顶

1 $router.push() //调用方法
2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
3 History.transitionTo() //监测更新,更新则调用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替换当前app路由
6 vm.render() //更新视图

2.History interface还可以对浏览器历史记录栈进行修改

window.history.pushState(stateObject, title, URL)

12.vue中兄弟组件的通信方式

//新建一个js文件, 这里叫做 bus.js;
//文件内容:
import Vue from 'vue';  
export default new Vue();
//然后在你需要触发的 组件中引入   import   bus  from  '文件路径'执行:bus.$emit('触发名称', 传输的数据 )
//------------------------------------------------------------------
//最后在你需要的通信的另一个组件中   mounted生命周期钩子中执行如下:
bus.$on('触发名称', res => {
    //写你需要的方法
})

13.理解Vue的响应式系统
任何一个 Vue Component 都有一个与之对应的 Watcher 实例。
Vue 的 data 上的属性会被添加 getter 和 setter 属性。
当 Vue Component render 函数被执行的时候, data 就会被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
data 被改动时(主要是用户操作), 即被写, setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新。

14.路由懒加载

  • vue异步组件
  • es提案的import()
  • webpack的require,ensure()

15.vue中关于数组、对象的响应式问题

由于 JavaScript 的限制, Vue 不能检测以下变动的数组:

1. 利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue

2. 修改数组的长度时,例如: vm.items.length = newLength

为了避免第一种情况,以下两种方式将达到像 vm.items[indexOfItem] = newValue 的效果, 同时也将触发状态更新:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)
避免第二种情况,使用 splice:

example1.items.splice(newLength)

只要在 data 中声明的基本数据类型的数据,基本不存在数据不响应问题,所以重点是数组和对象在vue中的数据响应问题,vue可以检测对象属性的修改,但无法监听数组的所有变动及对象的新增和删除,只能使用数组变异方法及$set方法

Object.defindProperty虽然能够实现双向绑定了,但是还是有缺点,只能对对象的属性进行数据劫持,所以会深度遍历整个对象,不管层级有多深,只要数组中嵌套有对象,就能监听到对象的数据变化无法监听到数组的变化,Proxy就没有这个问题,可以监听整个对象的数据变化,所以用vue3.0会用Proxy代替definedProperty。

  • 对于数组来说:对数组中所有能改变数组自身的方法,如 push、pop 等这些方法进行重写
    在这里插入图片描述
第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。

第二步:对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。

第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型。

注:直接修改数组中的元素视图上不会更新,如果要触发修改,可以用splice替换或者用Vue.set(object,key,value)
set 这个方法只能用于data 里面的子数组对象,而不能直接用于data (这个根数据)或者vue 实例
除了正常返回push、unshift和splice(插入)函数执行的result结果外,还通知了变化!ob.dep.notify() 所以对新增的数组元素实现了响应式的变化。

16.Proxy
针对上述问题,Vue3中利用Proxy来代替Object.defineProperty
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。var proxy = new Proxy(target,handler)

17.diff算法
在这里插入图片描述

  • Tree Diff 是对树每一层进行遍历,找出不同
  • Component Diff 是数据层面的差异比较
如果都是同一类型的组件(即:两节点是同一个组件类的两个不同实例,比如:<div id="before"></div><div id="after"></div>),按照原策略继续比较Virtual DOM树即可
如果出现不是同一类型的组件,则将该组件判断为dirty component,从而替换整个组件下的所有子节点。

当节点处于同一层级时,Diff提供三种DOM操作:删除、移动、插入。

首先执行patch函数,patch函数接收两个参数oldVnode和Vnode分别代表新的节点和之前的旧节点,判断两节点是否值得比较,值得比较则执行patchVnode。

如果两个节点都是一样的,那么就深入检查他们的子节点。如果两个节点不一样那就说明Vnode完全被改变了,就可以直接替换oldVnode。
虽然这两个节点不一样但是他们的子节点一样怎么办?别忘了,diff可是逐层比较的,如果第一层不一样那么就不会继续深入比较第二层了。

然后执行patchVnode函数,这个函数做了以下事情:

  • 找到对应的真实dom,称为el
  • 判断Vnode和oldVnode是否指向同一个对象,如果是,那么直接return
  • 如果他们都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
  • 如果oldVnode有子节点而Vnode没有,则删除el的子节点
  • 如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
  • 如果两者都有子节点,则执行updateChildren函数比较子节点,这一步很重要

在这里插入图片描述
四种匹配模式,匹配上了相应的指针移动。

18.Computed和watch

  • 计算属性具有缓存。计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 所依赖的量都没有发生改变,多次访问 计算属性会立即返回之前的计算结果,而不必再次执行函数。
  • 侦听器 watch 是侦听一个特定的值,当该值变化时执行特定的函数。例如分页组件中,我们可以监听当前页码,当页码变化时执行对应的获取数据的函数。watch 的依赖则是单个的,它每次只可以对一个变量进行监控。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值