krislee94 / docs

日常文档整理
1 stars 0 forks source link

Vue2 和Vue3面试题 #57

Open krislee94 opened 1 year ago

krislee94 commented 1 year ago

介绍一下Vue 2和Vue 3的主要区别和改进之处。 Vue 3的Composition API是什么?它与Vue 2的Options API有什么不同? 什么是响应式数据,在Vue中如何实现响应式数据? Vue 2中的v-bind和v-model指令有什么作用?Vue 3中是否有类似的指令? Vue 2和Vue 3中的虚拟DOM是如何工作的?有哪些优化措施? 什么是Vue Router?请解释一下Vue 2和Vue 3中的路由配置和导航守卫的不同之处。 在Vue 2和Vue 3中如何使用Vuex进行状态管理?是否有任何改进或新特性? Vue 3中的推导式props(Props Composition)是什么?它与Vue 2中的props有什么不同? Vue 2和Vue 3中的异步组件(Async Components)是如何工作的?有什么优点? 在Vue 3中,什么是Teleport(瞬移)?有什么用途和优势? 如何在Vue 2和Vue 3中实现服务端渲染(SSR)? Vue 3的性能改进有哪些方面?你在项目中如何优化Vue的性能? Vue 2和Vue 3中的动态组件(Dynamic Components)如何使用?有什么不同之处? 请解释Vue 2和Vue 3中的key属性的作用和用法。 什么是Vue Test Utils?你在写Vue组件单元测试时会用到哪些工具和方法?

krislee94 commented 1 year ago

介绍一下Vue 2和Vue 3的主要区别和改进之处。

  1. 性能改进。 Vue3引入了一个新的响应是系统。Proxy,代替了Vue2中的Object.defineProperty。 新的响应式系统比老版本更高效。能够在许多情况下提供更好的性能。
  2. 尺寸更小、Vue3的文件大小相对较小。包采用了monorepo。实现了从模块管理到包管理的转变。
  3. Composition API。Vue3引入了Compositon API,这是一种全新的API风格。
  4. 更好的TS支持。
  5. 更好的响应式处理,Vue 3的响应式系统设计更灵活,支持响应式对象的深层次监听和触发,使得开发者可以更方便地对数据的变化进行响应。
  6. Teleport Vue3引入了Teleport组件。它可以将组件的内容渲染到DOM结构中的任何位置,而不仅仅是当前组件的父元素内部。
  7. 单文件组件的改进:Vue3在单文件组件汇总对编译器的改进。使得开发者具备更大的灵活性和可扩展性。
krislee94 commented 1 year ago

Vue 3的Composition API是什么?它与Vue 2的Options API有什么不同?

在Vue 2中,使用Options API来定义组件。Options API是一种以对象形式传递选项的方式,通过在组件的对象中定义不同的选项(如data、methods、computed、watch等)来描述组件的属性、方法和响应式数据等。

在Vue 3中,Composition API 提供了更加灵活、模块化和可组合的方式来编写组件的逻辑。开发者可以将相关的逻辑组织成composables(可组合的函数),每个composable都封装了一部分逻辑,并可以在组件中进行复用。

  1. 组织方式:Options API 是将不同的选项(data, methods ,computed等)分开定义,而Composition API是通过定义多个composable函数来组织逻辑,并按需导入使用。
  2. 可读性和维护性:由于Composition API 将逻辑按功能组织成多个composables,使代码更加模块化。可多行和可维护性更强。想必之下,Options API在处理复杂逻辑时可能导致组件选项对象庞大且难以阅读。
  3. 逻辑复用。omposition API使得逻辑可以更容易地进行复用。开发者可以将一些共享的逻辑提取为composable,并在多个组件中进行复用。
  4. 设计模式支持:Composition API支持更多的设计模式,如函数式编程、插件和mixin等。这些方式可以更好地实现代码的重用和封装。
krislee94 commented 1 year ago

什么是响应式数据,在Vue中如何实现响应式数据?

  1. 在Vue 2中,响应式数据的实现涉及两个关键对象:Observer和Watcher。

在vue2中每个响应式对象(例如data对象)都会被封装成一个Observer对象,Observer对象会为对象的每个属性创建一个Dep(依赖)对象,并通过Object.defineProperty对每个属性进行劫持。当属性被读取或修改时,Observer会通知相关的依赖(Watchers)进行更新。 Watcher对象用于表示一个依赖,例如Vue组件模版中使用的表达式、计算属性或侦听器。每个Watcher对象都会和Dep对象简历关联。当Watcher被触发时,它会将自身添加相关Dep的依赖列表中。并在依赖的值发生变化时,更新操作。

