Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2021前端知识整理 #35

Open
HerryLo opened this issue Oct 29, 2022 · 0 comments
Open

2021前端知识整理 #35

HerryLo opened this issue Oct 29, 2022 · 0 comments
Assignees
Labels
Blog 文章

Comments

@HerryLo
Copy link
Member

HerryLo commented Oct 29, 2022

方便访问:语雀博文原文地址

这次对前端技术涉及到的点进行了一次有序的整理,这样记得更牢,也希望可以帮助到大家,下面是目录结构。(其中有很多内容被简化了,有兴趣可以查看每个目录下的参考文档,内容相对更加详情)

Js基础

javaScript数据类型5+1

基本类型 ——numberstringundefindbooleannull;
引用类型——Object;
目前基本类型中添加了一个新成员:symbol;
基本类型和引用类型区别:前者按值访问,因此为我们操作的就是存储在变量中的实际值;后者不能直接操作对象所在的内存空间,在操作对象时,实际操作的是该对象的引用。
基本类型存储在中;引用类型的 ‘变量’名称 存储在中,同时存储指针,该指针指向中的真实的值;(概念可以直接搜索 "js中栈和堆的概念和区别")

参考书:犀牛书

this指向

箭头函数:this继承包裹箭头函数的第一个普通函数中的this;

普通函数:new(this指向新对象)——bind/apply/call(this是强制指向的this)——this指向函数上下文对象——this指向window;

参考文章:https://juejin.cn/post/6844903941021384711

参考书:红宝书

闭包

一个函数引用另外一个函数内部变量就会形成闭包,闭包在平时开发中非常的多见;理解作用域链创建和使用的细节对理解闭包非常重要;

参考文章:https://juejin.cn/post/6844903747957719053

参考书:红宝书

原型

每个对象被创建开始,就与另一个对象关联,从另一个对象上继承其属性,这里的另一个对象就是原型;

原型存在的意义就是组成原型链。当范文一个对象属性时,先从对象本身找,找不到就去对象原型上找,如果还找不到,就去对象原型的原型上找,如此类推,直到找到为止;这条由对象及原型组成的查找链条就是原型链;

参考文章:https://juejin.cn/post/6844903936365690894

参考书:红宝书

继承

组合继承:通过 call修改子类this,将子类原型指向 new 父类();

寄生组合继承:通过 call 修改子类this,将子类原型指向父类原型(Object.create(父类.prototype)) ,并将子类原型构造函数指向子类;

参考书:红宝书

模块化

模块化特点:复用性、可维护性;

commonJs最早作为标准在NodeJs中引入, 作为NodeJs模块化标准;在commonJs中,require首次加载就会执行整个脚本,在内存中生成一个对象缓存下来,下次加载时可直接从缓存获取;
对于循环加载,只输出已执行的部分。还未执行部分不输出;

最新ES6的 ES Module,是静态加载;脚本被加载时,成为一个指向被加载模块的引用,不会缓存;

并发和并行

区别:并发是两个或多个事件在同一时间间隔内发生;并行是指两个或多个事件在同一时刻内发生;

并行通过提升硬件能力,只需要多核CPU即可达到;

并发中最常听到的就是高并发,在一定时间,服务器的吞吐量和QPS每秒响应请求数都是一定的。而在这样的情况下,要解决高并发的问题,最简单当然是提升机器性能,加内存,加硬盘,升级网速。当然,通过架构层面设计,也可以做点东西,部署多台机器,添加负载均衡层,将请求均匀;将数据库分库分表并读写分离;引入消息中间件等;缓存集群引入;

异步方案

回调函数:在浏览器端,异步问题就是Ajax网络请求响应异步,而回调函数可以一定程度解决这个响应异步的问题,缺点是:不易维护、不易读、不能直接return;

Generator:封装多个内部状态的异步解决方案,生成器函数;会返回一个迭代器,配合next()函数,可以控制开始、暂停和恢复代码;
场景:控制流管理、异步操作;

Promise:解决异步操作方案,包含异步操作结果的对象;内部存在三种状态,一旦开始不可更改状态;问题:无法取消,错误需要回调捕获;Promise是链式调用,每次调用then之后返回一个Promise,并且是一个全新的Promise;

Promise实现依据Promise/A+规范——

三种状态:pedding、fulfilled、rejected;

(Promise封装的Ajax为例子)进行异步处理流:执行Promise回调函数,由于异步操作,内部状态未变化。继续执行then回调,内部状态未变化,回调队列中then回调暂时不执行。此时,异步执行完成(网络请求响应),状态变化,Promise内部调用resolve函数,回调队列执行;

