2021年前端面试知识点大厂必备

本专题按照如下几个方便进行整理:javascript

  • HTTP && 浏览器
  • HTML && CSS
  • JS、TS、ES6
  • Vue
  • React
  • 构建工具 && 工程化
  • 性能优化

适合初次全面复习的同窗,查缺补漏,知识面比较全,复习完成后,再按照本人整理的面试高频题配合复习,使得找工做事半功倍,必定要理解,不要死记硬背,对于一些概念性的和原理的内容要深刻理解。css

“你从头读,尽可能往下读,直到你一窍不通时,再从头开始,这样坚持往下读,直到你彻底读懂为止。”html

HTTP && 浏览器

HTTP协议与数据请求

1. HTTP工做原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。客户端向服务器发送一个请求报文,服务器以一个状态行做为响应。前端

2.HTTP请求/响应的步骤

  • 1.客户端链接到Web服务器
  • 2.发送HTTP请求
  • 3.服务器接受请求并返回HTTP响应
  • 4.释放链接TCP链接
  • 5.客户端(浏览器)解析HTML内容

3.HTTP报文的组成成分

请求报文{ 请求行、请求头、空行、请求体 } 请求行:{http方法、页面地址、http协议、http版本} 响应报文{ 状态行、响应头、空行、响应体 }vue

Request Header:java

  1. GET /sample.Jsp HTTP/1.1  //请求行
  2. Host:  www.uuid.online/ //请求的目标域名和端口号
  3. Origin: http://localhost:8081/ //请求的来源域名和端口号 (跨域请求时,浏览器会自动带上这个头信息)
  4. Referer: https://localhost:8081/link?query=xxxxx //请求资源的完整URI
  5. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 //浏览器信息
  6. Cookie:  BAIDUID=FA89F036:FG=1; BD_HOME=1; sugstore=0  //当前域名下的Cookie
  7. Accept: text/html,image/apng  //表明客户端但愿接受的数据类型是html或者是png图片类型 
  8. Accept-Encoding: gzip, deflate  //表明客户端能支持gzip和deflate格式的压缩
  9. Accept-Language: zh-CN,zh;q=0.9  //表明客户端能够支持语言zh-CN或者zh(值得一提的是q(0~1)是优先级权重的意思,不写默认为1,这里zh-CN是1,zh是0.9)
  10. Connection: keep-alive  //告诉服务器,客户端须要的tcp链接是一个长链接

Response Header:node

  1. HTTP/1.1 200 OK  // 响应状态行
  2. Date:  Mon, 30 Jul 2018 02:50:55 GMT  //服务端发送资源时的服务器时间
  3. Expires:  Wed, 31 Dec 1969 23:59:59 GMT //比较过期的一种验证缓存的方式,与浏览器(客户端)的时间比较,超过这个时间就不用缓存(不和服务器进行验证),适合版本比较稳定的网页
  4. Cache-Control:  no-cache  // 如今最多使用的控制缓存的方式,会和服务器进行缓存验证,具体见博文”Cache-Control“
  5. etag:  "fb8ba2f80b1d324bb997cbe188f28187-ssl-df"  // 通常是Nginx静态服务器发来的静态文件签名,浏览在没有“Disabled cache”状况下,接收到etag后,同一个url第二次请求就会自动带上“If-None-Match”
  6. Last-Modified:  Fri, 27 Jul 2018 11:04:55 GMT //是服务器发来的当前资源最后一次修改的时间,下次请求时,若是服务器上当前资源的修改时间大于这个时间,就返回新的资源内容
  7. Content-Type:  text/html; charset=utf-8  //若是返回是流式的数据,咱们就必须告诉浏览器这个头,否则浏览器会下载这个页面,同时告诉浏览器是utf8编码,不然可能出现乱码
  8. Content-Encoding:  gzip  //告诉客户端,应该采用gzip对资源进行解码
  9. Connection:  keep-alive  //告诉客户端服务器的tcp链接也是一个长链接

4.HTTP 的 5 种方法

  • GET---获取资源
  • POST---传输资源
  • PUT---更新资源
  • DELETE---删除资源
  • HEAD---获取报文首部

HTTP 和 HTTPS

1.http 和 https 的基本概念

http: 是互联网上应用最为普遍的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。
https:是以安全为目标的 HTTP 通道,即 HTTP 下加入 SSL 层进行加密。react

https 协议的主要做用:创建一个信息安全通道,来确保数据的传输,确保网站的真实性。webpack

2.http 和 https 的区别?

  • http 是超文本传输协议,信息是明文传输,https 则是具备安全性的 ssl 加密传输协议。
  • Https 协议须要 ca 证书,费用较高。
  • 使用不一样的连接方式,端口也不一样,通常,http 协议的端口为 80,https 的端口为 443。
  • http 的链接很简单,是无状态的。

3.https 协议的工做原理

客户端在使用 HTTPS 方式与 Web 服务器通讯时有如下几个步骤,如图所示。nginx

  1. 客户端使用 https url 访问服务器,则要求 web 服务器创建 ssl 连接。
  2. web 服务器接收到客户端的请求以后,会将网站的证书(证书中包含了公钥),返回或 者说传输给客户端。
  3. 客户端和 web 服务器端开始协商 SSL 连接的安全等级,也就是加密等级。
  4. 客户端浏览器经过双方协商一致的安全等级,创建会话密钥,而后经过网站的公钥来加密会话密钥,并传送给网站。
  5. web 服务器经过本身的私钥解密出会话密钥。
  6. web 服务器经过会话密钥加密与客户端之间的通讯。

4.https 协议的优势

使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
HTTPS 协议要比 http 协议安全,可防止数据在传输过程当中不被窃取、改变,确保数据的完整性。 HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增长了中间人攻击的成本。

5.https 协议的缺点

  • https 握手阶段比较费时,会使页面加载时间延长 50%,增长 10%~20%的耗电。
  • https 缓存不如 http 高效,会增长数据开销。
  • SSL 证书也须要钱,功能越强大的证书费用越高。
  • SSL 证书须要绑定 IP,不能再同一个 ip 上绑定多个域名,ipv4 资源支持不了这种消耗。

从输入URL到页面加载的全过程

从输入URL到页面加载的主干流程

  1. 首先在浏览器中输入URL

  2. 查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,若是有则显示页面内容。若是没有则进行下一步。

    • 浏览器缓存:浏览器会记录DNS一段时间,所以,只是第一个地方解析DNS请求;
    • 操做系统缓存:若是在浏览器缓存中不包含这个记录,则会使系统调用操做系统, 获取操做系统的记录(保存最近的DNS查询缓存);
    • 路由器缓存:若是上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;
    • ISP缓存:若上述均失败,继续向ISP搜索。
  3. DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址

  4. 创建TCP链接:解析出IP地址后,根据IP地址和默认80端口,和服务器创建TCP链接

  5. 发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文做为TCP三次握手的第三次数据发送给服务器

  6. 服务器响应请求并返回结果:服务器对浏览器请求作出响应,并把对应的html文件发送给浏览器

  7. 关闭TCP链接:经过四次挥手释放TCP链接

  8. 浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:

    • 构建DOM树:词法分析而后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
    • 构建CSS规则树:生成CSS规则树(CSS Rule Tree)
    • 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
    • 布局(Layout):计算出每一个节点在屏幕中的位置
    • 绘制(Painting):即遍历render树,并使用UI后端层绘制每一个节点。

    浏览器渲染流程图

  9. JS引擎解析过程:调用JS引擎执行JS代码(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,做用域链、回收机制等等)

    • 建立window对象:window对象也叫全局执行环境,当页面产生时就被建立,全部的全局变量和函数都属于window的属性和方法,而DOM Tree也会映射在window的doucment对象上。当关闭网页或者关闭浏览器时,全局执行环境会被销毁。

    • 加载文件:完成js引擎分析它的语法与词法是否合法,若是合法进入预编译

    • 预编译:在预编译的过程当中,浏览器会寻找全局变量声明,把它做为window的属性加入到window对象中,并给变量赋值为'undefined';寻找全局函数声明,把它做为window的方法加入到window对象中,并将函数体赋值给他(匿名函数是不参与预编译的,由于它是变量)。而变量提高做为不合理的地方在ES6中已经解决了,函数提高还存在。

    • 解释执行:执行到变量就赋值,若是变量没有被定义,也就没有被预编译直接赋值,在ES5非严格模式下这个变量会成为window的一个属性,也就是成为全局变量。string、int这样的值就是直接把值放在变量的存储空间里,object对象就是把指针指向变量的存储空间。函数执行,就将函数的环境推入一个环境的栈中,执行完成后再弹出,控制权交还给以前的环境。JS做用域其实就是这样的执行流机制实现的。

浏览器渲染机制、重绘(Repaint)、重排/回流(Reflow):

  1. Reflow(回流):当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器须要从新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫作重排。

触发缘由:

1. 页面初始化的时候;
2. 操做DOM元素时;
3. 某些元素的尺寸改变了;
4. 若是 CSS 的属性发生变化了。
复制代码
  1. Repaint(重绘):当一个元素的外观发生改变,但没有改变布局,从新把元素外观绘制出来的过程,叫作重绘。

触发缘由:改变元素的 color、background、box-shadow 等属性

Reflow要比Repaint更花费时间,也就更影响性能。因此要尽可能避免过多的Reflow。

  1. 减小 reflow/repaint

    1. 样式集中修改,不要一条一条地修改 DOM 的样式。
    2. 不要把 DOM 结点的属性值放在循环里当成循环里的变量。
    3. 为动画的 HTML 元件使用 fixedabsoultposition,那么修改他们的 CSS 是不会 reflow 的。
    4. 不使用 table 布局。由于可能很小的一个小改动会形成整个 table 的从新布局。
    5. 尽可能只修改position:absolutefixed元素,对其余元素影响不大
    6. 动画开始GPU加速,translate使用3D变化

TCP三次握手

  1. 第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

  2. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。

握手过程当中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
复制代码

TCP 四次挥手

  1. 客户端进程发出链接释放报文,而且中止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即便不携带数据,也要消耗一个序号。

2)服务器收到链接释放报文,发出确认报文,ACK=1,ack=u+1,而且带上本身的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,可是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送链接释放报文(在这以前还须要接受服务器发送的最 后的数据)。

4)服务器将最后的数据发送完毕后,就向客户端发送链接释放报文,FIN=1,ack=u+1,因为在半关闭状态,服务器极可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

5)客户端收到服务器的链接释放报文后,必须发出确认,ACK=1,ack=w+1,而本身的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP链接尚未释放,必须通过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

6)服务器只要收到了客户端发出的确认,当即进入CLOSED状态。一样,撤销TCB后,就结束了此次的TCP链接。能够看到,服务器结束TCP链接的时间要比客户端早一些。

利用Socket创建网络链接的步骤

创建Socket链接至少须要一对套接字,其中一个运行于客户端,称为ClientSocket ,另外一个运行于服务器端,称为ServerSocket 。

套接字之间的链接过程分为三个步骤:服务器监听,客户端请求,链接确认。

  一、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待链接的状态,实时监控网络状态,等待客户端的链接请求。

  二、客户端请求:指客户端的套接字提出链接请求,要链接的目标是服务器端的套接字。

  为此,客户端的套接字必须首先描述它要链接的服务器的套接字,指出服务器端套接字的地址和端口号,而后就向服务器端套接字提出链接请求。

  三、链接确认:当服务器端套接字监听到或者说接收到客户端套接字的链接请求时,就响应客户端套接字的请求,创建一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式创建链接。

而服务器端套接字继续处于监听状态,继续接收其余客户端套接字的链接请求。

TCP和UDP的区别

  1. TCP是面向连接的,而UDP是面向无链接的。

  2. TCP仅支持单播传输,UDP 提供了单播,多播,广播的功能。

  3. TCP的三次握手保证了链接的可靠性; UDP是无链接的、不可靠的一种数据传输协议,首先不可靠性体如今无链接上,通讯都不须要创建链接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收。

  4. UDP的头部开销比TCP的更小,数据传输速率更高实时性更好