// Vue 2的响应式数据实现
class Vue {
  constructor(options) {
    this._data = options.data;
    this._observer = observe(this._data);
    // ...
  }
}

function observe(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return;
  }

  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key]);
  });
}

function defineReactive(obj, key, val) {
  observe(val); // 递归劫持子属性

  const dep = new Dep(); // 创建一个依赖对象

  Object.defineProperty(obj, key, {
    get() {
      // 添加当前Watcher到依赖列表中
      if (Dep.target) {
        dep.addWatcher(Dep.target);
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      // 依赖发生变化时,通知依赖的Watcher进行更新
      dep.notify();
    }
  });
}

class Dep {
  constructor() {
    this.watchers = [];
  }

  addWatcher(watcher) {
    this.watchers.push(watcher);
  }

  notify() {
    this.watchers.forEach(watcher => watcher.update());
  }
}

// 创建一个Vue实例
const vm = new Vue({
  data: {
    message: 'Hello Vue'
  }
});

在Vue3中,响应式数据的视线采用了Proxy对象。并且将Observer和Watcher概念合并成为了一个新的Reactivity模块。

  1. Proxy:Proxy是一个代理对象,在Vue3的响应式系统重用户代理目标对象。通过使用Proxy,可以拦截目标对象的读取、写入等操作,并触发相关的更新。roxy对象的代理操作被定义在ReactiveHandlers中。
  2. Effect:Effect是Vue 3中替代Watcher的概念。Effect函数用于定义响应函数,并自动追踪函数中依赖的响应式数据。当依赖的数据发生变化时,Effect函数会重新执行。Effect函数的定义和依赖追踪操作被定义在ReactiveEffects中。

// Vue 3的响应式数据实现
class Vue {
  constructor(options) {
    this._data = options.data;
    this._proxy = reactive(this._data);
    // ...
  }
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      // Track:追踪依赖
      track(target, key);
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const oldValue = target[key];
      // Trigger:触发更新
      const result = Reflect.set(target, key, value);
      if (oldValue !== value) {
        trigger(target, key);
      }
      return result;
    }
  });
}

function track(target, key) {
  // 添加依赖
  const effect = activeEffectStack[activeEffectStack.length - 1];
  if (effect) {
    let depsMap = targetMap.get(target);
    if (!depsMap) {
      depsMap = new Map();
      targetMap.set(target, depsMap);
    }
    let dep = depsMap.get(key);
    if (!dep) {
      dep = new Set();
      depsMap.set(key, dep);
    }
    if (!dep.has(effect)) {
      dep.add(effect);
      effect.deps.push(dep);
    }
  }
}

function trigger(target, key) {
  // 触发更新
  const depsMap = targetMap.get(target);
  if (!depsMap) {
    return;
  }
  const dep = depsMap.get(key);
  if (!dep) {
    return;
  }
  dep.forEach(effect => {
    effect();
  });
}

class Effect {
  constructor(fn) {
    this.fn = fn;
    this.deps = [];
  }

  update() {
    // 重新执行
    this.fn();
  }

  run() {
    // 追踪依赖
    activeEffectStack.push(this);
    this.fn();
    activeEffectStack.pop();
  }
}

const targetMap = new WeakMap();
const activeEffectStack = [];

// 创建一个Vue实例
const vm = new Vue({
  data: {
    message: 'Hello Vue'
  }
});
krislee94 commented 1 year ago

Vue 2中的v-bind和v-model指令有什么作用?Vue 3中是否有类似的指令?

有的。在Vue 2和Vue 3中,v-bind和v-model指令的作用是相同的,并且在Vue 3中v-bind指令可以使用简写语法。

krislee94 commented 1 year ago

Vue 2和Vue 3中的虚拟DOM是如何工作的?有哪些优化措施?

Vue 2和Vue 3都使用虚拟DOM来优化页面的渲染性能,其工作原理类似,通过对比差异来减少DOM操作的数量。同时,Vue 3还引入了一些新的优化措施,如批量异步更新、静态标记、编译优化和长列表优化等,进一步提升了性能和用户体验。

先大概说说虚拟dom

  1. 当数据发生变化时,Vue会首先生成一个新的虚拟DOM树。
  2. Vue会将心的虚拟DOM树和旧的虚拟DOM树进行对比,找出差异(diff算法)
  3. 根据差异,Vue会将需要更新的部分进行具体的DOM操作,以此达到更新视图的目的。