async/await:异步解决方案,需要配套使用;函数添加async后,函数会返回一个Promise;await内部实现了Generator,await是异步操作,后来的表达式不返回Promise的话,就会包装成Promise.resolve(返回值 ),然后去执行函数外的同步代码;

事件循环

一个事件循环又一个货多个任务队列;一个任务队列是一组任务;

1. 每个事件循环是一个正在运行的任务,它可以是一个任务或null;

2. 每个事件循环具有microtask队列(微任务队列),最初为空;

3. 每个事件循环都有一个执行微任务检查点的布尔值,该布尔值最初为false;

1. js的事件循环如何环执行?

浏览器事件循环执行顺序:

  1. 首先执行script脚本同步代码,属于宏任务;
  2. 当执行完当前所有同步任务后,执行栈为空,查询是否有异步任务代码需要执行;
  3. 执行当前宏任务下的所有微任务;
  4. 微任务执行完之后,如有必要会渲染页面;
  5. 然后开始下一轮事件循环;

2. 宏任务和微任务有哪些?

宏任务:script、setTimeout、setInterval、setImmediate、I/O、UI Render;

微任务:process.nextTick、promise、MutationObsever;


3. Nodejs的事件循环和浏览器事件循环区别?

Nodejs和浏览器端,宏任务和微任务交替执行,调用顺序基本相同。Nodejs执行会进行阶段区分,分为6个阶段:参考:Node.js 事件循环文档,每进入某个阶段,会从回调队列中取出函数执行。当队列为空或回调函数达到系统设定阈值,就进入下一个阶段。


异步方案

可以把ajax异步回调作为浏览器端的最初的异步解决方案,异步回调 ———> Promise实例 ——> Generator ——> async/await,大致是一个这样的线,我们需要关注的可能主要就是Promise 和 async/await的方案。

1. Promise内部实现了解嘛?它的具体工作流程是怎么样的?

Promise是异步解决方案,包含异步操作结果的对象,特点:内部存在三种状态,状态一旦变化不可更改状态;Promise是链式调用,每次调用then回调函数都会返回一个新的Promise,并且是一个全新的Promise;

Promise异步处理流:执行Promise实例回调函数,由于异步请求或者操作,此时状态未变化。继续执行then回调,由于状态未变化回调会被放入回调数组中。直到Promise实例中异步请求或者操作完成,状态发生变化,调用resolve函数,回调数组遍历执行,then回调函数拿到相关数据


2. 说一说async/await

参考:

Promise原理解析

图解Promise流程

浏览器渲染原理

浏览器是多进程应用,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。浏览器进程有:Browser进程(主进程)、插件进程、GPU进程、渲染进程(浏览器内核)。主进程只有一个是负责协调应用,同时默认每个Tab页面一个渲染进程,互不影响。我们常说的Js线程、GUI渲染线程就包含在渲染进程之中,还包括异步http请求线程、事件触发线程等。

1. 地址栏输入域名地址之后发生了什么?

1. DNS服务查询域名对应的目标服务IP地址;
2. 根据目标IP地址查找到目标服务器,并建立TCP连接;
3. 服务器响应返回数据或文本信息等;
4. 客户端获取到数据之后进行页面渲染;

2. 浏览器渲染的流程?

1. 请求html文件处理html标记构建DOM树;
2. 请求css文件处理css标记构建CSSOM树;
3. 将DOM和CSSOM合并成一个渲染树;
4. 根据渲染树布局,以计算每个节点的几何信息;
5. 将各节点绘制在屏幕上;

存在阻塞的css资源时,浏览器会延迟JS的执行和DOM的构建;

3. 重绘和回流?

重绘是当节点需要更改外观而不影响布局;回流是布局或几何属性需要改变;

参考:渲染性能

参考:

从浏览器多进程到JS单线程

你不知道的浏览器页面渲染机制

网络协议

关于TCP和UDP的介绍、TCP的三次握手和四次挥手就不细说,相关文章可以参考:传输层协议TCP和UDP
TCP三次握手和四次挥手

http协议是常用的网络传输协议,全称是超文本传输协议,它规定了http请求和响应的具体结构,当然还包含其他东西,例如:缓存、文件类型、参数、请求类型、状态等。它是建立在传输层TCP协议之上的,TCP握手成功之后,才可以进行网络数据传输。

HTTP/HTTPS

1. 什么是http协议?它是怎么样的?