GET与POST的区别

1.浏览器回退表现不一样 GET在浏览器回退时时无害的,而POST会再次提交请求

2.浏览器对请求地址的处理不一样 GET请求地址会被浏览器主动缓存,而POST不会,除非手动设置

3.浏览器对响应的处理不一样GET请求参数会被完整的保留在浏览器历史记录里,而POST中的参数不会被保留

4.参数大小不一样. GET请求在URL中传送的参数是有长度的限制,而POST没有限制

5.安全性不一样. GET参数经过URL传递,会暴露,不安全;POST放在Requset Body中,相对更安全

6.针对数据操做的类型不一样.GET对数据进行查询,POST主要对数据进行增删改!简单说,GET是只读,POST是写。

HTTP 请求跨域问题

  1. 跨域的原理

    跨域,是指浏览器不能执行其余网站的脚本。它是由浏览器的同源策略形成的。
    同源策略,是浏览器对 JavaScript 实施的安全限制,只要协议、域名、端口有任何一个不一样,都被看成是不一样的域。
    跨域原理,便是经过各类方式,避开浏览器的安全限制

  2. 解决方案

    最初作项目的时候,使用的是jsonp,但存在一些问题,使用get请求不安全,携带数据较小,后来也用过iframe,但只有主域相同才行,也是存在些问题,后来经过了解和学习发现使用代理和proxy代理配合起来使用比较方便,就引导后台按这种方式作下服务器配置,在开发中使用proxy,在服务器上使用nginx代理,这样开发过程当中彼此都方便,效率也高;如今h5新特性还有 windows.postMessage()

    • JSONP
      ajax 请求受同源策略影响,不容许进行跨域请求,而 script 标签 src 属性中的链 接却能够访问跨域的 js 脚本,利用这个特性,服务端再也不返回 JSON 格式的数据,而是 返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。

      步骤:

      1. 去建立一个script标签
      2. script的src属性设置接口地址
      3. 接口参数,必需要带一个自定义函数名,要否则后台没法返回数据
      4. 经过定义函数名去接受返回的数据
      //动态建立 script
      var script = document.createElement('script');
      
      // 设置回调函数
      function getData(data) {
          console.log(data);
      }
      
      //设置 script 的 src 属性,并设置请求地址
      script.src = 'http://localhost:3000/?callback=getData';
      
      // 让 script 生效
      document.body.appendChild(script);
      复制代码

      JSONP 的缺点:
      JSON 只支持 get,由于 script 标签只能使用 get 请求; JSONP 须要后端配合返回指定格式的数据。

    • document.domain 基础域名相同 子域名不一样

    • window.name 利用在一个浏览器窗口内,载入全部的域名都是共享一个window.name

    • CORS CORS(Cross-origin resource sharing)跨域资源共享 服务器设置对CORS的支持原理:服务器设置Access-Control-Allow-Origin HTTP响应头以后,浏览器将会容许跨域请求

    • proxy代理

    • window.postMessage() 利用h5新特性window.postMessage()

    • Websocket

客户端与服务端长链接的几种方式

  1. ajax 轮询 实现原理:ajax 轮询指客户端每间隔一段时间向服务端发起请求,保持数据的同步。

    优势:可实现基础(指间隔时间较短)的数据更新。

    缺点:这种方法也只是尽可能的模拟即时传输,但并不是真正意义上的即时通信,颇有可能出现客户端请求时,服务端数据并未更新。或者服务端数据已更新,但客户端未发起请求。致使屡次请求资源浪费,效率低下。【数据更新不及时,效率低下

  2. long poll 长轮询

    实现原理: long poll 指的是客户端发送请求以后,若是没有数据返回,服务端会将请求挂起放入队列(不断开链接)处理其余请求,直到有数据返回给客户端。而后客户端再次发起请求,以此轮询。在 HTTP1.0 中客户端能够设置请求头 Connection:keep-alive,服务端收到该请求头以后知道这是一个长链接,在响应报文头中也添加 Connection:keep-alive。客户端收到以后表示长链接创建完成,能够继续发送其余的请求。在 HTTP1.1 中默认使用了 Connection:keep-alive 长链接。

    优势:减小客户端的请求,下降无效的网络传输,保证每次请求都有数据返回,不会一直占用线程。

    缺点:没法处理高并发,当客户端请求量大,请求频繁时对服务器的处理能力要求较高。服务器一直保持链接会消耗资源,须要同时维护多个线程,服务器所能承载的 TCP 链接数是有上限的,这种轮询很容易把链接数顶满。每次通信都须要客户端发起,服务端不能主动推送。【没法处理高并发,消耗服务器资源严重,服务端不能主动推送

  3. iframe 长链接

    实现原理:
    在网页上嵌入一个 iframe 标签,该标签的 src 属性指向一个长链接请求。这样服务端就能够源源不断地给客户端传输信息。保障信息实时更新。

    优势:消息及时传输。

    缺点消耗服务器资源

  4. WebSocket

    实现原理: Websocket 实现了客户端与服务端的双向通讯,只须要链接一次,就能够相互传输数据,很适合实时通信、数据实时更新等场景。

    Websocket 协议与 HTTP 协议没有关系,它是一个创建在 TCP 协议上的全新协议,为了兼容 HTTP 握手规范,在握手阶段依然使用 HTTP 协议,握手完成以后,数据经过 TCP 通道进行传输。

    Websoket 数据传输是经过 frame 形式,一个消息能够分红几个片断传输。这样大数据能够分红一些小片断进行传输,不用考虑因为数据量大致使标志位不够的状况。也能够边生成数据边传递消息,提升传输效率。

    优势: 双向通讯。客户端和服务端双方均可以主动发起通信。 没有同源限制。客户端能够与任意服务端通讯,不存在跨域问题。 数据量轻。第一次链接时须要携带请求头,后面数据通讯都不须要带请求头,减小了请求头的负荷。 传输效率高。由于只须要一次链接,因此数据传输效率高。

    缺点: 长链接须要后端处理业务的代码更稳定,推送消息相对复杂;
    长链接受网络限制比较大,须要处理好重连。
    兼容性,WebSocket 只支持 IE10 及其以上版本。
    服务器长期维护长链接须要必定的成本,各个浏览器支持程度不一;
    成熟的 HTTP 生态下有大量的组件能够复用,WebSocket 则没有,遇到异常问题难以快速定位快速解决。【须要后端代码稳定,受网络限制大,兼容性差,维护成本高,生态圈小】

HTTP状态码及常见状态码

HTTP状态码

  • 1xx:指示信息类,表示请求已接受,继续处理
  • 2xx:指示成功类,表示请求已成功接受
  • 3xx:指示重定向,表示要完成请求必须进行更近一步的操做
  • 4xx:指示客户端错误,请求有语法错误或请求没法实现
  • 5xx:指示服务器错误,服务器未能实现合法的请求

常见状态码

  • 200 OK:客户端请求成功
  • 301 Moved Permanently:所请求的页面已经永久重定向至新的URL
  • 302 Found:所请求的页面已经临时重定向至新的URL
  • 304 Not Modified 未修改。
  • 403 Forbidden:对请求页面的访问被禁止
  • 404 Not Found:请求资源不存在
  • 500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还能够继续使用
  • 503 Server Unavailable:请求未完成,服务器临时过载或宕机,一段时间后可恢复正常

Cookie、sessionStorage、localStorage 的区别

相同点

  • 存储在客户端

不一样点

  • cookie数据大小不能超过4k;sessionStorage和localStorage的存储比cookie大得多,能够达到5M+
  • cookie设置的过时时间以前一直有效;localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage数据在当前浏览器窗口关闭后自动删除
  • cookie的数据会自动的传递到服务器;sessionStorage和localStorage数据保存在本地

HTML && CSS

DOCTYPE及做用

概念

DTD(document type definition,文档类型定义)声明于文档最前面,用来定义XML或(X)HTML的文件类型。浏览器会使用它来判断文档类型,并根据这个判断决定用什么引擎来解析和渲染他们。

解析引擎的两种模式

解析引擎有严格模式和混杂模式。严格模式的排版和 JS 运做模式是 以该浏览器支持的最高标准运行。混杂模式,向后兼容,模拟老式浏览器,防止浏览器没法兼容页面。

DOCTYPE的做用

DOCTYPE是用来声明文档类型和DTD规范的,其做用一是文件的合法性验证。若是文件代码不合法,那么浏览器解析时会出一些错误。二是浏览器会使用它来判断文档类型,并根据这个判断决定用什么引擎来解析和渲染他们。

HTML5 新特性、语义化

  1. 概念

    HTML5的语义化指的是合理正确的使用语义化的标签来建立页面结构。【正确的标签作正确的事】

  2. 语义化标签

    header nav main article section aside footer

  3. 语义化的优势:

    • 没CSS样式的状况下,页面总体也会呈现很好的结构效果
    • 代码结构清晰,易于阅读,
    • 利于开发和维护 方便其余设备解析(如屏幕阅读器)根据语义渲染网页。
    • 有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不一样的标签来赋予不一样的权重

HTML5新特性有哪些

  • 语义化标签
  • 音视频处理API(audio,video)
  • canvas / webGL
  • 拖拽释放(Drag and drop) API
  • history API
  • requestAnimationFrame
  • 地理位置(Geolocation)API
  • webSocket
  • web存储 localStorage、SessionStorage
  • 表单控件,calendar、date、time、email、url、search

渐进加强与优雅降级的理解及区别

渐进加强(Progressive Enhancement): 一开始就针对低版本浏览器进行构建页面,完成基本的功能,而后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

优雅降级(Graceful Degradation): 一开始就构建站点的完整功能,而后针对浏览器测试和修复。好比一开始使用 CSS3 的特性构建了一个应用,而后逐步针对各大浏览器进行 hack 使其能够在低版本浏览器上正常浏览。 二者区别 一、广义: 其实要定义一个基准线,在此之上的加强叫作渐进加强,在此之下的兼容叫优雅降级 二、狭义: 渐进加强通常说的是使用CSS3技术,在不影响老浏览器的正常显示与使用情形下来加强体验,而优雅降级则是体现html标签的语义,以便在js/css的加载失败/被禁用时,也不影响用户的相应功能。

/* 例子 */
.transition { /*渐进加强写法*/
  -webkit-transition: all .5s;
     -moz-transition: all .5s;
       -o-transition: all .5s;
          transition: all .5s;
}
.transition { /*优雅降级写法*/
          transition: all .5s;
       -o-transition: all .5s;
     -moz-transition: all .5s;
  -webkit-transition: all .5s;
}

复制代码

常见的兼容性问题

  1. 不一样浏览器的标签默认的margin和padding不同。*{margin:0;padding:0;}

  2. IE6双边距bug:块属性标签float后,又有横行的margin状况下,在IE6显示margin比设置的大。hack:display:inline;将其转化为行内属性。

  3. 设置较小高度标签(通常小于10px),在IE6,IE7中高度超出本身设置高度。hack:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。

  4. Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,可经过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。

  5. 超连接访问事后hover样式就不出现了,被点击访问过的超连接样式再也不具备hover和active了。解决方法是改变CSS属性的排列顺序:L-V-H-A ( love hate ): a:link {} a:visited {} a:hover {} a:active {}

CSS 选择器及优先级

