前言
先来说说自身成分,本人大三,某双非软件工程专业,从去年10月末学习前端到现在。春招早已开启多日,看着身边大佬逐渐拿到大厂office,自己也不能掉队,从3.14晚上认真(海)投简历到现在才终于拿到一个小厂的面试机会,毕竟人生第一次面试,准备了一晚上结果第二天面试完的时候面试官问我,你了解我们的公司吗?。。。
我:
自我介绍
万年不变的自我介绍:面试官您好,我叫xxx,就读于xxx,是一名25届软件工程专业的大三学生,想来贵公司参加前端实习生职位的面试。大一、大二的时候自学过一些其他编程语言的知识,例如python ...... 我个人比较喜欢前端,空闲时间里,喜欢在b站里跟着相关视频学习。近些年Chatgpt火了以后,又对AI非常感兴趣......此外,在掘金上写过多篇JS基础和底层相关系列文章。平时主要使用vue,熟悉全家桶开发,看过一点点源码,github上坚持着每日coding和日常学习,未来也会继续坚持并努力着。
1. 讲讲你项目中的账号密码登录的实现
好家伙一上来贴脸开大,画个大饼丢给我,不过这种情况下万变都离不开八股内容,对这问题进行分析不就是 JWT + token + Cookie嘛。
1.1 概念分析
1. JWT(JSON Web Token):JWT是一种用于身份验证和授权的开放标准。它由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。在账号密码登录中,服务器会生成一个JWT并将其发送给客户端作为身份验证凭证。
2. Token:Token是一种用于身份验证的令牌。在账号密码登录中,服务器靠JWT生成一个Token。Token的生成过程通常包括将用户的身份信息(例如用户名、角色等)加密到JWT的负载中,并使用服务器的密钥对其进行签名以防篡改。客户端收到Token后,将其存储在本地,例如浏览器的本地存储或移动设备的Keychain中。
3. Cookie: Cookie是一种存储在客户端的小型文本文件,大小只有4kb左右。在账号密码登录中。客户端收到Token后,会将其存储在浏览器的Cookie中。每次客户端向服务器发送请求时,浏览器会自动将Cookie附加在请求中并发送给服务器,从而实现身份验证。
1.2 登录流程
- 客户端将账号密码发送给服务器进行验证。
- 服务器校验账号密码成功后,靠JWT来生成一个令牌Token,并返回给客户端。
- 客户端收到Token后,将其存储在本地例如 cookie 或 localStorage 里。
- 客户端在以后的请求中,会将Token附加在请求头中发送给服务器。
- 服务器接收到请求时,验证Token有效性,若验证通过,则返回相应的数据。
1.3 token特点
- 无状态性:服务器无需记录用户的登录状态,因为Token本身包含了所有必要的信息。
- 可扩展性:Token可以存储更多的用户信息,例如权限、角色等。
- 安全性:Token通常使用密钥进行签名,防止被篡改和伪造。
2. 讲讲 LocalStorage、Cookie、SessionStorage
| 特性 | 大小限制 | 自动发送 | 安全性 | 使用场景 | 存储有效期 |
|---|---|---|---|---|---|
| Cookie | 通常限制为4KB | 每次请求自动发送给服务器 | 可能受到XSS和CSRF攻击 | 身份认证、持久登录 | 可设置过期时间,可以是会话级别(浏览器关闭后自动删除) |
| LocalStorage | 通常为5MB - 10MB | 否 | 相对较安全 | 持久性数据存储 | 永久有效,除非手动删除 |
| SessionStorage | 通常为5MB - 10MB | 否 | 相对较安全 | 临时数据存储 | 页面会话期间 |
3. 了解 XSS、CSRF 攻击吗
-
XSS 攻击:
- 概念:XSS 是一种常见的网络安全漏洞,攻击者通过在网页中注入恶意脚本,从而使用户在浏览器端执行恶意代码,达到窃取用户信息、会话劫持等恶意目的。
- 防范措施:对用户输入数据进行合适的过滤与转义,使用内容安全策略(CSP),避免直接使用
eval()等不安全的函数等。
-
CSRF 攻击:
- 概念:CSRF 是指攻击者利用用户已登录的身份,在用户不知情的情况下伪造请求,以用户的名义发送恶意请求,如更改账号信息、发起转账等。
- 防范措施:使用 CSRF Token 防护机制,在关键操作中要求验证用户身份或采用双重提交 cookie 策略等来防止 CSRF 攻击。
4. Cookie 过期失效怎么办
-
前端处理:在前端可以监测Cookie的过期时间,一旦Cookie过期,可以清除过期的Cookie并提示用户重新登录或执行其他必要的操作。
-
后端处理:在服务器端验证Cookie的有效性,如果发现Cookie已经过期,可以要求用户重新登录或执行相关的身份验证流程。
-
刷新Token:对于身份验证相关的Cookie,可以考虑使用刷新Token的机制,即在Cookie快要过期时,通过特定的接口请求获取新的Token,并更新Cookie中的Token值,从而延长用户的登录状态。
-
提醒用户:在用户登录时或者Cookie快要过期时,可以向用户发送提醒消息,告知他们需要重新登录或者更新Cookie以维持登录状态。
5. 如何隐藏页面元素
| 样式 | 文档流 | 响应事件 | 回流重绘 |
|---|---|---|---|
| display: none | 脱离文档流 | 无法响应事件 | 回流重绘 |
| visibility: hidden | 占据文档流 | 无法响应事件 | 重绘 |
| opacity: 0 | 占据文档流 | 响应事件 | 重绘或不重绘 |
| position: absolute | 脱离文档流 | 无法响应事件 | 回流重绘 |
| clip-path: circle(0%) | 占据文档流 | 无法响应事件 | 重绘 |
6. css选择器有哪些,优先级呢?
- id选择器
- 类名选择器
- 标签选择器
- 后代选择器
- 子级选择器
- 相邻兄弟选择器
- 群组选择器
- 属性选择器
- 伪元素选择器
- 伪类选择器
优先级:!important > 内联( style="color: red;" ) > id选择器 > 类名选择器 > 标签选择器
这个想详细了解的话可以看下本人以前文章:css选择器及优先级
7. 讲讲v-for的key
当使用 v-for 指令来渲染列表时,通常每个项提供一个唯一的 key 属性作为标识符,来追踪每个节点的身份,从而在进行 DOM diff 比较时能够更高效地更新虚拟 DOM。在列表发生变化时(例如增删元素),如果每个项有唯一的 key,Vue.js 就能够正确识别每个项的状态,并保留正确的状态信息。这对于表单输入、动画等场景特别重要。如果没有提供 key,Vue.js 可能会采取一种更低效的方式来重新渲染列表。尽量避免使用数组索引作为 key,因为当数组插入删除等操作变更数组下标时可能会导致一些错误。
8. 讲讲Map和forEach区别
forEach 主要用于遍历数组并执行操作,而 Map 则更适合用于对数组中的每个元素进行处理并返回一个新数组。
// 使用forEach遍历数组
const numbers = [1, 2, 3, 4];
numbers.forEach(num => {
console.log(num * 2); // 输出: 2, 4, 6, 8
});
// 使用Map处理数组
const doubledNumbers = numbers.map(num => num * 2);
console.log(doubledNumbers); // 输出: [2, 4, 6, 8]
9. 防抖节流
- 防抖: 在一定的时间内, 不管点击多少次, 只会触发一次
- 节流: 例如每次点击间隔超过一秒钟只触发一次
<!DOCTYPE html>
<body>
<button id="btn">提交</button>
<script>
let btn = document.getElementById('btn')
btn.addEventListener('click',debounce(handle, 1000))
//btn.addEventListener('click',throttle(handle, 1000))
function handle(e){
console.log('点击了按钮');
}
// 防抖: 在一定的时间内, 不管点击多少次, 只会触发一次
function debounce(fn, wait){
let timer = null
return function(...arg){
// 销毁上一次的定时器
clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this, ...arg)
}, wait)
}
}
// 节流: 每次点击间隔超过一秒钟只触发一次
function throttle(fn, wait){
let lastTime = Date.now() // 最开始点击的一次
return function(...arg){
let nowTime = Date.now() // 当前点击的时间
if(nowTime - lastTime > wait){
fn.call(this, ...arg)
lastTime = nowTime
}
}
}
</script>
</body>
</html>
10. 数组去重
这题当时问我的时候我还愣了一下,因为前天下午本人就正好做了一道字节的数组去重,我还问了面试官数组里面都是数字吗,面试官说是,那这题就是考察Set方法或者遍历数组。
let array = [1, 1, 2, 3, 1, 9, 10, 3, 2]
//Array.from()接收一个可迭代对象,返回一个新的数组实例,将得到的 Set 集合转换为一个新的数组。
let newArr = Array.from(new Set(array))
// 解构方法
let newArr1 = [...new Set(array)]
console.log(newArr) // 输出:[1, 2, 3, 9, 10]
console.log(newArr1) // 输出:[1, 2, 3, 9, 10]
// 循环方法
let newArr2 = []
array.forEach((num)=>{
if(!newArr2.includes(num)){
newArr2.push(num)
}
})
console.log(newArr2) // [1, 2, 3, 9, 10]
如果有uu想要了解数组里面不只有数字的话可以来看看这篇,难度拉满: 这是来自字节俩道面试题。。。汗颜
11. 项目里的验证码如何实现
我简历里写的是一个电商的项目,因为是两个面试官问我,这时候换了另外一个面试官,拿着我的简历又框框问起了我简历里面的项目。。。
12. 讲讲如何实现添加购物车操作
我这里用的是pinia状态管理工具以及计算属性来实现,然后又被框框问。。。
13. 讲讲计算属性和watch
computed计算属性,通过对已有的属性值进行计算得到一个新值,是基于它们的依赖进行缓存的。计算属性值会被缓存,只有当依赖数据发生变化时才会重新计算,这样可以避免重复计算提高性能。
watch用于监听数据的变化,定义需要监视的数据,它可以监听单个数据或者数组,并指定回调函数来执行特定的操作。和computed不同的是watch不会有缓存。
14. 讲讲你对pinia的认识
为什么要用 Pinia:
- Pinia 提供了一种更直观和简洁的方式来管理应用状态,相对于 Vuex,代码量更少,更易读和维护。
- 使用 Composition API 可以使状态管理逻辑更加清晰,提高可读性和可维护性。
- Pinia 支持插件系统,可以根据项目需求来引入插件扩展功能,例如异步状态、状态拦截等。
- 开发者可以使用 Composition API 中提供的函数式编程风格来定义和操作状态。
- 更好的支持TypeScript。
与Vuex相比,Pinia的优势:
- 基于 Composition API,对vue3开发者友好
- 更友好的支持TypeScript
- 不再需要mutation,因为action既支持同步也支持异步
- 不再有modules
- 不再有命名空间的概念,不需要记住复杂的关系
总结
感觉小公司对项目这方面能力很看重,怼着项目框框问,真汗流浃背了,反倒是八股问的很简单,虽然这是我第一次面试,自我感觉还行,但是我看到别人的发面腾讯的考题,觉得自己还是需要反复拷打......