面试题记录

100 阅读9分钟

Vue3

watch和watchEffect的区别

watchwatchEffect 都能响应式地执行回调函数。区别是追踪响应式依赖的方式不同:

  • watch 只追踪明确定义的数据源,不会追踪在回调中访问到的东西;默认情况下,只有在数据源发生改变时才会触发回调;watch需要传入指定的属性;watch 可以访问侦听数据的新值和旧值。
  • watchEffect 会初始化执行一次,在副作用发生期间追踪依赖,自动分析出侦听数据源;watchEffect需要传入回调函数;watchEffect 无法访问侦听数据的新值和旧值。

简单一句话,watch 功能更加强大,而 watchEffect 在某些场景下更加简洁。

Vue3响应式拦截底层大致原理

  1. 创建 Proxy 对象:当 Vue 3 需要创建一个响应式对象时,它会使用 new Proxy(target, handler) 来创建一个代理对象。其中 target 是要被代理的目标对象(通常是原始数据对象),handler 是一个对象,定义了拦截目标对象各种操作的行为。

  2. 定义 handler:在 handler 对象中,Vue 3 会定义一些陷阱函数(trap functions),如 get 和 set。这些函数会在代理对象上的相应操作被调用时执行。

    • get 陷阱函数:当访问代理对象的属性时,get 函数会被调用。Vue 3 在这里收集依赖关系,并将这些依赖关系存储在内部的数据结构中。这样,当数据发生变化时,可以通知相关的依赖进行更新。
    • set 陷阱函数:当修改代理对象的属性时,set 函数会被调用。Vue 3 在这里触发属性的变化,并通知所有依赖于该属性的观察者进行更新。这通常涉及重新运行与该属性相关的计算属性或方法,并更新 DOM。
  3. 依赖收集和触发更新:Vue 3 使用一个内部的数据结构来跟踪每个属性的依赖关系。当属性被访问时,Vue 3 会收集当前的观察者(如计算属性、渲染函数等),并将它们与属性关联起来。当属性被修改时,Vue 3 会触发这些观察者的更新。

  4. 优化和性能:Vue 3 还进行了一些优化,以减少不必要的计算和更新。例如,它会检查属性是否真的发生了变化,并且只会触发依赖于已更改属性的观察者的更新。

Vue3传送门

传送门(Teleport)是一种组件,用于将子组件的内容渲染到 DOM 的其他位置。这在需要将组件的内容插入到 DOM 结构中不在其父组件的层次结构中的情况特别有用,例如模态框、弹出菜单、提示框等。

<template>
  <div>
    <h1>这是主内容</h1>
    <button @click="showModal = true">显示模态框</button>

    <teleport to="body">
      <div v-if="showModal" class="modal">
        <h2>这是模态框!</h2>
        <button @click="showModal = false">关闭模态框</button>
      </div>
    </teleport>

    <style>
      .modal {
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        background: white;
        padding: 20px;
        border: 1px solid #ccc;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      }
    </style>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showModal: false,
    };
  },
};
</script>
  • <teleport to="body">
    • to 属性指定了目标位置。在这个例子中,我们选择了 body,意味着模态框的内容将直接添加到文档的 body 元素中。
    • 目标元素可以是任何有效的 CSS 选择器,如 body.class-name#id-name 等。
    • 在大量动态内容的场景下,使用 Teleport 可能会影响性能。

Vue中如何实现缓存页面

<keep-alive> <component :is="currentComponent"></component> </keep-alive>
  1. 动态组件:使用 <component :is="currentComponent"> 来实现动态组件,根据 currentComponent 的值加载不同的组件。
  2. <keep-alive> 组件:将动态组件包裹在 <keep-alive> 中,这样被切换的组件会被缓存,其状态(如输入内容、滚动位置等)也会保持。
<keep-alive> 组件可以通过 includeexclude 属性来控制缓存的组件。
  • include:只有匹配这个属性中列出的组件名称的组件才会被缓存。
  • exclude:任何组件名称与这个属性中列出的名称匹配的组件都不会被缓存。
<template>
  <div>
    <button @click="currentComponent = 'ComponentA'">Component A</button>
    <keep-alive include="ComponentA">
      <component :is="currentComponent"></component>
    </keep-alive>
  </div>
</template>

在这个示例中,只有 ComponentA 会被缓存,而 ComponentBComponentC 切换时则不会被缓存。

使用 v-slot 自定义插槽

你也可以利用 <keep-alive> 提供的插槽特性来自定义其行为。例如,可以为缓存的组件状态添加自定义逻辑。

<template>
  <div>
    <button @click="currentComponent = 'ComponentA'">Component A</button>
    <button @click="currentComponent = 'ComponentB'">Component B</button>

    <keep-alive>
      <component :is="currentComponent" v-slot="{ key }">
        {{ key }}
      </component>
    </keep-alive>
  </div>
</template>

注意事项

  • 在使用 <keep-alive> 时,要注意缓存的组件可能会持有状态,因此在设计组件时要考虑到这一点。
  • 只适合用于不受较高动态更新频率影响的组件,例如表单、列表等。

总结

  • <keep-alive> 用于高效缓存动态组件,提升应用性能。
  • 可以通过 include 和 exclude 控制缓存组件。
  • 支持组件的 activated 和 deactivated 生命周期钩子。

JS\TS

JS基础类型

Number、String、Boolean、undefined、Null

数组去重

使用扩展运算符和new Set() [...new Set(arr)]

