浏览器
url 的组成部分
- 协议(Protocol):表示访问网页时使用的通信协议,常见的有 HTTP、HTTPS、FTP 等。
- 域名(Domain Name):表示网站的名称,是网站在互联网上的唯一标识。域名由多个部分组成,包括主域名和子域名,例如www.example.com中的"www"是子域名,"example"是主域名,".com"是顶级域名。顶级域名:也就是后缀,例如.com、.cn等。(备注:域名可以说是一个IP地址的代称,目的是为了便于记忆后者。
- 端口号(Port):表示用于访问网站的端口号,默认为 80。例如,http://www.example.com:8080中的"8080"就是端口号。端口号的范围是:0~65535
- 路径(Path):表示网站上具体的文件或目录路径。例如,http://www.example.com/path/to/file中的"/path/to/file"就是路径(网址可以没有端口号)。
- 查询参数(Query
Parameters):表示向服务器传递的参数,用于定制请求的内容。查询参数以"?"开头,多个参数之间使用"&"分隔。例如,http://www.example.com/path/to/file?param1=value1¶m2=value2中的
param1=value1¶m2=value2就是查询参数,这种常见于项目中路由跳转的传参、get请求等。 - 锚点(Anchor):表示网页内部的定位点。锚点以"#"开头,用于跳转到网页的特定位置。例如,http://www.example.com/path/to/file#section1中的"#section1"就是锚点,常见于a标签的超链接。
输入 url 敲回车后发生了什么
在浏览器地址栏输⼊
URL浏览器查看缓存,如果请求资源在缓存中并且新鲜,跳转到解码步骤
如果资源未缓存,发起新请求
如果已缓存,检验是否⾜够新鲜,⾜够新鲜直接提供给客户端,否则与服务器进⾏验证。
检验新鲜通常有两个 HTTP 头进⾏控制 Expires 和 Cache-Control:
HTTP1.0 提供 Expires,值为⼀个绝对时间表示缓存新鲜⽇期 HTTP1.1 增加了 Cache-Control: max-age=time,值为以秒为单位的最⼤新鲜时间
浏览器解析 URL获取协议,主机,端⼝,路径等信息
浏览器组装⼀个 HTTP(GET)请求报⽂
浏览器获取主机
ip地址,过程如下:- 浏览器缓存
- 本机缓存
hosts⽂件- 路由器缓存
ISPDNS缓存DNS递归查询(可能存在负载均衡导致每次IP不⼀样)
开启一个
socket与目标 IP 地址端口建立 TCP 连接- 三次握手
建立 TCP 后发送 HTTP请求
服务器接受请求并解析,将请求转发到服务程序
服务器检查
HTTP请求头是否包含缓存验证信息,如果验证缓存新鲜,返回 304 等对应状态码处理程序读取完整请求并准备
HTTP响应服务器将响应报⽂通过
TCP连接发送回浏览器浏览器接收
HTTP响应,然后根据情况选择关闭TCP连接或者保留重⽤- 四次挥手
浏览器检查响应状态码
如果资源可缓存,缓存资源
对响应进行解码
根据资源类型确定如何处理
解析 HTML构建 DOM 树,下载 CSS、JS 资源等,构造 CSS 规则树,执行 JS 脚本
浏览器渲染时有哪些线程
- GUI 渲染线程
- JS 引擎线程
- 事件触发线程
- 定时触发器线程
- 异步 http 请求线程
- 合成线程
- IO 线程
defer 和 async 的区别
defer 脚本在后台加载,等待 dom 准备好时才加载脚本
而且脚本的运行有严格的顺序async 脚本后台加载,加载完立即执行,没有严格顺序
cookie 和 localStorage 的区别
面试官:Javascript 本地存储的方式有哪些?区别及应用场景? | web 前端面试 - 面试官系列
面试官: 既然有了 cookie 为什么还要 localStorage?😕😕😕Cookie 适合用于在客户端和服务器 - 掘金
Cookie、LocalStorage 和 SessionStorage:一次非常详细的对比!_cookie localstorage sessionstorage-CSDN 博客
简单请求、复杂请求、预检请求
什么是跨域
跨域(Cross-Origin Resource Sharing,简称 CORS)是一种安全策略,用于限制一个域的网页如何与另一个域的资源进行交互。这是浏览器实现的同源策略(Same-Origin Policy)的一部分,旨在防止恶意网站通过一个域的网页访问另一个域的敏感数据。
所谓同源,指的是两个页面必须具有相同的协议(protocol)、域名(host)和端口号(port)。
设置 document.domain 解决无法读取非同源网页的 Cookie 问题
要求主域名相同
跨文档通信 API:window.postMessage()
JSONP
- JSONP 是服务器与客户端跨源通信的常用方法
- 核心思想:网页通过添加一个
<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
CORS
- 普通跨域请求:只需服务器端设置 Access-Control-Allow-Origin
- 带 cookie 跨域请求:前后端都需要进行设置
Access-Control-Allow-Credentials设置为true
Webpack 本地代理(本地调试)
线上环境跨域如何解决
面试官:聊聊你知道的跨域解决方案跨域是开发中经常会遇到的一个场景,也是面试中经常会讨论的一个问题。掌握常见的跨域解决方案 - 掘金
- 通过设置 CORS
- JSONP
- nginx 反向代理
重排(回流)、重绘、如何避免
面试官:怎么理解回流跟重绘?什么场景下会触发? | web 前端面试 - 面试官系列
事件循环、宏微任务都有哪些
面试官:说说你对事件循环的理解 | web 前端面试 - 面试官系列
JS 是单线程的,如果需要处理异步任务,不能在异步任务那里阻塞住,因此有了事件循环。
首先将任务分为同步任务和异步任务,如果是同步任务则直接推入主线程去执行,异步任务则会加入到异步任务队列。当主线程执行结束后,会从任务队列中读取对应的任务,推入到主线程执行。这个过程重复执行就构成了事件循环。
而任务队列又分为宏任务队列和微任务队列,执行时会优先执行微任务队列的任务直至清空,然后取一个宏任务执行,宏任务结束后再次清空微任务队列的任务,循环往复。
浏览器的事件循环和 NodeJS 的事件循环的区别
面试官:说说对 Nodejs 中的事件循环机制理解? | web 前端面试 - 面试官系列
在浏览器事件循环中,是根据HTML5定义的规范来实现。而NodeJS的事件循环是基于libuv实现的,libuv是一个多平台的异步
IO 库。
Nodejs 事件循环分为六个阶段

- 定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数
- I/O 事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些 I/O 回调
- 闲置阶段(idle, prepare):仅系统内部使用
- 轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞
- 检查阶段(check):setImmediate() 回调函数在这里执行
- 关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close', ...)
每个阶段对应一个队列,当事件循环进入某个阶段时, 将会在该阶段内执行回调,直到队列耗尽或者回调的最大数量已执行, 那么将进入下一个处理阶段,直观表现是,宏任务队列全部执行完才会执行微任务队列,而浏览器是宏任务执行一个就去执行微任务队列。
除了上述 6
个阶段,还存在process.nextTick,其不属于事件循环的任何一个阶段,它属于该阶段与下阶段之间的过渡,
即本阶段执行结束, 进入下一个阶段前, 所要执行的回调,类似插队
浏览器缓存
HTTP 和 HTTPS 的区别
面试官:什么是 HTTP? HTTP 和 HTTPS 的区别? | web 前端面试 - 面试官系列
http2.0 和 http3.0 有什么区别
React
React Fiber 是什么
React 网络请求放在哪里合适
放在
componentDidMount和componentWillMount有什么区别
推荐放在componentDidMount中
原因:
componentWillMount生命周期在新版中已经过时或者被废弃- React 官方推荐在
componentDidMount中发送网络请求 - 如果使用服务端渲染,
componentWillMount这个生命周期的网络请求会发送两次,一次在服务端,一次在客户端,而componentDidMount没有这个问题,只会在客户端请求 - 如果组件 render
时发生了异常导致不能正常挂载,
componentDidMount则不会发送请求,而componentWillMount会发送请求,请求到的数据无法被使用,浪费资源
使用 Hooks 相比于类组件的优势
- 简化代码
- 类组件需要定义构造函数、生命周期方法等,而函数式组件只需要函数本身,减少了很多样板代码
- 易于组合和重用逻辑
- 使用 Hooks 可以容易地提取和复用组件中的逻辑
- 避免了 this
- 减少了 this 的复杂性,代码更清晰
JSX 如何编译渲染到页面中
浏览器不能识别 jsx 代码,需要转成 js 代码。
首先通过 Babel 转换工具将 jsx 转换为 js 代码
1
2
3const element = <h1>Hello, world!</h1>;
// 转换后,分别是 标签的类型、标签的属性值对象、标签的内容(子元素)
const element = React.createElement('h1', null, 'Hello, world!');React.createElement创建的对象也叫做虚拟 DOM将虚拟 DOM 转为真实 DOM
使用
React.createRoot创建根对象,然后将虚拟 DOM 渲染到 id 为 root 的 DOM 节点中。
Babel 是什么?转换原理?
Babel 是 JavaScript 编译器,可以将
TS、JSX、TSX 等转成需要的代码。
Babel 内部原理是将 JS 代码转换为 AST,对 AST
应用各种插件进行处理,最终输出编译后的 JS 代码。
React Hooks 为什么不能放在 if else 中
多个 Hooks 在 React 中是以链表的形式存在的,第一次渲染时会形成一个链表,存储着所有的 Hook 的信息,后边渲染时都会按照这个链表的信息更新 Hook,如果有判断条件,会导致重新渲染前后的链表不一致,导致状态混乱。
useEffect 和 useLayoutEffect 的区别
「React」useEffect 与 useLayoutEffect 使用与区别_useeffect 和 uselayouteffect 使用场景-CSDN 博客
useEffect
- 组件渲染到屏幕后异步执行
- 全部 dom 更新完成、浏览器绘制之后异步执行
- 不会阻塞页面渲染
useLayoutEffect 和 useEffect 类似,但也有区别
- 全部 dom 更新完成后同步执行,在浏览器绘制前执行
- 会阻塞浏览器渲染
一般推荐默认使用
useEffect,只有在涉及到需要在布局渲染阶段同步执行的 DOM 操作或有严格的顺序要求时,才使用useLayoutEffect。
组件中的 key 属性有什么作用
React 存在
Diff算法,而元素key属性的作用是用于判断元素是新创建的还是被移动的元素,从而减少不必要的元素渲染。
因此key的值需要为每一个元素赋予一个确定的标识
父子组件的子组件有个 key,每次渲染后 key 值+1 后会发生什么
组件被卸载并重新挂载
- React 使用
key来唯一标识列表中的每个子组件。 - 当
key改变时,React 会认为这是一个全新的组件。 - 结果是,原来的组件实例会被卸载,新的组件实例会被挂载。
组件的状态丢失
- 如果子组件内部有本地状态(
useState或类组件的state),状态会在每次key改变时被重置。 - 这是因为 React 认为这是一个新组件实例,旧的状态无法保留。
性能影响
- 每次
key改变,React 会卸载和重新挂载子组件,而不是重用现有的 DOM 和组件实例。 - 这会增加不必要的渲染和计算负担,从而影响性能,特别是当子组件包含复杂逻辑或有较多 DOM 元素时。
React 性能优化
面试官:说说你是如何提高组件的渲染效率的?在 React 中如何避免不必要的 render? | web 前端面试 - 面试官系列
shouldComponentUpdate,更新前手动比较更新前后的值,如果相同则返回false不进行更新,否则返回true更新
PureComponent
React.memo,缓存组件,给组件的props参数更新时,才会更新组件,只适用于函数式组件
避免使用内联函数
- 比如事件回调时,避免在回调时设置类似于箭头函数,而是在外层定义好,直接用函数名代替,避免重复生成函数。
使用 React.Fragments 避免额外标记
- 空标签或者 fragment 标签可以作为顶级标签使用,不会像组件引入任何额外标记
使用 Immutable
使用
Immutable可以给React应用带来性能的优化,主要体现在减少渲染的次数在做
react性能优化的时候,为了避免重复渲染,我们会在shouldComponentUpdate()中做对比,当返回true执行render方法Immutable通过is方法则可以完成对比,而无需像一样通过深度比较的方式比较
使用 lazy 组件懒加载
Context 和 Redux 的区别
【react】context VS redux 前言 自从新的 context API 和 hook 特性相继出来后,江湖上类似于 - 掘金
Context主要用于解决跨组件数据传递和共享问题,祖先组件通过
Context.Provider组件发布数据,后代组件可以通过Context.Consumer或者useContext钩子订阅数据进行消费Context + useReducer通过
Context管理状态,useReducer改变状态,可以简单实现状态管理的功能。但是订阅了 context 实例的组件,当数据更新时,即使这个组件没有消费更新的数据,该组件仍然会重新渲染。
Redux完整的状态管理框架。通过
React-Redux管理状态,当组件消费数据时,其他数据更新不会影响到该组件,只有消费的数据更新了,才会重新渲染。调试工具
Redux调试工具可以清楚的看到状态什么时候发生了什么变化,而Context不能看到,只能看到当前的状态
ReactContext
父组件向后代组件传递数据,
父组件通过React.createContext创建一个context
1 | const testContext = React.createContext('test'); |
通过testContext.Provider组件包裹后代组件,将要传递的数据放在Provider组件中。
1 | <testContext.Provider value={100}></testContext.Provider> |
后代组件获取这个数据可以使用testContext.Consumer组件
1 | <testContext.Consumer> |
或者在类组件中设置contextType属性接收
1 | class MyClass extend React.Component { |
React 的发布订阅模式
React 虚拟 dom diff 操作
面试官:说说 React diff 的原理是什么? | web 前端面试 - 面试官系列
- tree diff,层级比较,树中同一个层级的比较,只有添加、删除操作
- component diff,组件比较,组件比较类型,类型不同则直接删除旧的,添加新的(连同子组件一并删除、添加)
- element diff,元素比较,有删除、添加、移动操作。
- 有
key的情况下可以比较快的移动完成 - 主要是
index, oldIndex, maxIndex,通过这三个值比较,然后进行移动操作 - 按照新集合的顺序进行排列,即
index的值为0, 1, 2, ..., n,然后对比旧集合中key,依次将旧集合的位置索引赋值给oldIndex,所以oldIndex可能为2, 0, 1, 3, ..., n,初始值maxIndex设置为 0 - 按照如下规则对
index进行遍历oldIndex > maxIndex时,令maxIndex = oldIndexoldIndex === maxIndex时,不操作oldIndex < maxIndex时,将oldIndex位置的元素移动到index的位置。
- 有
React render 函数理解
面试官:说说 React render 方法的原理?在什么时候会被触发? | web 前端面试 - 面试官系列
render函数里面可以编写JSX,转化成createElement这种形式,用于生成虚拟DOM,最终转化成真实
DOM
在React 中,类组件只要执行了
setState 方法,就一定会触发 render
函数执行,函数组件使用useState更改状态不一定导致重新render
组件的props 改变了,不一定触发 render
函数的执行(父组件使用useRef传参,父组件更新 ref
对象,但是子组件不会重新渲染),但是如果 props
的值来自于父组件或者祖先组件的
state,在这种情况下,父组件或者祖先组件的
state 发生了改变,就会导致子组件的重新渲染
所以,类组件一旦执行了setState就会执行render方法,函数式组件useState
会判断当前值有无发生改变确定是否执行render方法,一旦父组件发生渲染,子组件也会渲染
- 在组件生命周期或 React 合成事件中,setState 是异步
- 在 setTimeout 或者原生 dom 事件中,setState 是同步
React 生命周期
react 生命周期总结(旧、新生命周期及 Hook)-腾讯云开发者社区-腾讯云
shouldComponentUpdate 周期有什么作用?
CSS
对于 CSS 预编译语言的理解
面试官:说说对 Css 预编语言的理解?有哪些区别? | web 前端面试 - 面试官系列
扩充了 CSS,增加了变量、函数等功能。可以认为是 CSS 的超集。不同的预编译语言有不同的解析器,最终这些都会被编译成对应的 CSS 文件。
主要有 sass, less, stylus,三种。
扩充了一些功能,如
变量
- sass:
$red: #abc使用$开头,冒号分隔 - less:
@red: #abc使用@开头,冒号分隔 - stylus:
red = #abc直接定义,等号分隔
- sass:
作用域
混入
- 将一部分样式抽离出来,然后被重复使用
代码模块化
实现三栏布局
position 每个属性和作用
JavaScript
竞态问题
- 通常做法是取消网络请求
对于 Promise 的理解
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大
- 链式操作减低了编码难度
- 代码可读性明显增强
有三种状态:
- pending(等待中)
- fulfilled(成功)
- rejected(失败)
从pending变为fulfilled或者rejected后,就不会再改变
async 异步函数如何捕获异常
1 | async function fn() { |
当执行fn函数时fetchData可能会抛出异常,如何捕获异常?
1. try-catch
1 | async function fn() { |
2. 内部catch
1 | async function fn() { |
3. 外部catch
1 | async function fn() { |
闭包是什么
一个函数和对其周围状态的引用捆绑在一起,这个组合就形成了闭包,换句话说,闭包可以在函数内部访问到函数外部的作用域。
闭包可以:
- 创建私有变量
- 延长变量的生命周期
通常的使用场景:
- 计数器
- 函数柯里化
- 防抖
有哪些模块规范
深入对比 esModule 和 commonjs 模块化的区别前言 commonjs 2009 年,Ryan Dahl 基于开源的 V - 掘金
主要有commonJS, AMD, CMD, ESModule,现在主要使用的是commonJS, ESModule。
两者的区别:
- 引入方式
commonJS是动态导入,可以在任何地方引入ESModule模块是静态导入(在编译阶段进行导入),不能动态加载语句,所以 import 不能写在块级作用域和判断条件内
- 使用语法
commonJS是通过module.exports,require导出和导入esModule通过import, export导入导出
- 模块导入方式
commonJS是运行一遍代码,将module.exports的值赋值给变量esModule模块输出的是一个值的引用, 使用的是动态绑定,esModule 导入导出的值都指向同一个内存地址,所以导入值会跟着导出值发生变化
- 加载模式
commonJS是同步加载esModule是异步加载
区别原因:
- 同步加载会导致浏览器出现卡顿的情况。而 node 大多运行在服务端,同步加载本地文件速度很快。浏览器加载模块通常是网络请求,同步的话会出现卡顿的情况。
JS 的有哪些数据类型
基本类型
Number,String,Boolean,Undefined,null,Symbol复杂类型
统称为 object 类型,常见的有
Object,Array,Function
面试官:说说 JavaScript 中的数据类型?存储上的差别? | web 前端面试 - 面试官系列
实现一个 request,失败后间隔 interval 重试,设置最大重试次数
1 | async function request(url, options, interval, maxCount) { |
字符串 slice, substring, substr 的区别
new 关键字的执行过程
面试官:说说 new 操作符具体干了什么? | web 前端面试 - 面试官系列
- 创建一个空对象
- 新对象原型指向构造函数的原型对象
- 将新对象设置为构造函数的
this,执行构造函数 - 函数返回值不为对象或者是
null,则返回新对象,否则返回函数的返回值
JS 的垃圾回收
深入了解 JavaScript 垃圾回收机制_javascript 的垃圾回收机制讲一下-CSDN 博客
ES6 常用 api
async, await 实现原理
async 表示声明一个异步函数,这个函数返回值是一个 Promise 对象,如果返回值不是 Promise 类型,则会将返回值包装成 Promise 类型。等同于 Promise.resolve(x)。
await 表示等待的意思,可以等待异步函数也就是 Promise 对象,也可以等待普通的变量值。并且只能在 async 函数中使用。
使用 yield 实现 async,核心原理是递归迭代执行 next
1 | function myAsync(gen) { |
async/await 可以看作是生成器的语法糖。
将生成函数的 * 替换成 async,将 yield 替换成 await
var, let, const 之间的区别
面试官:说说 var、let、const 之间的区别 | web 前端面试 - 面试官系列
变量提升
var有变量提升,let, const没有暂时性死区
var没有暂时性死区,let, const有,只有执行到let, const那行以后才可以使用变量。块级作用域
var没有块级作用域,在作用域内声明的,作用域外也可以使用。let, const在作用域内声明的不能在作用域外使用。重复声明
var可以重复声明,let, const在同一个作用域内不可重复声明修改声明的变量
var, let可以修改声明的变量,const声明时必须初始化,且后边不可修改。
事件流
事件:通常是使用 js 与浏览器 HTML 文档进行交互的操作,如点击事件等。
事件流三个阶段:
- 事件捕获阶段
- 事件处理阶段
- 事件冒泡阶段
原始事件模型
绑定方式简单,可以在 HTML 代码中直接绑定,也可以通过 js 代码绑定
1 | <input type="button" onclick="func()" /> |
1 | const rootEl = document.getElementById('root'); |
- 只支持事件冒泡,不支持捕获
- 同一个类型事件只能绑定一次,后边的会覆盖前边的
- 绑定速度快
标准事件模型
通过addEventListener和removeEventListener添加和删除事件监听,参数分别为eventType, handler, useCapture,表示事件类型、回调函数、是否捕获(true表示在捕获阶段执行,false表示在冒泡阶段执行)。
1 | const rootEl = document.getElementById('root'); |
- 支持事件捕获、事件处理、事件冒泡三个阶段
- 支持绑定多个事件,不冲突
IE 事件模型(基本不用)
分为事件处理阶段和事件冒泡阶段
事件冒泡
父子元素,子元素的事件会将该事件层层上报,使得父元素也发生该事件
事件委托
面试官:解释下什么是事件代理?应用场景? | web 前端面试 - 面试官系列
JS 中的事件冒泡、事件捕获、事件委托 DOM 事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件 - 掘金
JS 如何实现异步操作
详解 JS 的四种异步解决方案:回调函数、Promise、Generator、async/await(干货满满)...-CSDN 博客
- 最开始通过设置回调函数的形式
- Promise
- async、await
判断对象是不是数组
JS 判断是否为对象或数组的几种方法_js 判断是否是对象-CSDN 博客
Arrays.isArray()
val instanceof Array
val?.constructor === Array
isPrototypeOf,判断一个对象是不是在另一个对象的原型链上
Array.prototype.isPrototypeof(val)
sourcemap
blog.csdn.net/weixin_40599109/article/details/107845431
主要是映射转换后的代码和源码的关系,方便做调试。