2024前端面试题

477 阅读14分钟

这两次的面试题记录一下,有些已经忘了

vue

一、vue2和vue3的区别

  1. 性能
  • Vue 3 在性能上有显著提升。它通过优化虚拟 DOM 和更高效的响应系统,提供了更快的渲染速度和更少的内存占用。Vue 3 还引入了 Proxy 作为响应式系统的核心,比 Vue 2 的 Object.defineProperty 更高效。
  1. 语法和 API
  • Composition API: Vue 3 引入了 Composition API,这是一种新的方式来组织和复用逻辑。它与 Vue 2 的 Options API(datamethodscomputed 等)并存,允许更灵活和模块化的组件开发。Composition API 通过 setup() 函数提供响应式状态和生命周期钩子的访问。
  • Vue 2 主要依赖于 Options API,定义组件时通过 datamethodscomputed 等选项来组织代码。
  1. 响应式系统
  • Vue 3 使用 Proxy 实现响应式系统,这使得 Vue 3 能更精确地追踪依赖,减少不必要的重新渲染。相较于 Vue 2 使用的 Object.definePropertyProxy 可以处理更多的操作,并且性能更好。
  • Vue 2 使用 Object.defineProperty,在处理嵌套对象和数组时可能会遇到一些限制。
  1. TypeScript 支持
  • Vue 3 对 TypeScript 的支持更为完善。Vue 3 的整个源码是用 TypeScript 编写的,类型推导也更加准确。Composition API 特别适合 TypeScript,可以提供更强的类型安全性。
  • Vue 2 对 TypeScript 的支持相对较弱,虽然可以使用 TypeScript,但 TypeScript 的体验没有 Vue 3 那么优雅。
  1. 生命周期钩子
  • Vue 3 在生命周期钩子方面进行了一些调整和改进。例如,在 Composition API 中,生命周期钩子变成了 onMountedonUnmounted 等函数,这与 Options API 中的 mountedbeforeDestroy 等有所不同。
  • Vue 2 使用 beforeCreatecreatedmounted 等钩子函数。
  1. Fragments
  • Vue 3 支持组件有多个根节点(Fragments)。这意味着你可以在一个组件中返回多个元素,而不需要将它们包裹在一个单独的根元素中。
  • Vue 2 每个组件必须有一个单一的根元素。
  1. Teleport
  • Vue 3 引入了 Teleport 组件,用于将子组件的内容传送到 DOM 的其他位置。这在处理模态框、弹出菜单等需要将内容放置在 DOM 树不同位置的场景中非常有用。
  • Vue 2 没有原生的 Teleport 组件,通常需要通过其他手段(如 v-ifv-show)来实现类似功能。
  1. Suspense
  • Vue 3 还引入了 Suspense 组件,可以用于处理异步组件的加载状态,使得在组件加载完成之前可以显示备用内容。
  • Vue 2 没有原生的 Suspense 组件,通常需要通过自定义逻辑来处理异步数据加载。
  1. API 变化
  • Vue 3 在某些 API 和功能上做了一些调整或改进。例如,v-model 的使用方式有所变化,可以为不同的 prop 和事件提供更灵活的绑定。
  • Vue 2 使用 v-model 绑定到组件的 value prop 和 input 事件。

总结

Vue 3 在性能、语法和功能上都做了不少改进,特别是在响应式系统、TypeScript 支持和组件结构方面。然而,Vue 2 仍然是一个稳定的框架,适合现有项目和不急于迁移的情况。选择哪一版本主要取决于你的需求和现有的项目环境。

