防抖和节流
防抖:
触发高频事件后n秒内只执行一次,如果n秒内再次触发,则重新计算时间,多用于按钮防止重复点击 input输入校验
实现方式:每次触发事件都取消掉上次的延时调用,
节流:
高频事件在n秒内只执行一次,节流会稀释函数的执行频率.多用于高频操作,touchmove mousemove input keyup
实现方式: 每次触发的时候都判断当前是否有等待执行的延时函数
npm实现原理
npm script
npm 允许用户在pakage.json里面用scripts对象自定义用户执行脚本,npm script对外提供统一的接口,在运行一段脚本时,新建一个shell,会将当前的node_modules/.bin生成PATH变量, 结束之后再恢复PATH变量.
scripts:{
test: mocha test //替代 './node_modules/.bin/mocha test'
}
npm scripts脚本本来就是shell脚本,可以使用shell的能力--shell通配符,传参,bash多脚本执行顺序(&并行执行)(&&串行执行)
npm 钩子
npm脚本有pre和post钩子, 执行顺序 npm run prebuild && npm run build && npm run postbuild
npm 变量
npm install
执行自身的preinstall
宏任务和微任务
https://blog.csdn.net/weixin_36852235/article/details/89101233
https://time.geekbang.org/column/article/82764
视频讲解: https://www.youtube.com/watch?v=8aGhZQkoFbQ
当拿到一段javascript代码的时候,浏览器和node会不断的传递给javascript引擎去执行,引擎吧代码直接顺序执行,这个就是宿主发起的任务,在ES5之后,javascript引入的promise,这样不用浏览器或者node的传递,javascript引擎本身也可以发起任务,所以我们吧浏览器或者node发起的任务成为宏观任务,jsvsscript引擎发起的任务成为微观任务
宏任务 event loop
setTimeout setInterval requestAnimationFrame I/O UIRender
微任务
process.nextStick() promise(在微观任务队列最后添加任务) Object.Observe
setTimeout(()=>{
console.log("d1")
r.then(()=>{
console.log("d2")
})
}, 0);
var r = new Promise(function(resolve, reject){ resolve() });
r.then(() => {
var begin = Date.now();
while(Date.now() - begin < 1000); console.log("c1");
new Promise(function(resolve, reject){ resolve() }).then(() => console.log("c2"))
});
看了视屏之后发现画的图产生了一些误解:
V8引擎的event loop事件循环,只有webApi产生的回调都在task queen任务队列里面,
1.当前宏任务产生的微任务推到微任务列表,stack执行完毕之后,等待..
2.微任务队列把微任务推到调用栈执行,执行完毕,等待...,
3.webapi宏任务,task queen任务队列把回调推入调用栈执行
浏览器的工作原理
http缓存
作用:
1)减少冗余的数据传输
2)减少服务器的压力
3)加快的网页的呈现速度
http缓存只响应get请求响应的资源,浏览器先校验强缓存,再次校验协商缓存
强制缓存
pragma,cache-control:max-age,expire三个缓存头字段优先级依次降低,若文件没过期直接从memory cache/disk cache读取,不进行网络请求,请求状态200
协商缓存
etag/if-none-match,last-modified/if-modified-since响应头/请求头,先去进行网络请求,服务器根据if-none-match(hash值对比) | if-modified-since(过期时间判断)进行文件过期判断,没过期则返回304,过期则返回200且把文件返回
图片一般缓存在memory cache中,js一般缓存在disk cache中
浏览器缓存
cookies
每次和请求都会带上当前域名下的cookies 可以设置过期时间
localstorage
本地存储,不与后端交互,需要手动删除
sessionstorage
本地存储,不与后端交互,会话结束,缓存失效
浏览器async和defer (性能优化板块问题)
async和defer都是为了优化网页的加载速度,使得页面的空白时间更短,获得更好的客户体验
1)<script src=""></script>, 没有defer或者async,浏览器会立即加载并执行脚本,阻塞后续文档元素的载入.(这里是作用于词法分析还是语法分析?)
2)<script src="" async></script>,有async标记的资源在加载和执行的过程跟后续文档元素的解析和渲染并行执行,async标记由于因为加载和执行都是异步,所以并不能保证脚本的执行顺序,此类脚本最适用于 监控脚本 打点脚本等不影响主流程的脚本加载,若脚本之间有依赖关系,不可用async
3)<script src="" defer></script>,有deder标记的资源加载过程和后续文档元素的解析和渲染并行执行, 执行过程在所有的元素解析完成之后,在DOMcontentloaded之前执行,defer标记能够保证及时加载是异步的,但是执行的顺序是按照文档的顺序执行.
对于实用的角度来讲,把所有的脚本都丢到<body>标签下加载是最简单的一种方法,能够保证一切非脚本的文档元素得到最快速的加载和解析
值得注意的是,不管是defer还是async,脚本执行过程都会阻塞html文档的解析是因为js能操作DOM,浏览器为了避免重新构建DOM树,脚本执行进程和文档的解析进程之间互斥
利用async我们能做什么
性能优化之async缓存js资源,缓存到本地,下一页加载强制缓存直接从disk cache读取
<script async="" data-next-page="/p/[slug]" src="https://cdn2.jianshu.io/shakespeare/_next/static/puNPQDWhCvW09zKH9N80j/pages/p/%5Bslug%5D.js"></script>
性能优化之<link rel="preload"> js,css,图片资源预加载
<link rel="preload" href="https://cdn2.jianshu.io/shakespeare/_next/static/runtime/webpack-69a7d3bdb55520eaee8f.js" as="script">
<link rel="preload" href="wide.png" as="image" media="(min-width: 601px)">
DOMcontentloaded?
在network网页的时间流程里面 下载资源(finish) -> DOMcontentLoaded -> load
DOMcontentLoaded: 表示html文档元素解析完成,DOM生成完成,时间与js的位置,大小,html文档元素的复杂度有关
load:表示加载完成,包含图片,字体,js,css加载完成,时间与资源的大小相关
上个问题由于defer标记的资源在DOMcontentLoaded之前执行,意味着DOMcontentLoaded的触发变成了,html解析完成,有defer标记脚本必须等到defer脚本执行完成,,无defer脚本直接等到html解析完成之后便可触发
分别对应事件: ready(DOMcontentLoaded完成够触发) -> onload(load完成后触发)
ready事件可以监听多个,不会冲突和覆盖,load事件只能执行一次,
多次监听,后面会覆盖前面,只执行最后一次函数
文档解析parse: html文档->DOM(文档对象模型) css->cssOM(css对象模型)
合并渲染redener: DOM + cssOM
冒泡bubble和捕获capture?
cdn是什么?
页面性能优化之预加载
- <link rel="dns-prefetch">
dns prefetch通过指定的url告诉浏览器未来会用到的相关资源,让浏览器尽早的解析dns
<link rel="dns-prefetch" href="//example.com">
- <link rel="preconnect">
preconnect不仅会解析dns,还会建立TCP握手连接和TLS协议
<link rel="preconnect" href="http://example.com">
- <link rel="prefetch">
prefetch可以预先加载下一个页面或者未来所需要的文件,缓存在本地
prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源(空闲时间加载)
-
下次加载资源的时候会从 prefetch cache读取
<link rel="prefetch" href="image.png">
<link rel="prefetch" href="a.js">
- <link rel="prerender">
prerender可以预先加载下一个页面的所有资源
<link rel="prerender" href="/thenextpage.html"/>
- <link rel="preload">
preload提供一种声明式的命令,让浏览器提前加载资源(加载后不执行),在需要执行的时候再执行,有as属性可以提前知道资源加载的类型
preload和prefetch一样有预加载的能力,区别是: preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源;
<link rel="preload" href="a.js" as="script">
async加载js也可以缓存下一页的js,但是它会阻碍window.onload的执行,preload则不会
Preload 的好处
- 加载和执行分开,可不阻塞window.onload事件,在渐近式的程序(单页应用,动态加载的程序)中,preload会大大缩短路由到下一页面的时间
- 提前加载指定资源,不再出现依赖的font字体隔了一段时间才刷出
不能混用prefetch和preload
preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载,会带来双倍的网络请求
react render原理
render周期:
state,props和render的关系,只要state和props改变,就重新render,子组件也重新render
render原理:
state数据+JSX模板结合,生成(react.creatElement )新的虚拟DOM树(虚拟DOM都是一个js数组,完整的描述了当前节点的所有特性),对比直接生成真实的DOM(document.creatElement调用webapi)损耗小很多
,最后根据虚拟dom生成真实dom,当state和props发生更改的时候,生成新的虚拟DOM树,与老的虚拟DOM树diff,产出差异DOM树,进行真是DOM更新
虚拟DOM是什么:
虚拟DOM都是一个js数组,完整的描述了当前节点的所有特性
虚拟DOM的优点:
1.性能提升,使得每次render耗费时间短
2.跨平台的方案得以实现,虚拟DOM就是JS数组,各个平台都是借助虚拟DOM实现自己的渲染逻辑
diff算法:
传统的树与树的比较算法,时间复杂度为O(n^3) reactDiff时间复杂度可为O(n)
优化策略:
策略一(tree diff): DOM节点中跨层级的节点移动操作少
策略二(component diff):拥有相同类的两个组件 生成相似的树形结构.
策略三(element diff):对于同一层级的一组子节点,通过唯一id区分。
实现:
treeDiff: 同层次节点比较,updateDepth对虚拟DOM树进行层次控制,一旦节点类型不一样,直接替换
componentDiff: react基于组件式开发,同类型组件继续比较treeDiff,不同类型直接替换
elementDiff: 以key值进行标记,同层级节点提供三种方式: insert move remove
得到最后的 patches对象,进行必要的webapiDOM操作
TCP/IP协议
tcp/ip协议是网络传输层的协议,为应用层提供报文传输服务.
TCP场景
- 需链接,可靠传输应用,文件传输,HTTP,HTTPS.FTP等
UDP
- 无连接,实时传输,视屏会议,直播等
TCP链接实质是客户端和服务端保存一份关于对方的信息,如,ip 端口号等
三次握手
三次握手的实质是客户端和服务端确认对象收/发数据的能力
- 第一次: 客户端->服务端 发送传输请求[标志位SYN=1,随机序列号seq=100]
- 第二次:服务端->客户端 同意传输请求[标志位SYN=1 ACK=1,随机序列号seq=500,确认号ack=101]
- 第三次: 客户端->服务端 确认信息[标志位ACK=1,序列号seq=101,确认号ack=501]
连接建立完毕,可以开始传输报文
为什么是三次不是2次
2次握手,客户端不知道服务端已经ready,就不会发送报文,服务端白白浪费资源
客户端故障怎么处理
TCP有连接保活,计时器2小时没报文传输,发送探测报文,10次无响应则关闭连接
四次挥手
- 第一次:客户端->服务端 发送关闭请求[标志位FIN=1,随机序列号seq=100] (此时不再发送数据)
- 第二次:服务端->客户端 同意关闭请求[标志位ACK=1,随机序列号seq=500,确认号ack=101] (此时服务端处于关闭等待状态,仍然可以发送数据,)
[数据发送完毕] - 第三次: 服务端->客户端 发送释放资源请求[标志位FIN=1ACK=1,随机序列号seq=1000,确认号ack=101]
- 第四次: 客户端->服务端 同意释放[ACK=1,序列号seq=101,确认号ack=1001] (此时服务端立即释放资源,客户端等待关闭)
[等待一段时间,关闭连接]