唠一唠
还记得第一次接触到前端是在去年10月份,学校新开了一门Javaweb课,主要用到的技术就是jsp。当时对前端的兴趣就油然而生了,我喜欢前端带给我直接的交互感,喜欢写完代码就能看效果带来的成就感,哪怕全网都在唱衰前端已死,哪怕当今互联网大量的降本增效。我还是带着这份兴趣和期待去网上了解更多的前端知识,才发现其实学校教的jsp已经是非常古老的技术了,也就是在这时,我了解到了许多十分优秀的前端框架,Vue、react、Angular等等。这也是我正式接触Vue的开始,一段萌新误入前端的故事就此开始...
学习前端到现在已经半年多了,Vue3的基本知识也大体都学的差不多了,项目写了两个,八股也没怎么看,直接懵懵懂懂的就开始在BOSS上投递简历了。
一开始投递非常不顺利,大量的已读不回,大量甚至读也不读... 这一刻我好像确实体会到了网上那些关于前端的言论了。
不过也有好转,很快我接到了人生第一个面试,一家来自杭州的100-499人的小公司。三月一日投递的,约的三月四日面试。紧张感一下子就上来了。八股还没看过,还好留了大概两天缓冲期。周五下午投递,周六周日我就一头扎进自习室了。早上9点到晚上九点半被自习室的保安赶出去(自习室要关门了)。两天的高强度八股记忆,时间过的很快可又感觉很慢。紧接着很快就到了周一上午的面试环节了。
A公司
一进会议,紧张感就油然而生。之前准备好的自我介绍模板,一到面试啥都忘记了,直接即兴发挥了...
自我介绍
面试官,您好。首先,很感谢贵公司给的这次面试机会,请允许我先自我介绍一下。我叫XX,来自东华理工大学,软件工程2025届大三在读,个人主要技术栈的话是Vue3,熟悉Vue全家桶开发。写了个一个小型的全栈记账本项目,前端用到技术栈主要是Vue3+Ts+Pinia,后端用koa搭建,使用node写了一点简单的接口,目前后端还只是了解水平。还一个项目是跟随b站上视频跟的一个项目,由于第一个项目可能写的不规范,我在第二个项目中学到了很多东西,比如JWT鉴权,axios的二次封装,目录结构如何优化等等,这些学习到的技术我也都在空闲时间加到我的第一个项目中去了。个人对AI很感兴趣,2023年AI席卷互联网,前端AIGC的概念也由此诞生,我也带着这份兴趣,去网上学习了一些关于AI的基础知识,平时也有在Google的Colab平台玩过一些大模型,跟随b站的博主“老陈打码”学习了一点关于LangChain的知识。平时也会使用一些AI工具提升代码编写效率,比如GitHub Copilot,国产的通义千问等等。平时喜欢记录一些自己的学习过程,分享在稀土掘金平台,目前是掘金的四级创作者。在闲暇时间,平时最喜欢干的事就是阅读一些掘金大佬写的文章,学习更多的知识吧,不断强化自己。嗯,大概就这些。
面试官很不错,声音很有磁性,说话也很温柔。体验感不错。下面就是面试官问我的一些问题了。
1. 介绍一下记账项目的整体设计思路
这个就大致和面试官讲了一下开发这个项目的一些流程,以及灵感来源是手机上平时用的一个记账app。
2. 项目中下拉刷新查看上个月记账数据是如何实现的?
用的Vant库自带的功能,然后面试官追问你知道它底层是如何实现的吗,讲了一下监听盒子的滚动,到了一定距离触发回调函数,获取数据。
3. Computed和Watch的区别
- computed 是为了简化模板内的表达式,让代码更易于维护,尤其适合纯计算场景,且结果会被自动缓存复用只有到依赖数据发生变化的时候属性值会被重新计算,默认在声明时立刻执行一次。
- watch 则更多地用于在数据变化后的副作用操作,例如执行异步任务、手动修改其他数据、或者调用外部API等,且每次数据变化都会执行相应的回调。和computed相比不同的是,watch不会有缓存。默认在声明时不会立刻执行一次。,可设置 immediate: true 改成立刻执行一次。
4. 介绍一下Vue中的常见指令
比较简单我不介绍了,直接列出来了
- v-if/v-else
- v-for
- v-model
- v-bind
- v-html
- v-show
- v-on
- v-text
我大概就说了上面这几种,然后分别介绍一下它们的用法
5. Vue2和Vue3的区别
-
响应式原理不同,vue2是基于Object.defineProperty方法来实现观察者模式的响应式数据绑定。 Vue3采用的是ES6新增的Proxy实现响应式。
-
API设计不同,Vue2是基于 Options API(选项式),Vue3引入了Composition API(组合式),我本人使用的主要是Vue3,感觉相对于Vue2来说,Vue3的组合式APi在项目开发中更加方便随意,我可以将同一个模块的代码声明在一起,比如这个模块的变量,函数等等,后期也方便维护。
-
生命周期钩子不同,Vue3 对生命周期钩子进行了调整,移除了 beforeCreate 和 created,新增了 setup 钩子,这是执行所有初始化逻辑的地方,而且在 setup 中访问的响应式状态不需要this上下文。
-
Vue3对TS的支持更好
6. 组件之间的通讯方式
-
父向子传值,父组件通过
v-bind绑定属性值,子组件通过props接收,Vue3中我们使用defineProps。 -
子向父传值,子组件通过
$emit发布自定义事件,在Vue3中我们使用defineEmits。父组件通过v-on或者简写成@接收。 -
Refs,父组件在声明子组件的地方上绑定
ref="xxx",然后通过xxx.value访问子组件内的函数或者方法。 -
在父组件中通过
provide提供一个对象或函数,可供其子孙组件消费,子孙组件通过inject接收来自祖先组件提供的数据或方法 -
Event Bus,全局事件总线。创建一个全局事件中心(通常是Vue实例),用于在不相关的组件间传递消息。在Vue 2中,可以直接在原型上挂载一个事件总线实例(例如
Vue.prototype.$bus = new Vue()),而在Vue 3中,由于不再推荐修改Vue原型链,可以使用mitt、event-emitter等第三方库创建一个独立的事件总线。 -
利用状态管理库,如Pinia,Vuex来实现组件之间的通讯。
7. 简单介绍一下浏览器渲染
8. 怎样做到减少回流
还是上面第七点的文章中讲过,面试考题:从输入URL到页面渲染。
9. Webpack的打包流程
-
1. 读取配置文件
- 找到项目中的webpack.config.js,解析其中各种配置项,比如入口(entry),输出(output),lodaer,插件(plugin)等。
-
2.确定入口文件
- 入口文件是webpack构建流程的起点,通常都是应用程序的主JS文件,可以从这里开始追踪应用的所有依赖。
-
3. 解析模块依赖
- 从入口文件开始,递归的解析所有导入的模块的依赖关系,包括JavaScript文件,CSS,图片或者其他资源。
-
4. 编译模块
- 对每个模块,由于webpack只能理解和处理JavaScript代码以及JSON文件。对于其他文件,我们使用加载器(loader)进行预加载,使其能够被当做模块并包含进最终的打包流程当中。例如,我们可以使用ts-loader将TypeScript编译为JavaScript文件,使用sass-loader和less-loader将Sass/less语法编译成css等等。
-
5. 生成依赖图
- 在webpack解析和编译的过程中,会构建出一个完整的模块依赖树,记录了各个模块之间的依赖关系。
-
6. 代码优化
- 此阶段会使用一系列的插件执行一系列优化操作,例如删除未使用的代码,提取公共模块chunks、Tree Shaking、Scope Hoisting等。
-
7. 生成输出文件
- 根据配置的输出路径和文件名模板,Webpack 将编译后的模块合并成一个或多个bundle,并写入到磁盘。这个过程中可能还会生成额外的辅助文件,如source map用于调试,或者manifest文件用于动态加载 chunk。
-
8. 打包完成
- 最终,Webpack 完成整个打包过程,并可以通过监听模式(watch mode)持续观察文件变化,重新执行打包流程。
10. pinia-plugin-persistedstate实现pinia持久化存储的原理是什么?
由于我在项目中用了该插件对pinia进行持久化存储,面试官就问到了。
-
原理:
- 插件通常使用浏览器提供的
localStorage或sessionStorageAPI 来保存状态数据。当 store 中的数据发生变化时,插件会监听这些变化并将新的状态数据自动保存到存储中。页面加载时,插件会在创建 store 实例之前从存储中恢复之前保存的状态数据,并将其应用到对应的 store 上。
- 插件通常使用浏览器提供的
11. 项目中的验证码是使用的现成组件库还是什么?
自己写的,这个我在之前的文章-- 如何实现封装一个验证码组件? 中有详细介绍,感兴趣的掘友可以去看看。
12. 谈谈你对koa的理解
这个问题也是因为项目是全栈项目,后端用的koa搭建的,所以说简历上写了的东西,掘友们一定要去深入了解一下。
-
理解
- Koa是一款基于node.js的轻量级框架,整体代码大概几千行,相对其他框架来说,更加轻量化、更加简洁。其提供了优雅、强大的中间件系统。支持promise,支持async和await,使得异步编程更加直观和易于后期维护。
想了解更细节的可以看我社团同学写的文章 -- 详谈koa【koa语法 | koa/router | 中间件 | 洋葱模型】
12. node.js的事件循环机制
当时不是很了解,只了解浏览器的事件循环机制,和面试官说的应该和浏览器的事件循环机制差不多。
浏览器的事件循环机制之前有写过文章 -- JavaScript事件循环机制:深入解析Event loop
- node.js的事件循环机制
-
单线程与Libuv:
- Node.js同样是单线程执行JavaScript代码,但通过C++编写的Libuv库实现了跨平台的异步I/O处理,使得Node.js能有效处理大量并发请求而不会阻塞主线程。
-
任务队列:
-
Node.js的事件循环也包含多个任务队列,包括:
- timers(定时器):与浏览器的宏任务类似,存放setTimeout和setInterval回调。
- I/O callbacks:处理网络请求、文件系统等I/O操作完成后的回调。
- idle, prepare:内部使用。
- poll:轮询等待新的I/O事件,处理完成后也会处理微任务队列。
- check:用于执行setImmediate回调。
- close callbacks:处理close事件的回调。
-
-
执行顺序:
-
Node.js的事件循环在一轮循环中会执行以下步骤:
- 执行microtask队列(包括Promise、process.nextTick等)。
- 如果microtask队列为空,则执行poll阶段,检查是否有新的I/O事件,如果有则处理并再次执行microtasks。
- 当满足条件时,执行timers阶段,处理到期的定时器回调。
- 继续check阶段,setImmediate回调在这里执行。
- 最后是close callbacks阶段。
-
14. 反问环节
问了下公司环境以及主要使用的技术,说进去之后要我转react。
上午面的,下午HR就打电话过来了,待遇为4000 + 300餐补,后面我就拒了,还是想去一些更大一点的公司,我也没想到第一次面试就过了,属实有点惊讶,不过给的待遇也不高。然后就再接再厉面其他公司了。
开始面试刷经验
之后陆陆续续又面了几家中厂。
之后面到小鹅通,属实给我整麻了,没有专门的面试平台写代码,几个代码题都是面试官口里念,要我说答案,构建工具原理算是问到知识盲区了,题目就不解释了,问到的问题我总结了如下图
然后面了七八家厂之后,我们计算机社团的社长给我联系了同校19级b站学姐给我内推了b站前端面试,当时整个人都懵了,感觉自己完全还不够格。但社长态度却很强硬,给我面!不要怕!内心十分挣扎,行吧,那只能硬着头皮上了。目标就是不因为面评差被b站拉黑就行。社长周四上午帮我投的简历,下午就打电话约了第二天面试,效率是真快,但因为周五临时有点事,后面又约到了下周一一面。
大厂面试的促进作用,我开始了正式闭关...
一直到面试前,一直都待在学校自习室,早上8点多去,晚上9点半自习室熄灯,被保安赶回来了,回到宿舍还是看各种面经,背各种八股。很快一面就到来了...
B站一面
提前10分钟就点进去了面试链接,虽然之前面了很多家公司了,但是对面是b站,内心还是有点紧张。很快,一面面试官也进来了,比原本约定的时间提早了5分钟就开始了。面试官可能刚面完别人,看起来有点疲惫的样子。面试官让我先做个自我介绍,自我介绍之前都已经倒背如流了,结果因为有点紧张,最后还是即兴发挥了,不过也还不错,比预想的要好。其实自我介绍说完就已经没那么紧张了,整个人都放松了下来,面试官人很不错,说话很温柔,会耐心听我讲完。下面就是一面的面试题了。
1. 介绍一下git的常见指令
-
初始化新仓库:
git init用于在当前目录下创建一个新的Git仓库。
-
添加远程仓库:
git remote add origin <repository-url>将本地仓库关联到远程仓库,
origin是远程仓库的默认名称,<repository-url>是远程仓库的URL。 -
克隆仓库:
git clone <repository-url>从远程仓库克隆一个全新的本地仓库副本。
-
添加文件到暂存区:
git add <file>或者一次性添加所有改动
git add .将指定文件或所有改动过的文件添加到暂存区,准备提交。
-
提交更改:
git commit -m "<commit-message>"将暂存区的所有内容提交到本地仓库,并附带一条提交消息。
-
查看提交历史:
git log显示提交历史记录。
-
拉取远程仓库的最新变更:
git pull从远程仓库获取并合并最新的提交到本地分支。
-
推送本地提交到远程仓库:
git push origin <branch-name>将本地分支的提交推送到远程仓库的指定分支。
-
切换分支:
git checkout <branch-name>切换到指定分支。
-
创建新分支并切换到该分支:
```
git checkout -b <new-branch-name>
```
11. 合并分支:
```
git merge <branch-name>
```
将指定分支的最新提交合并到当前所在分支。
12. 删除分支:
```
git branch -d <branch-name>
```
删除已合并的本地分支;若要强制删除未合并的分支:
```
git branch -D <branch-name>
```
13. 解决合并冲突: 在合并或拉取时出现冲突时,解决冲突后:
```
git add <conflicted-file>
git commit -m "Resolved merge conflict"
```
14. 撤销工作区的修改:
```
git checkout -- <file>
```
丢弃工作区对指定文件的修改(未add到暂存区的改动)。
15. 撤销暂存区的修改:
```
git reset <file>
```
移除暂存区中指定文件的改动,但保留工作区的改动。
16. 强制推送:
```
git push origin <branch-name> --force
```
强制覆盖远程分支(谨慎使用,可能导致远程仓库丢失提交)。
之后复盘面试的时候,社长说还有一个很重要的git指令没说 -- git rebase
老实说之前确实没怎么听过这个指令,又学到新知识了,不错 ^.^。下面是网上搜来的一些关于git rebase的一些知识。
- git rebase
主要作用用来改变历史,尤其是当你想将一系列提交合并、重新排序或在无冲突的基础上应用到另一个分支时。下面是一些基础用法:
1. 变基到上游分支
- git rebase [upstream-branch]
作用:将当前分支的所有未合并提交放置在
[upstream-branch]的顶部,从而让本地分支与上游分支保持一致,并且保持提交历史线性。2. 交互式变基(合并提交)
- git rebase -i [base-branch]
这将启动一个交互式的编辑器,允许你重新排列提交、合并连续的提交、修改提交信息等。
-i表示 interactive(交互式)。在编辑器中,你可以对每一行代表的提交进行操作:
pick保留该提交(默认)squash将该提交与其前一个提交合并fixup类似于squash,但不保留合并时的提交消息,使用前一个提交的消息reword修改该提交的注释信息edit在变基过程中暂停,让你有机会修改提交内容或注释drop删除该提交3. 变基并解决冲突
如果在变基过程中遇到冲突,你需要手动解决冲突,然后执行:
git add [resolved-files]
git rebase --continue
若想放弃变基并返回到变基前的状态:
- git rebase --abort
4. 变基到指定提交
- git rebase [commit-hash]
将当前分支的头部移动到指定提交之后,并将所有未合并的提交放在指定提交之上。
变基并保留分支指针不动
- git rebase --onto [new-base] [old-base]
这个命令会将
<old-base>与当前分支之间的所有提交移到<new-base>分支上,而当前分支的指针仍指向原来的最后一个提交,这时需要手动更新分支指针。注意:
git rebase会改变提交历史,因此在多人协作的环境中应当谨慎使用,特别是不要在已被共享的分支上随意执行git rebase,因为它可能导致其他人难以合并你的更改。在公开分支上,一般建议使用git merge来合并分支,以保持提交历史的完整性和可追溯性。
2. == 和 === 的区别,为什么 [] == ![] 为 true
面试常考题了,涉及到了类型转换,其实理清楚之后就不难了,我当时回答的也很流畅,这里我就直接引用我社团另一位同学的文章了,感兴趣的可以去看看 -- 面试官 :[] == ! [] 为什么返回 true ?
3. ref和reactive的区别
大厂面试,你要是只是简单聊聊这两者的区别,肯定是不够的。我感觉更重要的是自己要懂得拓展来讲,我面试的时候,先聊了聊他们两者的区别,自然而然就会聊到Vue的响应式,于是这个时候你自己先把响应式的原理给面试官讲一遍,面试官对你的第一印象就更好。感觉面试的应试技巧是很重要的。
好,扯了点题外话,下面开始回答上面这个问题。
先聊区别:
-
-
所包装的类型不同: ref可以用来包装原始数据类型和引用数据类型,也就是说它可以将任何类型的值都变成响应式的,但是使用时需要带上.value。而reactive只能用来包装对象。
- 既然聊到了这里,就可以给面试官讲一下为什么它们所包装的类型不同,聊一聊reactive基于ES6新增的proxy对数据进行拦截,因为proxy是接受对象作为参数,所以reactive用来包装对象。可以和面试官聊一聊你说你之前有研究过ref和reactive的源码,所以了解源码还是很重要的。
-
-
-
响应式原理不同: ref 内部使用 RefImpl 对象包裹原始值,通过 .value 访问时会触发依赖收集和更新通知。reactive 则是利用 ES6 的 Proxy 对象,对整个对象的属性读写进行拦截,实现了深度响应式。
- 都聊到这里了,再和面试官主动讲一讲如何他们两个是如何实现响应式的,讲解一下副作用函数收集,和副作用函数触发。再聊一聊vue2和vue3响应式的区别,为什么vue3的响应式更好等等问题。面试官对你的好感又会上一个台阶。
-
其实区别大致就这两个,真正研究了一点Vue的源码,这种题目回答起来应该是得心应手。
4. 生命周期相关
- 父子组件都有
created和mounted,问父组件的created和mounted和子组件的created以及mounted的它们四个的执行顺序
在 Vue.js 中,父子组件的生命周期钩子执行顺序如下:
-
父组件生命周期钩子执行顺序:
beforeCreatecreatedbeforeMount- 子组件开始执行生命周期钩子
- 子组件
beforeCreate - 子组件
created - 子组件
beforeMount - 子组件
mounted - 子组件的
mounted钩子全部执行完毕后 - 父组件
mounted
总结来说,父组件的 created 钩子在子组件的 created 钩子之前执行,而父组件的 mounted 钩子会在所有子组件的 mounted 钩子执行完毕后执行。这有助于确保父组件的 DOM 已经准备就绪,然后再执行子组件的 DOM 挂载相关的逻辑。
5. Vuex和Pinia的区别,使用感受
讲了一下Vuex的使用方法和Pinia的方法,以及Vuex主要是在Vue2中使用,和Vue2的选项式api符合,当然Vue3也可以用,Pinia主要在Vue3中使用,与Vue3的组合式API符合。Vuex的结构更加严谨,更适合大型复杂应用开发,有一套成熟完善的生态系统,保证了状态管理的严格和可控性。而Pinia更适合中小型项目,对于那些追求轻量级解决方案的项目更加友好。个人平时都有用,主要是用Pinia,因为主要写的是Vue3,Pinia更符合Vue3的组合式API书写。
6. 项目中用到的持久化储存如何实现
第一家面的公司已经问过了,果然写在简历里就容易被问到,这里就不再列一遍了,上文有介绍过。
7. 你平时如何调试自己的代码的
-
浏览器开发者工具:
- Chrome DevTools、Firefox Developer Tools、Microsoft Edge DevTools 等现代浏览器均内置了强大的开发者工具,通过它们可以查看和修改DOM结构、CSS样式、JavaScript代码,并且可以实时调试代码。
- 源代码查看与编辑:在 Sources 或 Debugger 面板中,可以查看并编辑网页加载的JavaScript文件,同时可以设置断点、逐行执行、查看变量值等。
- Console控制台:使用
console.log()、console.error()、console.warn()等方法输出调试信息,查看变量值、函数调用结果等。 - Elements面板:可以检查和修改DOM结构,查看CSS样式及其计算值,以及布局和尺寸信息。
- Network面板:分析HTTP请求和响应,调试网络相关问题。
- Memory面板:检查内存使用情况,寻找内存泄漏等问题。
-
断点调试:
- 在代码中设置断点,浏览器在执行到断点时会暂停,此时可以查看当前作用域下的变量值、逐步执行代码(Step Into、Step Over、Step Out)。
-
Source Maps:
- 对于压缩或混淆过的代码,通过Source Map可以将浏览器开发者工具中的源代码映射到未经压缩的实际源代码,方便调试。
-
移动端和内置浏览器调试:
- 对于移动端应用内的网页,可以使用设备的USB连接电脑,并开启远程调试功能(如Chrome DevTools的Remote Devices或Safari的Web Inspector)。
- 第三方应用内置浏览器可通过相应工具或开发文档提供的方法接入调试。
-
Vue Devtools:
- Vue Devtools 不仅可以提供可视化的组件树结构展示,还允许开发者查看组件状态,监控数据变化,时间旅行调试,组件快照等等。
-
在线调试:
- 对于线上代码,如果开启了Source Maps,并且服务端支持,可以通过浏览器开发者工具远程调试线上代码,或通过Browsersync等工具同步本地代码到线上环境进行调试。
8. 如何调试网络请求
这个问题是前一个问题的相关问题,由于前一个问题没回答到这个,面试官又追问了这个问题
-
1. 使用Chrome DevTools:也就是浏览器右键,点击检查,来到网络,在这里可以查看一些网络请求和响应信息。
- 光回答这些可能太单调了,还是那句话,自己拓展到其他领域讲讲,比如我就给面试官主动讲了讲请求头有哪些东西,响应头有哪些东西,这里可以聊很多东西,比如根据请求头中的Cookie字段我们可以讲一讲jwt鉴权,但是我没讲很多,就说了一下项目中使用了token,以及为什么请求头中会携带上token。果不其然,之后面试官就要我聊一聊jwt鉴权,早就准备好了,所以十分轻松就说完了。在讲到Connection字段又和面试官聊了聊长连接。讲这些的时候,时间也正在飞速流逝,但这一切的前提是面试官不会主动打断你说话哦。
-2. Postman模拟请求:利用第三方工具模拟请求,测试接口。
9. 介绍一下常用的状态码
很常规的八股文,不过讲的时候要有顺序,从1xx 依次讲到 5xx,并且介绍一下这些状态码的应用场景就行
-
1xx(信息提示):
- 100:继续。客户端应当继续发送请求的剩余部分,如果请求已经完成,则忽略此响应。
- 101:协议切换。服务器同意客户端的协议切换请求。
-
2xx(成功):
- 200:请求成功,请求的数据在响应正文中返回。
- 201:请求成功并且服务器创建了新的资源。
- 202:服务器已接受请求,但尚未处理完成。
-
3xx(重定向):
- 301:永久重定向,请求的资源已永久转移到新的URI。
- 302(有时是303 See Other或307 Temporary Redirect):临时重定向,请求的资源临时位于新的URL上。
- 304:请求的资源未修改过,客户端可以继续使用缓存的版本。
-
4xx(客户端错误):
- 400:客户端请求有误,服务器无法理解。
- 401:请求未经授权,需要身份验证。
- 403:服务器理解请求但拒绝执行。
- 404:服务器找不到请求的资源。
- 405:服务器不支持请求所用的HTTP方法。
-
5xx(服务器错误):
- 500:服务器遇到了意外情况,无法完成请求。
- 502:服务器作为网关或代理时,收到了无效响应。
- 503:服务器暂时无法处理请求,通常由于服务器过载或维护。
- 504:服务器作为网关或代理时,未能及时从上游服务器(如应用服务器)获取响应。
10. 聊一聊JWT鉴权
之前自己和面试官主动说了token,果然就被问到了更细节的东西。
先介绍了一下JWT的结构,主要由下面三部分组成:
1. Header(头部) : 包含JWT元数据,通常包括令牌类型(固定为"JWT")和签名算法(如HS256、RS256等)。
2. Payload(载荷) : 包含实际的声明,可以是有关用户的任何信息,如用户ID、用户名、角色、过期时间等。这些声明可以分为三种类型:
- Registered Claims: 如
exp(过期时间)、iat(发行时间)、sub(主题)等。 - Public Claims: 由IANA注册或者在特定命名空间下的自定义声明。
- Private Claims: 应用程序专用的声明。
3. Signature(签名) : 根据前面两部分以及一个密钥生成的签名,用于验证数据完整性,确保JWT未被篡改。签名算法可以是HMAC SHA家族(如HS256、HS384、HS512)或者RSA和ECDSA家族(如RS256、ES256等)。
然后讲了一下JWT的工作流程。
JWT的工作流程:
- 签发Token: 用户登录时,服务端验证用户凭证(用户名/密码等),验证成功后,服务端生成一个包含用户相关信息的JWT,通过Header指定的签名算法加密生成签名,并将整个JWT返回给客户端。
- 客户端存储和使用Token: 客户端收到JWT后,通常将其存储在Cookie、LocalStorage或SessionStorage中。每次发起请求时,将JWT放入HTTP请求头(如
Authorization头,格式通常为Bearer <token>)中。 - 服务端验证Token: 服务端在接收到带有JWT的请求后,通过Header中的签名算法和事先知道的密钥对JWT进行校验。如果校验通过,则认为客户端持有有效的身份凭证,从而允许访问受保护的资源。
然后介绍了我如何在项目中用的,这里我直接截取我项目中的核心代码了:
前端:
- 请求拦截:
- 前端登录,存放token:
if(res.status == 200) {
// 保存用户信息
localStorage.setItem('token',res.token)
showSuccessToast('登录成功')
// 1s后跳转页面
setTimeout(() =>{
router.push('/cost')
},1500)
}else{
showSuccessToast(res.msg)
}
后端:
- 引入jsonwebtoken,手写生成token函数sign
- 后端验证token函数verify
上述两个函数拿到定义接口的地方去用:
- 登录接口:
- 示例:对记账接口进行限制
注意,这里是传入函数,不要调用!
11. 介绍一下Node.js中的流
- 流是一个核心概念,用于有效地处理数据流,特别是针对大容量数据时,能够避免一次性加载所有数据到内存中带来的性能问题。流是基于事件驱动的API设计,允许开发者以连续的小块(chunk)来读取或写入数据,而不是直接操作完整的数据集合。
关于Node.js中流的分类主要为下面几种:
-
可读流 :
- 可读流是从各种数据源(如文件、网络连接、进程输出等)逐步读取数据的抽象接口。
- 提供了多个事件,比如
data事件会在每次有新数据块可用时触发,end事件在数据读取完毕时触发。 - 可以通过
.read()方法或监听事件来获取数据。
-
可写流 :
- 可写流用于将数据逐步写入目的地,如文件、网络连接或进程输入等。
- 提供了
write()方法来写入数据块,以及end()方法来标记写入结束。 - 当缓冲区满或者流关闭时,会触发相应的事件,如
drain、finish和close。
-
可读写流 :
- 这类流同时实现了可读和可写的特性,例如TCP套接字既可以读取也可以写入数据。
- 具备上述可读和可写流的所有功能。
-
转换流 :
- 转换流是在读取数据的同时对其进行处理,并将处理过的数据写入到下游的流。
- 在读取数据的过程中执行某种中间操作,如压缩、解压、加密或解密等。
Node.js中流处理的.pipe()方法,是流的核心功能之一,它用于将一个流自动连接到另一个流,形成一个数据处理管道,这样数据就会从一个流自动流向下一个,简化了数据流的传递过程,并且在整个过程中都遵循“流”的理念,即数据按需逐步处理,而非一次性全部处理。
也就是说,Node.js中的流极大地提高了在内存受限环境下的数据处理能力,尤其是对于大规模I/O密集型任务,提供了更加高效和灵活的解决方案。
由于前面几个问题我拓展开来聊了,占用了比较多的时间,最后就考了个算法题,如下:
const tree = {
val:1,
children: [
{
val:2,
children: [
{
val:3,
children:[
{
val:4,
children:[]
}
]
}
]
},
{
val:5,
children: [
{
val:6,
children:[]
},
{
val:7,
children:[]
}
]
}
]
}
问: 求该树的最大深度
很常规的一道算法题,力扣上也有类似的题目。
解法:
function maxLevel(root) {
let level = 0;
function dfs(root, l) {
level = Math.max(level, l);
for (let child of root.children) {
dfs(child, l + 1);
}
}
dfs(root, 1);
return level;
}
前面回答的时间比较久,到这已经快一个小时了,然后后面就是问了一些面试官关于前端学习的建议,面试官给的建议就是对待事情保持好奇心吧,沟通能力和逻辑思维能力有时候比目前的实力更重要,毕竟是实习生,还年轻,以后有机会可以学。嗯 一面记忆中大概就这些了。
一面最后面试官已经口头上说了一面肯定是给过的,然后第二天下午就约了二面,时间在周四下午,还有两天时间备战...