专栏首页用户8771298的专栏2021vue面试题+答案
原创

2021vue面试题+答案

2021vue面试题+答案

vue视频教程系列:

Vue3+ElementPlus+Koa2 全栈开发后台系统

视频课程:点击观看

完整课程目录:点击查看

Vue3.0高阶实战:开发高质量音乐Web app

视频课程:点击观看

完整课程目录:点击查看

VUE全面教学+VUE开源项目超级实战:

视频课程:点击观看

完整课程目录:点击查看

最新Vue.JS教程快速入门到项目实战(Vue3/VueJS技术详解)

视频课程:点击观看

完整课程目录:点击查看

最新最全前端毕设项目(小程序+VUE+Noed+React+uni app+Express+Mongodb)

视频课程:点击观看

完整课程目录:点击查看

v-show 与 v-if 有什么区别?

v-if真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。

所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

computed 和 watch 的区别和运用的场景?

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

Vue 组件通讯有哪几种方式

  1. props 和$emit 父组件向子组件传递数据是通过 prop 传递的,子组件传递数据给父组件是通过$emit 触发事件来做到的
  2. $parent,$children 获取当前组件的父组件和当前组件的子组件
  3. $attrs 和$listeners A->B->C。Vue 2.4 开始提供了$attrs 和$listeners 来解决这个问题
  4. 父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。(官方不推荐在实际业务中使用,但是写组件库时很常用)
  5. $refs 获取组件实例
  6. envetBus 兄弟组件数据传递 这种情况下可以使用事件总线的方式
  7. vuex 状态管理

Vue 的生命周期方法有哪些 一般在哪一步发请求

beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问

created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。这里没有$el,如果非要想与 Dom 进行交互,可以通过 vm.$nextTick 来访问 Dom

beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。

mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点

beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁(patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程

updated 发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器端渲染期间不被调用。

beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行善后收尾工作,比如清除计时器。

destroyed Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

activated keep-alive 专属,组件被激活时调用

deactivated keep-alive 专属,组件被销毁时调用

异步请求在哪一步发起?

可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。

如果异步请求不需要依赖 Dom 推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

  • 能更快获取到服务端数据,减少页面 loading 时间;
  • ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

相关代码如下

import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
import { isObject } from "./util"; // 工具方法
​
export function reactive(target) {
  // 根据不同参数创建不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
  if (!isObject(target)) {
    return target;
  }
  const observed = new Proxy(target, baseHandler);
  return observed;
}
​
const get = createGetter();
const set = createSetter();
​
function createGetter() {
  return function get(target, key, receiver) {
    // 对获取的值进行放射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回当前对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {
      console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {
      console.log("属性值被修改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此方法
  set, // 当修改属性时调用此方法
};

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

相关代码如下

import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
import { isObject } from "./util"; // 工具方法
​
export function reactive(target) {
  // 根据不同参数创建不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
  if (!isObject(target)) {
    return target;
  }
  const observed = new Proxy(target, baseHandler);
  return observed;
}
​
const get = createGetter();
const set = createSetter();
​
function createGetter() {
  return function get(target, key, receiver) {
    // 对获取的值进行放射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回当前对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {
      console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {
      console.log("属性值被修改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此方法
  set, // 当修改属性时调用此方法
};

为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性(Vue 为什么不能检测数组变动 )。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组

push();
pop();
shift();
unshift();
splice();
sort();
reverse();
复制代码

由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。

Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。 Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

computed 的实现原理

computed 本质是一个惰性求值的观察者。

computed 内部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立刻求值,同时持有一个 dep 实例。

其内部通过 this.dirty 属性标记计算属性是否需要重新求值。

当 computed 的依赖状态发生改变时,就会通知这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,

有的话,会重新计算,然后对比新旧值,如果变化了,会重新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。)

没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)

Vue 中的 key 到底有什么用?

key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)

diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。

更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:

function createKeyToOldIdx(children, beginIdx, endIdx) {
  let i, key;
  const map = {};
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key;
    if (isDef(key)) map[key] = i;
  }
  return map;
}

v-model 的原理?

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'>
    
相当于
​
<input v-bind:value="something" v-on:input="something = $event.target.value">
复制代码

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

父组件:
<ModelChild v-model="message"></ModelChild>
​
子组件:
<div>{{value}}</div>
​
props:{
    value: String
},
methods: {
  test1(){
     this.$emit('input', '小红')
  },
},

v-model 的原理?

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'>
    
相当于
​
<input v-bind:value="something" v-on:input="something = $event.target.value">
复制代码

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

父组件:
<ModelChild v-model="message"></ModelChild>
​
子组件:
<div>{{value}}</div>
​
props:{
    value: String
},
methods: {
  test1(){
     this.$emit('input', '小红')
  },
},

什么是 MVVM?

Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表

MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用

(1)View 层

View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。

(2)Model 层

Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。

(3)ViewModel 层

ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。

MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

我们以下通过一个 Vue 实例来说明 MVVM 的具体实现,有 Vue 开发经验的同学应该一目了然:

(1)View 层

<div id="app">
    <p>{{message}}</p>
    <button v-on:click="showMessage()">Click me</button>
</div>
​

(2)ViewModel 层

var app = new Vue({
    el: '#app',
    data: {  // 用于描述视图状态   
        message: 'Hello Vue!', 
    },
    methods: {  // 用于描述视图行为  
        showMessage(){
            let vm = this;
            alert(vm.message);
        }
    },
    created(){
        let vm = this;
        // Ajax 获取 Model 层的数据
        ajax({
            url: '/your/server/data/api',
            success(res){
                vm.message = res;
            }
        });
    }
})
​

(3) Model 层

{
    "url": "/your/server/data/api",
    "res": {
        "success": true,
        "name": "IoveC",
        "domain": "www.cnblogs.com"
    }
}

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 面试题答案

    get:GET是通过URL提交数据,因此GET可提交的数据量就跟URL所能达到的最大长度有直接关系

    学长冷月
  • java面试题及答案2021,java2021最新面试题及答案

    String x = "string"; String y = "string"; String z = new String("string"); Syste...

    一诺千金
  • php面试题及答案

    答案:Safe_mode是php的安全模式。开启之后,主要会对系统操作、文件、权限设置等方法产生 影响,主要用来应对webshell。以下是受到影响的一些函数:

    wangxl
  • Js面试题__附答案

    JavaScript是客户端和服务器端脚本语言,可以插入到HTML页面中,并且是目前较热门的Web开发语言。同时,JavaScript也是面向对象编程语言。

    一墨编程学习
  • 面试题目及答案

    1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] de...

    zhang_derek
  • 2021react面试题附答案

    组件是 React 应用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的部分。每个组件彼此独立,而不会影响 UI 的其余部分。

    it优课
  • (面试题)python面试题集锦-附答案

      GIL是python的全局解释器锁,在一个进程中如果有多个线程执行,其中一个线程在执行的时候会霸占python解释器(加锁即GIL),那么其他线程就不能执行...

    py3study
  • 面试题:70道Spring面试题和答案

    Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标...

    搜云库技术团队
  • java面试题2019_java面试题及答案_java面试题库

    1、一个.java源文件中是否可以包括多个类(不是内部类)?有什么限制? 2、Java有没有goto? 3、&和&&的区别? 4、switch语句能否作用在by...

    用户7886150
  • vue面试题+答案,2021前端面试

    MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范

    it优课
  • 前端面试题Vue答案

    追问:computed 和 watch 应用场景? 关键词 computed+缓存

    酷走天涯
  • Unity面试题(包含答案)

    在主线程运行的同时开启另一段逻辑处理,来协助当前程序的执行,协程很像多线程,但是不是多线程,Unity的协程实在每帧结束之后去检测yield的条件是否满足。

    bering
  • Spring MVC 面试题和答案

    Spring MVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model,View,Controller ...

    田维常
  • 8.6练习面试题答案

    1. 成员内部类:定义在类的里面,方法的外面,就称为成员内部类,使用的多一些,在类里面的,生命周期要长一些

    软件小生活
  • 8.2练习面试题答案

    字符流和字节流的使用非常相似,但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)然后通过缓冲区再操作文件

    软件小生活
  • vue必会面试题+答案

    React是pull的方式侦测变化,当React知道发生变化后,会使用Virtual Dom Diff进行差异检测,但是很多组件实际上是肯定不会发生变化的,...

    it优课
  • 【面试必备】Swift 面试题及其答案

    答案:optional类型被用来表示任何类型的变量都可以表示缺少值。在Objective-C中,引用类型的变量是可以缺少值得,并且使用nil作为缺少值。基本的数...

    编程怪才-凌雨画
  • 50道CSS面试题(附答案)

    标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin 低版本IE盒子模型:宽度=内容宽度(content+bo...

    用户1093975
  • Java面试分享(题目+答案)

    Java高级架构

扫码关注云+社区

领取腾讯云代金券