差别

  1. 批量异步更新:Vue2中的更新是同步的。及时有多个数据变化。每次变化都会引起重新渲染。为了减少不必要的渲染。Vue3使用批量异步更新策略。将多次数据变化合并到一个更新周期中,并使用异步队列来缓冲和刷新DOM更新。
  2. Vue 3使用静态标记来优化静态节点的渲染。在编译阶段,Vue会对模板进行静态分析,标记出静态节点,这些节点的结构在每次渲染期间保持不变,因此可以在渲染时跳过差异的比较和更新操作
  3. 编译优化:Vue 3引入了新的编译器,可以生成更优化的渲染函数。编译器可以根据模板的特性进行优化,例如静态提升、静态节点提取、事件侦听器缓存等,以生成更高效的渲染函数。
  4. 长列表优化:对于具有大量数据的列表,Vue 3引入了虚拟滚动和可视区域渲染的机制。它可以只渲染当前可见的部分,并在滚动时动态更新显示内容,大大减少渲染的节点数量和处理的复杂性。Vue 3的长列表优化通过组件和虚拟滚动机制实现。组件将列表项的内容传送到可视区域之外的位置,减少节点数量。组件通过计算可视区域内需要渲染的列表项,来动态渲染和更新列表内容。这些优化措施可以提高长列表的性能和用户体验。
krislee94 commented 1 year ago

什么是Vue Router?请解释一下Vue 2和Vue 3中的路由配置和导航守卫的不同之处。

  1. Vue2中,使用VueRouter实例来配置路由。通过调用router.map()方法可以定义路由规则。指定URL路径对应的组件。
    const router = new VueRouter({
    routes: [
    {
      path: '/home',
      component: Home
    },
    // ...
    ]
    })

    这种方式基本上没有变化,仍然适用于Vue 3中的vue-router。

同时Vue3使用createRouter

const router = createRouter({
  routes: [
    {
      path: '/home',
      component: Home
    },
    // ...
  ]
})

beforeEach 、afterEach 保持不变。 beforeEnter --> beforeRouteEnter , beforeRouteUpdate.新增导航守卫函数用户当前路由复用的时候执行逻辑、beforeRouteLeave组件离开的时候执行。 beforeRouteUpdate和beforeRouteLeave函数。这些变化使得Vue 3的路由配置和导航守卫更加灵活和强大。

krislee94 commented 1 year ago

在Vue 2和Vue 3中如何使用Vuex进行状态管理?是否有任何改进或新特性?

vue2中使用Vuex.Store. Vue3中,使用createStore. 同时Vue3中更推荐使用Pinia。 想比vuex中更精简。

krislee94 commented 1 year ago

Vue 3中的推导式props(Props Composition)是什么?它与Vue 2中的props有什么不同?

Vue3推导式的props是一种新的特性,用于根据组件的上下文推导出props的默认值,它可以显著简化组件的props的配置,提高代码的可读性和可维护性。

vue2中需要通过声明props并指定默认值来定义组件的props。这种方式有时候会使得组件的props声明变得冗长。特别是当props的默认值依赖于其他props或计算属性时。

Vue 3中的推导式props提供了一种更简洁的方式来定义props的默认值。它允许我们在props配置中使用JavaScript表达式,并在运行时根据组件的上下文来推导出props的默认值。

vue2写法:

props: {
  count: {
    type: Number,
    default: function () {
      return this.initialCount * 2
    }
  }
},

vue3写法

const props = defineProps({
    count:{
type:number,
default: ()=> initialCount * 2
}
})
krislee94 commented 1 year ago

Vue 2和Vue 3中的异步组件(Async Components)是如何工作的?有什么优点?

在Vue 2和Vue 3中,异步组件是一种延迟加载的组件,它们在需要时才会被加载和渲染,而不是在初始加载时就被包含在应用程序中。

Vue3

import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))

Vue2

Vue.component('AsyncComponent', (resolve) => {
  import('./AsyncComponent.vue').then((module) => {
    resolve(module.default)
  })
})
  1. 减少初始加载时间
  2. 提高性能和资源利用:只有当组件需要被渲染时,才会加载相应的异步组件,避免了不必要的组件加载和渲染,节省了资源和提高了性能。特别是对于复杂的应用程序,减少初始加载的组件数量可以加快应用程序的整体渲染速度。
  3. 分割代码:异步组件使得我们可以根据需要将应用程序的代码分割成小块,只加载当前页面所需的组件,而不是一次性加载整个应用程序的所有组件。这样可以减少下载的文件大小,提高网页的加载速度。

具体使用参考:https://cn.vuejs.org/guide/components/async.html

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

搭配 Suspense 使用

<Suspense>
└─ <Dashboard>
   ├─ <Profile>
   │  └─ <FriendStatus>(组件有异步的 setup())
   └─ <Content>
      ├─ <ActivityFeed> (异步组件)
      └─ <Stats>(异步组件)

在这个组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有 ,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。

krislee94 commented 1 year ago

在Vue 3中,什么是Teleport(瞬移)?有什么用途和优势?

