面试题相关

1、浏览器输入URL发生了什么?

1、url解析,判断是搜索内容还是url链接
2、缓存查找、DNS解析、进入网络请求阶段
3、经历三次握手建立TCP连接
4、服务器响应返回数据
5、经历四次挥手断开TCP连接
6、浏览器解析渲染

2、浏览器如何渲染页面

1、html渲染成DOM树
2、css渲染成CSSOM树
3、两者结合生成Render树
4、计算布局,绘制页面的所有节点

3、有什么方法可以保持前后端实时通信

1、长轮询、短轮询(小型应用,实时性不高 )
2、WebSocket (微信、网络互动等)
3、iframe(客服通信等)
4、SSE(金融股票数据)

4、说一说跨域是什么?如何解决跨域问题?

跨域:受浏览器的同源策略影响,浏览器不能解析其他网站的脚本
解决跨域:jsonp,proxy(代理),线上:Nginx,WebSocket

5、$nextTick作用

修改data中数据后需要立即获取dom的值,不能获取到更新后的值,将获取dom值的方法放在$nextTick中,等数据更新完节点渲染完成才会触发事件,获取到的为最新的节点,$nextTick本质是返回一个promise
使用场景:在created()中操作DOM,则将操作DOM的方法放在$nextTick

6、HashRouter 和 HistoryRouter

HashRouter 和 HistoryRouter都是前端路由的两种模式
区别:
hash模式在url中存在#,history中不存在
hash通过winddow.onhashchange获得url的hash值来进行后续操作
history通过history.pushstate来实现页面窗口的跳转且不刷新页面
hash不需要后端配合,history需要后端配合否则刷新会出现404页面

7、cookie、localStorage、sessionStorage区别

cookie:一旦浏览器关闭储存的数据就会丢失 ,储存空间比较小,只有4kb左右,一般用来解决http无状态,服务端会在响应头中添加set-cookie,设置一个或多个cookie,浏览器接收到会自动储存在浏览器内存中,下次发送请求的时候会自动添加到cookie头部
localStorage:只有在手动清除的时候数据才会被清除
sessionStorage:关闭浏览器的时候数据就会被清除

8、深拷贝和浅拷贝

区别:
浅拷贝只复制了引用,修改新对象时会影响原来对象
深拷贝会创造一个一模一样的对象,修改新对象时不会影响原来对象
实现:
浅拷贝

let a = [1,2,3,4];
let b = a;
b[0] = 5;
console.log(a,b)
//[5,2,3,4]  [5,2,3,4] 

深拷贝

//1、扩展运算符
let a = [1,2,3,4];
let b = [...a];
b[0] = 5;
console.log(a,b)
//[1, 2, 3, 4]  [5, 2, 3, 4]

//2、通过递归实现拷贝
let a = [1,2,3,4];
let b = JSON.parse(JSON.stringify(a));
b[0] = 5;
console.log(a,b)
//[1, 2, 3, 4]  [5, 2, 3, 4]

9、params和query传参

1、query传参,参数通常用于传递非敏感信息,它们会显示在URL中,可以被用户看到,并且可以被浏览器历史记录保存。

// 使用 router.push
this.$router.push({ path: 'search', query: { q: 'some query' } });
// 或者使用 router.replace
this.$router.replace({ path: 'search', query: { q: 'some query' } });

2、params参数不会显示在URL中,它们不会出现在浏览器的历史记录中,除非你在路由配置中设置了params: true。于传递敏感信息或不希望用户看到的参数很有用。params一般用在动态路由,params传参时要指定路由的名称,而不是路径,因为只有命名路由才支持params。

动态路由
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})
传参
this.$router.push({ name: 'user', params: { id: 123 } });
this.$router.replace({ name: 'user', params: { id: 123 } });

10、防抖节流

防抖:多次触发,只执行最后一次(相当于回城)
节流:规定时间内,只触发一次(相当于技能cd)
场景
防抖在连续触发事件只触发一次回调的场景有:
1、搜索框输入文本请求,只需要用户在最后一次输入完成后进行回调
2、手机号、邮箱输入验证
3、实时聊天
节流在间隔一段时间执行一次回调的场景有:
1、滚动加载,加载更多或滚动到底部监听
2、窗口大小调整
3、按钮点击事件
4、鼠标移动事件(懒加载)

11、居中的几种方式

1、flex布局

