1.JWT跨域认证解决方案(Token生成)
用户认证一般是用户发送用户名和密码,服务器验证通过后,在session中保存相关数据,服务器返回一个session_id,写入用户的cookie,随后的每一次请求都会通过cookie将session_id传回服务器。当时服务器集群或跨域的服务导向架构,要求sesion数据共享,每台服务器能读取session,一个方案是session数据持久化,写入数据库,收到请求后向持久层请求数据,另一种方案是服务器不保存session数据,所有数据保存在客户端。
1)原理
服务器认证后生成一个JSON对象,发回给用户,以后用户与服务端通信时都要发回这个JSON对象,服务器只靠这个对象认定用户身份,服务器在生成这个对象时加上签名。服务器不保存任何session数据,变成无状态,比较容易实现。
2)JWT数据机构
是一个很长的字符串,中间用.分成3各部分,分别为Header,Payload(负载),Signature(签名)。 Header是JSON对象,描述JWT的元数据,通常会指定alg属性(签名的算法,默认HS256),typ属性(表示token的类型,统一JWT),然后用Base64URL转成字符串。Payload也是JSON对象,用来存放实际需要传递的数据,规定了7个官方字段(iss签发人,exp过期时间,sub主题,aud受众,nbf生效时间,iat签发时间,jti编号),也可以定义私有字段.JWT默认不加密,这个JSON对象也用Base64URL转成字符串。Signature是对前两部分的签名,防止数据篡改,需要指定一个密钥,只有服务器知道,然后用Header里面指定的签名算法产生签名,算出千米过后把3各部分拼成字符串
3)Base64URL
Base64有3个字符+,/,=,在URL有特殊含义,所以要替换带哦,所以=被省略,+替换成-,/替换成_
4)使用方式
收到JWT可以存在cookie,localStorage,每次通信都带上这个JWT,放在cookie会自动发送,但不能跨域,所以更好的是放在HTTP请求的头信息Authorization字段,或者跨域的时候放在POST请求的数据体
5)特点
JWT默认不加密,但也可以加密,生成原始Token后可以用密钥加密一次,不加密时不能把秘密数据写进JWT,JWT不仅可以用于认证,也可以用于交换信息,有效使用可以降低服务器查询数据库的次数。JWT签发后到期之前会始终有效,所以应该把有效期设置的短一点,对于重要权限,使用时应该再次进行认证,为减少盗用,JWT应该使用HTTPS进行传输。
2.Websocket原理
1)即时通讯技术
Ajax短轮询,允许浏览器脚本JS发送http请求,无法满足即时通信。Comet,基于http长连接的服务器推技术,保持长连接,客户端需要的数据更新时,服务器主动将数据推送给客户端,实现方式是基于Ajax的长轮询(浏览器发出请求,服务器端接收到请求后阻塞请求直到有数据或者超时才返回,JS处理请求返回信息后再次请求重新建立连接,期间有新的数据到达就先保存,知道重新建立连接,浏览器将所有数据一次性取回)和基于Iframe及htmlfile的流(iframe是html标记,该标记的src属性会保持对指定服务器的长连接请求,服务器端可以不停的返回数据,这种方式使进度栏显示加载没完成,htmlfile用于解决IE中加载显示问题)。Websocket,SSE
2)Websocket特点
建立在TCP协议上,握手采用HTTP协议,可以发送文本或二进制数据,没有同源限制,默认端口也是80和443。
3)Websocket的优越性
HTTP只能由客户端发起通信,所以一般需要轮询,由于必须不停连接或保持连接始终打开,很浪费资源,比如带上多余header造成无用的数据传输。Websocket使双向发送或接受信息,浏览器和服务器握手之后就可以建立连接,本质上是通过http协议建立通道
4)原理
客户端发起http请求,3次握手后简历TCP连接,HTTP请求中多了Upgrade:websocket,connection:upgrade等字段,告诉服务器使用的是websoket协议,要求使用对应助理处理。浏览器会生成3个字段Sec-Websocket-key验证是否websocket护理,~Protocol区分同URL下不同服务需要的协议,Version告诉服务器协议版本。然后服务器收到握手请求后同样用HTTP协议,返回Accept,这个是加密后的key,~Protocol是最终使用的协议。客户端收到后,借助TCP进行全双工通信
5)应用场景
即时聊天,多玩家游戏,在线协同编辑,实时数据流拉去,游戏应用程序,实时地图位置。如果要获取旧数据或只想获取一次数据就是用HTTP。
6)断线
客户端定时给服务端发送消息(心跳包)证明在线。TCP本身有心跳包机制,但是检查不到一些物理原因的断线。一般在逻辑层发空的echo包,用一个定时器,一定时间间隔发一个空包过去就行,心跳包用于维持长连接,保活。客户端第一次发送请求到服务端会携带唯一标识,时间戳,服务端会去数据库/缓存查询请求的唯一标识,存在就取出时间戳并进行计算,判断是否大于指定时间,解决断线。
3.垃圾回收机制
JS有自动的垃圾回收机制,找出不再使用/引用的变量或属性(生命周期结束),按照固定时间间隔周期性的释放所占内存空间
1)引用计数算法
常常引起内存泄漏,主要通过记录引用类型值的引用次数,次数变为0就无法访问,加以释放,但是没办法解决循环引用,只能手动赋值为null
2)标记清除(最常见)
变量进入环境时,将内存中的变量标记为进入环境,去除环境中的变量以及被环境中的变量引用的变量的标记,还有标记的会被视为准备删除的变量,垃圾回收器销毁带标记的值。
3)减少垃圾回收
对象object优化,避免过多新建对象,应尽量实现对象重用,比如把对象的属性全部清理。数组array优化通过长度赋值为0实现重用,不要赋值[]。函数function优化:动态创建方法的地方就可能产生内存垃圾。
4.图片懒加载原理
将img的src设为同一张占位图片,给img设自定义属性data-src,真正的图片存在data-src,监听到进入可视窗口(scrollTop判断窗口顶部与文档顶部的距离,getBoundingClientRect判断是否在视口内),就把自定义属性的地址存到src,实现懒加载效果。也可以用Intersection Observer的api,if("Intersection Observer" in window),所有需要懒加载的图片注册observer。还可以用background-image控制是否加载,有lazy选择器的时候就: .img.lazy{background-image:none}
5.DNS解析+查询过程
输入url后,浏览器DNS缓存查询-》判断是否进行域名直接查询,有就连DNS服务器,没有就查操作系统给缓存-》查看本地hosts文件-》查找本地DNS的缓存服务器-》传信息给本地DNS递归服务器,根据内置的根域名服务器ip地址寻求根域名服务器帮助-》根域名收到解析请求,把顶级域名服务器ip地址返回给DNS-》本地dns获得返回的ip地址再去找对应的顶级域名服务器-》最后根据ip找到对应的权威服务器-》权威服务器把对应的主机ip返回给本地dns
DNS同时支持UDP+TCP
DNS查询时,如包含AXFR类型的特殊查询(用于DNS区域传输,在多个命名服务器之间快速迁移记录),由于查询返回的响应较大(UDP携带信息不超512字节,MTU有1500字节限制-》以太网的通常设置(数据链路层对数据帧长度的限制),不可接受区域传输功能),对数据的准确有需求,所以用TCP传输/其他可靠协议处理。UDP不能支持大量IPV6地址或DNS安全签名记录的传输,EDNS为DNS提供扩展,DNS通过UDP协议携带最多4096字节数据,但是MTU限制导致数据分片及丢失,导致这个特性不够可靠。客户端收到被截断的DNS响应,通过TC字段判断是否需要TCP重复发出DNS查询请求。DNS引入TLS提供隐私,但也会带来性能额外开销。后面也定义了通过HTTPS发送DNS查询和获取DNS响应的协议。
6.Session,cookie,token的理解
1)发展史
交互式web应用兴起,需要管理会话,由于HTTP请求无状态,用session id进行区分,但是太多session id都保存在服务器会对服务器造成巨大开销,而且限制了服务器扩展能力,比如多个机器组成的集群,session id被保存在其中一个机器中,下一次请求转发到别的机器时就没有session id,所以当时采用session sticky,让请求一直粘连在那个机器上。解决这个问题,可以不用服务器保存session id,所以给客户端发送token,并进行加密,下次访问时token通过httpheader来携带就行。cookie是浏览器能永久存储的数据,由服务器生成发给浏览器,浏览器保存,下次请求同一网站会把该cookie发给服务端。
2)Token优势
客户端存的token是无状态的,且能被扩展,基于这种无状态和不存Session信息,负载均衡器能把用户信息从一个服务传到其他服务器中,请求中发token不再发cookie能防止CSRF,cookie只是存储机制不用于认证。
7.浏览器渲染中css/js加载阻塞
为避免JS阻塞渲染,一般对JS执行async或defer加载或把JS文件放到HTML底部加载,浏览器会同步解析HTML,CSS,构建render树,进行计算和绘制,会在还没完全接收HTML文件时开始渲染,显示网页,执行HTML中,根据需要继续请求图片,CSS,JS文件
1)CSS阻塞
CSS和DOM解析同时进行,与script执行互斥,Webkit内核中进行script执行优化,只有在JS访问CSS时互斥。CSSOM树的构建通过阻塞JS代码而阻塞DOM的解析。如果JS访问了某个元素的样式,需要等待这个样式被下载完成再能继续执行。CSSOM树的构建会阻塞render树的构建
2)CSS阻塞解决
将style,link放到head里,提前加载好CSS资源。针对阻塞render树,对于一些首次渲染不用的CSS资源,通过媒体查询判断是否需要首次渲染:link里加一个media
3)JS阻塞
JS阻塞是不可预测的,因为可以随时修改DOM节点但乃至动态加载。内莱鸟JS会阻塞DOM解析和渲染,而且会一直阻塞,外联同步脚本阻塞DOM,但是因为无法确认脚本内容,所以会有先渲染一次已构建DOM,确保加载的脚本能获得最新的DOM。如果加载完要立刻执行用async,加载完不用立刻执行,再页面结构加载完(DOM解析完)再执行,用defer,使用这两个属性后,加载步阻塞渲染,运行仍会阻塞渲染。
4)JS阻塞解决
页面渲染内容为script请求内容,就放在head,页面渲染内容和script无关,script放在body标签的最后。需要js代码提前加载,即放在head里或DOM节点前,script标签添加defer或async属性
8.跨域
1)Jsonp
用JSONP跨域是跨域的服务端把客户端所需要的数据放进客户端本地的js方法里进行调用,客户端在本地js对返回数据进行处理,只支持get方法。本质是利用了不会被拦截的script,src,link标签,jsonp声明一个回调函数作为参数值传给跨域请求数据的服务器,函数形参是要获取的目标数据,创建一个script标签,把跨域的api数据接口地址赋值给script的src,还在地址中向服务器传递该函数名,服务端收到请求后把传递的函数名和需要给的数据拼接成字符串,通过http协议返回给客户端,客户端再调用之前声明的回调函数对返回的数据进行操作。
2)CORS
对于跨域资源请求,其实已经将请求发给服务端,浏览器也收到了响应,但是浏览器看到跨域就把消息拦截了,所以在服务器告诉浏览器这个数据是每个源都能获取的,在后端进行配置,让任何源都可以通过ajax发起请求获取提供的数据就行.浏览器之间发送CORS简单请求(同时满足请求方法是GET,HEAD,POST之一,通信不超过Accept,Accept-Language,Content-Language,Last-event-ID,Content-Type字段),具体是在头信息加一个origin字段,说明本次请求来自哪个源,如果服务器判断可以接受该源,就返回一个包含Access-Control-Allow-Origin字段表示接收该域名的请求。发送非简单请求,会在正式通信前进行预检请求(OPTIONS),询问服务器当前域名是否可以请求服务器以及使用HTTP动词和头信息,得到正确答复才会正式请求。后端的响应头需要设置Acess-Control-Allow-Origin,~Credentials设置为true,允许跨域带cookie,此时Origin不能为通配符*,~Headers设置跨域请求允许的请求头,~Methods设置允许的请求方式。
3)postMessage跨域
是h5引入的api,window对象的方法,是客户端之间直接的数据传递,可以同域/跨域,第一个参数是发送的消息,第二个参数是目标window对象打开页面的协议和域名。允许来自不同源的脚本采用异步方式进行有限的通信,可以解决多窗口通信,可以在目标窗口添加message的监听事件,给每个窗口进行推送。监听用onmessage,发送信息用postmessage。核心是用到iframe标签,让通信的A,B通过iframe标签构成父子集,再使用postMessage发送消息,message事件进行监听和数据接收。但是在事件回调方面,message事件中由于跨域限制,无法获取消息来源,就无法回送消息。
iframe标签
iframe是HTML内联框架元素,能把另一个HTML页面嵌入当前页面,每个嵌入的浏览上下文都有自己的绘画历史记录和DOM树,包含嵌入内容大哥浏览上下文称为父级浏览上下文。脚本访问框架内容必须遵守同源策略,跨域可以通过window.postMessage实现。尽量别用iframe,降低页面性能,会阻塞页面加载,window的onload需要所有iframen加载完毕才触发,除非用js动态设置其src.iframe加载资源用的同一个连接池,可能会把可用连接用光。可以用H5的Web组件,imports把其他html文档绑定到其他html文档中
9.webpack基本原理
1)构建阶段
数据流从module->Ast->dependences->module,先转AST再从AST找以来,loaders处理完的结果必须是能被acorn处理的标准JS语法 构建module子类-》转译module成JS-》用acron把JS解析成AST集合(抽象语法树)-》webpack遍历AST触发各种钩子,babel对源码做等价转换,解读JS文本对应的资源依赖,把依赖对象加入module依赖列表-》处理模块依赖,控制流回到第一步-》解析完所有依赖,构建阶段结束
2)生成阶段
主要是把module按规则组织成chunks,entry及entry触达的模块组合成一个chunk,动态引入语句引入的模块各自组成一个chunk 构建编译的ChunkGraph对象-》遍历compiilation.modules集合,将module按entry/动态引入的规则分配给不同Chunk对象-》遍历完毕后得到完整的chunks集合对象-》调用方法遍历module/chunk,把信息记录到compilation.assets对象-》触发seal回调-》assets写入文件系统
3)工作原理
分析项目结构,找到JS模块和浏览器不能直接运行的拓展语言sass等,转换和打包成合适的格式供浏览器使用。
4)打包原理
一切视为模块,定义entry.js,对所有依赖的文件进行跟踪,各个模块通过loader,plugins处理,打包在一起
5)核心概念
entry:入口,chunk:代码块,loader:模块转换器,plugins:扩展插件,bundle:webpack打包出的文件
6)webpack的基本功能
代码转换(ts->js,scss->css),文件优化(压缩js,css,html,压缩合并图片),代码分割(提取公共代码,提取首屏不需要的部分异步加载),模块合并,自动刷新(监听代码,自动构建刷新浏览器),代码校验,自动发布(自动构建出线上发布代码并传给发布系统)
7)构建流程
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
8)Loader
告诉webpack遇到那些文件用那些loader加载和转换打包成js。css-loader:合并css,style-loader:把css内容注入js,sass-loader:解析sass,url-loader:文件转成base64 URI,vue-loader:处理vue文件
9)Plugin
用来扩展webpack功能,通过在构建流程注入钩子实现,给webpack带来很大的灵活性。