本文记录了我学习过程中整理的前端相关的知识点,以摘抄为主,绝大部分非原创。未能全部都标明出处,在此致歉
前言
为什么选择前端
前端和后端,我选择将前端作为我的主要技术栈。这和我个人的学习习惯有关。
我喜欢学以致用,对于一门尚未掌握的技术,我倾向于先看文档看个百分之二三十,有个大体的了解后,就开始动手去做。但后端的微服务等技术,我缺乏实际的实践环境,没法实践去应用,学起来就感觉很虚。
但前端不太一样。在我自己的项目中,为了实现更快的响应时间、更实用美观的界面,我有充分的动力去学习新的技术,然后将其运用,这形成了一个正反馈的过程。比如说我去年用webassembly写了一个Game of Life,当我对代码进行优化后,我确实能看到帧数肉眼可见地提升了。
为什么要记八股
最近在准备找实习,网上的八股光看也不太能记得住,还得是自己整理记录下。
我之前有点认为记八股是一种应试行为,有点抵触。但有天晚上我排查EuDs63/postkid出现的一个问题,为了完全搞懂,我就自发地去搜React的生命周期。
这时候我意识到八股的那些问题,其实不少是蛮常见的问题。要写好代码确实得去知道。
如果被问八股,要学会自己主动去延伸,进而在一定程度上把握面试的主动权。
安全
xss攻击
- Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
- 三种类型:
类型 存储区 插入点 存储型 XSS 后端数据库 HTML 反射型 XSS URL HTML DOM 型 XSS 后端数据库/前端存储/URL JavaScript - DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
- 防范
- 阻止攻击者提交恶意代码
- 输入过滤: 道高一尺魔高一丈,难
- 输入长度限制
- 防止浏览器执行恶意代码
- 预防存储型和反射型 XSS 攻击
- 改成纯前端渲染,把代码和数据分隔开。
- 对 HTML 做充分转义
- 预防 DOM 型 XSS 攻击
- 预防存储型和反射型 XSS 攻击
- 利用 CSP 来抵御或者削弱 XSS 攻击,一个 CSP 兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本
- 对关键 Cookie 设置 http-only 属性
- 阻止攻击者提交恶意代码
- 参考:
JavaScript Prototype 污染攻击
- 参考
prototype
是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法- 一个对象的
__proto__
属性,指向这个对象所在的类的prototype
属性
CSRF
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
特点:
- CSRF(通常)发生在第三方域名。
- CSRF攻击者不能获取到Cookie等信息,只是使用
防护策略
- 阻止不明外域的访问
- 同源检测
- 使用Origin Header确定来源域名
- 使用Referer Header确定来源域名
- 如果直接在本域发起攻击,同源策略无法达到防护的作用
- Samesite Cookie
- 同源检测
- 提交时要求附加本域才能获取的信息
- CSRF Token
- 我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击
- 双重Cookie验证
- 难以做到子域名的隔离
- CSRF Token
- 阻止不明外域的访问
参考:
Html
表单数据校验 - 学习 Web 开发 | MDN
锚点
- 示例
<a href="#javascript深入之重新认识箭头函数的this" aria-hidden="true" class="header-anchor">#</a>
aria-hidden="true"
属性通常用于指示屏幕阅读器等辅助技术,告知它们该元素对用户来说是不可见的,因此屏幕阅读器会忽略该元素。- 参考 :锚元素 - HTML(超文本标记语言) | MDN
HTML5语义化
- title v.s. h1
- strong v.s. b
浏览器
浏览器一帧都会干些什么?
- 接受输入事件
- 执行事件回调
- 开始一帧
- 执行 RAF (RequestAnimationFrame)
- 页面布局,样式计算
- 绘制渲染
- 执行 RIC (RequestIdelCallback)
浏览器引擎的工作原理
浏览器事件循环
输入URL到页面渲染的过程
- DNS 查询顺序
- 浏览器 DNS 缓存
- 浏览器调用 getaddrinfo() 查询数据,其中 getaddrinfo() 会根据如下顺序查询
- 先看 /etc/hosts 文件
- 通过主机名服务/mDNS 查询
- 通过 systemd-resolved 查询
- 最后通过 DHCP 下发的 DNS 查询
- 一级级往上查询直到根 DNS 服务器
- 参考
浏览器缓存(强缓存,协商缓存)具体字段头是哪些,有何区别
什么是浏览器缓存?
当我们访问一个网站时,会加载各种资源,如 HTML文档、JS、CSS、图片等。浏览器会将一些不经常变的资源保存在本地,这样下次访问相同网站时,就直接从本地加载资源,并不通过请求服务器,这就是浏览器缓存
根据位置分类
- memory cache:主要用来缓存有 preloader 相关指令的资源;关闭tab页后就释放
- disk cache
Response Header中的相关属性
Expires
- HTTP/1
Cache-Control
- HTTP/1.1
- 优先级比 Expires 要高
- 值:public / private / no-store / no-cache / max-age=60
- no-cache 告诉浏览器要使用缓存文件,但是每次需要跟服务器确认是最新文件以后才能用,一般使用 Etag 或者 Last-Modified 字段来控制缓存
Last-Modified
/If-Modified-Since
- HTTP/1
- 浏览器第一次访问资源,服务器会 Response Header 里添加 Last-Modified 的值
- 浏览器在下一次请求这个资源的时候,检测到有 Last-Modified 这个 Header。浏览器就会在 Request 中加上 If-Modified-Since,If-Modified-Since 的值就是 Last-Modified 的值
ETag
/If-None-Match
- ETag由服务器生成,三种方式:基于内容 / 基于版本 / 自定义生成
- 浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的 Etag值放到request header 里的 If-None-Match 里
- ETag 的优先级要高于 Last-Modified
强缓存
- 不会向服务器发送请求,直接从缓存中读取资源
- 通过 Expires 和 Cache-Control 来实现的
协商缓存
- 在强缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识来决定是否使用缓存
- 利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对 Header 来管理
场景:发版,要求无视缓存
- 每次对应静态文件里有内容改动的时候,自动加一段 hash 到静态文件名里
用户行为
- F5 刷新: 因浏览器而异
- Ctrl + F5 强制刷新的时候,会暂时禁用强缓存和协商缓存
后端配置
setHeader
之类- Nginx 配置
参考:
同源页面
如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。
- 举例:
http://store.company.com/dir/page.html
与http://store.company.com/dir2/other.html
同源http://store.company.com/dir/page.html
与http://news.company.com/dir/other.html
不同源,因为主机不同
- 当一个网页加载的资源(如脚本、样式表、图片等)来自于与当前页面不同的域名、协议或端口时,就称为跨域请求。
- 参考:
跨域问题的解决
- 代理
- 原理:同源策略只受限于浏览器访问服务器,对于服务器访问服务器并没有限制的特点。中间服务器做了一个请求转发的功能
- 实现:
- CORS
- CORS 跨源资源共享(Cross-Origin Resource Sharing)。
- 总体思路是:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许
- 三种请求
- 简单请求
- 需要预检的请求
- 附带身份凭证的请求
- 服务器通过响应header
Access-Control-Allow-Origin
进行控制
- 参考:
预检请求 OPTIONS
- 为什么需要预检请求
- 安全性:同源政策只会挡回应,不会挡请求,所以假如某个恶意攻击者发送DELETE 的请求,同源政策不会挡下这个请求(如果该请求后有回应,回应的部分才会挡下),换句话说如果没有多一层过滤,恶意攻击者任意发DELETE 请求,就可能任意删掉服务器端的资源。有了预检请求,等于是多一层过滤,当预检请求通过了,才会对服务器发送真正的请求
- 相容性:如果浏览器发送一个服务器端没支援的请求,可能导致服务器端出问题。这时预检请求会是一道防护,先确保服务器端有支援,才真正发请求
Access-Control-Allow-Origin
:和简单请求一样,表示允许的源Access-Control-Allow-Methods
:表示允许的后续真实的请求方法Access-Control-Allow-Headers
:表示允许改动的请求头Access-Control-Max-Age
:给定了该预检请求可供缓存的时间长短,单位为秒- 参考
Cookie,sessionStorage和localStorage
Cookie、sessionStorage和localStorage都是用于在客户端存储数据的机制,但它们在功能和使用方式上有一些重要的差异:
Cookie:
- Cookie是最古老和最基本的客户端存储机制之一。
- Cookie是由服务器发送到用户浏览器的小型文本文件,存储在用户的计算机上。
- 每次发送HTTP请求时,浏览器都会将相应的Cookie发送回服务器。
- Cookie通常用于存储用户偏好设置、跟踪用户行为等信息。
- Cookie有大小限制(通常为几KB)。
- 可以通过设置Cookie的过期时间来控制Cookie的生命周期。
sessionStorage:
- sessionStorage是HTML5引入的Web存储机制之一,用于在浏览器中临时存储会话数据。
- sessionStorage中存储的数据仅在当前会话期间有效,当用户关闭浏览器标签页或窗口时会被清除。
- sessionStorage中存储的数据仅限于当前页面或同源页面之间共享,不同页面之间的sessionStorage数据不会共享。
localStorage:
- localStorage也是HTML5引入的Web存储机制之一,用于在浏览器中持久存储数据。
- localStorage中存储的数据在用户关闭浏览器后仍然保留,直到用户显式删除它们。
- localStorage中存储的数据也只限于同源页面之间共享,不同源页面之间的localStorage数据不会共享。
主要差异总结如下:
生命周期:Cookie可以通过设置过期时间来控制生命周期,而sessionStorage的数据仅在当前会话期间有效,localStorage的数据则在浏览器关闭后仍然保留。
数据共享:
- sessionStorage: 多窗口之间sessionStorage不可以共享状态,但是在某些特定场景下新开的页面会复制之前页面的sessionStorage
- localStorage的数据仅在同源页面之间共享
- Cookie的数据则在同源或跨域请求中都会发送到服务器端。
存储容量:sessionStorage和localStorage通常具有更大的存储容量限制(几兆),而Cookie相对较小(几kb)。
延伸
- Chrome禁用三方Cookie:前端必知的影响 - 掘金
- bgstaal/multipleWindow3dScene: A quick example of how one can “synchronize” a 3d scene across multiple windows using three.js and localStorage use localStorage to maintain state across windows
- EuDs63/BookRecommend_Front: 图书推荐系统 使用sessionStorage和localStorage来存储jwt
参考:
IndexedDB
- 浏览器中的数据库
- 用户能修改吗?
- Chrome浏览器并没有提供给用户直接修改的方式
- 存储的位置位于
c:\Users\USERNAME\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\
- 被加密了,但还是不推荐存储任何服务端敏感的内容于用户端
- Edit IndexedDB data by using the Console tool - Microsoft Edge Developer documentation | Microsoft Learn
- 参考
- IndexedDB - Web API 接口参考 | MDN
- IndexedDB
- Edit data in IndexedDB by the user - Stack Overflow
- Tristan on X: “当初做 Unsplash Wanderer,在核心体验上,还是下了不少功夫。 所谓核心体验,就是围绕图片展现做文章,首先遇到的就是加载体验问题: 一开始,直接加载拿到的图片URL,这种方式简单粗暴,但缺点很明显,那就是加载时长问题,由于图片较大,有明显的白屏等待时间; https://t.co/x6sIboVrUv” / X
- View and change IndexedDB data - Microsoft Edge Developer documentation | Microsoft Learn
sessionStorage
sessionStorage中你所说的不能在所有同源窗⼝中共享是什么意思?多个tal栏不可以共享?浏览器同源策略是什么?会话级别的储存⽅式的会话是什么意思?
缓存除了indexDB、service worker、localStorage外还知道别的不(面试官提了个memory cache)
什么情况下用post和get
讲一讲get和post的区别?
script标签中的defer和async的区别?
SEO
兼容
- 例:
addEventListener("paste", (event) => {
event.preventDefault();
//兼容不同浏览器中获取剪贴板数据的方式
let paste = (event.clipboardData || window.clipboardData).getData("text");
paste = paste.toUpperCase();
const selection = window.getSelection();
if (!selection.rangeCount) return; //检查是否存在文本范围
selection.deleteFromDocument();
selection.getRangeAt(0).insertNode(document.createTextNode(paste));
selection.collapseToEnd();
});
网络
TCP/IP五层模型
- 分层
- 应用层: SMTP,TELNET,DNS
- 传输层: TCP/UDP、 TLS
- 网络层: ICMP,IP
- 数据链路层: mac地址,以太网,ARP
- 物理层
- 参考
TCP
- 可靠性:只要不得到确认,就重新发送数据报,直到得到对方的确认为止
- 参考:Python 绝技 —— TCP 服务器与客户端 | Secrypt Agency
http 缓存机制
安全协议
- SSL (Secure Socket Layer)
- TLS (Transport Layer Security)
- 加密
- 身份验证
- 完整性
- 参考:什么是传输层安全性(TLS)? | Cloudflare
http 和 https 区别
- HTTPS 是在 HTTP 协议基础上实施 TLS 加密,所有网站以及其他部分 web 服务都使用该协议。因此,任何使用 HTTPS 的网站都使用 TLS 加密。
- 参考:为什么 HTTP 不安全?| HTTP 与 HTTPS | Cloudflare
https 加/解密
https 攻击方式
DNS解析过程
三次握手与四次挥手
- 状态机:
- 三次握手过程:
- client发送连接请求报文,seq=x
- server接受连接后回复ACK=x+1,seq=y ,并为此处连接分配资源
- client接收到ACK报文后,回复ACK=y+1
- 四次挥手过程:
- client发送FIN,seq=x
- server发送ACK=x+1
- server发送FIN
- client发送ACK
- 参考
tcp和udp的区别
就是要你懂网络–一个网络包的旅程 | plantegg
HTTP 的状态码
- 分类:
- 1 表示消息
- 2 表示成功
- 3 表示重定向
- 4 表示请求错误
- 5 表示服务器错误
- 常见
- 200: 服务器已成功处理了客户端的请求,并且已返回所请求的资源
- 206: 服务器成功处理了部分请求,一般用来做断点续传,或者是视频文件等大文件的加载
- 301: 请求的网页已永久移动到新位置,常用于域名更换
- 302: 临时重定向不会缓存,常用于未登陆的用户访问用户中心重定向到登录页面
- 304: 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
- 401: 请求要求身份验证
- 404: 服务器找不到请求的网页
- 405: 禁用请求中指定的方法
- 500: 服务器内部错误
- 502: 连接超时错误
- 参考
轮询、长轮询(Long polling)、WebSocket
- 轮询:定期向服务器发出请求
- 长轮询过程
- 请求发送到服务器。
- 服务器在有消息之前不会关闭连接。
- 当消息出现时 —— 服务器将对其请求作出响应。
- 浏览器立即发出一个新的请求。
- WebSocket提供了一种在浏览器和服务器之间建立持久连接来交换数据的方法。数据可以作为“数据包”在两个方向上传递,而无需中断连接也无需额外的 HTTP 请求。
- 参考
列举一些使用tcp和udp的应用层协议,http(tcp)、websocket(tcp)、http3(udp),为啥http3会用udp
如果线上某个页面不显示了,可能是哪些问题(dns劫持,但我不太了解这个)
cdn原理
TypeScript
interface v.s. type
- An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
- An interface can have multiple merged declarations, but a type alias for an object type literal cannot.
- 参考Typescript 中的 interface 和 type 到底有什么区别 - 掘金
- 可以互相拓展
// interface extends type type Name = { name: string; } interface User extends Name { age: number; } // type extends interface interface Name { name: string; } type User = Name & { age: number; }
- interface 能够声明合并
interface User { name: string age: number } interface User { sex: string } /* User 接口为 { name: string age: number sex: string } */
- 用interface描述数据结构
- 用type描述类型关系
联合类型(Union Types)
- 参考TypeScript类型守卫、联合类型、交叉类型 - 古兰精 - 博客园
- 通过
&
运算符可以将现有的多种类型叠加到一起成为一种类型
类型守卫
- 作用:确保所使用的类型均可以正常使用,从而针对不同类型,使用不同的业务处理。
- 示例:
function evaluatePrice(vehicle: Vehicle) {
switch(vehicle.vType) {
case "car":
return vehicle.transmission * EVALUATION_FACTOR;
case "truck":
return vehicle.capacity * EVALUATION_FACTOR;
case "motorcycle":
return vehicle.make * EVALUATION_FACTOR;
}
}
交叉类型
- 参考TypeScript类型守卫、联合类型、交叉类型 - 古兰精 - 博客园
- 通过 & 运算符可以将现有的多种类型叠加到一起成为一种类型
- 同名基础类型属性的合并: 需要判断是否可能存在,不存在则为
never
- 同名非基础类型属性的合并:示例:
interface D { d: boolean; } interface E { e: string; } interface F { f: number; } interface A { x: D; } interface B { x: E; } interface C { x: F; } type ABC = A & B & C; let abc: ABC = { x: { d: true, e: '***', f: 666 } };
泛型
杂项
SVG
- 全称: Scalable Vector Graphics(可缩放矢量图形)
- 介于DOM和Canvas之间,既提供了图形的绘制能力,又提供了元素的DOM交互能力
- 特点
- SVG不存在回流这种布局问题,最多也就是重绘。但是DOM的动画就可能导致回流。
- SVG是矢量图形,而Canvas绘制的是位图
- 当屏幕尺寸越大时,SVG的渲染速度差异不大,但是Canvas的渲染时长明显增加
- 当绘制对象过多时,SVG的渲染时长指数增加,Canvas保持稳定增长
- Canvas不支持DOM事件
- 参考
Ajax
- 5 states:
0. UNSENT
- OPENED
- HEADERS_RECEIVED
- LOADING
- DONE
AXIOS
- Getting Started | Axios Docs
- axios内部的设计模式,它为什么好?拦截器怎么实现的?
Babel
webpack
- 简述Webpack和Vite的区别 - 掘金
- Vite 的目标不是要干掉 webpack - 知乎
- Vite 的实现原理,确实很巧妙 - 掘金
- webpack中loader和plugin的区别,你用过哪些
- Getting Started | webpack
REST API
设计模式
防抖和节流
- 防抖:在触发频率高的事件中,执行耗费性能的操作,连续操作后只有最后一次生效
//自己实现debounce function debounce(func,wait=0){ let timeId = null; //为什么不在内部声明id?因为我如果在内部声明,每一次触发事件就会有一个新的变量产生 return function(...args){ const self=this; if(timeId){ clearTimeout(timeId); } timeId=setTimeout(()=>{ func.apply(self,args); },wait) }; }
- 节流:在触发频率高的事件中,执行耗费性能操作,连续触发,单位时间内只有一次生效
function throttle(func,wait=0){ let timeId; return function(...args){ const self=this; if(!timeId){ func.apply(self,args); timeId = setTimeout(()=>{ timeId = undefined; },delay); } }; }
- 参考
滚屏性能优化
- 将
touchstart
和touchmove
事件的passive
选项设置为ture- 原理:
touchstart
和touchmove
是cancelable
的。事件处理程序中可能会调用preventDefault()
,所以需要等待事件处理程序结束再滚动。 - 注: 大部分浏览器(Safari 和 Internet Explorer 除外)将文档级节点 Window、Document 和 Document.body 上的 wheel、mousewheel、touchstart 和 touchmove 事件的 passive 默认值更改为 true。
- 原理:
- 参考:
package.json
- 版本
- 每个版本号都形如:
1.2.3
,有三部分组成,依次叫主版本号、次版本号、修订号;- 当新版本无法兼容基于前一版本的代码时,则提高主版本号;
- 当新版本新增了功能和特性,但仍兼容前一版本的代码时,则提高次版本号;
- 当新版本仅仅修正了漏洞或者增强了效率,仍然兼容前一版本代码,则提高修订号;
^
兼容某个大版本。如:^1.1.2
,表示>=1.1.2 <2.0.0~
兼容某个次版本。如:~1.1.2
,表示>=1.1.2 <1.2.0
- 每个版本号都形如:
- package-lock.json里面定义的是某个包的具体版本,以及包之间的层叠关系
- package-lock.json 需要提交到仓库吗
- 应用: 可以保证项目中成员、运维部署成员或者是 CI 系统, 在执行 npm install后, 保证在不同的节点能得到完全一致的依赖安装的内容,减少bug的出现
- 给外部环境用的库: 在不使用 package-lock.json的情况下,就可以复用主项目已经加载过的包,减少依赖重复和体积
- 参考:详解package.json和package-lock.json - 掘金
什么是懒加载?什么是按需加载?
怎么做到组件的按需引入,我提到利用treeshaking
SSR 渲染的原理
JWT (Json Web Token)
见[进一步学习jwt]
首屏优化
说一说useState和Hooks的执行顺序
项目中的分片上传,如何实现的?
如何优化解决虚拟滚动中滚速过快情况下的白屏问题
瀑布流
如何实现的长列表? 瀑布流 如何实现的长列表? 你是如何判断谁是底部元素的? 如果一次只请求10条数据,前端只展示了5条数据,如何判断底部元素的交叉状态?
响应式
git merge和 git rebase的区别
策略模式
脱离文档流和不脱离文档流会有什么不同的表现
rem
你知道eventBus吗?
rollup/vite 等构建工具;
面试题:React实现一个Dialog模板_react dialog-CSDN博客
html5新特性
实现es6中的flatten()
实现个栈(用了好几种方法实现,包括最原始的单链表),还讲了讲symbol等乱七八糟的东西
看你用到了Object.prototype.toString()方法,能给我介绍一下JavaScript中的Prototype吗?为什么不用Object.toString()或者Array.prototype.toString()