display:flex;
justify-content:center;   /*水平居中*/
align-items:center;  /*垂直居中*/
height: 100vh; /*或者其他需要的高度 */

2、grade布局

display:grade;
place-items:center;/* 水平和垂直都居中*/
height: 100vh; /*或者其他需要的高度 */

3、定位

.parent { 
position: relative;
height: 100vh; 
}

.child {
position: absolute;
top: 50%;
left: 50%;
transform:translate(-50%, -50%);
}

12、数组相关的方法

1、添加元素

.push() 从数组末尾添加一个或多个元素,并返回新的长度
.unshift() 从数组开头添加一个或多个元素,并返回新的长度

2、删除元素

.pop() 删除数组最后一个元素,并返回删除的元素
.shift()删除数组第一个元素,并返回删除的元素
.splice() 通过删除、添加、替换来改变数组的内容,splice(start,deleteCount, ...items) ,返回被删除元素组成的数组
.slice() 截取数组片段,返回新数组不修改原数组,slice(start, end)

3、排序和翻转

.sort()  对数组进行排序
.reverse() 翻转数组中的元素

4、查找元素

.find() 查找第一个满足条件的元素
.findIndex() 查找第一个满足条件元素的索引
.indexOf() 从前向后查找,返回满足条件的第一个元素索引
.lastIndexOf() 从后向前查找,返回满足条件的第一个元素索引

5、遍历和过滤

.forEach()  无返回值,仅执行回调函数,若回调中修改元素的值,原数组同步修改
.map() 返回新数组,原数组不会改变,回调需显示return结果,否则新数组显示undefined
.filter() 返回新数组,数组中包含所有通过条件的测试元素

6、合并数组

.concat() 返回新数组,不会修改原数组
...    扩展运算符 返回新数组,不会修改原数组

13、Promise

promise是js中用来处理异步的方案,有三个状态:padding(等待)、fulfilled(成功)、rejected(失败)
1、创建一个promise对象,通过resolvereject 来更改状态
2、在thencatah中处理成功或者失败的状态
promise的组合

promise.all() 处理多个promise,直到所有都完成
promise.race  处理多个promise,直到其中一个完成
使用async、await语法糖来简化异步代码

14、async、await

基于promise的异步编程语法糖,简化异步代码的书写
核心:
async声明异步函数,await暂停等待promise返回结果,避免嵌套回调。
统一使用try、catch捕获异常也可以使用.then来捕获异常。

15、图片懒加载

核心原理:
延迟加载非可视区图片,仅当图片即将进入用户可视区时才加载,从而减少初始加载资源,提高页面性能。
实现思路:
1、初始化img图片是,不直接给src设置真实的图片地址,而是将图片存放在自定义的属性data-src或data-lazy中。
2、监听页面滚动或窗口大小变化,判断图片是否即将进入视口。
3、当检测到图片进入预设区,将自定义属性中的真实地址赋值给src,浏览器自动发起请求并加载图片。
4、图片加载完成后,移除懒加载相关的监听和类名,减少性能消耗。

16、vuex和pinia区别

1、模块化设计

vuex存在state、mutations、getters、actions、modules五个模块进行管理。
pinai有state、getters、actions三个模块设计,pinia比vuex简化了流程。

2、设计架构

vuex采用全局单例模式,通过一个store对象来管理所有状态。
pinia采用了分离模式,每个组件都有自己的store实例,通过在组件中创建store实例来管理状态。

3、体积和性能

vuex体积较大,性能相对稳定可靠,是vue.js提供的状态管理库。
pinia体积相对较小,且性能较好,因为他采用了es6新的语法和数据处理方式。

4、TypeScript支持

Vuex 对 TypeScript 的支持较弱,需要手动声明类型,类型推导不够直观。
Pinia 原生支持 TypeScript,自动推导 state、getters、actions 的类型,减少类型声明代码,开发体验更流畅。

17、vue2与vue3区别

1、响应式机制不同
vue2使用object.defineProperty实现,需要提前遍历对象属性。

 存在缺点:
  无法监听对象新增 / 删除的属性(需用this.$set手动处理)
  无法监听数组通过索引修改的元素
  初始化是要递归遍历所有属性,大对象性能损耗明显

vue3使用proxy代理整个对象,解决上述问题

自动监听对象新增 / 删除属性
原生支持数组索引修改和length变化
懒代理机制,只有访问属性时才会递归,性能更优

2、代码组织方式的差异