二、前端怎么进行优化

  1. 性能优化
  • 减少HTTP请求:合并CSS和JavaScript文件,使用图像精灵(sprites)和数据URI来减少请求次数。
  • 异步加载资源:使用asyncdefer属性来异步加载JavaScript文件,减少页面渲染阻塞。
  • 懒加载:对图片和其他媒体资源使用懒加载技术,仅在需要时加载。
  • 资源压缩和合并:压缩和合并CSS、JavaScript文件,使用工具如Webpack、Gulp或Parcel进行优化。
  • 浏览器缓存:利用HTTP缓存机制(如Cache-Control、ETag)来减少资源重新加载的次数。
  • 使用CDN:将静态资源托管在内容分发网络(CDN)上,以减少延迟并提高加载速度。
  1. 渲染优化
  • 减少DOM操作:减少对DOM的频繁操作,使用文档片段(DocumentFragment)等技术来批量更新DOM。
  • 使用虚拟DOM:如果使用React等库,利用虚拟DOM进行高效的DOM更新。
  • 优化CSS选择器:使用高效的CSS选择器,避免使用低效的选择器(如*)和过于复杂的层叠选择器。
  1. 代码优化
  • 模块化:将代码分成模块,使用ES6模块(importexport)或CommonJS规范,提升代码的可维护性和可重用性。
  • 使用TypeScript:使用TypeScript可以捕捉更多的编译时错误,增加代码的健壮性和可维护性。
  • 代码分割:使用动态导入(import())或类似技术进行代码分割,按需加载不同的模块和功能。
  1. 网络优化
  • HTTP/2和HTTP/3:利用HTTP/2或HTTP/3的多路复用、头部压缩等特性来提高网络传输效率。
  • 优化API请求:减少API请求次数,使用批量请求(batch requests)或缓存API响应来降低延迟。
  1. 用户体验优化
  • 响应式设计:确保网站在各种设备和屏幕尺寸下都能良好展示,使用CSS媒体查询(media queries)来实现响应式设计。
  • 可访问性(a11y) :确保网站对所有用户,包括有视觉、听觉、行动障碍的用户,都是可访问的。遵循WCAG(Web Content Accessibility Guidelines)标准。
  • 加载指示器:使用加载动画或进度条来提高用户体验,告知用户内容正在加载中。
  1. SEO优化
  • 优化关键字:确保页面的标题、描述、头部标签(H1, H2等)包含适当的关键字。
  • 结构化数据:使用JSON-LD、Microdata或RDFa等格式提供结构化数据,帮助搜索引擎更好地理解页面内容。
  1. 工具和资源
  • 性能分析工具:使用Lighthouse、WebPageTest、GTmetrix等工具来分析网站性能并获取改进建议。
  • 调试工具:利用浏览器开发者工具(DevTools)进行调试和优化,包括网络面板、性能面板等。

持续关注前端技术的发展和优化最佳实践,不断调整和优化代码和资源,以确保提供最佳的用户体验。

三、watch和watcheffect的区别

Vue 中, `watch``watchEffect`  都是用于响应式数据变化的工具,但它们的使用场景和工作方式有所不同。

1.  `watch` 

- **用法**: `watch`  是一个用于观察特定数据源(如响应式数据、计算属性等)的变化,并在变化时执行回调函数。
- **特点**:
  - 需要明确指定要观察的数据。
  - 适合于需要在数据变化时执行特定逻辑的场景。
  - 可以访问新值和旧值。

**示例**:
import { ref, watch } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});
2.  `watchEffect` 

- **用法**: `watchEffect`  是一个更为简洁的 API,用于自动追踪其内部使用的响应式数据,无需显式指定要观察的数据源。
- **特点**:
  - 适合于需要自动追踪多个响应式数据的场景。
  - 在其内部使用的任何响应式数据变化时,都会重新执行该函数。
  - 不提供旧值,只能获取新值。

**示例**:
import { ref, watchEffect } from 'vue';

const count = ref(0);

watchEffect(() => {
  console.log(`count is: ${count.value}`);
});
### 总结

- **使用  `watch` **:当你需要监控特定的数据变化,并且需要访问新旧值时。
- **使用  `watchEffect` **:当你希望自动追踪多个响应式数据,并在任何一个数据变化时重新执行逻辑时。

选择使用哪一个取决于具体的需求和场景。

四、watch监听,immediate和deep区别

Vue.js中,`watch`是一个用来监听数据变化并执行相应操作的选项。
在`watch`选项中,有两种常见的监听方式:immediate和deep。

1. **immediate:**
   - 当设置`immediate: true`时,Vue会在初始渲染组件时立即执行`handler`,
   而不需要等到数据发生变化。
   - 这在需要在组件挂载时立即执行一些操作时非常有用,比如初始化一些数据、发送网络请求等。

