• 【2022】前端面试之vue汇总


    vue响应式原理

    数据响应式原理的核心就是采用了数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。get()方法可以读取数据、收集依赖,set()方法可以改写数据,在数据变动时会对数据进行比较,如果数据发生了变化,会发布消息通知订阅者,触发监听回调,更新视图。
    watcher: 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

    defineProperty() 的缺点

    • 在对一些属性进行操作时,使用这种方法无法拦截,比如通过下标方式修改数组数据或者给对象新增属性,这都不能触发组件的重新渲染,因为 Object.defineProperty 不能拦截到这些操作。更精确的来说,对于数组而言,大部分操作都是拦截不到的,只是 Vue 内部通过重写函数的方式解决了这个问题。

    vue2.0如何监听数组或对象

    Vue 不允许动态添加根级响应式属性

    1. 官网给的建议是:使用vue.$Set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)或者vue.set
    2. vue源码里缓存了array的原型链,然后重写了这几个方法,触发这几个方法的时候会observer数据,意思是使用这些方法不用再进行额外的操作,视图自动进行更新。splice()

    区别在于Vue.set()是将set函数绑定在Vue构造函数上,this. s e t ( ) 是将 s e t 函数绑定在 V u e 原型上。 s e t 和 set()是将set函数绑定在Vue原型上。 set和 set()是将set函数绑定在Vue原型上。setset的区别

    vue.$set的实现原理

    • 如果目标是数组,直接使用数组的 splice 方法触发相应式;
    • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

    vue2.0为什么不劫持数组

    1.Obejct.definePropetry这个api,其实他是可以监听到数组下标变化的,对于js来说,数组也是Object。
    2.为什么vue不劫持数组:
    因为数组不像对象那样,即使位置错乱,key和value对应关系一般不会变,每个value更新只会造成一次set。而数组则不一样,它只能通过下标去做key,这个key是不固定的,位置变化就会造成多次set操作,vue官方也考虑到性能问题,所以没有对数组做劫持,而是将数组的7个变异方法进行重写,也就是更改了Array原型上的方法达到劫持的效果。

    vue3.0是怎么做的劫持

    1.通过使用 Proxy 对对象进行代理,从而实现数据劫持。使用Proxy 的好处是它可以完美的监听到任何方式的数据改变,唯一的缺点是兼容性的问题,因为 Proxy 是 ES6 的语法。
    2.Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢?
    判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
    监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

    为什么vue2.0不用proxy

    vue2.x之前之所以不用Proxy,主要Proxy是es6提供的新特性,兼容性不好,最主要的是这个属性无法用polyfill来兼容。Polyfill 指的是用于实现浏览器并不支持的原生 API 的代码。

    比如说 querySelectorAll 是很多现代浏览器都支持的原生 Web API,
    但是有些古老的浏览器并不支持,那么假设有人写了一段代码来实现这个功能.
    使这些浏览器也支持了这个功能,那么这就可以成为一个 Polyfill。
    
    • 1
    • 2
    • 3

    proxy的优势

    Proxy 与 Object.defineProperty 优劣对比

    1. 响应式是惰性的。
      在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。
      在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。
    2. Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的

    为什么要用 Proxy 替代 defineProperty ?

    1.defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
    Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。
    2.Proxy API的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作, 这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。
    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

    mvvm和mvc的区别

    这都是软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。

    • mvc:MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。其中 View 负责页面的显示逻辑,Model 负责存储页面的业务数据,以及对相应数据的操作。Controller 层是 View 层和 Model 层的纽带,当用户与页面产生交互的时候,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。
    • mvvm:Model代表数据模型,View代表UI视图,负责数据的展示;ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。当Model中的数据改变时会触发View层的刷新,View中用户交互操作而改变的数据也会在Model中同步。
      这种模式实现了 Model和View的数据自动同步,因此开发者只需要专注于数据的维护操作即可,而不需要自己操作DOM。

    computed和watch的区别

    对于Computed计算属性:

    • 支持缓存,只有依赖的数据发生了变化,才会重新计算
    • 不支持异步,当Computed中有异步操作时,无法监听数据的变化
    • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed
    • 在computed中,属性有一个get方法和一个set方法,当数据发生变化时,会调用set方法。
      对于watch侦听器:
    • 它不支持缓存,数据变化时,它就会触发相应的操作
    • 支持异步监听,第一个参数是最新的值,第二个是变化之前的值
    • 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
      immediate:组件加载立即触发回调函数
      deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。

    v-if和v-show的区别

    • v-if是动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显隐;
    • v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染;
    • v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改show属性值,也就是常说的display;
    • v-if适合运营条件不大可能改变;v-show适合频繁切换
    • v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下,无论首次条件是否为真,都被编译,然后被缓存,而且DOM元素保留;

    v-model 是如何实现的,语法糖实际是什么?

    (1)作用在表单元素上 : 给input绑定了v-on的value值,这个值指向了变量,并且在触发 input 事件的时候通过v-bind去动态把 message设置为目标值。
    (2)作用在组件上 :在自定义组件中,v-model 默认会利用名为 value 的 prop和名为 input 的事件
    本质是一个父子组件通信的语法糖,通过prop和$.emit实现。

    data为什么是一个函数而不是对象

    在vue中,我们想要实现组件的复用,那就需要每个组件有自己的私有数据,而不互相干扰。
    数据以函数返回值的形式定义,这样当每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
    如果是一个对象的话,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化,就会造成混乱难以维护。
    除此之外:
    1.根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况。
    2.组件实例对象data必须为函数,**目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。**采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象。
    在源码中,自定义组件会进入mergeOptions进行选项合并,进入if判断,若data类型不是function,则出现警告提示。

    说说你对vue的理解

    为什么Vue中的v-if和v-for不建议一起用?

    作用:

    • v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染
    • v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组或者对象,而 item 则是被迭代的数组元素的别名
    • 在 v-for 的时候,建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化
    • 从源码里面可以看到v-for的优先级大于v-if

    那两个都想用怎么办?

    1. 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
    2. 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环。
    3. 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
  • 相关阅读:
    2022年最新辽宁水利水电施工安全员考试题库及答案
    Codeforces 1696E. Placing Jinas 高维前缀和、组合数取模
    Spring Data JPA使用@DynamicInsert和@DynamicUpdate注解进行动态插入和更新
    vue项目启动npm install和npm run serve时出现错误Failed to resolve loader:node-sass
    (2022版)一套教程搞定k8s安装到实战 | Ingress
    AndroidStudio报错:Plugin with id ‘kotlin-android‘ not found.
    PE结构学习(3)_RVA转换成FOA
    Windows内核--WRK和真实的Windows内核源代码差多少?(1.3)
    谈一谈在两个商业项目中使用MVI架构后的感悟
    【LeetCode】No.101. Symmetric Tree -- Java Version
  • 原文地址:https://blog.csdn.net/weixin_45817780/article/details/126303545