前端面试项目拷打

Axios相关

1.在Axios二次封装时,具体封装了哪些内容,如何处理请求拦截和响应拦截?

axios二次封装的目的:为了统一处理请求和响应拦截器、错误处理、请求超时、请求头配置等,提高代码可维护性和复用性。

  • 首先创建axios实例,配置基础的URL和请求超时时间。
  • 请求拦截器:用于在发送请求之前进行一些预处理,像,检查本地存储中是否存在token,当前请求的URL是否在白名单中,如果token存在且URL不在白名单中,会在请求头中添加token.
  • 响应拦截器:用于在接收到响应之后进行一些处理。根据后台返回的code数据,使用ElMessage显示信息,如果token错误或过期,会清除本地存储中的相关数据,并将用户重定向到登录页面。
  • 导出Axios实例:在其他部分使用,可以通过这个实例发出HTTP请求,不需要每次都重新配置Axios 。如在API请求封装文件导入了axios的二次封装,getCode函数内部调用了request.post方法发出请求。request.post就是axios文件中配置好的实例post方法。

2.如何处理全局的错误码返回的,比如token失效、权限不足等情况?
++在响应式拦截器中,根据后端返回的code进行不同处理++

3.Aixos封装是否支持取消请求,如果一个用户在短时间内连续点击多个请求?

++使用axios.CancelToken来取消重复请求,防止用户多次点击按钮导致接口请求重复。++

Vuex相关

4.如何实现Vuex数据持久化的?

Vuex 默认存储在内存中,刷新页面后会丢失数据。我使用 vuex-persistedstate 进行持久化存储,存入 localStoragesessionStorage。刷新后将动态路由添加到main.js中。

vuex持久化是通过localStorage实现的,具体来说,是在状态初始化时从localStorage中读取数据,并在状态变化时将新的状态保存到localStorage中,这种做法可以确保即使在页面刷新后,vuex的状态依然保持不变。

在mutation中,通过设置动态菜单的函数,动态导入vue组件并设置路由组件,实现了动态路由的添加,在mutation中监听状态变化,然后将新的状态保存在LocalStorage中。

5.在Vuex中如何涉及store结构

使用 模块化 (modules) 的方式,将 Vuex 按功能拆分。

6.Vuex和Pinia有什么区别?

vuex通过一个store对象来管理所有状态,使状态管理变得集中和统一

pinia:分离模式,每个组件都可以拥有自己的store实例,提高性能。没有modules配置,体积也更小。

路由相关

1.前置路由守卫的作用?router.beforeEach()

用户导航到某个路由之前进行身份验证或其他逻辑判断.

重定向:用户尝试访问非登录页面且token不存在,则重定向到登录页面,如果用户已经登录(token存在)尝试访问登录页面时,则重定向到首页。

2.Aiosx Token校验的作用和路由守卫结合的原因:

axios设置请求拦截器来添加token,在响应式拦截器中处理token过期的情况,主要时为了确保每个API请求都带有正确的认证信息。

将前置路由守卫和Axios Token结合使用的原因:

  1. 细粒度控制:路由守卫提供了更细粒度的访问控制,可以在用户尝试访问某个页面之前就阻止未授权的访问,而不必等到 API 请求失败。
  2. 用户体验:通过路由守卫,可以防止用户在尝试访问受保护的页面时看到加载中的状态或错误信息,直接重定向到登录页面可以提高用户体验。

3.路由重定向的实现 redirect

用于在用户登录后根据存储在本地的路由信息决定重定向到哪个页面。如果本地没有存储路由的信息,则重定向到登录页面。

组件库的使用:

1.Element Plus和Vant的主要区别是什么?在什么场景下使用它们?

Element Plus:使用于PC端,基于vue3的UI组件库。依赖 Vue 3 生态,如 setup()Composition API

Vant:适用于移动端。内置 Flex 布局,支持 Vue 2Vue 3。