http是TCP/IP协议应用层协议,主要负责数据或文本图片等内容的传输,它是建立在传输层TCP协议之上的。http分为请求报文和响应报文,从Web客户端发往Web服务器的HTTP报文称为请求报文(request message)。从服务器发往客户端的报文称为响应报文,报文分为:起始行、 首部字段、主体等;


2. http和https的区别?

http和https从字面的区别就是一个s,这个s就是SSL/TCL加密协议。说到加密协议就绕不开加密技术,在SSL/TCL加密协议中既有用到非对称加密,也有用到对称加密。SSL/TCL加密协议相当于是在应用层和传输层之间加了一层,可称为加密层。

大致流程:客户端向服务器端索要加密证书获取公钥等证书信息;双方协商生成"对话密钥";双方采用"对话密钥"进行加密通信。非对称加密保证"对话密钥"的安全传输,对称加密保证客户端和服务端对数据的加解密。


3. http网络缓存如何配置?

如果需要开启强缓存和协商缓存,可在服务端nginx web服务器进行对应的配置,开启对应的网络缓存。其他服务端web服务器也可配置。(配置细节网上一大堆)


4. 强缓存和协商缓存的区别?

强缓存:Cache-Control,常用属性有max-age、no-cache、no-store等。max-age表示相对时间内的缓存,在这个相对时间内不会再去请求对应资源;no-cache表示不意味着不缓存,它的意思是在使用缓存资源之前,它必须经过服务器的检查;
no-store表示不缓存。

协商缓存:Last-modified 和 ETag,这两个类似,可以理解为文件标识。也可以将ETag(你选择的版本ID)或者Last-modified日期添加到响应首部中。客户端下次获取资源时,他会分别通过If-None-Match(与ETage对应)和If-Modified-Since(与Last-Mofied对应)两个请求首部将值发送给服务器。如果服务器发现两次值都是对等的,就是返回一个HTTP 304。它们之间的区别:ETag 只要资源变化,它就会改变;Last-modified 不识别秒单位里的修改。

如何使用—————前端中保证HTML资源是最新的,设置如:max-age=300、ETag、Last-modified,当然也可考虑使用no-cache、ETag、Last-modified配合。而CSS和JS找资源已经被注入到HTML中,资源文件地址通常使用哈希值加入文件名中保证资源是最新,css和js文件可设置:max-age=31536000或last-modified 配合使用。


5. 了解http2.0嘛?为什么说http2.0更好?

http2.0基于二进制分帧层,http2.0可以在共享TCP连接的基础上同时发送请求和响应。在http1.x中,是通过文本的方式传输数据,基于文本的方式传输数据存在很多缺陷,文本的表现形式有多样性,因此要做到健壮性考虑的场景必然有很多,但是二进制则不同,只有0和1的组合,因此选择了二进制传输,实现方便且健壮。

为了保证http不受影响,那就需要在应用层(HTTP2.0)和传输层(TCP or UDP)之间增加一个二进制分帧层。在二进制分帧层上,http2.0会将所有传输的信息分为更小的消息和帧,并采用二进制格式编码,其中http1.x的首部信息会被封装到Headers帧,而Request Body则封装到Data帧。在传输中会共用一个TCP流(TCP连接中的一个虚拟通道,可以承载双向的消息),不至于重复连接。


参考:

书籍:HTTP权威指南

SSL/TLS协议运行机制的概述

前端框架

前端框架目前市面主流就是React和Vue,对于框架的使用和学习,前期建议多翻翻文档,中期根据自己在使用过程中遇到的问题学习,后期就可以考虑翻源码了。由于工作原因,我对react了解更多,所以分享主要就是React。

React和Vue作为前端框架在本质上做的是相同的事,在浏览器和开发操作之间加了一个中间层,来进项目的辅助管理和开发。

React框架

1. JSX本质是什么,它和JS的关系是什么?为什么使用JSX?

JSX是类HTML的语法结构,实质是JS的语法扩张。它语法结构简洁,通俗易懂,对于研发效率和体验有大的提升。


2. JSX背后的功能模块是什么,这个功能模块做了哪些事情?

JSX通过babel语法转换之后,实际就是通过React.createElement函数来创建React元素。createElement接收三个参数type类型,config配置,children子元素。通过createElement就可创建出虚拟DOM对象。


3. react16.3新旧生命周期,消失的旧生命周期有哪些?

去掉ComponentWillMountCompoentWillReceiveProps
升级为getDeicvedStateFromProps,保证生命周期更加单一,更可控;去掉ComponentWillUpdate新增getSnapshotDeforeUpdate;