2. **deep:**
   - 当设置`deep: true`时,Vue会递归监听对象内部值的变化,而不仅仅是对象的引用发生变化。
   - 这在需要深度监听对象或数组内部值的变化时非常有用,比如监听对象内部属性的变化。

举个例子,假设我们有一个Vue组件,需要监听一个对象`user`的变化,
并且需要在组件挂载时立即执行操作,同时也需要深度监听对象内部值的变化,我们可以这样写:

```javascript
watch: {
  user: {
    handler(newVal, oldVal) {
      // 执行操作
    },
    immediate: true,
    deep: true
  
}

这样就实现了在组件挂载时立即执行操作,并且深度监听user对象内部值的变化。也可实现路由的监听

五、vue中路由拦截

Vue 中,路由拦截主要是通过 Vue Router 提供的导航守卫(navigation guards)来实现的。
导航守卫允许你在路由变化之前或之后执行一些逻辑,比如验证用户的身份、检查权限等。

Vue Router 提供的导航守卫有以下几种:

1. **全局守卫**:
   -  `beforeEach` :在每次路由切换之前执行。
   -  `afterEach` :在每次路由切换之后执行。

   **示例**:
import Vue from 'vue';
   import Router from 'vue-router';

   Vue.use(Router);

   const router = new Router({
     routes: [
       { path: '/login', component: Login },
       { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } },
     ],
   });

   router.beforeEach((to, from, next) => {
     if (to.matched.some(record => record.meta.requiresAuth)) {
       const isAuthenticated = false; // 这里应该是你实际的认证逻辑
       if (!isAuthenticated) {
         next({ path: '/login' });
       } else {
         next();
       }
     } else {
       next();
     }
   });

   export default router;
2. **路由独享守卫**:
   -  `beforeEnter` :在路由进入之前执行,适用于单个路由。

   **示例**:
const Dashboard = {
     template: '<div>Dashboard</div>',
     beforeEnter: (to, from, next) => {
       const isAuthenticated = false; // 这里应该是你实际的认证逻辑
       if (!isAuthenticated) {
         next({ path: '/login' });
       } else {
         next();
       }
     },
   };

   const router = new Router({
     routes: [
       { path: '/dashboard', component: Dashboard },
     ],
   });
3. **组件内守卫**:
   -  `beforeRouteEnter``beforeRouteUpdate``beforeRouteLeave` :用于组件内部的路由守卫。

   **示例**:
export default {
     name: 'Dashboard',
     beforeRouteEnter(to, from, next) {
       const isAuthenticated = false; // 这里应该是你实际的认证逻辑
       if (!isAuthenticated) {
         next({ path: '/login' });
       } else {
         next();
       }
     },
   };
总结

- **全局守卫**适合于需要在整个应用中统一处理逻辑的场景,比如权限验证。
- **路由独享守卫**适合于特定路由的处理。
- **组件内守卫**适合于组件内部的逻辑处理。

通过这些守卫,你可以灵活地控制路由的访问权限和逻辑。

六、说下pinia

PiniaVue 3 的一个状态管理库,它被设计为 Vuex 的替代品,提供了更简单和更灵活的 API。以下是一些 Pinia 的主要特点和功能:

1. **简单易用**:
PiniaAPI 设计得非常直观,使用起来比 Vuex 更加简单,特别适合新手。

2. **模块化**
:Pinia 允许你将状态分成多个 store(仓库),每个 store 可以管理自己的状态、getter 和 action。这使得状态管理更加清晰和可维护。

3. **TypeScript 支持**:
Pinia 提供了良好的 TypeScript 支持,可以帮助开发者更好地进行类型检查。

4. **持久化**:
Pinia 可以与持久化插件结合使用,轻松实现状态的持久化存储。

5. **支持 Composition API**:
Pinia 完全支持 Vue 3Composition API,使得在组件中使用状态变得更加灵活。

6. **开发者工具**:
Pinia 提供了与 Vue Devtools 的集成,方便调试和查看状态变化。

基本用法示例

首先,安装 Pinia:
npm install pinia
然后在你的 Vue 应用中创建一个 store:
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})
在组件中使用这个 store:
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
import { useCounterStore } from './stores/counter'

const counterStore = useCounterStore()
const { count, doubleCount, increment } = counterStore
</script>
以上就是 Pinia 的基本用法和一些特点。如果你有其他具体问题或想了解更多,欢迎提问!

js

1、promise的用处

  • 优雅处理异步操作,避免回调地狱。
  • 提供统一错误处理机制。
  • 可组合和链式调用,串联或并行执行异步操作。
  • 与现代 JavaScript 生态兼容,可结合 async/await 使用,且被众多框架和库广泛应用。

2、promise有哪些方法

一、Promise.all ()

  • 作用:接收一个 Promise 数组,只有当所有输入的 Promise 都成功时才成功,返回一个包含所有成功结果的数组;若有一个 Promise 被拒绝,则整体被拒绝。
  • 用途:适用于需要并行执行多个异步操作并在所有操作都成功后进行统一处理的场景,比如同时发起多个网络请求并聚合结果。

二、Promise.allSettled ()

  • 作用:接收一个 Promise 数组,当所有输入的 Promise 都已确定状态(无论是成功还是失败)后完成,返回一个数组,其中每个元素对应一个输入的 Promise,包含其状态和结果或拒绝原因。
  • 用途:可以获取所有 Promise 的最终状态,无论成功与否,适用于需要知道所有异步操作的具体结果而不关心整体是否成功的情况。

三、Promise.race ()

  • 作用:接收一个 Promise 数组,只要其中一个 Promise 完成(成功或失败),就立即完成并返回该 Promise 的结果。
  • 用途:可用于设置超时机制,当多个异步操作中有一个较快完成时就取其结果,或者在多个可能的异步事件中最先发生的事件决定后续操作。

.then()方法用于指定 Promise 成功状态(fulfilled)时的回调函数。它可以接收 Promise 成功时返回的值,并进行进一步的处理。同时,.then()可以链式调用,使得多个异步操作能够以一种简洁的方式依次执行。这种链式调用的特性使得代码更加清晰可读,并且可以方便地进行数据传递和流程控制。

总的来说,在 Promise 的方法体系中,.then()是处理 Promise 成功情况的关键方法,它与.catch()(处理失败情况)和.finally()(无论成功失败都执行)等方法一起,为 JavaScript 的异步编程提供了强大而灵活的工具。

3、js的数组有哪些方法?

一、添加和删除元素

  1. push():在数组末尾添加一个或多个元素,返回新数组的长度。
  2. pop():删除数组末尾的元素,返回被删除的元素。
  3. unshift():在数组开头添加一个或多个元素,返回新数组的长度。
  4. shift():删除数组开头的元素,返回被删除的元素。

二、查找和遍历

  1. indexOf():返回指定元素在数组中的第一个索引,如果不存在则返回 -1。
  2. lastIndexOf():返回指定元素在数组中的最后一个索引,如果不存在则返回 -1。
  3. forEach():遍历数组,对每个元素执行给定的函数。
  4. map():创建一个新数组,其结果是对原数组中的每个元素调用给定函数的结果。

三、排序和反转

  1. sort():对数组元素进行排序,默认按照字符串编码顺序排序,可传入比较函数进行自定义排序。
  2. reverse():反转数组中元素的顺序

四、拼接和切片

  1. concat():连接两个或多个数组,返回一个新数组。
  2. slice():从数组中提取一部分,返回一个新数组。

4、js的数组里想删除里面某个元素?

  • splice():通过指定索引和删除数量来删除元素。
  • filter():创建一个新数组,过滤掉不需要的元素。不直接修改原数组,需重新赋值。使用时要注意可能的副作用和性能影响,尤其是在处理大型数组时。选择合适的方法取决于具体的需求和场景。

5、js的数组里面2个数怎么交换位置

  • 使用临时变量,先将一个值存入临时变量,再进行交换。
  • 利用 ES6 的解构赋值特性,更加简洁地实现交换。