选择器

  • id选择器(#myid)
  • 类选择器(.myclass)
  • 标签选择器(div, h1,p)
  • 相邻选择器(h1 + p)
  • 子选择器(ul > li)
  • 后代选择器(li a)
  • 通配符选择器(*)
  • 属性选择器(a[rel="external"])
  • 伪类选择器(a:hover, li:nth-child)

优先级

带!important 标记的样式属性优先级最高; 样式表的来源不一样时,优先级顺序为:内联样式> 内部样式 > 外部样式 > 浏览器用户 自定义样式 > 浏览器默认样式
样式表的来源相同时:!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性

CSS 盒子模型

CSS 盒模型本质上是一个盒子,它包括:边距,边框,填充和实际内容。CSS 中的盒子模型包括 IE 盒子模型和标准的 W3C 盒子模型。在标准的盒子模型中,width 指 content 部分的宽度,在 IE 盒子模型中,width 表示 content+padding+border 这三个部分的宽度,故在计算盒子的宽度时存在差别:

标准盒模型: 一个块的总宽度=width+margin(左右)+padding(左右)+border(左右)

怪异盒模型: 一个块的总宽度=width+margin(左右)(既 width 已经包含了 padding 和 border 值)

BFC(块级格式上下文)

BFC的概念

BFCBlock Formatting Context 的缩写,即块级格式化上下文。BFC是CSS布局的一个概念,是一个独立的渲染区域,规定了内部box如何布局, 而且这个区域的子元素不会影响到外面的元素,其中比较重要的布局规则有内部 box 垂直放置,计算 BFC 的高度的时候,浮动元素也参与计算。

BFC的原理布局规则

  • 内部的Box会在垂直方向,一个接一个地放置
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  • 每一个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,不然相反
  • BFC的区域不会与float box重叠
  • BFC是一个独立容器,容器里面的子元素不会影响到外面的元素
  • 计算BFC的高度时,浮动元素也参与计算高度
  • 元素的类型和display属性,决定了这个Box的类型。不一样类型的Box会参与不一样的Formatting Context

如何建立BFC?

  • 根元素,即HTML元素
  • float的值不为none
  • position为absolute或fixed
  • display的值为inline-block、table-cell、table-caption
  • overflow的值不为visible

BFC的使用场景

  • 去除边距重叠现象
  • 清除浮动(让父元素的高度包含子浮动元素)
  • 避免某元素被浮动元素覆盖
  • 避免多列布局因为宽度计算四舍五入而自动换行

CSS3新特性

  • 过渡
/*全部属性从原始值到制定值的一个过渡,运动曲线ease,运动时间0.5秒*/ 
transition:all,.5s
复制代码
  • 动画
//animation:动画名称,一个周期花费时间,运动曲线(默认ease),动画延迟(默认0),播放次数(默认1),是否反向播放动画(默认normal),是否暂停动画(默认running)
/*执行一次logo2-line动画,运动时间2秒,运动曲线为 linear*/
animation: logo2-line 2s linear;
复制代码
  • 形状转换
//transform:适用于2D或3D转换的元素
//transform-origin:转换元素的位置(围绕那个点进行转换)。默认(x,y,z):(50%,50%,0)
transform:translate(30px,30px);
transform:rotate(30deg);
transform:scale(.8);
复制代码
  • 选择器:nth-of-type()

  • 阴影 文字阴影: text-shadow: 2px 2px 2px #000;(水平阴影,垂直阴影,模糊距离,阴影颜色) 盒子阴影: box-shadow: 10px 10px 5px #999

  • 边框 border-image: url(border.png);

  • 背景

  • 文字

  • 渐变

  • Filter(滤镜)

  • 弹性布局、栅格布局、多列布局

  • 媒体查询

position 属性的值有哪些及其区别

固定定位 fixed: 元素的位置相对于浏览器窗口是固定位置,即便窗口是滚动的它也不会移动。Fixed 定 位使元素的位置与文档流无关,所以不占据空间。 Fixed 定位的元素和其余元素重叠。

相对定位 relative: 若是对一个元素进行相对定位,它将出如今它所在的位置上。而后,能够经过设置垂直 或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,不管是 否进行移动,元素仍然占据原来的空间。所以,移动元素会致使它覆盖其它框。

绝对定位 absolute: 绝对定位的元素的位置相对于最近的已定位父元素,若是元素没有已定位的父元素,那 么它的位置相对于。absolute 定位使元素的位置与文档流无关,所以不占据空间。 absolute 定位的元素和其余元素重叠。

粘性定位 sticky: 元素先按照普通文档流定位,而后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。然后,元素定位表现为在跨越特定阈值前为相对定 位,以后为固定定位。

默认定位 Static: 默认值。没有定位,元素出如今正常的流中(忽略 top, bottom, left, right 或者 z-index 声 明)。 inherit: 规定应该从父元素继承 position 属性的值。

box-sizing属性

box-sizing 规定两个并排的带边框的框,语法为 box-sizing:content-box/border-box/inherit

content-box:宽度和高度分别应用到元素的内容框,在宽度和高度以外绘制元素的内边距和边框。【标准盒子模型】

border-box:为元素设定的宽度和高度决定了元素的边框盒。【IE 盒子模型】

inherit:继承父元素的 box-sizing 值。

让一个元素水平垂直居中,到底有多少种方案?

  • 水平居中

    • 对于 行内元素 : text-align: center;

    • 对于肯定宽度的块级元素:

      (1)width和margin实现。margin: 0 auto;

      (2)绝对定位和margin-left: -width/2, 前提是父元素position: relative

    • 对于宽度未知的块级元素

      (1)table标签配合margin左右auto实现水平居中。使用table标签(或直接将块级元素设值为 display:table),再经过给该标签添加左右margin为auto。

      (2)inline-block实现水平居中方法。display:inline-block和text-align:center实现水平居中。

      (3)绝对定位+transform,translateX能够移动自己元素的50%。

      (4)flex布局使用justify-content:center

  • 垂直居中

    1. 利用 line-height 实现居中,这种方法适合纯文字类
    2. 经过设置父容器 相对定位 ,子级设置 绝对定位,标签经过margin实现自适应居中
    3. 弹性布局 flex :父级设置display: flex; 子级设置margin为auto实现自适应居中
    4. 父级设置相对定位,子级设置绝对定位,而且经过位移 transform 实现
    5. table 布局,父级经过转换成表格形式,而后子级设置 vertical-align 实现。(须要注意的是:vertical-align: middle使用的前提条件是内联元素以及display值为table-cell的元素)。

页面布局

1.Flex 布局

布局的传统解决方案,基于盒状模型,依赖 display 属性 + position 属性 + float 属性。它对于那些特殊布局很是不方便,好比,垂直居中就不容易实现。

Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。指定容器 display: flex 便可。 简单的分为容器属性和元素属性。

容器的属性:

  • flex-direction:决定主轴的方向(即子 item 的排列方法)flex-direction: row | row-reverse | column | column-reverse;
  • flex-wrap:决定换行规则 flex-wrap: nowrap | wrap | wrap-reverse;
  • flex-flow: .box { flex-flow: || ; }
  • justify-content:对其方式,水平主轴对齐方式
  • align-items:对齐方式,竖直轴线方向
  • align-content

项目的属性(元素的属性):

  • order 属性:定义项目的排列顺序,顺序越小,排列越靠前,默认为 0
  • flex-grow 属性:定义项目的放大比例,即便存在空间,也不会放大
  • flex-shrink 属性:定义了项目的缩小比例,当空间不足的状况下会等比例的缩小,若是 定义个 item 的 flow-shrink 为 0,则为不缩小
  • flex-basis 属性:定义了在分配多余的空间,项目占据的空间。
  • flex:是 flex-grow 和 flex-shrink、flex-basis 的简写,默认值为 0 1 auto。
  • align-self:容许单个项目与其余项目不同的对齐方式,能够覆盖
  • align-items,默认属 性为 auto,表示继承父元素的 align-items 好比说,用 flex 实现圣杯布局

2.Rem 布局

首先 Rem 相对于根(html)的 font-size 大小来计算。简单的说它就是一个相对单例 如:font-size:10px;,那么(1rem = 10px)了解计算原理后首先解决怎么在不一样设备上设置 html 的 font-size 大小。其实 rem 布局的本质是等比缩放,通常是基于宽度。

优势:能够快速适用移动端布局,字体,图片高度

缺点

①目前 ie 不支持,对 pc 页面来说使用次数很少;
②数据量大:全部的图片,盒子都须要咱们去给一个准确的值;才能保证不一样机型的适配;
③在响应式布局中,必须经过 js 来动态控制根元素 font-size 的大小。也就是说 css 样式和 js 代码有必定的耦合性。且必须将改变 font-size 的代码放在 css 样式以前。

3.百分比布局

经过百分比单位 " % " 来实现响应式的效果。经过百分比单位能够使得浏览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果。 直观的理解,咱们可能会认为子元素的百分比彻底相对于直接父元素,height 百分比相 对于 height,width 百分比相对于 width。 padding、border、margin 等等不管是垂直方向仍是水平方向,都相对于直接父元素的 width。 除了 border-radius 外,还有好比 translate、background-size 等都是相对于自身的。

缺点

(1)计算困难
(2)各个属性中若是使用百分比,相对父元素的属性并非惟一的。形成咱们使用百分比单位容易使布局问题变得复杂。

4.浮动布局

浮动布局:当元素浮动之后能够向左或向右移动,直到它的外边缘碰到包含它的框或者另一个浮动元素的边框为止。元素浮动之后会脱离正常的文档流,因此文档的普通流中的框就变的好像浮动元素不存在同样。

优势

这样作的优势就是在图文混排的时候能够很好的使文字环绕在图片周围。另外当元素浮动了起来以后,它有着块级元素的一些性质例如能够设置宽高等,但它与inline-block仍是有一些区别的,第一个就是关于横向排序的时候,float能够设置方向而inline-block方向是固定的;还有一个就是inline-block在使用时有时会有空白间隙的问题

缺点

最明显的缺点就是浮动元素一旦脱离了文档流,就没法撑起父元素,会形成父级元素高度塌陷

清除浮动的方式

  • 添加额外标签
<div class="parent">
    //添加额外标签而且添加clear属性
    <div style="clear:both"></div>
    //也能够加一个br标签
复制代码
  • 父级添加overflow属性,或者设置高度
  • 创建伪类选择器清除浮动
//在css中添加:after伪元素
.parent:after{
    /* 设置添加子元素的内容是空 */
    content: '';
    /* 设置添加子元素为块级元素 */
    display: block;
    /* 设置添加的子元素的高度0 */
    height: 0;
    /* 设置添加子元素看不见 */
    visibility: hidden;
    /* 设置clear:both */
    clear: both;
}
复制代码

CSS预处理器Sass、Less、Stylus的区别

什么事CSS预处理器?

CSS预处理器是一种语言用来为CSS增长一些变成的特性,无需考虑浏览器兼容问题,例如你能够在CSS中使用变量,简单的程序逻辑、函数等在编程语言中的一些基本技巧,可让CSS更加简洁,适应性更强,代码更直观等诸多好处 基本语法区别

Sass是以.sass为扩展名,Less是以.less为扩展名,Stylus是以.styl为扩展名 变量的区别

Sass 变量必须是以$开头的,而后变量和值之间使用冒号(:)隔开,和css属性是同样的。 Less 变量是以@开头的,其他sass都是同样的。 Stylus 对变量是没有任何设定的,能够是以$开头或者任意字符,并且变量之间能够冒号,空格隔开,可是在stylus中不能用@开头 三种预处理器都有:嵌套、运算符、颜色函数、导入、继承、混入。Stylus还有一些高级特性。例如循环、判断等

隐藏页面中某个元素的方法

1.opacity:0,该元素隐藏起来了,但不会改变页面布局,而且,若是该元素已经绑定 一些事件,如click 事件,那么点击该区域,也能触发点击事件的

2.visibility:hidden,该元素隐藏起来了,但不会改变页面布局,可是不会触发该元素已 经绑定的事件 ,隐藏对应元素,在文档布局中仍保留原来的空间(重绘)

3.display:none,把元素隐藏起来,而且会改变页面布局,能够理解成在页面中把该元素。 不显示对应的元素,在文档布局中再也不分配空间(回流+重绘)

JS、TS、ES6

JS中的8种数据类型及区别

包括值类型(基本对象类型)和引用类型(复杂对象类型)

基本类型(值类型): Number(数字),String(字符串),Boolean(布尔),Symbol(符号),null(空),undefined(未定义)在内存中占据固定大小,保存在栈内存中

引用类型(复杂数据类型): Object(对象)、Function(函数)。其余还有Array(数组)、Date(日期)、RegExp(正则表达式)、特殊的基本包装类型(String、Number、Boolean) 以及单体内置对象(Global、Math)等 引用类型的值是对象 保存在堆内存中,栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址。

简单介绍一下 symbol?

Symbol 是 ES6 的新增属性,表明用给定名称做为惟一标识,这种类型的值能够这样建立,let id=symbol(“id”);Symbl 确保惟一,即便采用相同的名称,也会产生不一样的值,有内置方法 Object.getOwnPropertySymbols(obj)能够得到全部的 symbol。 也有一个方法 Reflect.ownKeys(obj)返回对象全部的键,包括 symbol。

null 和 undefined的区别

undefined是访问一个未初始化的变量时返回的值,而null是访问一个还没有存在的对象时所返回的值。undefined看做是空的变量,而null看做是空的对象

JS中的数据类型检测方案

1.typeof

console.log(typeof 1);               // number
console.log(typeof true);            // boolean
console.log(typeof 'mc');            // string
console.log(typeof function(){});    // function
console.log(typeof console.log());   // function
console.log(typeof []);              // object 
console.log(typeof {});              // object
console.log(typeof null);            // object
console.log(typeof undefined);       // undefined
复制代码

优势:可以快速区分基本数据类型

缺点:不能将Object、Array和Null区分,都返回object

2.instanceof

console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false 
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true
复制代码

优势:可以区分Array、Object和Function,适合用于判断自定义的类实例对象

缺点:Number,Boolean,String基本数据类型不能判断

3.Object.prototype.toString.call()

var toString = Object.prototype.toString;
console.log(toString.call(1));                      //[object Number]
console.log(toString.call(true));                   //[object Boolean]
console.log(toString.call('mc'));                   //[object String]
console.log(toString.call([]));                     //[object Array]
console.log(toString.call({}));                     //[object Object]
console.log(toString.call(function(){}));           //[object Function]
console.log(toString.call(undefined));              //[object Undefined]
console.log(toString.call(null));                   //[object Null]
复制代码

优势:精准判断数据类型

缺点:写法繁琐不容易记,推荐进行封装后使用

变量计算

强制类型转换

// 1.字符串拼接
var a = 100 + 10 // 110
var b = 100 + '10' // '10010'
// 2.==运算符
100 = '100' // true
0 == '' // true
null = undefined // true [nullundefined都会转换成false]
// 3.if语句
let a = true
if(a){
// 
}

let b = 100
if(b){
// 会将b强制转换成boolean类型
}

let c = ''
if(c){
// 空字符串会被强制转换成boolean
}
//4.逻辑运算符
console. Log (10 && 0) // 0
console. log('' || 'abc') // 'abc'
console. log(!window.abc) // true
//判断一个变量会被当作true仍是false,使用双非判断
var a = 100
console.log(!!a)
复制代码

var && let && const

ES6以前建立变量用的是var,以后建立变量用的是let/const

三者区别

  1. var定义的变量,没有块的概念,能够跨块访问, 不能跨函数访问。
    let定义的变量,只能在块做用域里访问,不能跨块访问,也不能跨函数访问。
    const用来定义常量,使用时必须初始化(即必须赋值),只能在块做用域里访问,且不能修改。

  2. var能够先使用,后声明,由于存在变量提高;let必须先声明后使用。

  3. var是容许在相同做用域内重复声明同一个变量的,而let与const不容许这一现象。

  4. 在全局上下文中,基于let声明的全局变量和全局对象GO(window)没有任何关系 ;
    var声明的变量会和GO有映射关系;

  5. 解决暂时性死区

暂时性死区是浏览器的bug:检测一个未被声明的变量类型时,不会报错,会返回undefined
如:console.log(typeof a) //undefined
而:console.log(typeof a)//未声明以前不能使用
let a

  1. let /const/function会把当前所在的大括号(除函数以外)做为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等相似的需求,无需再本身构建闭包来存储,只要基于let的块做用特征便可解决

变量提高

当浏览器开辟出供代码执行的栈内存后,代码并无自上而下当即执行,而是继续作了一些事情:把当前做用域中全部带var、function关键字的进行提早的声明和定义 =>变量提高机制 【预解析】

  • 带var的只是提早声明,没有赋值,默认值是undefined。
  • 带function的声明加赋值。
  • 不带var的a=3表示给全局设置window.a=3和在全局做用域下var a=3是同样的;

在变量提高阶段,遇到大括号判断体等,不论条件是否成立,都要进行变量提高,而在高版本浏览器中,函数只声明、不赋值。

JS的单线程&同步

  1. 什么是单线程
    单线程即同一时间只有一个线程,只能作一件事
    缘由:避免DOM渲染的冲突
    解决方案:异步
    实现方式:event-loop

  2. JS的 同步任务/异步任务

    • 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务

    • 异步:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行

  3. JavaScript为何须要异步

    若是在JS代码执行过程当中,某段代码执行太久,后面的代码迟迟不能执行,产生阻塞(即卡死),会影响用户体验。

  4. JavaScript怎么实现异步

    JS 实现异步时经过 事件循环(Event Loop),是JS异步的解决方案。 JS实现异步的具体解决方案

    一、同步代码,直接执行
    二、异步代码先放在 异步队列
    三、待同步函数执行完毕,轮询执行异步队列 中的函数

  5. 目前JS解决异步的方案有哪些

    • 回调函数
    • 事件监听
    • 发布-订阅
    • Promise
    • Generator
    • Async/Await

JS堆栈内存的运行机制

Snipaste_2021-07-28_19-05-38.png

JS内存空间分为栈(stack)堆(heap)池(通常也会归类为栈中) 。 其中存放变量,存放复杂对象,存放常量,因此也叫常量池。

  • 基本类型:--> 内存(不包含闭包中的变量)
  • 引用类型:--> 内存

栈内存(Stack):浏览器在计算机内存中分配出一块内存供代码执行的环境栈(ECStack),也称栈内存 ;

基本数据类型都是存到栈里面的。 引用数据类型指针存到栈内存。

堆内存(Heap):浏览器会把内置的属性和方法放到一个单独的内存中,

引用数据类型是先开辟一个堆内存,一个16进制的地址,按照键、值分别存放,最后把地址放到栈中供代码关联使用;

js 中存在多种做用域(全局,函数私有的,块级私有的),引擎在编译执行代码的过程当中,首先会建立一个执行栈,也就是栈内存(ECStack => 执行环境栈),而后执行代码。

代码执行前首先会造成本身的EC(执行上下文),执行上下文分为全局执行上下文(EC(G))和函数执行上下文(EC(...)),其中函数的执行上下文是私有的。

建立执行上下文的过程当中,可能会建立:

  • GO(Global Object):全局对象 浏览器端,会把GO赋值给window
  • VO(Varible Object):变量对象,存储当前上下文中的变量。
  • AO(Active Object):活动对象

而后把上下文压缩进栈,进栈后,在当前上下文再依次执行代码; 全局执行器上下文(EC(G))进栈(ECStack)执行,执行完代码就会把造成的上下文释放(出栈),执行后一些没用的上下文也将出栈,有用的上下文会压缩到栈底(闭包)。栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。当页面关闭全局上下文出栈。

JS编译机制:VO/AO/GO

VO 变量对象:每个执行上下文都会有本身的一个VO变量对象,用来存放在当前上下文中建立的变量和函数。(函数私有上下文叫 AO 活跃对象,但也是变量对象)。

GO 全局对象:他是一个堆内存(存储的都是浏览器内置的 api 属性方法),在浏览器端,让 window 指向它

VO(G)全局变量对象:全局上下文中用来存储全局变量的空间,他不是 GO=》只不过某些状况下 VO(G)中的东西会和 GO 中的东西有所关联而已;

函数执行:

  • 建立执行栈ECStack:引擎在编译执行代码的过程当中,首先会建立一个执行栈
  • 造成全局执行上下文EC(FN):函数执行的时候,造成一个全新的私有上下文EC(FN),供字符串代码执行
  • 建立全局变量或对象 GO
  • 进栈执行:进栈执行,从上面进去,把全局往下压
  • 代码执行以前还须要:
    • 1.初始化做用域链(scopeChain):<EC(FN),EC(G)>
    • 2.初始化 this 指向:window
    • 3.初始化实参集合:arguments
    • 4.形参赋值
    • 5.变量提高
    • 6.代码执行

JS垃圾回收机制

  1. 项目中,若是存在大量不被释放的内存(堆/栈/上下文),页面性能会变得很慢。当某些代码操做不能被合理释放,就会形成内存泄漏。咱们尽量减小使用闭包,由于它会消耗内存。

  2. 浏览器垃圾回收机制/内存回收机制:

    浏览器的Javascript具备自动垃圾回收机制(GC:Garbage Collecation),垃圾收集器会按期(周期性)找出那些不在继续使用的变量,而后释放其内存。

    标记清除:在js中,最经常使用的垃圾回收机制是标记清除:当变量进入执行环境时,被标记为“进入环境”,当变量离开执行环境时,会被标记为“离开环境”。垃圾回收器会销毁那些带标记的值并回收它们所占用的内存空间。
    谷歌浏览器:“查找引用”,浏览器不定时去查找当前内存的引用,若是没有被占用了,浏览器会回收它;若是被占用,就不能回收。
    IE浏览器:“引用计数法”,当前内存被占用一次,计数累加1次,移除占用就减1,减到0时,浏览器就回收它。

  3. 优化手段:内存优化 ; 手动释放:取消内存的占用便可。

    (1)堆内存:fn = null 【null:空指针对象】

    (2)栈内存:把上下文中,被外部占用的堆的占用取消便可。

  4. 内存泄漏

    在 JS 中,常见的内存泄露主要有 4 种,全局变量、闭包、DOM 元素的引用、定时器

做用域和做用域链

建立函数的时候,已经声明了当前函数的做用域==>当前建立函数所处的上下文。若是是在全局下建立的函数就是[[scope]]:EC(G),函数执行的时候,造成一个全新的私有上下文EC(FN),供字符串代码执行(进栈执行)

定义:简单来讲做用域就是变量与函数的可访问范围,由当前环境与上层环境的一系列变量对象组成
1.全局做用域:代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局做用域。
2.函数做用域:在固定的代码片断才能被访问

做用:做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突。

做用域链参考连接通常状况下,变量到 建立该变量 的函数的做用域中取值。可是若是在当前做用域中没有查到,就会向上级做用域去查,直到查到全局做用域,这么一个查找过程造成的链条就叫作做用域链。

闭包的两大做用:保存/保护

  • 闭包的概念

    函数执行时造成的私有上下文EC(FN),正常状况下,代码执行完会出栈后释放;可是特殊状况下,若是当前私有上下文中的某个东西被上下文之外的事物占用了,则上下文不会出栈释放,从而造成不销毁的上下文。 函数执行函数执行过程当中,会造成一个全新的私有上下文,可能会被释放,可能不会被释放,不论释放与否,他的做用是:

(1)保护:划分一个独立的代码执行区域,在这个区域中有本身私有变量存储的空间,保护本身的私有变量不受外界干扰(操做本身的私有变量和外界没有关系);

(2)保存:若是当前上下文不被释放【只要上下文中的某个东西被外部占用便可】,则存储的这些私有变量也不会被释放,能够供其下级上下文中调取使用,至关于把一些值保存起来了;

咱们把函数执行造成私有上下文,来保护和保存私有变量机制称为闭包

闭包是指有权访问另外一个函数做用域中的变量的函数--《JavaScript高级程序设计》

稍全面的回答: 在js中变量的做用域属于函数做用域, 在函数执行完后,做用域就会被清理,内存也会随之被回收,可是因为闭包函数是创建在函数内部的子函数, 因为其可访问上级做用域,即便上级函数执行完, 做用域也不会随之销毁, 这时的子函数(也就是闭包),便拥有了访问上级做用域中变量的权限,即便上级函数执行完后做用域内的值也不会被销毁。

  • 闭包的特性

    • 一、内部函数能够访问定义他们外部函数的参数和变量。(做用域链的向上查找,把外围的做用域中的变量值存储在内存中而不是在函数调用完毕后销毁)设计私有的方法和变量,避免全局变量的污染。

      1.1.闭包是密闭的容器,,相似于set、map容器,存储数据的

      1.2.闭包是一个对象,存放数据的格式为 key-value 形式

    • 二、函数嵌套函数

    • 三、本质是将函数内部和外部链接起来。优势是能够读取函数内部的变量,让这些变量的值始终保存在内存中,不会在函数被调用以后自动清除

  • 闭包造成的条件

    1. 函数的嵌套
    2. 内部函数引用外部函数的局部变量,延长外部函数的变量生命周期
  • 闭包的用途

    1. 模仿块级做用域
    2. 保护外部函数的变量 可以访问函数定义时所在的词法做用域(阻止其被回收)
    3. 封装私有化变量
    4. 建立模块
  • 闭包应用场景

    闭包的两个场景,闭包的两大做用:保存/保护。 在开发中, 其实咱们随处可见闭包的身影, 大部分前端JavaScript 代码都是“事件驱动”的,即一个事件绑定的回调方法; 发送ajax请求成功|失败的回调;setTimeout的延时回调;或者一个函数内部返回另外一个匿名函数,这些都是闭包的应用。

  • 闭包的优势:延长局部变量的生命周期

  • 闭包缺点:会致使函数的变量一直保存在内存中,过多的闭包可能会致使内存泄漏

JS 中 this 的五种状况

  1. 做为普通函数执行时,this指向window
  2. 当函数做为对象的方法被调用时,this就会指向该对象
  3. 构造器调用,this指向返回的这个对象
  4. 箭头函数 箭头函数的this绑定看的是this所在函数定义在哪一个对象下,就绑定哪一个对象。若是有嵌套的状况,则this绑定到最近的一层对象上。
  5. 基于Function.prototype上的 apply 、 call 和 bind 调用模式,这三个方法均可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表,`` bind方法经过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this指向除了使用new `时会被改变,其余状况下都不会改变。若为空默认是指向全局对象window。

建立对象有几种方法

// 第一种:字面量
var o1 = {name: "o1"}
var o2 = new Object({name: "o2"})
// 第二种:经过构造函数
var M = function(name){this.name = name}
var o3 = new M("o3")
// 第三种:Object.create()
var p = {name: "p"}
var o4 = Object.create(p)

复制代码

类和实例

ES6提供了更接近面向对象(注意:javascript本质上是基于对象的语言)语言的写法,引入了Class(类)这个概念,做为对象的模板。经过class关键字,能够定义类。 类的建立(es5):new 一个 function,在这个 function 的 prototype 里面增长属性和 方法。

原型规则和示例

5条原型规则,是学习理解原型链的基础。
1.全部的引用类型(数组、对象、函数),都具备对象特性,便可自由扩展属性(除了“nul”意外)
2.全部的引用类型(数组、对象、函数),都有一个_proto_(隐式原型)属性,属性值是一个普通的对象
3.全部的函数,都有一个prototype(显式原型)属性,属性值也是一个普通的对象
4.全部的引用类型(数组、对象、函数),__ proto __ 属性值指向它的构造函数的" __ prototype __ "属性值
5.当试图获得一个对象的某个属性时,若是这个对象自己没 有这个属性,那么会去它的__proto__(即它的构造函数的 prototype)中寻找

var obj = {}; obj.a = 100;//符合第一条能够自由扩展属性
var arr = []; arr.a = 100;
function fn(){}
fn.a=100;

console. log(obj.__proto__)
console. log(arr.__proto__)
console. log(fn.__proto__)

console. log(fn.prototype)

console.log(obj.__proto__===Object. prototype)
复制代码

示例:

// 构造函数
function Foo(name, age) {
    this.name = name;
}
Foo.prototype. alertName = function(){
    alert(this.name);
}
//建立示例
var f = new Foo('zhangsan');
f.printName = function (){
    console.log(this. name);
}
// 测试
f.printName();
f.alertName();
f.toString() //要去f._proto_._proto_中查找 原型链
复制代码

原型链就是我从个人实例对象网上找构造这个实例相关联的对象,而后这个关联的对象再往上找它又有创造它的原型对象以此类推,一直到Object.prototype原型对象终止。Object.prototype原型对象是整个原型链的顶端,到这就截止了。

原型关系:

  • 每一个 class都有显示原型 prototype
  • 每一个实例都有隐式原型 _ proto_
  • 实例的_ proto_指向对应 class 的 prototype

基于原型的执行规则:即原型链

  • 获取属性 xialuo.name或执行方法 xialuo. sahi()时
  • 先在自身属性和方法寻找
  • 若是找不到则自动去隐式原型_ proto_中查找

原型:  在 JS 中,每当定义一个对象(函数也是对象)时,对象中都会包含一些预约义的属性。其中每一个函数对象都有一个prototype 属性,这个属性指向函数的原型对象

原型链:函数的原型链对象constructor默认指向函数自己,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然相似。所以能够利用__proto__一直指向Object的原型对象上,而Object原型对象用Object.prototype.proto=null表示原型链顶端。如此造成了js的原型链继承。同时全部的js对象都有Object的基本防范

特色:  JavaScript对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本。当咱们修改原型时,与之相关的对象也会继承这一改变。

new运算符的实现机制

  1. 首先建立了一个新的空对象
  2. 设置原型,将对象的原型设置为函数的prototype对象。
  3. 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,若是是值类型,返回建立的对象。若是是引用类型,就返回这个引用类型的对象。

JS中的多种继承方案、继承(含es6)

(1)第一种是以原型链的方式来实现继承,可是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被全部的实例对象所共享,容易形成修改的混乱。还有就是在建立子类型的时候不能向超类型传递参数。

(2)第二种方式是使用借用构造函数的方式,这种方式是经过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,可是它存在的一个问题就是没法实现函数方法的复用,而且超类型原型定义的方法子类型也没有办法访问到。

(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。经过借用构造函数的方式来实现类型的属性的继承,经过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,可是因为咱们是以超类型的实例来做为子类型的原型,因此调用了两次超类的构造函数,形成了子类型的原型中多了不少没必要要的属性。

(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来建立新的对象,实现的原理是,向函数中传入一个对象,而后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。

(5)第五种方式是寄生式继承,寄生式继承的思路是建立一个用于封装继承过程的函数,经过传入一个对象,而后复制一个对象的副本,而后对象进行扩展,最后返回这个对象。这个扩展的过程就能够理解是一种继承。这种继承的优势就是对一个简单对象实现继承,若是这个对象不是咱们的自定义类型时。缺点是没有办法实现函数的复用。

(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例作为子类型的原型,致使添加了没必要要的原型属性。寄生式组合继承的方式是使用超类型的原型的副原本做为子类型的原型,这样就避免了建立没必要要的属性。

JS中的深浅拷贝

  1. 深拷贝

最简单的方法就是JSON.parse(JSON.stringify()),可是这种拷贝方法不能够拷贝一些特殊的属性(例如正则表达式,undefine,function)

//对象深度克隆的简单实现
var shallowCopy = function(obj) { // 只拷贝对象 
    if (typeof obj !== 'object') return;
    // 根据 obj 的类型判断是新建一个数组仍是对象 
    var newObj = obj instanceof Array ? [] : {}; 
    // 遍历 obj,而且判断是 obj 的属性才拷贝 
    for (var key in obj) { 
        if (obj.hasOwnProperty(key)) { 
            newObj[key] = obj[key]; 
        }
    }
    return newObj;
}
复制代码
  1. 浅拷贝
//方法 1
Object.assign(target, ...sources)
//方法 2
function simpleClone(obj) {
    var result = {};
    for (var i in obj) {
        result[i] = obj[i];
    }
    return result;
}
复制代码
复制代码

EventLoop 事件循环

JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,因此会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,由于宏任务队列的执行时间较长,因此微任务队列要优先于宏任务队列。微任务队列的表明就是,Promise.thenMutationObserver,宏任务的话就是setImmediate setTimeout setInterval

JS运行的环境。通常为浏览器或者Node。 在浏览器环境中,有JS 引擎线程和渲染线程,且两个线程互斥。 Node环境中,只有JS 线程。 不一样环境执行机制有差别,不一样任务进入不一样Event Queue队列。 当主程结束,先执行准备好微任务,而后再执行准备好的宏任务,一个轮询结束。

浏览器中的事件环(Event Loop)

事件环的运行机制是,先会执行栈中的内容,栈中的内容执行后执行微任务,微任务清空后再执行宏任务,先取出一个宏任务,再去执行微任务,而后在取宏任务清微任务这样不停的循环。

  • eventLoop 是由JS的宿主环境(浏览器)来实现的;

  • 事件循环能够简单的描述为如下四个步骤:

    1. 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
    2. 此期间WebAPIs完成这个事件,把回调函数放入队列中等待执行(微任务放到微任务队列,宏任务放到宏任务队列)
    3. 执行栈为空时,Event Loop把微任务队列执行清空;
    4. 微任务队列清空后,进入宏任务队列,取队列的第一项任务放入Stack(栈)中执行,执行完成后,查看微任务队列是否有任务,有的话,清空微任务队列。重复4,继续从宏任务中取任务执行,执行完成以后,继续清空微任务,如此反复循环,直至清空全部的任务。

    事件循环流程

  • 浏览器中的任务源(task):

    • 宏任务(macrotask)
      宿主环境提供的,好比浏览器
      ajax、setTimeout、setInterval、setTmmediate(只兼容ie)、script、requestAnimationFrame、messageChannel、UI渲染、一些浏览器api

    • 微任务(microtask)
      语言自己提供的,好比promise.then
      then、queueMicrotask(基于then)、mutationObserver(浏览器提供)、messageChannel 、mutationObersve

Node 环境中的事件环(Event Loop)

Node是基于V8引擎的运行在服务端的JavaScript运行环境,在处理高并发、I/O密集(文件操做、网络操做、数据库操做等)场景有明显的优点。虽然用到也是V8引擎,但因为服务目的和环境不一样,致使了它的API与原生JS有些区别,其Event Loop还要处理一些I/O,好比新的网络链接等,因此Node的Event Loop(事件环机制)与浏览器的是不太同样。

2020120317343116.png 执行顺序以下:

  • timers: 计时器,执行setTimeout和setInterval的回调
  • pending callbacks: 执行延迟到下一个循环迭代的 I/O 回调
  • idle, prepare: 队列的移动,仅系统内部使用
  • poll轮询: 检索新的 I/O 事件;执行与 I/O 相关的回调。事实上除了其余几个阶段处理的事情,其余几乎全部的异步都在这个阶段处理。
  • check: 执行setImmediate回调,setImmediate在这里执行
  • close callbacks: 执行close事件的callback,一些关闭的回调函数,如:socket.on('close', ...)

Promise

Promise的概念

  • 主要用于异步计算。promise是用来解决异步函数的顺序问题.
  • 能够将异步操做队列化,按照指望的顺序执行,返回符合预期的结果。
  • 能够在对象之间传递和操做Promise,帮助咱们处理队列。

回调有四个问题

  • 嵌套层次很深,难以维护
  • 没法正常使用return和throw
  • 没法正常检索堆栈信息
  • 多个回调之间难以创建联系
new Promise(
    /*执行器executor */
    function (resolve, reject){
        //段耗时很长的异步操做
        resolve();//数据处理完成
        reject(); //数据处理出错
}
.then(function A(){
    //成功,下一步
},function B(){
    //失败,作相应处理
}
复制代码

Promise详解如何实现异步执行

  • Promise是一个代理对象,它和原先要进行的操做并没有关系。
  • 它经过引入一个回调,避免更多的回调。

Promise的内部是如何实现异步执行的呢?

经过查看Promise的源码实现,发现其异步执行是经过asap这个库来实现的。

asap是as soon as possible的简称,在Node和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环以前),即把任务放在微任务队列中执行。

宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时, js 引擎会将全部任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫作 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的全部任务顺序执行;以后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

// asap 用法
asap(function () {
    // ...
});
复制代码

Promise有3个状态:

  • pending|【待定】初始状态
  • fulfilled|【实现】操做成功
  • rejected【被否决】操做失败

Promise状态发生改变,就会触发.then()里的响应函数处理后 续步骤。 Promise状态一经改变,不会再变。 Promise实例一经建立,执行器当即执行。

.then()

.then()接受两个函数做为参数,分别表明fulilledrejected .then()返回一个新的Promise实例,因此它能够链式调用 .当前面的Promise状态改变时,.then()根据其最终状态,选择特定 的状态响应函数执行 .状态响应函数能够返回新的Promise,或其它值 .若是返回新的Promise,那么下一级.then()会在新Promise状态改变以后执行 .若是返回其它任何值,则会马上执行下一级.then()

.then()里有.then()的状况

由于.then()返回的仍是Promise实例。 会等里面的.then()执行完,在执行外面的。 对于咱们来讲,此时最好将其展开,会更好读。

错误处理

Promise会自动捕获内部异常,并交给rejected响应函数处理。 错误处理的两种作法:  reject('错误信息).then(null,message =>{}) throw new Error('错误信息')catch(message=>{}) 推荐使用第二种,更加清晰好读,而且能够捕获前面的错误。

Promise.all() 批量执行

  • Promise.all(【p1,p2,p….】)用于将多个Promise实例,包装成一个新的Promise实例。
  • 返回的实例就是普通Promise
  • 它接受一个数组做为参数。
  • 数组里能够是Promise对象,也能够是别的值,只有Promise会等待状态改变。
  • 当全部子Promise都完成,该Promise完成,返回值是所有值的数组
  • 有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。 Promise.all() 最多见就是和.map() 连用。 实现队列1.使用.forEach()2.使用.reduce()

Promise.resolve()

  • 返回一个fulfilled的Promise实例,或原始Promise实例。
  • 参数为空,返回一个状态为fulfilled 的Promise实例
  • 参数是一个跟Promise无关的值,同上,不过fulfuilled响应函数会获得这个参数
  • 参数为Promise实例,则返回该实例,不作任何修改
  • 参数为thenable,马上执行它的.then()

Promise.reject()

返回一个rejected的Promise实例。 Promise.reject()其余特性同Promise.resolve(),但不认thenable

Promise.race()

相似Promise.all(),区别在于它有任意一个完成就算完成。 场景用法: 把异步操做和定时器放在一块儿 若是定时器先触发,就认为超时,告知用户

把回调包装成Promise

把回调包装成Promise最为常见。它有两个显而易见的好处: 可读性更好 返回的结果能够加入任何Promise队列

c。

原生AJAX核心四步操做

ajax是一种异步通讯的方法,从服务端获取数据,达到局部刷新页面的效果。 过程:

  1. 建立XMLHttpRequest对象;
  2. 调用open方法传入三个参数 请求方式(GET/POST)、url、同步异步(true/false);
  3. 监听onreadystatechange事件,当readystate等于4时返回responseText;
  4. 调用send方法传递参数。

JS高阶编程技巧:惰性函数/柯理化函数/高阶函数 constructor构造函数模式 类和实例 call/apply/bind DOM/BOM的核心操做 DOM2级事件的核心运行机制 事件对象 发布订阅设计模式 浏览器底层渲染机制和DOM的回流重绘 事件传播机制和事件代理

ES6/ES7的核心知识 解头函数ArrowFunction 解构聚值和拓展运算符 JS底层运行机制:单线程和同步 Set/Map数据结构 异步编程 Gonerator生成器函数 Intorator选代器和for of循环

AJAX/HTTP先后端数据交互

前端性能优化汇总(包含强缓存和弱缓存)

Vue

简述MVVM

什么是MVVM?

视图模型双向绑定,是Model-View-ViewModel的缩写,也就是把MVC中的Controller演变成ViewModel。Model层表明数据模型,View表明UI组件,ViewModelViewModel层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。之前是操做DOM结构更新视图,如今是数据驱动视图

MVVM的优势:

1.低耦合。视图(View)能够独立于Model变化和修改,一个Model能够绑定到不一样的View上,当View变化的时候Model能够不变化,当Model变化的时候View也能够不变;
2.可重用性。你能够把一些视图逻辑放在一个Model里面,让不少View重用这段视图逻辑。
3.独立开发。开发人员能够专一于业务逻辑和数据的开发(ViewModel),设计人员能够专一于页面设计。
4.可测试

说说Vue的MVVM实现原理

  1. Vue做为MVVM模式的实现库的2种技术

    a. 模板解析
    b. 数据绑定

  2. 模板解析:实现初始化显示

    a. 解析大括号表达式
    b. 解析指令

  3. 数据绑定:实现更新显示

    a. 经过数据劫持实现

建立了两种对象Observer和complie,先建立的Observer,后建立的complie,observer是为了监视/劫持data中全部层次的属性,同时还为每一种属性建立了另一种对象dep,dep与data中的属性一一对应,complie做用是用来编译模版,初始化界面,调用update对象,complie还为每一个表达式建立了对应的watcher同时指定了更新节点的回调函数,将watcher添加到全部对应的dep中,

说说你对 Vue 的理解

Vue 是一个构建数据驱动的渐进性框架,它的目标是经过 API 实现响应数据绑定和视图更新。

优势:
一、数据驱动视图,对真实 dom 进行抽象出 virtual dom, 并配合 diff 算法、响应式和观察者、异步队列等手段以最小代价更新 dom,渲染页面
二、组件化,组件用单文件的形式进行代码的组织编写,使得咱们能够在一个文 件里编写 html\css\js 而且配合 Vue-loader 以后,支持更强大的预处理器等功能
三、强大且丰富的 API 提供一系列的 api 能知足业务开发中各种需求
四、因为采用虚拟 dom,但让 Vue ssr 先天不足
五、生命周期钩子函数,选项式的代码组织方式,写熟了仍是蛮顺畅的,但仍然 有优化空间(Vue3 composition-api)
六、生态好,社区活跃

缺点:
一、因为底层基于 Object.defineProperty 实现响应式,而这个 api 自己不支持 IE8 及如下浏览器
二、csr 的先天不足,首屏性能问题(白屏)
三、seo 不友好

说说你对vue虚拟DOM的理解

什么是虚拟dom
说白了就是以js对象的形式去添加dom元素
本质上是优化了diff算法
虚拟dom自己也有本身的缺陷他更适合批量修改dom
尽可能不要跨层级修改dom
设置key能够最大的利用节点,避免重复渲染

1、什么是vdom?

Virtual DOM 就是用JS对象来模拟真实DOM结构,而后用这个树构建一个真正的 DOM 树, 插到文档当中。当状态变动的时候,从新构造一棵新的对象树。而后用新的树和旧的树 进行比较,记录两棵树差别 把所记录的差别应用到所构建的真正的 DOM 树上,视图就更新了。Virtual DOM 本质上就是在 JS 和 DOM 之间作了一个缓存。【版本1.1】

创建一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树,那么每次 dom 的更改就变成了 js 对象的属性的更改,这样一来就能查找 js 对象 的属性变化要比查询 dom 树的性能开销小。【版本1.2】

总结起来就两点,1、用JS对象来模拟真实的DOM结构,用这个对象构建真正的DOM树,当状态变动时,从新构建一个新的对象树,而后新旧树进行对比,记录两者差别并应用到所构建的真正的树上,视图也就更新了。2、每次变动时由原来的操做真实的DOM变成了查找js对象的属性变化,直接在内存中操做js对象,性能开销更小,效率更高。【版本2】

总结起来就是,Virtual DOM 就是用JS对象来模拟真实DOM结构,而后用JS对象树构建真正的DOM树。当状态变动时,从新构建一棵新的对象树,而后新旧树经过diff算法进行比较,若存在差别则将差别应用到所构建的真正的树上,视图就更新了。这个比较过程,由原来的查询真实DOM树变成查找js对象属性,性能开销小了,效率也就高了。Virtual DOM 本质上就是在 JS 和 DOM 之间作了一个缓存。【版本3】

2、为什么要用vdom?

  1. 虚拟dom就是为了解决操做真是dom带来的性能问题而出现的,将DOM对比操做放在JS层,提升效率

  2. DOM结构的对比,放在JS层来作(图灵完备语言:能实现逻辑代码的语言)操做内存中的js显然效率更高

3、vdom核心函数有哪些

核心函数:
h('标签名', {...属性名...}, [...子元素...])
h('标签名', {...属性名...}, '.........')
patch(container, vnode)
patch(vnode, newVnode)

Vue底层实现原理

vue.js是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter和getter,在数据变更时发布消息给订阅者,触发相应的监听回调
Vue是一个典型的MVVM框架,模型(Model)只是普通的javascript对象,修改它则试图(View)会自动更新。这种设计让状态管理变得很是简单而直观

Observer(数据监听器): Observer的核心是经过Object.defineProprtty()来监听数据的变更,这个函数内部能够定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher

Watcher(订阅者): Watcher订阅者做为Observer和Compile之间通讯的桥梁,主要作的事情是:

  1. 在自身实例化时往属性订阅器(dep)里面添加本身
  2. 自身必须有一个update()方法
  3. 待属性变更dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调

Compile(指令解析器): Compile主要作的事情是解析模板指令,将模板中变量替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变更,收到通知,更新试图

经常使用指令

  • v-if:判断是否隐藏;
  • v-for:数据循环出来;
  • v-bind:class:绑定一个属性;
  • v-model:实现双向绑定

v-model 是什么?有什么用呢?

参考回答: 一则语法糖,至关于 v-bind:value="xxx" 和 @input,意思是绑定了一个 value 属性的值, 子组件可对 value 属性监听,经过$emit('input', xxx)的方式给父组件通信。本身实现 v-model 方式的组件也是这样的思路。

vue-loader解释下

vue-loader就是一个加载器,能把vue组件转化成javascript模块 为何要转译vue组件? 能够动态的渲染一些数据,对三个标签template(结构)、style(表现)、script(行为)都作了优化,script中能够直接使用es6 style 也默承认以使用sass而且还给你提供做用域的选择,另外开发阶段还给你提供热加载 还能够以下使用:

<template src="../hello.vue"></template>
复制代码

导航钩子有哪些?它们有哪些参数

导航钩子翻译过来就是路由的生命周期函数(vue-router) 他其实主要分为两种全局和局部

全局的钩子函数
beforeEach:在路由切换开始时调用
afterEach:在路由切换离开是调用

局部到单个路由 beforeEnter

组件的钩子函数
beforeRouterEnter,
beforeRouterUpdate,
beforeRouterLeave

to:即将进入的目标对象
from:当前导航要高开的导航对象
next:是一个函数调用resolve执行下一步

谈谈对vue生命周期的理解?

每一个Vue实例在建立时都会通过一系列的初始化过程,vue的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动做或者事件

  • create阶段:vue实例被建立
    beforeCreate: 建立前,此时data和methods中的数据都尚未初始化
    created: 建立完毕,data中有值,未挂载
  • mount阶段: vue实例被挂载到真实DOM节点
    beforeMount:能够发起服务端请求,去数据
    mounted: 此时能够操做DOM
  • update阶段:当vue实例里面的data数据变化时,触发组件的从新渲染
    beforeUpdate :更新前
    updated:更新后
  • destroy阶段:vue实例被销毁
    beforeDestroy:实例被销毁前,此时能够手动销毁一些方法
    destroyed:销毁后

组件生命周期

生命周期(父子组件) 父组件beforeCreate --> 父组件created --> 父组件beforeMount --> 子组件beforeCreate --> 子组件created --> 子组件beforeMount --> 子组件 mounted --> 父组件mounted -->父组件beforeUpdate -->子组件beforeDestroy--> 子组件destroyed --> 父组件updated

加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

挂载阶段 父created->子created->子mounted->父mounted

父组件更新阶段 父beforeUpdate->父updated

子组件更新阶段 父beforeUpdate->子beforeUpdate->子updated->父updated

销毁阶段 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

computed与watch

通俗来说,既能用 computed 实现又能够用 watch 监听来实现的功能,推荐用 computed, 重点在于 computed 的缓存功能 computed 计算属性是用来声明式的描述一个值依赖了其它的值,当所依赖的值或者变量 改变时,计算属性也会跟着改变; watch 监听的是已经在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法。

watch 属性监听 是一个对象,键是须要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操做,监听属性的变化,须要在数据变化时执行异步或开销较大的操做时使用

computed 计算属性 属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会从新计算,主要当作属性来使用 computed中的函数必须用return返回最终的结果 computed更高效,优先使用。data 不改变,computed 不更新。

使用场景 computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能 watch:当一条数据影响多条数据的时候使用,例:搜索数据

组件中的data为何是一个函数?

1.一个组件被复用屡次的话,也就会建立多个实例。本质上,这些实例用的都是同一个构造函数。 2.若是data是对象的话,对象属于引用类型,会影响到全部的实例。因此为了保证组件不一样的实例之间data不冲突,data必须是一个函数。

为何v-for和v-if不建议用在一块儿

1.当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每一个 v-for 循环中。若是要遍历的数组很大,而真正要展现的数据不多时,这将形成很大的性能浪费 2.这种场景建议使用 computed,先对数据进行过滤

为何 Vuex的mutation中不能作异步操做?

Vuex中全部的状态更新的惟一途径都是mutation,异步操做经过 Action 来提交 mutation实现,这样使得咱们能够方便地跟踪每个状态的变化,从而让咱们可以实现一些工具帮助咱们更好地了解咱们的应用。 每一个mutation执行完成后都会对应到一个新的状态变动,这样devtools就能够打个快照存下来,而后就能够实现 time-travel 了。若是mutation支持异步操做,就没有办法知道状态是什么时候更新的,没法很好的进行状态的追踪,给调试带来困难。

v-for中key的做用

  • 当 Vue.js 用 v-for 更新已渲染过的元素列表时,它默认用“就地复用”策略。若是数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每一个元素,而且确保它在特定索引下显示已被渲染过的每一个元素。重复的key会形成渲染错误。

  • key的做用主要是为了让vue能够区分元素,更高效的对比更新虚拟DOM;

  • Vue在patch过程当中判断两个节点是不是相同节点,key是一个必要条件,是惟一标识,如不定义key,Vue只能认为比较的两个节点是同一个,这致使了频繁更新元素,使得整个patch过程比较低效,影响性能;

  • 从源码中能够知道,Vue判断两个节点是否相同时主要判断二者的key和元素类型等,所以若是不设置key,它的值就是undefined,则可能永远认为这是两个相同的节点,只能去作更新操做,这形成了大量的dom更新操做,明显是不可取的。

vue组件的通讯方式

  • 父子组件通讯

    父->子props,子->父 $on、$emit 获取父子组件实例 parent、children Ref 获取实例的方式调用组件的属性或者方法 父->子孙 Provide、inject 官方不推荐使用,可是写组件库时很经常使用

  • 兄弟组件通讯

    Event Bus 实现跨组件通讯 Vue.prototype.$bus = new Vue() 自定义事件

  • 跨级组件通讯

    Vuex、$attrs、$listeners Provide、inject

双向绑定实现原理

当一个Vue实例建立时,Vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter而且在内部追踪相关依赖,在属性被访问和修改时通知变化。每一个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程当中把属性记录为依赖,以后当依赖项的 setter 被调用时,会通知 watcher从新计算,从而导致它关联的组件得以更新。

v-model的实现以及它的实现原理吗?

  1. vue中双向绑定是一个指令v-model,能够绑定一个动态值到视图,同时视图中变化能改变该值。v-model是语法糖,默认状况下相于:value和@input
  2. 使用v-model能够减小大量繁琐的事件处理代码,提升开发效率,代码可读性也更好
  3. 一般在表单项上使用v-model
  4. 原生的表单项能够直接使用v-model,自定义组件上若是要使用它须要在组件内绑定value并处理输入事件
  5. 我作过测试,输出包含v-model模板的组件渲染函数,发现它会被转换为value属性的绑定以及一个事件监听,事件回调函数中会作相应变量更新操做,这说明神奇魔法其实是vue的编译器完成的。

nextTick的实现

  1. nextTickVue提供的一个全局API,是在下次DOM更新循环结束以后执行延迟回调,在修改数据以后使用$nextTick,则能够在回调中获取更新后的DOM
  2. Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启1个队列,并缓冲在同一事件循环中发生的全部数据变动。若是同一个watcher被屡次触发,只会被推入到队列中-次。这种在缓冲时去除重复数据对于避免没必要要的计算和DOM操做是很是重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操做完成后才调用;
  3. 好比,我在干什么的时候就会使用nextTick,传一个回调函数进去,在里面执行dom操做便可;
  4. 我也有简单了解nextTick实现,它会在callbacks里面加入咱们传入的函数,而后用timerFunc异步方式调用它们,首选的异步方式会是Promise。这让我明白了为何能够在nextTick中看到dom操做结果。

nextTick的实现原理是什么?

在下次 DOM 更新循环结束以后执行延迟回调,在修改数据以后当即使用 nextTick 来获取更新后的 DOM。 nextTick主要使用了宏任务和微任务。 根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,若是以上都不行则采用setTimeout定义了一个异步方法,屡次调用nextTick会将方法存入队列中,经过这个异步方法清空当前队列。

使用过插槽么?用的是具名插槽仍是匿名插槽或做用域插槽

vue中的插槽是一个很是好用的东西slot说白了就是一个占位的 在vue当中插槽包含三种一种是默认插槽(匿名)一种是具名插槽还有一种就是做用域插槽 匿名插槽就是没有名字的只要默认的都填到这里具名插槽指的是具备名字的

keep-alive的实现

做用:实现组件缓存,保持这些组件的状态,以免反复渲染致使的性能问题。 须要缓存组件 频繁切换,不须要重复渲染

场景:tabs标签页 后台导航,vue性能优化

原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将知足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在须要从新渲染的时候再将vnode节点从cache对象中取出并渲染。

mixin

mixin 项目变得复杂的时候,多个组件间有重复的逻辑就会用到mixin
    多个组件有相同的逻辑,抽离出来
    mixin并非完美的解决方案,会有一些问题
    vue3提出的Composition API旨在解决这些问题【追求完美是要消耗必定的成本的,如开发成本】
    场景:PC端新闻列表和详情页同样的右侧栏目,能够使用mixin进行混合
    劣势:1.变量来源不明确,不利于阅读 2.多mixin可能会形成命名冲突 3.mixin和组件可能出现多对多的关系,使得项目复杂度变高
复制代码

vuex是什么?原理是什么?怎么使用?哪一种功能场景使用它?

状态管理库,相似 React 中的 Rudux

关于vuex vuex是一个专门为vue构建的状态集管理,主要是为了解决组件间状态共享的问题,强调的是数据的集中式管理,说白了主要是便于维护便于解耦因此不是全部的项目都适合使用vuex,若是你不是构建大型项目使用vuex反而使你的项目代码繁琐多余

vuex的核心: state mutations getters actions modules

Vuex的理解及使用场景

Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每个 Vuex 应用的核心就是 store(仓库)。

  1. Vuex 的状态存储是响应式的;当 Vue 组件从 store 中读取状态的时候,

若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新 2. 改变 store 中的状态的惟一途径就是显式地提交 (commit) mutation, 这样使得咱们能够方便地跟踪每个状态的变化 Vuex主要包括如下几个核心模块:

  1. State:定义了应用的状态数据
  2. Getter:在 store 中定义“getter”(能够认为是 store 的计算属性),

就像计算属性同样,getter 的返回值会根据它的依赖被缓存起来, 且只有当它的依赖值发生了改变才会被从新计算 3. Mutation:是惟一更改 store 中状态的方法,且必须是同步函数 4. Action:用于提交 mutation,而不是直接变动状态,能够包含任意异步操做 5. Module:容许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

Vuex管理状态的机制

1)对Vuex基本理解1)是什么:Vuex是一个专为Vue.js应用程序开发的状态管理的vue插件2)做用:集中式管理vue多个组件共享的状态和从后台获取的数据states帮助组件管理状态的,基于state的还有一个计算属性数据getters,getters是从state中读取数据并计算的,他们两个的数据都是给组件去读,组件中读取state状态数据使用 s t o r e . s t a t e m a p S t a t e ( ) , 读取计算属性数据也有两个方法是 store.state或mapState(),读取计算属性数据也有两个方法是 store.getters和mapGetters();更新状态数据涉及到actions和mutations,经过$store.dispatch或mapAction()触发action的调用,而后actions会经过commit()触发mutations调用,mutations则直接更新状态;actions还能够同后台API进行双向通讯。

单向数据流

“单向数据流”理念的极简示意:

  • state:驱动应用的数据源。
  • view:以声明方式将 state 映射到视图 。
  • actions:响应在 view 上的用户输入致使的状态变化

单向数据流过程:

简单的单向数据流(unidirectional data flow)是指用户访问View,View发出用户交互的Action,在Action里对state进行相应更新。state更新后会触发View更新页面的过程。这样数据老是清晰的单向进行流动,便于维护而且能够预测

vue的diff算法

问题:渲染真实的DOM开销是很大的,修改了某个数据,若是直接渲染到真实dom上会引发整个DOM树的重绘和重排。 Virtual Dom 根据真实DOM生成的一个Virtual DOM,当Virtual DOM某个节点的数据改变后生成一个新的Vnode,而后Vnode和oldVnode做对比,发现有不同的地方就直接修改在真实的DOM上,而后使oldVnode的值为Vnode. 注意:在采起diff算法比较的时候,只会在同层级进行,不会跨层级比较。 当数据发生改变时,set方法会让调用Dep.notify()方法通知全部订阅者Watcher,订阅者就会调用patch函数给真实的DOM打补丁,更新响应的试图。

Vue项目中实现路由按需加载(路由懒加载)的3中方式:

  1. vue异步组件
  2. es6提案的import()
  3. webpack的require.ensure()

你知道Vue3有哪些新特性吗?它们会带来什么影响?

  • 性能提高

更小巧、更快速 支持自定义渲染器 支持摇树优化:一种在打包时去除无用代码的优化手段 支持Fragments和跨组件渲染

  • API变更

模板语法99%保持不变 原生支持基于class的组件,而且无需借助任何编译及各类stage阶段的特性 在设计时也考虑TypeScript的类型推断特性 重写虚拟DOM能够期待更多的编译时提示来减小运行时的开销 优化插槽生成能够单独渲染父组件和子组件 静态树提高下降渲染成本 基于Proxy的观察者机制节省内存开销

  • 不兼容IE11

检测机制更加全面、精准、高效,更具可调试式的响应跟踪

Vue3.0 编译作了哪些优化?

a. 生成 Block tree Vue.js 2.x 的数据更新并触发从新渲染的粒度是组件级的,单个组件内部 须要遍历该组 件的整个 vnode 树。在 2.0 里,渲染效率的快慢与组件大小成正相关:组件越大,渲染 效率越慢。而且,对于一些静态节点,又无数据更新,这些遍历都是性能浪费。 Vue.js 3.0 作到了经过编译阶段对静态模板的分析,编译生成了 Block tree。 Block tree 是一个将模版基于动态节点指令切割的嵌套区块,每一个 区块内部的节点结构是固定的, 每一个区块只须要追踪自身包含的动态节点。因此,在 3.0 里,渲染效率再也不与模板大小 成正相关,而是与模板中动态节点的数量成正相关。

b. slot 编译优化 Vue.js 2.x 中,若是有一个组件传入了 slot,那么每次父组件更新的时候,会强制使子组 件 update,形成性能的浪费。 Vue.js 3.0 优化了 slot 的生成,使得非动态 slot 中属性的更新只会触发子组件的更新。 动态 slot 指的是在 slot 上面使用 v-if,v-for,动态 slot 名字等会致使 slot 产生运行时动 态变化可是又没法被子组件 track 的操做。 c. diff 算法优化

Vue3.0 是如何变得更快的?(底层,源码)

a. diff 方法优化 Vue2.x 中的虚拟 dom 是进行全量的对比。 Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对比的时候,值对比 带有 patch flag 的节点,而且能够经过 flag 的信息得知当前节点要对比的具体内容化。 b. hoistStatic 静态提高 Vue2.x : 不管元素是否参与更新,每次都会从新建立。 Vue3.0 : 对不参与更新的元素,只会被建立一次,以后会在每次渲染时候被不停的复用。 c. cacheHandlers 事件侦听器缓存 默认状况下 onClick 会被视为动态绑定,因此每次都会去追踪它的变化可是由于是同一 个函数,因此没有追踪变化,直接缓存起来复用便可。 原做者姓名: 欧阳呀

2.0存在的问题 1.对原始数据进行克隆一份 2.须要分别给对象中的每一个属性设置监听 3.0里面使用的是proxy监听对象中的全部的属性

Vue3.0 新特性

Composition API 与 React.js 中 Hooks 的异同点

a. React.js 中的 Hooks 基本使用 React Hooks 容许你 "勾入" 诸如组件状态和反作用处理等 React 功能中。Hooks 只能 用在函数组件中,并容许咱们在不须要建立类的状况下将状态、反作用处理和更多东西 带入组件中。 React 核心团队奉上的采纳策略是不反对类组件,因此你能够升级 React 版本、在新组 件中开始尝试 Hooks,并保持既有组件不作任何更改。 案例: useState 和 useEffect 是 React Hooks 中的一些例子,使得函数组件中也能增长状态和 运行反作用。 咱们也能够自定义一个 Hooks,它打开了代码复用性和扩展性的新大门。

b. Vue Composition API 基本使用 Vue Composition API 围绕一个新的组件选项 setup 而建立。setup() 为 Vue 组件提供了 状态、计算值、watcher 和生命周期钩子。 并无让原来的 API(Options-based API)消失。容许开发者 结合使用新旧两种 API (向下兼容)。

c. 原理 React hook 底层是基于链表实现,调用的条件是每次组件被 render 的时候都会顺序执行 全部的 hooks。 Vue hook 只会被注册调用一次,Vue 能避开这些麻烦的问题,缘由在于它对数据的响 应是基于 proxy 的,对数据直接代理观察。(这种场景下,只要任何一个更改 data 的地 方,相关的 function 或者 template 都会被从新计算,所以避开了 React 可能遇到的性能 上的问题)。 React 中,数据更改的时候,会致使从新 render,从新 render 又会从新把 hooks 从新注 册一次,因此 React 复杂程度会高一些。 m

你都作过哪些Vue的性能优化?

编码阶段
尽可能减小data中的数据及层次结构,data中的数据都会增长getter和setter,会收集对应的watcher v-if和v-for不能连用 若是须要使用v-for给每项元素绑定事件时使用事件代理 SPA 页面采用keep-alive缓存组件 在更多的状况下,使用v-if替代v-show key保证惟一 使用路由懒加载、异步组件 防抖、节流 第三方模块按需导入 长列表滚动到可视区域动态加载 图片懒加载
SEO优化
预渲染 服务端渲染SSR 打包优化 压缩代码 Tree Shaking/Scope Hoisting 使用cdn加载第三方模块 多线程打包happypack splitChunks抽离公共文件 sourceMap优化
用户体验
骨架屏 PWA 还能够使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

vue与React 的比较

相同点: 1.都是组件化开发和虚拟DOM(Virtual Dom) 2.都支持经过props进行父子组件间数据通讯 3.都支持数据驱动视图,不直接操做DOM,更新状态数据界面就自动更新 4.都支持服务端渲染 5.都支持native的方案,React的 React Native, Vue 的Weex

不一样点: 1.数据绑定:vue实现了数据的双向绑定,react的数据流动是单向的 2.组件的写法不同,React推荐的是JSX语法,也就是把HTML和CSS都写进JavaScript,即"all in js";vue推荐的作法是webpack+vue+loader的单文件组件格式,即html,css,js写在同一个文件中; 3.数据状态管理不一样,state对象在react应用中是不可变的,须要使用setState方法更新状态;在vue中state对象不是必须的,数据由data属性在vue对象中管理 4.Virtual Dom不同,vue会跟踪每一个组件的依赖关系,不须要从新渲染整个组件树; 而对于react而言,每当应用的状态改变时,所有的组件都会被渲染,因此react中会须要shouldComponentUpdate这个生命周期函数方法来进行控制 5.React严格上只针对MVC的View层,Vue则是MVVM模式

React

react中父子组件传值

使用公共组件进行状态提高

react中父子组件中参数互传,子传父是先在父组件上绑定属性设置为一个函数,当子组件须要给父组件传值的时候,则经过props调用该函数将参数传入到该函数当中,此时就能够在父组件中的函数中接收到该参数了,这个参数则为子组件传过来的值

父传子是在父组件中直接绑定一个正常的属性,这个属性就是指具体的值,在子组件中,用props就能够获取到这个值

任意组件间通讯

1.能够new一个 Vue 的 EventBus,进行事件监听,一边执行监听,一边执行新增 VUE的eventBus 就是发布订阅模式,是能够在React中使用的;

2.使用pubsub-js

3.redux

setState 既存在异步状况也存在同步状况

1.异步状况 在React事件当中是异步操做

import React,{ Component } from "react";
class Count extends Component{
    constructor(props){
        super(props);
        this.state = {
            count:0
        }
    }

    render(){
        return (
            <> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增长</button> </>
        )
    }
    
    btnAction = ()=>{
        //不能直接修改state,须要经过setState进行修改
        this.setState({
            count: this.state.count + 1
        });
        console.log(this.state.count);
    }
}

export default Count;

复制代码

2.同步状况 若是是在setTimeout事件或者自定义的dom事件中,都是同步的

import React,{ Component } from "react";
class Count extends Component{
    constructor(props){
        super(props);
        this.state = {
            count:0
        }
    }

    render(){
        return (
            <> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增长</button> </>
        )
    }
    
    btnAction = ()=>{
        //不能直接修改state,须要经过setState进行修改
        //同步
        setTimeout(()=>{
            this.setState({
                count: this.state.count + 1
            });
            console.log(this.state.count);
        })
    }
}

export default Count;

复制代码

3.同步状况 自定义dom事件

import React,{ Component } from "react";
class Count extends Component{
    constructor(props){
        super(props);
        this.state = {
            count:0
        }
    }

    render(){
        return (
            <> <p>count:{this.state.count}</p> <button id="btn">绑定点击事件</button> </>
        )
    }
    
    componentDidMount(){
        //自定义dom事件,也是同步修改
        document.querySelector('#btn').addEventListener('click',()=>{
            this.setState({
                count: this.state.count + 1
            });
            console.log(this.state.count);
        });
    }
}

export default Count;

复制代码

屡次的异步setState,更新前会进行合并

import React,{ Component } from "react";
class Count extends Component{
    constructor(props){
        super(props);
        this.state = {
            count:0
        }
    }

    render(){
        return (
            <> <p>count:{this.state.count}</p> <button onClick={this.btnAction}>增长</button> </>
        )
    }
    
    btnAction = ()=>{
        //不能直接修改state,须要经过setState进行修改
        this.setState({
            message:'hi',
            count: this.state.count + 1
        },()=>{
            console.log('a:',this.state.count);
        });
        this.setState({
            count: this.state.count + 1
        },()=>{
            console.log('b:',this.state.count);
        });
        this.setState({
            count: this.state.count + 1
        },()=>{
            console.log('c:',this.state.count);
        });
        this.setState({
            count: this.state.count + 1
        },()=>{
            console.log('d:',this.state.count);
        });
        //会输出:a:2 b:2 c:2 d:2
    }
}

export default Count;
复制代码

屡次的异步setState,更新前不进行合并

btnAction = ()=>{
        //不能直接修改state,须要经过setState进行修改
        this.setState((preState,props)=>{
            return {
                count: preState.count + 1
            }
        },()=>{
            console.log('a:',this.state.count);
        });
        this.setState((preState,props)=>{
            return {
                count: preState.count + 1
            }
        },()=>{
            console.log('b:',this.state.count);
        });
        this.setState((preState,props)=>{
            return {
                count: preState.count + 1
            }
        },()=>{
            console.log('c:',this.state.count);
        });
        this.setState((preState,props)=>{
            return {
                count: preState.count + 1
            }
        },()=>{
            console.log('d:',this.state.count);
        });
        
        //会输出:a:1 b:2 c:3 d:4
    }
复制代码

生命周期

安装
当组件的实例被建立并插入到 DOM 中时,这些方法按如下顺序调用:

constructor()
static getDerivedStateFromProps()
render()
componentDidMount()

更新中
更新可能由道具或状态的更改引发。当从新渲染组件时,这些方法按如下顺序调用:

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载
当组件从 DOM 中移除时调用此方法:

componentWillUnmount()


复制代码

Portals

Portals 提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构以外的 DOM 节点中。结构不受外界的控制的状况下就能够使用portals进行建立

异步组件

// 异步懒加载
const Box = lazy(()=>import('./components/Box'));
// 使用组件的时候要用suspense进行包裹
<Suspense fallback={<div>loading...</div>}> {show && <Box/>} </Suspense>
复制代码

immutable.js

immutable内部提供的全部数据类型,对其数据进行任意操做,操做获得的结果是修改后的值 而且修改后的值是一个新的对象,原来的对象没有发生任何变化。 immutable.js文档

github.com/immutable-j…

学习文档

rhadow.github.io/2015/05/10/…

const map1 = Map({a:1,b:2,c:3});
const map2 = map1.set('b',50);
console.log(map1);
console.log(map2);
复制代码

构建工具 && 工程化

性能优化

节流 && 防抖

1,解释 节流:事件触发后,规定时间内,事件处理函数不能再次被调用。也就是说在规定的时间内,函数只能被调用一次,且是最早被触发调用的那次。

防抖:屡次触发事件,事件处理函数只能执行一次,而且是在触发操做结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待必定的时间(这时间是码农本身去定义的,好比 1 秒),若是没有再次被触发,那么就执行,若是被触发了,那就本次做废,从新重新触发的时间开始计算,并再次等待 1 秒,直到能最终执行!

2,使用场景: 节流:滚动加载更多、搜索框搜的索联想功能、高频点击、表单重复提交…… 防抖:搜索框搜索输入,并在输入完之后自动搜索、手机号,邮箱验证输入检测、窗口大小 resize 变化后,再从新渲染。

几条关于优化渲染效率的建议

  • 合法地去书写HTML和CSS ,且不要忘了文档编码类型。
  • 样式文件应当在head标签中,而脚本文件在body结束前,这样能够防止阻塞的方式。
  • 简化并优化CSS选择器,尽可能将嵌套层减小到最小。
  • DOM 的多个读操做(或多个写操做),应该放在一块儿。不要两个读操做之间,加入一个写操做。
  • 若是某个样式是经过重排获得的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。
  • 不要一条条地改变样式,而要经过改变class,或者csstext属性,一次性地改变样式。
  • 尽可能用transform来作形变和位移
  • 尽可能使用离线DOM,而不是真实的网页DOM,来改变元素样式。好比,操做Document Fragment对象,完成后再把这个对象加入DOM。再好比,使用cloneNode()方法,在克隆的节点上进行操做,而后再用克隆的节点替换原始节点。
  • 先将元素设为display: none(须要1次重排和重绘),而后对这个节点进行100次操做,最后再恢复显示(须要1次重排和重绘)。这样一来,你就用两次从新渲染,取代了可能高达100次的从新渲染。
  • position属性为absolutefixed的元素,重排的开销会比较小,由于不用考虑它对其余元素的影响。

往期文章

  • HTTP 面试题:待更...
  • JS 面试题:
  • Vue 面试题:
  • React 面试题:
相关文章
相关标签/搜索