判断数据类型

  1. 使用 typeof 运算符

typeof 运算符可以用来检查基本数据类型(如字符串、数字、布尔值等)。

javascript复制代码
console.log(typeof "Hello"); // "string"
console.log(typeof 10);       // "number"
console.log(typeof true);     // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null);     // "object" (这是一个历史遗留问题)
console.log(typeof Symbol());  // "symbol"
console.log(typeof BigInt(123)); // "bigint"
  1. 使用 instanceof

instanceof 操作符可以用于检测对象是否是特定构造函数的实例。

javascript复制代码
class Person {}
const person = new Person();

console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
  1. 使用 Array.isArray()

用来判断一个变量是否为数组。

javascript复制代码
const arr = [1, 2, 3];
const notArr = { key: 'value' };

console.log(Array.isArray(arr));    // true
console.log(Array.isArray(notArr)); // false
  1. 使用 Object.prototype.toString.call()

这种方法可以更准确地识别各种对象类型,包括内置对象和自定义对象。

javascript复制代码
console.log(Object.prototype.toString.call("Hello")); // "[object String]"
console.log(Object.prototype.toString.call(10));      // "[object Number]"
console.log(Object.prototype.toString.call([]));       // "[object Array]"
console.log(Object.prototype.toString.call({}));       // "[object Object]"
console.log(Object.prototype.toString.call(null));     // "[object Null]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
  • 使用 typeof 判断基本类型。
  • 使用 instanceof 判断对象实例。
  • 使用 Array.isArray() 判断是否为数组。
  • 使用 Object.prototype.toString.call() 获取更精确的对象类型。

js与ts区别

  1. JS是弱类型;TS是强类型。
  2. TS是JS的超集。主要用于解决大项项目的代码复杂性。
  3. TS的数据类型有:布尔值、数字、字符串、数组、 元组(tuple)、枚举(enum)、any、void、null和undefined、never、object ,unknown;JS的数据类型有:string,number,boolean,null,undefined,object,array,function,symbol
  4. TS需要将代码编译为js代码才能在浏览器中执行;JS可以直接在浏览器中运行
  5. TS有类型断言 变量 as 类型; 可以直接告知该变量的实际类型

ts定义接口

在 TypeScript 中,可以使用 interface 关键字来定义接口。接口用于描述对象的形状,包括其属性及方法的类型。 可以使用extends继承别的interface

ts定义接口的interface,如何设置为全部可选

使用 Partial 内置工具类型

 interface Person { name: string; age: number; } 
 // 使用 Partial 来使所有属性可选 
 const person: Partial<Person> = { name: "Alice" // age 属性是可选的 };

ts中的泛型

泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。

在数组中取第二到第五个元素

slice()  方法返回一个新的数组对象,这一对象是一个由 start 和 end 决定的原数组的浅拷贝(包括 start,不包括 end),其中 start 和 end 代表了数组元素的索引。原始数组不会被改变。

let a = [0,1,2,3,4,5,6]
// 传值,采用左闭右开 [1,5)
let b = a.slice(1,5)

Threejs

Threejs如何优化渲染性能

在使用 Three.js 处理 3D 模型时,优化性能是非常重要的,尤其是在构建复杂场景或需要支持低功耗设备的情况下。以下是一些常见的优化策略:

  1. 减少多边形数量
  • 简化模型:使用 3D 建模软件(如 Blender、Maya 等)来简化模型的网格,减少多边形数量。
  • LOD(细节层次) :实现不同细节层次的模型,根据相机距离动态加载不同分辨率的模型。
  1. 使用纹理而不是几何体
  • 使用纹理:尽量使用纹理贴图来模拟复杂表面,而不是增加更多的几何体细节。
  • 合并纹理:将多个小纹理合并成一张大纹理(例如使用纹理图集),以减少材质切换的次数。
  1. 降低材质和光源数量
  • 减少材质数量:尽量使用统一的材质,以减少渲染时的状态切换。
  • 限制光源:使用较少的动态光源,考虑使用环境光或全局光照替代阴影和高光。
  1. 缓存和重用对象
  • 实例化:对于重复的模型,可以使用 THREE.InstancedMesh 来减少开销。
  • 对象池:使用对象池管理可重用的对象,避免频繁创建和销毁对象。
  1. 优化动画
  • 骨骼动画:尽量使用骨骼动画而不是顶点动画,这样可以提高性能。
  • 动画混合:使用动画混合(Animation blending)来平滑过渡,减少动画切换的频繁性。
  1. 限制视野范围
  • 剔除干扰:开启视锥体剔除(frustum culling),确保只渲染在摄像机视野内的对象。
  • 使用遮挡剔除:使用遮挡剔除技术来避免渲染被其他物体遮挡的部分。
  1. WebGL 状态管理
  • 减少状态切换:尽量减少材质、着色器之间的切换,以提高性能。
  • 压缩纹理:使用压缩纹理格式(如 KTX、DDS),以减少内存占用和提升加载速度。
  1. 调整渲染设置
  • 降低渲染质量:根据需求调整渲染设置,例如减少抗锯齿、阴影质量等。
  • 选择适当的渲染循环:根据场景需求选择 requestAnimationFrame 或者固定时间步长更新。
  1. 使用工具和库
  • 使用优化工具:使用工具(如 draco)进行模型压缩和优化。
  • Three.js 自带的优化功能:利用 Three.js 中的一些优化特性,如 BufferGeometry 等。