4. React团队为什么要去掉旧的生命周期函数?

React16+引入Fiber架构,Fiber会将一个大的更新任务拆解为多个小任务,而且它是可中止,可恢复的。React16+生命周期被划分为rendercommit两个阶段。render阶段在执行过程中允许被打断,而commit阶段操作涉及真实DOM渲染,是不可打断的。

ComponentWillMount
ComponentWillUpdate
ComponentWillReceiveProps
这些生命周期,它们都处于render阶段,而在Fiber架构中,render阶段会被打断,重复被执行。在这些生命周期可能习惯做的事情可能有:setState、异步请求、操作真实DOM等。而在Fiber异步渲染控制下,这些生命周期可能会导致非常严重的bug(例如在这些废弃的生命周期中调用支付接口)。


5. React组件数据如何流动?实现数据通信的方案有哪些?

react是自上而下的单向组件数据流

1.父-子组件通过prop属性传递数据,子-父组件可通过函数;
2.兄弟组件共享同一个父组件,达到兄弟组件通信;
3.Context API实现全局通信;(目前还没试过)
4.redux数据状态容,进行可预测的状态管理;
5.发布/订阅模式实现任意组件通信;

6. 为什么是React Hooks?

相对于Class组件,函数组件更加轻量,更加符合UI=render(data)特点。同时在Fiber架构的加持下,Hooks的实现不是问题。配合函数组件的发展,Hooks应运而生,从而是函数组件真正把数据和渲染绑定到一起。当然Hooks也还是存在部分不足:部分周期不存在;不能很好的消化“复杂”,组件的拆分和组织是一个大的挑战,不易把握。


7. 为什么Hooks执行顺序如此重要?

Hooks本质是链表。例如 使用useText、useState创建state时,hook创建的state会以单链表形式保存,更新时,函数组件重新调用,hooks会依次遍历单链表,读取数据并更新,这一过程完全按照创建时的顺序来的。因此当更新时,位置一旦改变,执行顺序被替换,运行就会出现bug。


8. 调和(协调)和diff的关系或区别?

调和指的是虚拟DOM映射到真实DOM的过程。调和过程并不能喝diff画等号。调和是“使一致”的过程,而diff是“找不同”的过程,它只是“使一致”过程中的一个环节。(当然常说的调和相关问题多半就是diff过程的)


9. react的diff逻辑和思路?

1.因为时间付扎渡的原因,diff过程只针对同层的节点作比较;
2.对于同类型的组件,才有进一步对比的必要性;
3.对于列表组件,通过key属性来维持节点的稳定性,避免总是生产新节点;

10. setState的工作流是怎么样的?

非并发(concurrent)模式:setState会出现异步和同步的现象。在生命周期和合成事件中是同步,而在setTimeout、setInterval、DOM原生函数等函数中是同步的。那么这是为什么尼?在合成事件或生命周期执行时,批量更新的任务锁就被开启了,我们所做的setState操作会被放入到批量更新队列中,直到函数执行完,批量更新的任务锁才会被关闭。批量更新的任务锁是一个同步操作,而一旦你在setTimeout函数使用setState,此时setTimeout函数回调会被放入下一个宏任务执行,而当setState执行时,批量更新的任务锁时关闭的,它就不会放入到批量更新队列中,而是直接执行。

并发(concurrent)模式:setState不会出现异步和同步的现象。因为存在时间切片,只要当前时间片没有结束,依旧可以将多个 setState 合并成一个,即使是在setTimeout中被调用。而对于超过当前时间片的操作,会通过MessageChannel放入到下一个宏任务中继续执行。(MessageChannel接收消息的时机比 Promise 所在的 microTask 要晚,但是早于 setTimeout)


11. Stack Reconciler栈调和 有怎么样的局限性?

浏览器中Js线程和渲染线程是互斥的。这两个线程不能穿插执行,必须串行。而当Js线程长时间占用主线程,那么渲染线程的更新就不得不长时间的等待,这时就会导致页面卡顿。

Stack Reconciler栈调和是一个同步递归过程,虚拟DOM树diff算法遍历是深度优先遍历。由于它是同步的,不可在被打断。当处理结构复杂,体量庞大的虚拟DOM树时,Stack Reconciler时间会很长,以为这Js主线程长时间占用主线程,进而导致上述中说道的渲染卡顿/页面卡死。


12. 说一说Fiber架构?