在Vue 3中,Teleport(瞬移)是一种新特性,它允许我们将一个组件的内容在DOM中的任意位置进行渲染,而不受组件的嵌套层级限制。简单来说,Teleport可以将组件的内容“瞬移”到指定的目标位置。

Teleport通过标签在组件模板中使用,其中通过to属性指定目标位置的CSS选择器或DOM元素引用。例如:

<teleport to="#target">
  <!-- 组件内容 -->
</teleport>

Teleport有以下几个主要用途和优势:

提供更灵活的布局控制:Teleport能够将组件的内容渲染到DOM中的任意位置,不受组件嵌套层级的限制。这使得我们能够更精确地控制组件在页面中的布局,并实现更高级的布局效果。

解决组件插槽限制:在Vue 2中,插槽(slot)在组件嵌套层级中有一定的限制,父组件无法将内容插入到子组件的任意位置。而使用Teleport,我们可以轻松地将内容瞬移到子组件内的任意位置。

模态框和弹出窗口:Teleport非常适合用于创建模态框和弹出窗口等需要将组件的内容渲染到页面上特定位置的场景。通过使用Teleport,我们可以方便地将这些组件的内容渲染到内的某个位置,从而实现浮动弹出窗口的效果。

改善CSS层叠和样式控制:使用Teleport可以将组件的内容渲染到页面的某个位置,这可以避免嵌套层级带来的CSS层叠和样式控制问题。我们可以在目标位置上直接应用样式,而不必考虑组件嵌套层级的影响。

krislee94 commented 1 year ago

Vue 3中实现服务端渲染(SSR)?

创建一个Vue应用实例,并将其导出。

创建一个服务端入口文件,用于处理服务端请求并渲染Vue应用。

在服务端入口文件中,使用createRenderer创建一个新的渲染器。

在服务端入口文件中,使用renderToString或者renderToStream方法来将Vue应用实例渲染成HTML字符串或流。

配置服务端打包的构建脚本,将Vue应用打包成一个用于服务端渲染的bundle文件。

在服务器中启动一个Node.js实例,并将请求交给服务端入口文件处理。

krislee94 commented 1 year ago

Vue 2和Vue 3中的动态组件(Dynamic Components)如何使用?有什么不同之处?

在Vue 2中,你可以使用Vue的内置指令来创建一个动态组件。你需要在父组件中定义一个变量来存储要渲染的子组件名称,然后使用元素,并通过:is属性绑定这个变量来动态渲染组件。

<template>
  <div>
    <button @click="activeComponent = 'ComponentA'">Component A</button>
    <button @click="activeComponent = 'ComponentB'">Component B</button>
    <component :is="activeComponent"></component>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  components: {
    ComponentA,
    ComponentB
  },
  data() {
    return {
      activeComponent: ''
    };
  }
};
</script>

而在vue3中。

在Vue 3中,使用动态组件的方式略有不同。Vue 3引入了一个新的内置组件,并更改了动态组件的语法。你可以使用元素来渲染和挂载其他组件。以下是一个Vue 3中使用动态组件的示例:

krislee94 commented 1 year ago

请解释Vue 2和Vue 3中的key属性的作用和用法。

在Vue中,key属性是用于帮助Vue识别和跟踪与每个VNode(虚拟节点)相关的元素的唯一身份标识。它的作用是在Vue进行重新渲染时,能够准确地确定哪些元素需要被更新、重用或移除。

krislee94 commented 1 year ago

什么是Vue Test Utils?你在写Vue组件单元测试时会用到哪些工具和方法?

Vue Test Utils是一个官方提供的用于测试Vue组件的工具库,它提供了一组用于创建、操作和断言Vue组件行为的API。

在编写Vue组件的单元测试时,我通常会使用以下工具和方法:

mount:用于挂载Vue组件,返回一个包装器对象,可以用于访问和操作组件实例。 shallowMount:类似于mount,但是只渲染组件的当前级别,而不渲染子组件。这在测试一个组件时,如果需要模拟子组件行为,可以更加高效。 find:用于在包装器中查找匹配选择器的元素或组件。可以用于断言组件是否正确渲染了特定的元素或组件。 setProps:用于设置组件的props。可以用于模拟不同的props值,并断言组件在不同props下的行为。 setData:用于设置组件的data。可以用于模拟不同的组件状态,并断言组件在不同状态下的行为。 trigger:用于触发组件的事件。可以用于模拟用户交互,并断言组件在不同交互下的行为。 nextTick:用于等待Vue的异步更新完成。可以在断言之前等待组件的异步更新完成,以确保断言的准确性。 expect:用于断言组件的行为和状态。可以使用expect来断言组件是否正确渲染、是否触发了特定的事件、是否更新了特定的数据等。