vue2使用选项式 API(Options API),代码按data、methods、computed等选项划分,逻辑分散
vue3:新增组合式 API(Composition API),可按业务逻辑聚合代码,适合复杂组件

3、生命周期钩子

beforeCreate===>setup()
created=======>setup()
beforeMount ===>onBeforeMount
mounted=======>onMounted
beforeUpdate===>onBeforeUpdate
updated =======>onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted =====>onUnmounted

4、其他重要变化

生命周期:Vue3 的生命周期需从vue导入(如onMounted替代 Vue2 的mounted),且setup替代了beforeCreate和created
模板语法:支持多根节点、动态指令参数(如v-bind:[key])、更灵活的v-slot语法
移除特性:删除filter(推荐用计算属性替代)、$on/$off(移除实例事件 API)
TypeScript 支持:Vue3 源码用 TS 重写,类型定义更完善,开发时类型提示更友好

18、组件传参

1、父子组件传参 props
父组件

<template>
  <ChildComponent :message="parentMsg" :user="userInfo" />
</template>
<script setup>
import { ref, reactive } from 'vue'
import ChildComponent from './ChildComponent.vue'
 
const parentMsg = ref('来自父组件的消息')
const userInfo = reactive({ name: '张三', age: 20 })
</script>

子组件

<template>
  <div>{{ message }}</div>
  <div>{{ user.name }}</div>
</template>
<script setup>
import { defineProps } from 'vue'
 
// 定义接收的 props(两种方式)
// 方式1:仅声明类型
const props = defineProps(['message', 'user'])
 
// 方式2:带类型校验(推荐)
const props = defineProps({
  message: String,
  user: {
    type: Object,
    required: true
  }
})
 
// 使用 props(无需 .value,直接访问)
console.log(props.message)
</script>

2、子组件给父组件传参 Emits
子组件


<template>
  <button @click="handleClick">发送数据</button>
</template>
<script setup>
import { defineEmits } from 'vue'
 
// 定义可触发的事件(两种方式)
const emit = defineEmits(['sendData', 'update'])
// 或带类型校验
const emit = defineEmits({
  sendData: (data) => typeof data === 'string', // 验证数据格式
  update: (num) => num > 0
})
 
const handleClick = () => {
  // 触发事件并传递数据
  emit('sendData', '来自子组件的消息')
  emit('update', 100)
}
</script>

父组件


<template>
  <ChildComponent 
    @sendData="onReceive" 
    @update="onUpdate" 
  />
</template>
<script setup>
const onReceive = (data) => {
  console.log('收到子组件数据:', data)
}
const onUpdate = (num) => {
  console.log('收到更新值:', num)
}
</script>

3、跨层级通讯provide、inject
顶级组件


<script setup>
import { provide, ref } from 'vue'
 
// 提供普通值
provide('theme', 'dark')
 
// 提供响应式数据(子组件会同步更新)
const user = ref({ name: '张三' })
provide('currentUser', user)
</script>

深层子组件

<script setup>
import { inject } from 'vue'
 
// 接收数据(第二个参数为默认值)
const theme = inject('theme', 'light')
const currentUser = inject('currentUser')
 
// 使用响应式数据
console.log(currentUser.value.name)
</script>

4、其他方式
refs 访问子组件:父组件通过 ref 获取子组件实例,直接调用方法或访问数据(谨慎使用,耦合度高)
pinia、vuex

19、axios封装

代码复用,统一的请求逻辑与集中处理错误,减少重复代码,提高可维护性。
基本流程:
创建axios实例
设置统一配置:baseURL(基础URL)、timeout(超时时间)、headers(默认请求头)等参数
添加请求和响应拦截器,请求拦截器中设置token等,响应拦截器中处理数据或错误,如统一的状态码等。
封装常用的方法,如GET、POST等

20、原型与原型链

原型的本质为对象
所有的函数都有一个原型属性prototype,prototype默认包含一个constructor属性,该属性指向函数对象本身。
所有的对象都有一个隐式属性proto,指向创建该构造函数的原型prototype。
原型链:
当查找对象的属性或方法时会先从自身查找,找不到然后通过proto去他的原型中查找,也就是他的构造函数prototype中查找,如果原型中找不到,也就是该构造函数中也没有,就会往原型后面的原型中去查找,直到找到或者达到顶端null,这样就形成了链式结构,也就是原型链,他的终点是null。
原型链可以使对象共享属性和方法。