特点:可中断、可恢复、存在优先级。

 Scheduler ————> Reconciler ————> Renderer
 更新优先级         找不同         渲染不同

Fiber架构模式下,每个更新任务会被赋予一个优先级。当然有任务A进入调度器,这个任务优先级更高,而Reconciler中已有任务B在执行,那么,Reconciler会将任务B终止,更高优先级的任务A被推入Reconciler。当A任务完成之后,新一轮调度会将之前中断的任务B重新推入Reconciler,继续它的渲染之旅。

render开始 ——————> (工作单元| 工作单元 | 工作单元) ——————> commit提交渲染

13. ReactDOM.render调用栈的初始化阶段、render阶段

初始化阶段:会创建root对象这个对象挂载_internalRoot属性,而_internalRoot也就是FiberRootFiberRoot的本质是一个FiberRootNode对象,其中包含current属性,current对象是一个FiberNode实例。current对象就是一个Fiber节点,并是Fiber树的头部节点;确定Fiber的优先级,结合优先级创建当前Fiber的update对象,并将其入队调度FiberRoot;接下来进入render阶段;(此时相当于只有一个Fiber头部节点)

render阶段:通过createWorkInProgress函数,创建rootFiber节点的副本workInProgress节点树(即current节点的副本节点),他们通过alternate互相引用;接着会触发beginWork函数,进而实现对新的Fiber节点的创建。循环遍历,组件元素Fiber会不断被创建(每个元素节点对应一个Fiber节点),直到创建到最后一个为止,此时Fiber树(单链表)基本完成;重点,此时已经遍历到了单链表的最底部节点,然后会由下自上的依次生成真实DOM节点,同时被它的父组件副作用链,这个副作用链也是一个单链表,直遍历到根节点,此时的根节点上的副作用链就包含的全部的DOM更新。那么剩下的只需要拿到root下的副作用链更新即可了。


参考:

修言 深入浅出搞定 React

React 架构的演变 - 从同步到异步

React 架构的演变 - 从递归到循环

React 架构的演变 - 更新机制

React 架构的演变 - Hooks 的实现

性能优化

可使用Chrome Lighthouse进行性能测评,根据测评结果也可以得出一些改进的点:

react框架层面可以做的优化方向核心点:减少重新 render 的次数减少计算的量,主要是减少重复计算。有下列具体方案:

1. 拆分公共组件和业务组件,根据具体业务分离,降低组件颗粒度;
2. shouldComponentUpdate中拦截非不要渲染;
3. 对于简单的数据类型,可考虑使用React.PureComponent;
4. 函数组件考虑使用React.meno,React.memo 与 React.PureComponent 非常相似;(版本React 16.6.0)
5. React Hooks组件使用useCallback缓存函数,避免每次返回一个新的函数;(版本React v16.8)
6. React Hooks组件使用useMemo缓存计算值;(版本React v16.8)

一个前端项目下通用的优化技巧:使用缓存、节流、压缩、按需加载、全局管理等方法或技巧。如下:

1. 避免频繁渲染更新,即使必须的情况下,也需要考虑是否使用节流函数;
2. 对于长列表页面,考虑翻页加载、点击下一页或者虚拟长列表,避免数据量过大,渲染卡顿;
3. 统一控制项目中的定时器函数,避免定时器偷跑;
4. 拆分Js文件,可考虑按需加载,提升加载速度;
5. 保证首屏必要数据渲染,可增加过渡图片,提升用户体验;
6. 对于后端接口多余数据可考虑清洗数据,只保留必要数据;
7. 避免过多的数据请求,可考虑使用数据缓存,提升用户体验;
8. 对于大图考虑CDN图片加载,也可考虑图片懒加载;
9. 避免使用过多css选择器嵌套;
10. 代码文件gzip压缩等,服务器相关缓存配置;

以上更多的是各种技巧和原则,如果要知道具体的标准,可以参考google为Web性能表现提供的文档developers.google.cn/Web Performance

设计模式

参考文章:https://www.yuque.com/yopai/pp6bv5/mtu8xy

结尾

以上内容是以前端面试之道目录结构作为大纲进行的系统的整理,有修改也有添加,其中的内容大部分都是查看博客和相关资料获取的,因为自己也有薄弱的地方,所以有些目录下的内容偏少。如果有有更多补偿可以评论或者单独发我,谢谢✍✍。

@HerryLo HerryLo self-assigned this Oct 29, 2022
@HerryLo HerryLo added the Blog 文章 label Oct 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Blog 文章
Projects
None yet
Development

No branches or pull requests

1 participant