浏览器
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
⽂件- 路由器缓存
ISP
DNS
缓存DNS
递归查询(可能存在负载均衡导致每次IP
不⼀样)
开启一个
socket
与目标 IP 地址端口建立 TCP 连接- 三次握手
建立 TCP 后发送 HTTP请求
服务器接受请求并解析,将请求转发到服务程序
服务器检查
HTTP
请求头是否包含缓存验证信息,如果验证缓存新鲜,返回 304 等对应状态码处理程序读取完整请求并准备
HTTP
响应服务器将响应报⽂通过
TCP
连接发送回浏览器浏览器接收
HTTP
响应,然后根据情况选择关闭TCP
连接或者保留重⽤- 四次挥手
浏览器检查响应状态码
如果资源可缓存,缓存资源
对响应进行解码
根据资源类型确定如何处理
解析 HTML构建 DOM 树,下载 CSS、JS 资源等,构造 CSS 规则树,执行 JS 脚本
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 前端面试 - 面试官系列
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 = oldIndex
oldIndex === 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
主要是映射转换后的代码和源码的关系,方便做调试。