2.如何在同一个项目中同时兼容PC端和移动端的UI组件?

方法 1:使用不同的组件库(Element Plus + Vant)

方法 2:使用响应式 CSS + flex/grid

复制代码
.container {
  display: flex;
  flex-direction: column;
}

@media screen and (min-width: 768px) {
  .container {
    flex-direction: row;
  }
}

方法 3:使用 rem + vw 进行适配

复制代码
html {
  font-size: calc(100vw / 10);
}

页面根据屏幕大小自动缩小,无需手动调整组件。

二维码支付

1.如何使用 Qrcode 生成二维码?如何优化二维码的清晰度?

我使用 qrcode 库来生成二维码

优化清晰度

  • errorCorrectionLevel: "H"(最高容错等级,防止二维码损坏后无法识别)
  • scale: 8(增大像素密度,避免模糊)
  • margin: 1(减少周围白边)

2.在支付过程中,你是如何保证数据安全性的?

  • HTTPS 加密 → 确保支付信息不被劫持。
  • Token 验证 → 在支付请求中附带 JWT,后端验证后才能处理支付请求。
  • 签名机制 → 前端请求支付接口时,后端返回 签名字符串,防止数据篡改。
  • 定期刷新 Token → 避免 Token 被滥用。
Vite相关

1.为什么选择 Vite 作为构建工具?相较于 Webpack,它的优势是什么?

webpack会对所有模块进行打包操作,配置复杂,需要编写大量配置文件,功能丰富,适合大型复杂的项目。

vite没有打包的步骤,而是直接启动一个开发服务器,按需加载模块,响应速度提高。大部分无需自己写配置文件。轻量和速度优势,适合中小型项目。

2.vite的快速冷启动和即时热更新

快速冷启动:指在vite启动开发服务器时,能够迅速完成初始化并启动服务,使得开发者可以立即开始开发工作。

即时热更新:当代码发生变化时,vite能够实时地将这些变化推送到浏览器中,无需手动刷新页面,允许开发者在编写代码时能够立即看到效果从而加快开发速度和迭代效率。

3.webSocket

WebSocket是HTML5引入的一项技术,用于在Web应用程序中实现实时双向通信。WebSocket允许服务器主动向客户端推送数据,不需要客户端发起请求。

特点:

  • 实时双向通信:一旦建立了WebSocket连接,客户端和服务器可以通过该连接进行双向的实时数据传输。
  • 持久化连接:WebSocket连接保持打开状态,允许客户端和服务器之间进行持续通信,无需频繁建立连接。
  • 高效性:通过减少HTTP请求的次数和降低网络延迟,WebSocket提供了更高效和实时的通信方式。

vite在开发服务器和浏览器之间建立了一个持久的双向通信通道(websocket),通过者通道,开发服务器可以实时地将代码变化推送给浏览器。

异步处理

1.在 API 调用中,你是如何处理请求失败或超时的?

在axios实例中设置了timeout属性为10000毫秒(10s),意味着如果请求在10s内没有完成,会自动触发超时错误。超时错误会进入响应拦截中。

  • 设置 timeout,超时自动取消
  • try-catch 处理异常,显示友好错误提示

2.async/await.then().catch() 方式的区别是什么?

async/await → 语法简洁,可读性高
then().catch() → 适用于 Promise 链式调用

3.如果多个 API 需要并行调用,你会如何优化?

使用 Promise.all() 并行执行多个 API:提高性能,减少等待时间。

复制代码
const [user, orders] = await Promise.all([
  axios.get("/api/user"),
  axios.get("/api/orders"),
]);
性能优化

1.KeepAlive缓存?

<keep-alive>是一个内置组件,用于缓存组件实例,避免在切换时重复渲染和销毁,对于性能优化特别有用,尤其在用户频繁切换页面或者组件的情况下,可以减少不必要的DOM操作和计算,提高用户体验。

用法:<keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,当组件再次变得活跃状态时,会复用这些缓存的实例,而不是重新创建。