21、computed和watch区别

1、computed依赖data中的属性,进行计算并缓存,watch不支持
2、computed不支持异步操作,当有异步操作时无效,不能监听数据变化,watch可以。
3、computed的中的函数必须用return返回,watch不需要。
4、computed的中属性都有一个get和set方法,当数据发生变化时调用set方法。

22、watchEffect和watch区别

watchEffect自动追踪依赖,并执行副作用
watch手动指定监听目标
不同:
watch 需要传入监听的数据源,而 watchEffect 可以自动收集数据源作为依赖。
watch 可以访问到改变之前和之后的值,watchEffect 只能获取改变后的值。
watch 运行的时候不会立即执行,值改变后才会执行,而 watchEffect 运行后可立即执行。这一点可以通过 watch 的配置项 immediate 改变。

import { ref, watchEffect, watch } from 'vue';

const count = ref(0);

// watchEffect
watchEffect(() => {
console.log('Count changed:', count.value);
});

// watch
watch(count, (newVal, oldVal) => {
console.log(`从 ${oldVal} 变为 ${newVal}`);
});

23、vue路由(组件)懒加载原理

vue路由懒加载依赖于import()动态导入语法实现异步组件,当路由被访问时import会返回一个promise对象,这个对象会在组件加载完毕后resolve,组件会作为参数传递给回调函数。这样只有组件在实际需要时才会加载相应的模块,从而实现路由懒加载,
路由懒加载一般用在非首页路由中,首页一般需要用户访问时立即呈现给用户。

24、setup函数的作用是什么?

setup是vue3 Composition api的入口函数
setup代替了vue2中 data、methods、computed 等选项。
更好地组织逻辑复用和模块化代码。

25、vue中实现全局组件和自定义指令

// main.js
import MyButton from './components/MyButton.vue';
const app = createApp(App);
app.component('MyButton', MyButton);
app.mount('#app');
// main.js
const app = createApp(App);
app.directive('highlight', {
  mounted(el) {
    el.style.backgroundColor = '#f0e68c';
  }
});
app.mount('#app');

26、keep-alive

功能:
组件缓存、保留其数据和状态,避免重复渲染
钩子:

activated:组件被激活时触发 。
deactivated:组件被缓存时触发 。

27、前端如何处理高并发

1、请求和并与批量处理
将多个接口请求合并为单个批量请求,减少网络往返次数。例如使用Promise.all同时发起多个请求,或在商品详情页一次性获取商品信息、评论和推荐数据
典型实现方式包括:

后端提供批量查询接口
前端使用Promise.all封装并行请求
对相同业务场景的数据进行逻辑分组

2、缓存与重复请求拦截

内存缓存:使用Map存储已请求数据,重复调用直接返回缓存
请求状态跟踪:记录进行中的请求,防止相同参数重复发送
本地存储:对静态数据使用localStorage持久化缓存

3、使用防抖与节流技术

防抖(debounce):延迟执行直到操作停止(如搜索联想场景)
节流(throttle):固定时间间隔执行(如滚动加载场景)

4、前端性能优化

可以采用代码压缩、图片懒加载、缓存优化等方式来减少前端的负载。

5、负载均衡

通过负载均衡器(如 Nginx、HAProxy),将流量分发到多个服务器,提高整体处理能力。

28、单页面应用优缺点

优点:

用户体验好:页面切换无需重新加载,减少白屏,操作连贯
前后分离:提升开发效率和代码可维护性
减轻服务器压力:首次加载后,后续操作由客户端处理,减少服务器请求

缺点:

首屏加载慢:首次加载时会加载所有路由,并且会下载所有资源(HTML、CSS、JS),网络差时体验不好(解决方法按需加载,懒加载,vue-router)
SEO优化空难:内容由js动态生成,搜索引擎可能无法正确抓取
安全分险增加:js处理了大量的操作逻辑,使得代码更容易被查看和篡改,跨站脚本攻击(xss)风险,如果对用户输入的数据没有进行严格的过滤和验证,就容易受到跨站脚本攻击。

29、uniapp实现页面跳转

1、uni.navigateTo保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack返回原页面
2、uni.redirectTo 关闭当前页面,跳转到非TabBar界面
3、uni.relaunch 关闭所有页面,打开到应用内的某页面
4、uni.switchTab 跳转到TabBar页面,并关闭所有非TabBar页面
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容