本专题按照如下几个方便进行整理:javascript
适合初次全面复习的同窗,查缺补漏,知识面比较全,复习完成后,再按照本人整理的面试高频题配合复习,使得找工做事半功倍,必定要理解,不要死记硬背,对于一些概念性的和原理的内容要深刻理解。css
“你从头读,尽可能往下读,直到你一窍不通时,再从头开始,这样坚持往下读,直到你彻底读懂为止。”html
HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。客户端向服务器发送一个请求报文,服务器以一个状态行做为响应。前端
请求报文{ 请求行、请求头、空行、请求体 } 请求行:{http方法、页面地址、http协议、http版本} 响应报文{ 状态行、响应头、空行、响应体 }vue
Request Header:java
Response Header:node
http: 是互联网上应用最为普遍的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。
https:是以安全为目标的 HTTP 通道,即 HTTP 下加入 SSL 层进行加密。react
https 协议的主要做用:创建一个信息安全通道,来确保数据的传输,确保网站的真实性。webpack
客户端在使用 HTTPS 方式与 Web 服务器通讯时有如下几个步骤,如图所示。nginx
使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
HTTPS 协议要比 http 协议安全,可防止数据在传输过程当中不被窃取、改变,确保数据的完整性。 HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增长了中间人攻击的成本。
费时
,会使页面加载时间延长 50%,增长 10%~20%的耗电。缓存
不如 http 高效,会增长数据开销。证书费
用越高。IP
,不能再同一个 ip 上绑定多个域名,ipv4 资源支持不了这种消耗。首先在浏览器中输入URL
查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,若是有则显示页面内容。若是没有则进行下一步。
DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址
创建TCP链接:解析出IP地址后,根据IP地址和默认80端口,和服务器创建TCP链接
发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文做为TCP三次握手的第三次数据发送给服务器
服务器响应请求并返回结果:服务器对浏览器请求作出响应,并把对应的html文件发送给浏览器
关闭TCP链接:经过四次挥手释放TCP链接
浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:
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做用域其实就是这样的执行流机制实现的。
DOM
的变化影响了元素的几何信息(DOM
对象的位置和尺寸大小),浏览器须要从新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫作重排。触发缘由:
1. 页面初始化的时候;
2. 操做DOM元素时;
3. 某些元素的尺寸改变了;
4. 若是 CSS 的属性发生变化了。
复制代码
触发缘由:改变元素的 color、background、box-shadow 等属性
Reflow要比Repaint更花费时间,也就更影响性能。因此要尽可能避免过多的Reflow。
减小 reflow/repaint
fixed
或 absoult
的 position
,那么修改他们的 CSS 是不会 reflow 的。position:absolute
或fixed
元素,对其余元素影响不大GPU
加速,translate
使用3D
变化第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。
握手过程当中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
复制代码
客户端进程发出链接释放报文
,而且中止发送数据。释放数据报文首部,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链接至少须要一对套接字,其中一个运行于客户端,称为ClientSocket ,另外一个运行于服务器端,称为ServerSocket 。
套接字之间的链接过程分为三个步骤:服务器监听,客户端请求,链接确认。
一、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待链接的状态,实时监控网络状态,等待客户端的链接请求。
二、客户端请求:指客户端的套接字提出链接请求,要链接的目标是服务器端的套接字。
为此,客户端的套接字必须首先描述它要链接的服务器的套接字,指出服务器端套接字的地址和端口号,而后就向服务器端套接字提出链接请求。
三、链接确认:当服务器端套接字监听到或者说接收到客户端套接字的链接请求时,就响应客户端套接字的请求,创建一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式创建链接。
而服务器端套接字继续处于监听状态,继续接收其余客户端套接字的链接请求。
TCP是面向连接
的,而UDP是面向无链接的。
TCP仅支持单播传输
,UDP 提供了单播,多播,广播的功能。
TCP的三次握手保证了链接的可靠性
; UDP是无链接的、不可靠的一种数据传输协议,首先不可靠性体如今无链接上,通讯都不须要创建链接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收。
UDP的头部开销
比TCP的更小,数据传输速率更高
,实时性更好
。
1.浏览器回退表现不一样 GET在浏览器回退时时无害的,而POST会再次提交请求
2.浏览器对请求地址的处理不一样 GET请求地址会被浏览器主动缓存,而POST不会,除非手动设置
3.浏览器对响应的处理不一样GET请求参数会被完整的保留在浏览器历史记录里,而POST中的参数不会被保留
4.参数大小不一样. GET请求在URL中传送的参数是有长度的限制,而POST没有限制
5.安全性不一样. GET参数经过URL传递,会暴露,不安全;POST放在Requset Body中,相对更安全
6.针对数据操做的类型不一样.GET对数据进行查询,POST主要对数据进行增删改!简单说,GET是只读,POST是写。
跨域的原理
跨域,是指浏览器不能执行其余网站的脚本。它是由浏览器的同源策略
形成的。
同源策略,是浏览器对 JavaScript 实施的安全限制,只要协议、域名、端口
有任何一个不一样,都被看成是不一样的域。
跨域原理,便是经过各类方式,避开浏览器的安全限制
。
解决方案
最初作项目的时候,使用的是jsonp,但存在一些问题,使用get请求不安全,携带数据较小,后来也用过iframe,但只有主域相同才行,也是存在些问题,后来经过了解和学习发现使用代理和proxy代理配合起来使用比较方便,就引导后台按这种方式作下服务器配置,在开发中使用proxy,在服务器上使用nginx代理,这样开发过程当中彼此都方便,效率也高;如今h5新特性还有 windows.postMessage()
JSONP:
ajax 请求受同源策略影响,不容许进行跨域请求,而 script 标签 src 属性中的链 接却能够访问跨域的 js 脚本,利用这个特性,服务端再也不返回 JSON 格式的数据,而是 返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。
步骤:
//动态建立 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
ajax 轮询 实现原理:ajax 轮询指客户端每间隔一段时间向服务端发起请求,保持数据的同步。
优势:可实现基础(指间隔时间较短)的数据更新。
缺点:这种方法也只是尽可能的模拟即时传输,但并不是真正意义上的即时通信,颇有可能出现客户端请求时,服务端数据并未更新。或者服务端数据已更新,但客户端未发起请求。致使屡次请求资源浪费,效率低下。【数据更新不及时,效率低下
】
long poll 长轮询
实现原理: long poll 指的是客户端发送请求以后,若是没有数据返回,服务端会将请求挂起放入队列(不断开链接)处理其余请求,直到有数据返回给客户端。而后客户端再次发起请求,以此轮询。在 HTTP1.0 中客户端能够设置请求头 Connection:keep-alive,服务端收到该请求头以后知道这是一个长链接,在响应报文头中也添加 Connection:keep-alive。客户端收到以后表示长链接创建完成,能够继续发送其余的请求。在 HTTP1.1 中默认使用了 Connection:keep-alive 长链接。
优势:减小客户端的请求,下降无效的网络传输,保证每次请求都有数据返回,不会一直占用线程。
缺点:没法处理高并发,当客户端请求量大,请求频繁时对服务器的处理能力要求较高。服务器一直保持链接会消耗资源,须要同时维护多个线程,服务器所能承载的 TCP 链接数是有上限的,这种轮询很容易把链接数顶满。每次通信都须要客户端发起,服务端不能主动推送。【没法处理高并发,消耗服务器资源严重,服务端不能主动推送
】
iframe 长链接
实现原理:
在网页上嵌入一个 iframe 标签,该标签的 src 属性指向一个长链接请求。这样服务端就能够源源不断地给客户端传输信息。保障信息实时更新。
优势:消息及时传输。
缺点:消耗服务器资源
。
WebSocket
实现原理: Websocket 实现了客户端与服务端的双向通讯,只须要链接一次,就能够相互传输数据,很适合实时通信、数据实时更新等场景。
Websocket 协议与 HTTP 协议没有关系,它是一个创建在 TCP 协议上的全新协议,为了兼容 HTTP 握手规范,在握手阶段依然使用 HTTP 协议,握手完成以后,数据经过 TCP 通道进行传输。
Websoket 数据传输是经过 frame 形式,一个消息能够分红几个片断传输。这样大数据能够分红一些小片断进行传输,不用考虑因为数据量大致使标志位不够的状况。也能够边生成数据边传递消息,提升传输效率。
优势: 双向通讯。客户端和服务端双方均可以主动发起通信。 没有同源限制。客户端能够与任意服务端通讯,不存在跨域问题。 数据量轻。第一次链接时须要携带请求头,后面数据通讯都不须要带请求头,减小了请求头的负荷。 传输效率高。由于只须要一次链接,因此数据传输效率高。
缺点: 长链接须要后端处理业务的代码更稳定,推送消息相对复杂;
长链接受网络限制比较大,须要处理好重连。
兼容性,WebSocket 只支持 IE10 及其以上版本。
服务器长期维护长链接须要必定的成本,各个浏览器支持程度不一;
成熟的 HTTP 生态下有大量的组件能够复用,WebSocket 则没有,遇到异常问题难以快速定位快速解决。【须要后端代码稳定,受网络限制大,兼容性差,维护成本高,生态圈小】
相同点:
不一样点:
概念
DTD(document type definition,文档类型定义)声明于文档最前面,用来定义XML或(X)HTML的文件类型。浏览器会使用它来判断文档类型,并根据这个判断决定用什么引擎来解析和渲染他们。
解析引擎的两种模式
解析引擎有严格模式和混杂模式。严格模式的排版和 JS 运做模式是 以该浏览器支持的最高标准运行。混杂模式,向后兼容,模拟老式浏览器,防止浏览器没法兼容页面。
DOCTYPE的做用
DOCTYPE是用来声明文档类型和DTD规范的,其做用一是文件的合法性验证。若是文件代码不合法,那么浏览器解析时会出一些错误。二是浏览器会使用它来判断文档类型,并根据这个判断决定用什么引擎来解析和渲染他们。
概念:
HTML5的语义化指的是合理正确的使用语义化的标签来建立页面结构
。【正确的标签作正确的事】
语义化标签:
header nav main article section aside footer
语义化的优势:
没CSS样式的状况下,页面总体也会呈现很好的结构效果
代码结构清晰
,易于阅读,利于开发和维护
方便其余设备解析(如屏幕阅读器)根据语义渲染网页。有利于搜索引擎优化(SEO)
,搜索引擎爬虫会根据不一样的标签来赋予不一样的权重渐进加强(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;
}
复制代码
不一样浏览器的标签默认的margin和padding不同。*{margin:0;padding:0;}
IE6双边距bug:块属性标签float后,又有横行的margin状况下,在IE6显示margin比设置的大。hack:display:inline;将其转化为行内属性。
设置较小高度标签(通常小于10px),在IE6,IE7中高度超出本身设置高度。hack:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。
Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,可经过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。
超连接访问事后hover样式就不出现了,被点击访问过的超连接样式再也不具备hover和active了。解决方法是改变CSS属性的排列顺序:L-V-H-A ( love hate ): a:link {} a:visited {} a:hover {} a:active {}
选择器
优先级
带!important 标记的样式属性优先级最高; 样式表的来源不一样时,优先级顺序为:内联样式> 内部样式 > 外部样式 > 浏览器用户 自定义样式 > 浏览器默认样式
样式表的来源相同时:!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
CSS 盒模型本质上是一个盒子,它包括:边距,边框,填充和实际内容。CSS 中的盒子模型包括 IE 盒子模型和标准的 W3C 盒子模型。在标准的盒子模型中,width 指 content 部分的宽度,在 IE 盒子模型中,width 表示 content+padding+border 这三个部分的宽度,故在计算盒子的宽度时存在差别:
标准盒模型: 一个块的总宽度=width+margin(左右)+padding(左右)+border(左右)
怪异盒模型: 一个块的总宽度=width+margin(左右)(既 width 已经包含了 padding 和 border 值)
BFC的概念
BFC
是 Block Formatting Context
的缩写,即块级格式化上下文。BFC
是CSS布局的一个概念,是一个独立的渲染区域,规定了内部box如何布局, 而且这个区域的子元素不会影响到外面的元素,其中比较重要的布局规则有内部 box 垂直放置,计算 BFC 的高度的时候,浮动元素也参与计算。
BFC的原理布局规则
垂直方向
,一个接一个地放置垂直方向的距离由margin决定
。属于同一个BFC的两个相邻Box的margin会发生重叠不会与float box重叠
子元素不会影响到外面的元素
浮动元素也参与计算高度
display属性,决定了这个Box的类型
。不一样类型的Box会参与不一样的Formatting Context
。如何建立BFC?
BFC的使用场景
/*全部属性从原始值到制定值的一个过渡,运动曲线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(滤镜)
弹性布局、栅格布局、多列布局
媒体查询
固定定位 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: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
垂直居中
line-height
实现居中,这种方法适合纯文字类绝对定位
,标签经过margin实现自适应居中table 布局
,父级经过转换成表格形式,而后子级设置 vertical-align 实现
。(须要注意的是:vertical-align: middle使用的前提条件是内联元素以及display值为table-cell的元素)。布局的传统解决方案,基于盒状模型,依赖 display 属性 + position 属性 + float 属性。它对于那些特殊布局很是不方便,好比,垂直居中就不容易实现。
Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。指定容器 display: flex 便可。 简单的分为容器属性和元素属性。
容器的属性:
项目的属性(元素的属性):
首先 Rem 相对于根(html)的 font-size 大小来计算。简单的说它就是一个相对单例 如:font-size:10px;,那么(1rem = 10px)了解计算原理后首先解决怎么在不一样设备上设置 html 的 font-size 大小。其实 rem 布局的本质是等比缩放,通常是基于宽度。
优势:能够快速适用移动端布局,字体,图片高度
缺点:
①目前 ie 不支持,对 pc 页面来说使用次数很少;
②数据量大:全部的图片,盒子都须要咱们去给一个准确的值;才能保证不一样机型的适配;
③在响应式布局中,必须经过 js 来动态控制根元素 font-size 的大小。也就是说 css 样式和 js 代码有必定的耦合性。且必须将改变 font-size 的代码放在 css 样式以前。
经过百分比单位 " % " 来实现响应式的效果。经过百分比单位能够使得浏览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果。 直观的理解,咱们可能会认为子元素的百分比彻底相对于直接父元素,height 百分比相 对于 height,width 百分比相对于 width。 padding、border、margin 等等不管是垂直方向仍是水平方向,都相对于直接父元素的 width。 除了 border-radius 外,还有好比 translate、background-size 等都是相对于自身的。
缺点:
(1)计算困难
(2)各个属性中若是使用百分比,相对父元素的属性并非惟一的。形成咱们使用百分比单位容易使布局问题变得复杂。
浮动布局:当元素浮动之后能够向左或向右移动,直到它的外边缘碰到包含它的框或者另一个浮动元素的边框为止。元素浮动之后会脱离正常的文档流,因此文档的普通流中的框就变的好像浮动元素不存在同样。
优势
这样作的优势就是在图文混排的时候能够很好的使文字环绕在图片周围。另外当元素浮动了起来以后,它有着块级元素的一些性质例如能够设置宽高等,但它与inline-block仍是有一些区别的,第一个就是关于横向排序的时候,float能够设置方向而inline-block方向是固定的;还有一个就是inline-block在使用时有时会有空白间隙的问题
缺点
最明显的缺点就是浮动元素一旦脱离了文档流,就没法撑起父元素,会形成父级元素高度塌陷
。
<div class="parent">
//添加额外标签而且添加clear属性
<div style="clear:both"></div>
//也能够加一个br标签
复制代码
//在css中添加:after伪元素
.parent:after{
/* 设置添加子元素的内容是空 */
content: '';
/* 设置添加子元素为块级元素 */
display: block;
/* 设置添加的子元素的高度0 */
height: 0;
/* 设置添加子元素看不见 */
visibility: hidden;
/* 设置clear:both */
clear: both;
}
复制代码
什么事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
,把元素隐藏起来,而且会改变页面布局,能够理解成在页面中把该元素。 不显示对应的元素,在文档布局中再也不分配空间(回流+重绘)
包括值类型(基本对象类型)和引用类型(复杂对象类型)
基本类型(值类型): Number(数字),String(字符串),Boolean(布尔),Symbol(符号),null(空),undefined(未定义)在内存中占据固定大小,保存在栈内存中
引用类型(复杂数据类型): Object(对象)、Function(函数)。其余还有Array(数组)、Date(日期)、RegExp(正则表达式)、特殊的基本包装类型(String、Number、Boolean) 以及单体内置对象(Global、Math)等 引用类型的值是对象 保存在堆内存中,栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址。
Symbol 是 ES6 的新增属性,表明用给定名称做为惟一标识,这种类型的值能够这样建立,let id=symbol(“id”);Symbl 确保惟一,即便采用相同的名称,也会产生不一样的值,有内置方法 Object.getOwnPropertySymbols(obj)能够得到全部的 symbol。 也有一个方法 Reflect.ownKeys(obj)返回对象全部的键,包括 symbol。
undefined是访问一个未初始化的变量时返回的值,而null是访问一个还没有存在的对象时所返回的值。undefined看做是空的变量,而null看做是空的对象
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
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基本数据类型不能判断
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)
复制代码
ES6以前建立变量用的是var,以后建立变量用的是let/const
三者区别:
var定义的变量,没有块的概念,能够跨块访问
, 不能跨函数访问。
let定义的变量,只能在块做用域里访问,不能跨块访问,也不能跨函数访问。
const用来定义常量,使用时必须初始化(即必须赋值),只能在块做用域里访问,且不能修改。
var能够先使用,后声明
,由于存在变量提高;let必须先声明后使用。
var是容许在相同做用域内重复声明同一个变量
的,而let与const不容许这一现象。
在全局上下文中,基于let声明的全局变量和全局对象GO(window)没有任何关系 ;
var声明的变量会和GO有映射关系;
解决暂时性死区
:
暂时性死区是浏览器的bug:检测一个未被声明的变量类型时,不会报错,会返回undefined
如:console.log(typeof a) //undefined
而:console.log(typeof a)//未声明以前不能使用
let a
当浏览器开辟出供代码执行的栈内存后,代码并无自上而下当即执行,而是继续作了一些事情:把当前做用域中全部带var、function关键字的进行提早的声明和定义 =>变量提高机制 【预解析】
a=3
表示给全局设置window.a=3
和在全局做用域下var a=3
是同样的;在变量提高阶段,遇到大括号
、判断体
等,不论条件是否成立,都要进行变量提高,而在高版本浏览器中,函数只声明、不赋值。
什么是单线程
单线程即同一时间只有一个线程,只能作一件事
缘由:避免DOM渲染的冲突
解决方案:异步
实现方式:event-loop
JS的 同步任务/异步任务
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步:不进入主线程、而进入"任务队列
"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行
JavaScript为何须要异步
若是在JS代码执行过程当中,某段代码执行太久,后面的代码迟迟不能执行,产生阻塞(即卡死),会影响用户体验。
JavaScript怎么实现异步
JS 实现异步时经过 事件循环(Event Loop),是JS异步的解决方案。 JS实现异步的具体解决方案
一、同步代码,直接执行
二、异步代码先放在 异步队列
中
三、待同步函数执行完毕,轮询执行异步队列 中的函数
目前JS解决异步的方案有哪些
Promise
Async/Await
JS内存空间分为栈(stack) 、堆(heap) 、池(通常也会归类为栈中) 。 其中栈存放变量,堆存放复杂对象,池存放常量,因此也叫常量池。
栈
内存(不包含闭包中的变量)堆
内存栈内存(Stack):浏览器在计算机内存中分配出一块内存供代码执行的环境栈(ECStack),也称栈内存 ;
基本数据类型都是存到栈里面的。 引用数据类型指针存到栈内存。
堆内存(Heap):浏览器会把内置的属性和方法放到一个单独的内存中,
引用数据类型是先开辟一个堆内存,一个16进制的地址,按照键、值分别存放,最后把地址放到栈中供代码关联使用;
js 中存在多种做用域(全局,函数私有的,块级私有的),引擎在编译执行代码的过程当中,首先会建立一个执行栈,也就是栈内存(ECStack
=> 执行环境栈),而后执行代码。
代码执行前首先会造成本身的EC
(执行上下文),执行上下文分为全局执行上下文(EC(G)
)和函数执行上下文(EC(...)
),其中函数的执行上下文是私有的。
建立执行上下文的过程当中,可能会建立:
GO(Global Object)
:全局对象 浏览器端,会把GO赋值给windowVO(Varible Object)
:变量对象,存储当前上下文中的变量。AO(Active Object)
:活动对象而后把上下文压缩进栈,进栈后,在当前上下文再依次执行代码; 全局执行器上下文(EC(G))进栈(ECStack)执行,执行完代码就会把造成的上下文释放(出栈),执行后一些没用的上下文也将出栈,有用的上下文会压缩到栈底(闭包)。栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。当页面关闭全局上下文出栈。
VO 变量对象:每个执行上下文都会有本身的一个VO变量对象,用来存放在当前上下文中建立的变量和函数。(函数私有上下文叫 AO 活跃对象,但也是变量对象)。
GO 全局对象:他是一个堆内存(存储的都是浏览器内置的 api 属性方法),在浏览器端,让 window 指向它
VO(G)全局变量对象:全局上下文中用来存储全局变量的空间,他不是 GO=》只不过某些状况下 VO(G)中的东西会和 GO 中的东西有所关联而已;
函数执行:
ECStack
:引擎在编译执行代码的过程当中,首先会建立一个执行栈EC(FN)
:函数执行的时候,造成一个全新的私有上下文EC(FN)
,供字符串代码执行GO
项目中,若是存在大量不被释放的内存(堆/栈/上下文),页面性能会变得很慢。当某些代码操做不能被合理释放,就会形成内存泄漏。咱们尽量减小使用闭包,由于它会消耗内存。
浏览器垃圾回收机制/内存回收机制:
浏览器的
Javascript
具备自动垃圾回收机制(GC:Garbage Collecation
),垃圾收集器会按期(周期性)找出那些不在继续使用的变量,而后释放其内存。
标记清除:在js
中,最经常使用的垃圾回收机制是标记清除:当变量进入执行环境时,被标记为“进入环境”,当变量离开执行环境时,会被标记为“离开环境”。垃圾回收器会销毁那些带标记的值并回收它们所占用的内存空间。
谷歌浏览器:“查找引用”,浏览器不定时去查找当前内存的引用,若是没有被占用了,浏览器会回收它;若是被占用,就不能回收。
IE浏览器:“引用计数法”,当前内存被占用一次,计数累加1次,移除占用就减1,减到0时,浏览器就回收它。
优化手段:内存优化 ; 手动释放:取消内存的占用便可。
(1)堆内存:fn = null 【null:空指针对象】
(2)栈内存:把上下文中,被外部占用的堆的占用取消便可。
内存泄漏
在 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 形式
二、函数嵌套函数
三、本质是将函数内部和外部链接起来。优势是能够读取函数内部的变量,让这些变量的值始终保存在内存中,不会在函数被调用以后自动清除
闭包造成的条件:
闭包的用途:
闭包应用场景
闭包的两个场景,闭包的两大做用:保存/保护
。 在开发中, 其实咱们随处可见闭包的身影, 大部分前端JavaScript 代码都是“事件驱动”的,即一个事件绑定的回调方法; 发送ajax请求成功|失败的回调;setTimeout的延时回调;或者一个函数内部返回另外一个匿名函数,这些都是闭包的应用。
闭包的优势:延长局部变量的生命周期
闭包缺点:会致使函数的变量一直保存在内存中,过多的闭包可能会致使内存泄漏
this
指向window
。this
就会指向该对象
。this
指向返回的这个对象
。this
绑定看的是this所在函数定义在哪一个对象下
,就绑定哪一个对象。若是有嵌套的状况,则this绑定到最近的一层对象上。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原型对象是整个原型链的顶端,到这就截止了。
原型关系:
基于原型的执行规则:即原型链
原型: 在 JS 中,每当定义一个对象(函数也是对象)时,对象中都会包含一些预约义的属性。其中每一个函数对象都有一个prototype
属性,这个属性指向函数的原型对象。
原型链:函数的原型链对象constructor默认指向函数自己,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然相似。所以能够利用__proto__一直指向Object的原型对象上,而Object原型对象用Object.prototype.proto=null表示原型链顶端。如此造成了js的原型链继承。同时全部的js对象都有Object的基本防范
特色: JavaScript
对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本。当咱们修改原型时,与之相关的对象也会继承这一改变。
空对象
设置原型
,将对象的原型设置为函数的prototype
对象。this
指向这个对象,执行构造函数的代码(为这个新对象添加属性)(1)第一种是以原型链的方式来实现继承
,可是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被全部的实例对象所共享,容易形成修改的混乱。还有就是在建立子类型的时候不能向超类型传递参数。
(2)第二种方式是使用借用构造函数
的方式,这种方式是经过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,可是它存在的一个问题就是没法实现函数方法的复用,而且超类型原型定义的方法子类型也没有办法访问到。
(3)第三种方式是组合继承
,组合继承是将原型链和借用构造函数组合起来使用的一种方式。经过借用构造函数的方式来实现类型的属性的继承,经过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,可是因为咱们是以超类型的实例来做为子类型的原型,因此调用了两次超类的构造函数,形成了子类型的原型中多了不少没必要要的属性。
(4)第四种方式是原型式继承
,原型式继承的主要思路就是基于已有的对象来建立新的对象,实现的原理是,向函数中传入一个对象,而后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。
(5)第五种方式是寄生式继承
,寄生式继承的思路是建立一个用于封装继承过程的函数,经过传入一个对象,而后复制一个对象的副本,而后对象进行扩展,最后返回这个对象。这个扩展的过程就能够理解是一种继承。这种继承的优势就是对一个简单对象实现继承,若是这个对象不是咱们的自定义类型时。缺点是没有办法实现函数的复用。
(6)第六种方式是寄生式组合继承
,组合继承的缺点就是使用超类型的实例作为子类型的原型,致使添加了没必要要的原型属性。寄生式组合继承的方式是使用超类型的原型的副原本做为子类型的原型,这样就避免了建立没必要要的属性。
最简单的方法就是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
Object.assign(target, ...sources)
//方法 2
function simpleClone(obj) {
var result = {};
for (var i in obj) {
result[i] = obj[i];
}
return result;
}
复制代码
复制代码
JS
是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,因此会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,由于宏任务队列的执行时间较长,因此微任务队列要优先于宏任务队列。微任务队列的表明就是,Promise.then
,MutationObserver
,宏任务的话就是setImmediate setTimeout setInterval
JS运行的环境。通常为浏览器或者Node。 在浏览器环境中,有JS 引擎线程和渲染线程,且两个线程互斥。 Node环境中,只有JS 线程。 不一样环境执行机制有差别,不一样任务进入不一样Event Queue队列。 当主程结束,先执行准备好微任务,而后再执行准备好的宏任务,一个轮询结束。
事件环的运行机制是,先会执行栈中的内容,栈中的内容执行后执行微任务,微任务清空后再执行宏任务,先取出一个宏任务,再去执行微任务,而后在取宏任务清微任务这样不停的循环。
eventLoop 是由JS的宿主环境(浏览器)来实现的;
事件循环能够简单的描述为如下四个步骤:
浏览器中的任务源(task):
宏任务(macrotask)
:
宿主环境提供的,好比浏览器
ajax、setTimeout、setInterval、setTmmediate(只兼容ie)、script、requestAnimationFrame、messageChannel、UI渲染、一些浏览器api
微任务(microtask)
:
语言自己提供的,好比promise.then
then、queueMicrotask(基于then)、mutationObserver(浏览器提供)、messageChannel 、mutationObersve
Node
是基于V8引擎的运行在服务端的JavaScript
运行环境,在处理高并发、I/O密集(文件操做、网络操做、数据库操做等)场景有明显的优点。虽然用到也是V8引擎,但因为服务目的和环境不一样,致使了它的API与原生JS有些区别,其Event Loop还要处理一些I/O,好比新的网络链接等,因此Node的Event Loop(事件环机制)与浏览器的是不太同样。
执行顺序以下:
timers
: 计时器,执行setTimeout和setInterval的回调pending callbacks
: 执行延迟到下一个循环迭代的 I/O 回调idle, prepare
: 队列的移动,仅系统内部使用poll轮询
: 检索新的 I/O 事件;执行与 I/O 相关的回调。事实上除了其余几个阶段处理的事情,其余几乎全部的异步都在这个阶段处理。check
: 执行setImmediate
回调,setImmediate在这里执行close callbacks
: 执行close
事件的callback
,一些关闭的回调函数,如:socket.on('close', ...)回调有四个问题
new Promise(
/*执行器executor */
function (resolve, reject){
//段耗时很长的异步操做
resolve();//数据处理完成
reject(); //数据处理出错
}
.then(function A(){
//成功,下一步
},function B(){
//失败,作相应处理
}
复制代码
Promise的内部是如何实现异步执行的呢?
经过查看Promise的源码实现,发现其异步执行是经过asap这个库来实现的。
asap是as soon as possible的简称,在Node和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环以前),即把任务放在微任务队列中执行。
宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时, js 引擎会将全部任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫作 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的全部任务顺序执行;以后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。
// asap 用法
asap(function () {
// ...
});
复制代码
pending
|【待定】初始状态fulfilled
|【实现】操做成功rejected
【被否决】操做失败Promise状态发生改变,就会触发.then()里的响应函数处理后 续步骤。 Promise状态一经改变,不会再变。 Promise实例一经建立,执行器当即执行。
.then()接受两个函数做为参数,分别表明fulilled
和rejected .then()
返回一个新的Promise实例,因此它能够链式调用 .当前面的Promise状态改变时,.then()根据其最终状态,选择特定 的状态响应函数执行 .状态响应函数能够返回新的Promise,或其它值 .若是返回新的Promise,那么下一级.then()会在新Promise状态改变以后执行 .若是返回其它任何值,则会马上执行下一级.then()
由于.then()返回的仍是Promise实例。 会等里面的.then()执行完,在执行外面的。 对于咱们来讲,此时最好将其展开,会更好读。
Promise会自动捕获内部异常,并交给rejected响应函数处理。 错误处理的两种作法: reject('错误信息).then(null,message =>{}) throw new Error('错误信息')catch(message=>{}) 推荐使用第二种,更加清晰好读,而且能够捕获前面的错误。
返回一个rejected的Promise实例。 Promise.reject()其余特性同Promise.resolve(),但不认thenable
相似Promise.all(),区别在于它有任意一个完成就算完成。 场景用法: 把异步操做和定时器放在一块儿 若是定时器先触发,就认为超时,告知用户
把回调包装成Promise最为常见。它有两个显而易见的好处: 可读性更好 返回的结果能够加入任何Promise队列
c。
ajax是一种异步通讯的方法,从服务端获取数据,达到局部刷新页面的效果。 过程:
XMLHttpRequest
对象;open
方法传入三个参数 请求方式(GET/POST)、url、同步异步(true/false)
;onreadystatechange
事件,当readystate
等于4时返回responseText
;JS高阶编程技巧:惰性函数/柯理化函数/高阶函数 constructor构造函数模式 类和实例 call/apply/bind DOM/BOM的核心操做 DOM2级事件的核心运行机制 事件对象 发布订阅设计模式 浏览器底层渲染机制和DOM的回流重绘 事件传播机制和事件代理
ES6/ES7的核心知识 解头函数ArrowFunction 解构聚值和拓展运算符 JS底层运行机制:单线程和同步 Set/Map数据结构 异步编程 Gonerator生成器函数 Intorator选代器和for of循环
AJAX/HTTP先后端数据交互
前端性能优化汇总(包含强缓存和弱缓存)
什么是MVVM?
视图模型双向绑定
,是Model-View-ViewModel
的缩写,也就是把MVC
中的Controller
演变成ViewModel。Model
层表明数据模型,View
表明UI组件,ViewModel
是View
和Model
层的桥梁,数据会绑定到viewModel
层并自动将数据渲染到页面中,视图变化的时候会通知viewModel
层更新数据。之前是操做DOM结构更新视图,如今是数据驱动视图
。
MVVM的优势:
1.低耦合
。视图(View)能够独立于Model变化和修改,一个Model能够绑定到不一样的View上,当View变化的时候Model能够不变化,当Model变化的时候View也能够不变;
2.可重用性
。你能够把一些视图逻辑放在一个Model里面,让不少View重用这段视图逻辑。
3.独立开发
。开发人员能够专一于业务逻辑和数据的开发(ViewModel),设计人员能够专一于页面设计。
4.可测试
。
Vue做为MVVM模式的实现库的2种技术
a. 模板解析
b. 数据绑定
模板解析:实现初始化显示
a. 解析大括号表达式
b. 解析指令
数据绑定:实现更新显示
a. 经过数据劫持实现
建立了两种对象Observer和complie,先建立的Observer,后建立的complie,observer是为了监视/劫持data中全部层次的属性,同时还为每一种属性建立了另一种对象dep,dep与data中的属性一一对应,complie做用是用来编译模版,初始化界面,调用update对象,complie还为每一个表达式建立了对应的watcher同时指定了更新节点的回调函数,将watcher添加到全部对应的dep中,
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 不友好
什么是虚拟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?
虚拟dom就是为了解决操做真是dom带来的性能问题而出现的,将DOM对比操做放在JS层,提升效率
DOM结构的对比,放在JS层来作(图灵完备语言:能实现逻辑代码的语言)操做内存中的js显然效率更高
3、vdom核心函数有哪些
核心函数:
h('标签名', {...属性名...}, [...子元素...])
h('标签名', {...属性名...}, '.........')
patch(container, vnode)
patch(vnode, newVnode)
vue.js是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter和getter,在数据变更时发布消息给订阅者,触发相应的监听回调
Vue是一个典型的MVVM框架,模型(Model)只是普通的javascript对象,修改它则试图(View)会自动更新。这种设计让状态管理变得很是简单而直观
Observer(数据监听器): Observer的核心是经过Object.defineProprtty()来监听数据的变更,这个函数内部能够定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher
Watcher(订阅者): Watcher订阅者做为Observer和Compile之间通讯的桥梁,主要作的事情是:
Compile(指令解析器): Compile主要作的事情是解析模板指令,将模板中变量替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变更,收到通知,更新试图
参考回答: 一则语法糖,至关于 v-bind:value="xxx" 和 @input,意思是绑定了一个 value 属性的值, 子组件可对 value 属性监听,经过$emit('input', xxx)的方式给父组件通信。本身实现 v-model 方式的组件也是这样的思路。
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
的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动做或者事件
create阶段
:vue实例被建立beforeCreate
: 建立前,此时data和methods中的数据都尚未初始化created
: 建立完毕,data中有值,未挂载mount阶段
: vue实例被挂载到真实DOM节点beforeMount
:能够发起服务端请求,去数据mounted
: 此时能够操做DOMupdate阶段
:当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
:当一条数据影响多条数据的时候使用,例:搜索数据
1.一个组件被复用屡次的话,也就会建立多个实例。本质上,这些实例用的都是同一个构造函数。 2.若是data是对象的话,对象属于引用类型,会影响到全部的实例。因此为了保证组件不一样的实例之间data不冲突,data必须是一个函数。
1.当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每一个 v-for 循环中。若是要遍历的数组很大,而真正要展现的数据不多时,这将形成很大的性能浪费 2.这种场景建议使用 computed,先对数据进行过滤
Vuex中全部的状态更新的惟一途径都是mutation,异步操做经过 Action 来提交 mutation实现,这样使得咱们能够方便地跟踪每个状态的变化,从而让咱们可以实现一些工具帮助咱们更好地了解咱们的应用。 每一个mutation执行完成后都会对应到一个新的状态变动,这样devtools就能够打个快照存下来,而后就能够实现 time-travel 了。若是mutation支持异步操做,就没有办法知道状态是什么时候更新的,没法很好的进行状态的追踪,给调试带来困难。
当 Vue.js 用 v-for
更新已渲染过的元素列表时,它默认用“就地复用”策略。若是数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每一个元素,而且确保它在特定索引下显示已被渲染过的每一个元素。重复的key会形成渲染错误。
key
的做用主要是为了让vue能够区分元素,更高效的对比更新虚拟DOM;
Vue在patch
过程当中判断两个节点是不是相同节点,key是一个必要条件,是惟一标识,如不定义key,Vue
只能认为比较的两个节点是同一个,这致使了频繁更新元素,使得整个patch
过程比较低效,影响性能;
从源码中能够知道,Vue判断两个节点是否相同时主要判断二者的key和元素类型等,所以若是不设置key,它的值就是undefined
,则可能永远认为这是两个相同的节点,只能去作更新操做,这形成了大量的dom更新操做,明显是不可取的。
父子组件通讯
父->子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从新计算,从而导致它关联的组件得以更新。
vue
中双向绑定是一个指令v-model
,能够绑定一个动态值到视图,同时视图中变化能改变该值。v-model
是语法糖,默认状况下相于:value和@input
。v-model
能够减小大量繁琐的事件处理代码,提升开发效率,代码可读性也更好v-model
v-model
,自定义组件上若是要使用它须要在组件内绑定value并处理输入事件v-model
模板的组件渲染函数,发现它会被转换为value属性的绑定以及一个事件监听,事件回调函数中会作相应变量更新操做,这说明神奇魔法其实是vue的编译器完成的。nextTick
是Vue
提供的一个全局API
,是在下次DOM
更新循环结束以后执行延迟回调,在修改数据以后使用$nextTick
,则能够在回调中获取更新后的DOM
;Vue
将开启1个队列,并缓冲在同一事件循环中发生的全部数据变动。若是同一个watcher
被屡次触发,只会被推入到队列中-次。这种在缓冲时去除重复数据对于避免没必要要的计算和DOM
操做是很是重要的。nextTick
方法会在队列中加入一个回调函数,确保该函数在前面的dom操做完成后才调用;nextTick
实现,它会在callbacks
里面加入咱们传入的函数,而后用timerFunc
异步方式调用它们,首选的异步方式会是Promise
。这让我明白了为何能够在nextTick
中看到dom
操做结果。在下次 DOM 更新循环结束以后执行延迟回调,在修改数据以后当即使用 nextTick 来获取更新后的 DOM。 nextTick主要使用了宏任务和微任务。 根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,若是以上都不行则采用setTimeout定义了一个异步方法,屡次调用nextTick会将方法存入队列中,经过这个异步方法清空当前队列。
vue中的插槽是一个很是好用的东西slot说白了就是一个占位的 在vue当中插槽包含三种一种是默认插槽(匿名)一种是具名插槽还有一种就是做用域插槽 匿名插槽就是没有名字的只要默认的都填到这里具名插槽指的是具备名字的
做用:实现组件缓存,保持这些组件的状态,以免反复渲染致使的性能问题。 须要缓存组件 频繁切换,不须要重复渲染
场景:tabs标签页 后台导航,vue性能优化
原理:Vue.js
内部将DOM
节点抽象成了一个个的VNode
节点,keep-alive
组件的缓存也是基于VNode
节点的而不是直接存储DOM
结构。它将知足条件(pruneCache与pruneCache)
的组件在cache
对象中缓存起来,在须要从新渲染的时候再将vnode
节点从cache
对象中取出并渲染。
mixin 项目变得复杂的时候,多个组件间有重复的逻辑就会用到mixin
多个组件有相同的逻辑,抽离出来
mixin并非完美的解决方案,会有一些问题
vue3提出的Composition API旨在解决这些问题【追求完美是要消耗必定的成本的,如开发成本】
场景:PC端新闻列表和详情页同样的右侧栏目,能够使用mixin进行混合
劣势:1.变量来源不明确,不利于阅读 2.多mixin可能会形成命名冲突 3.mixin和组件可能出现多对多的关系,使得项目复杂度变高
复制代码
状态管理库,相似 React 中的 Rudux
关于vuex vuex是一个专门为vue构建的状态集管理,主要是为了解决组件间状态共享的问题,强调的是数据的集中式管理,说白了主要是便于维护便于解耦因此不是全部的项目都适合使用vuex,若是你不是构建大型项目使用vuex反而使你的项目代码繁琐多余
vuex的核心: state mutations getters actions modules
Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每个 Vuex 应用的核心就是 store(仓库)。
若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新 2. 改变 store 中的状态的惟一途径就是显式地提交 (commit) mutation, 这样使得咱们能够方便地跟踪每个状态的变化 Vuex主要包括如下几个核心模块:
就像计算属性同样,getter 的返回值会根据它的依赖被缓存起来, 且只有当它的依赖值发生了改变才会被从新计算 3. Mutation:是惟一更改 store 中状态的方法,且必须是同步函数 4. Action:用于提交 mutation,而不是直接变动状态,能够包含任意异步操做 5. Module:容许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中
1)对Vuex基本理解1)是什么:Vuex是一个专为Vue.js应用程序开发的状态管理的vue插件2)做用:集中式管理vue多个组件共享的状态和从后台获取的数据states帮助组件管理状态的,基于state的还有一个计算属性数据getters,getters是从state中读取数据并计算的,他们两个的数据都是给组件去读,组件中读取state状态数据使用 store.getters和mapGetters();更新状态数据涉及到actions和mutations,经过$store.dispatch或mapAction()触发action的调用,而后actions会经过commit()触发mutations调用,mutations则直接更新状态;actions还能够同后台API进行双向通讯。
“单向数据流”理念的极简示意:
简单的单向数据流(unidirectional data flow)是指用户访问View,View发出用户交互的Action,在Action里对state进行相应更新。state更新后会触发View更新页面的过程。这样数据老是清晰的单向进行流动,便于维护而且能够预测
问题:渲染真实的DOM开销是很大的,修改了某个数据,若是直接渲染到真实dom上会引发整个DOM树的重绘和重排。 Virtual Dom 根据真实DOM生成的一个Virtual DOM,当Virtual DOM某个节点的数据改变后生成一个新的Vnode,而后Vnode和oldVnode做对比,发现有不同的地方就直接修改在真实的DOM上,而后使oldVnode的值为Vnode. 注意:在采起diff算法比较的时候,只会在同层级进行,不会跨层级比较。 当数据发生改变时,set方法会让调用Dep.notify()方法通知全部订阅者Watcher,订阅者就会调用patch函数给真实的DOM打补丁,更新响应的试图。
更小巧、更快速 支持自定义渲染器 支持摇树优化:一种在打包时去除无用代码的优化手段 支持Fragments和跨组件渲染
模板语法99%保持不变 原生支持基于class的组件,而且无需借助任何编译及各类stage阶段的特性 在设计时也考虑TypeScript的类型推断特性 重写虚拟DOM
能够期待更多的编译时提示来减小运行时的开销 优化插槽生成
能够单独渲染父组件和子组件 静态树提高
下降渲染成本 基于Proxy的观察者机制
节省内存开销
检测机制
更加全面、精准、高效,更具可调试式的响应跟踪
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 算法优化
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监听对象中的全部的属性
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
编码阶段
尽可能减小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压缩等。
相同点: 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中父子组件中参数互传,子传父是先在父组件上绑定属性设置为一个函数,当子组件须要给父组件传值的时候,则经过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 提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构以外的 DOM 节点中。结构不受外界的控制的状况下就能够使用portals进行建立
// 异步懒加载
const Box = lazy(()=>import('./components/Box'));
// 使用组件的时候要用suspense进行包裹
<Suspense fallback={<div>loading...</div>}> {show && <Box/>} </Suspense>
复制代码
immutable内部提供的全部数据类型,对其数据进行任意操做,操做获得的结果是修改后的值 而且修改后的值是一个新的对象,原来的对象没有发生任何变化。 immutable.js文档
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 变化后,再从新渲染。
head
标签中,而脚本文件在body
结束前,这样能够防止阻塞的方式。class
,或者csstext
属性,一次性地改变样式。transform
来作形变和位移Document Fragment
对象,完成后再把这个对象加入DOM。再好比,使用cloneNode()
方法,在克隆的节点上进行操做,而后再用克隆的节点替换原始节点。display: none
(须要1次重排和重绘),而后对这个节点进行100次操做,最后再恢复显示(须要1次重排和重绘)。这样一来,你就用两次从新渲染,取代了可能高达100次的从新渲染。position
属性为absolute
或fixed
的元素,重排的开销会比较小,由于不用考虑它对其余元素的影响。