在Vue Router中,通过路由元信息(meta)来控制哪些路由下的组件需要<keep-alive>缓存

复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      meta: { keepAlive: true } // 标记该路由下的组件需要被缓存
    },
    {
      path: '/bar',
      component: Bar
    }
  ]
});

在App.vue或者布局组件中使用<keep-alive>并结合路由的meta信息来决定是否缓存。

复制代码
<template>
  <div id="app">
    <router-view v-if="$route.meta.keepAlive" key="$route.path"></router-view>
    <keep-alive>
      <router-view v-if="!$route.meta.keepAlive" key="$route.path"></router-view>
    </keep-alive>
  </div>
</template>

生命周期钩子:

别<keep-alive>包裹的组件会有特殊的生命周期钩子:

activated:当组件被激活(从缓存中取出并显示到页面)时调用。

deactivated:当组件被停用(从界面移除并缓存起来)时调用。

这意味着原本的mounted和destroyed钩子在被缓存的组件不会被调用,因为组件实例没有真正地被创建或销毁。

2.懒加载非核心模块时,具体是如何实现的,有没有遇到预加载或依赖加载的问题?

定义:路由懒加载也叫延迟加载,在需要的时候进行加载,随用随载。把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,更加高效。

为什么需要懒加载:如果没有很多页面都放在同一个js文件中,运用webpack打包后文件将异常大,进入页面时,需要加载的内容多,时间长,会出现长时间的白屏。运用懒加载可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。

懒加载做了什么事:将路由对应的组件打包成一个个的js代码块,只有这个路由被访问时才加载对应组件,否则不加载。

非懒加载示例:

复制代码
// 非懒加载
import Home from '@/components/Home'
 
const routes = [
  {
    path: '/home',
    name: 'home',
    component: Home
  }
]

三种方式实现懒加载

vue异步组件

vue-router配置路由,使用vue的异步组件技术,实现按需加载,在这种情况下每一个组件就会生成一个js文件,不能分类指定chunkName

复制代码
// vue异步组件
{
  path: '/home',
  name: 'home',
  component: resolve => require(['放入需要加载的路由地址'], resolve)
}

ES6推荐方式import()

直接将组件引入的方式,import是Es6的一个语法标准

使用魔法注释/* webpackChunkName: 'ImportFuncDemo' */ 打包到一个js文件里。

复制代码
import Vue from 'vue';
import Router from 'vue-router';
// 官网可知:下面没有指定webpackChunkName,每个组件打包成一个js文件。
const Foo = () => import('../components/Foo')
const Aoo = () => import('../components/Aoo')
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
// const Foo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Foo')
// const Aoo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Aoo')
export default new Router({
    routes: [
        {
            path: '/Foo',
            name: 'Foo',
            component: Foo
        },
        {
            path: '/Aoo',
            name: 'Aoo',
            component: Aoo
        }
    ]
})

webpack提供的require.ensure()实现懒加载

可以实现按需加载,多个路由指定相同的chunkName,会合并打包成一个js文件。

chunkName用于命名生成的懒加载文件

复制代码
import Vue from 'vue';
import Router from 'vue-router';
const HelloWorld=resolve=>{
//@指向src目录 这个路径是需要懒加载的组件路径
		require.ensure(['@/components/HelloWorld'],()=>{
			resolve(require('@/components/HelloWorld'))
		})
	}
Vue.use('Router')
export default new Router({
	routes:[{
	{path:'./',
	name:'HelloWorld',
	component:HelloWorld
	}
	}]
})
相关推荐
花生侠16 分钟前
记录:前端项目使用pnpm+husky(v9)+commitlint,提交代码格式化校验
前端
一涯24 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调31 分钟前
记一次 Vite 下的白屏优化
前端·css
1undefined232 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
WaiterL1 小时前
一文读懂 MCP 与 Agent
前端·人工智能·cursor