left-pad实现
https://www.cnblogs.com/shengxinjing/p/5315549.html
从输入 URL 到页面加载完成的过程中都发生了什么事情
http://fex.baidu.com/blog/2014/05/what-happen/
重绘(redraw)和重排(reflow)
https://zhuanlan.zhihu.com/p/24599399
vue面试题
https://www.cnblogs.com/sichaoyun/p/8406194.html
https://segmentfault.com/a/1190000008102870
https://yuque.antfin-inc.com/junchen.yjc/quesub/avf828
number对象
https://blog.csdn.net/qq20004604/article/details/70882202#t14
react面试题
https://segmentfault.com/a/1190000016761186
https://zhuanlan.zhihu.com/p/47760913
croppejs
https://github.com/fengyuanchen/cropperjs/tree/v2
https://github.com/jhscpang/books
css渲染
https://yanhaijing.com/css/2014/04/04/10-influence-css-rendering-speed-method-and-suggestions/
inline-block
https://blog.csdn.net/wmaoshu/article/details/52964251
sort
http://louiszhai.github.io/2016/12/23/sort/
mobx
https://www.cnblogs.com/rubylouvre/p/6058045.html
webpack原理
https://juejin.cn/post/6844903971325214734
jsBridge
https://segmentfault.com/a/1190000010356403
前端面试题
https://github.com/huruji/FE-Interview
https://github.com/qiu-deqing/FE-interview
https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers
https://github.com/paddingme/Front-end-Web-Development-Interview-Question
https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Question
-
错误类型
-
垃圾回收机制,内存溢出泄露排查
-
XSS SOL注入 网络劫持 控制台注入 钓鱼
-
SSR
-
原型,原型链
-
vue利用ES5的什么特性实现双向数据绑定
defineProperty -
图谱去抖懒加载
intersectionOvserver
不能一道视口就加载,而是停留多少秒后加载 -
lacalStorage,sessionStrorage
-
内核
-
iframe缺点
-
垂直居中
-
clearfix
-
文字相关css可继承
-
js延迟加载
-
异步加载
-
浏览器缓存
-
性能优化
减少http请求
repaint reflow设置style回导致reflow ,通过设置calss
减少DOM操作
使用json进行数据交换
SEO
-
WeakMap,WeakSet
-
对象深拷贝
-
二维数组平铺
-
jsonp
-
promise
-
bind
-
二叉树
堆排序 -
盒模型
-
box-sizing
transition
translate -
call,apply,bind
-
闭包
内嵌函数 -
设计模式
-
数组方法
-
字符串方法
-
箭头函数
this
不能用作构造函数 -
事件循环
-
状态码
-
长连接
-
cookies,sessionStorage,loacalStorage
-
null undefined 指针,对象
-
浏览器缓存
-
display:none 回流
visible:hidden 重绘 -
setState执行机制
-
jsbridge
-
登录注册鉴权
-
前端路由
-
postMessage
hash
webScorket -
service worker
web worker -
css变量
-
viewport 1px
-
二维数组扁平化
-
金额千分位分割符
-
http方法
-
清除浮动
-
restful
-
PWA
-
riddle
1.XSS跨站脚本攻击(Cross Site Scripting)
1)通过
这样我们就可以获取别人的cookie信息了
2) 能过append
3.网络劫持攻击
很多的时候,我们的网站不是直接就访问到我们的服务器上的,中间会经过很多层代理,如果在某一个环节,数据被中间代理层的劫持者所截获,他们就能获取到使用你网站的用户的密码等保密数据。比如,我们的用户经常会在各种饭馆里面,连一些奇奇怪怪的wifi,如果这个wifi是黑客所建立的热点wifi,那么黑客就可以结果该用户收发的所有数据。这里,建议站长们网站都使用https进行加密。这样,就算网站的数据能被拿到,黑客也无法解开。
如果你的网站还没有进行https加密的化,则在表单提交部分,最好进行非对称加密–即客户端加密,只有服务端能解开。这样中间的劫持者便无法获取加密内容的真实信息了。
4.控制台注入代码
不知道各位看官有没有注意到天猫官网控制台的警告信息,如图4.1所示,这是为什么呢?因为有的黑客会诱骗用户去往控制台里面粘贴东西(欺负小白用户不懂代码),比如可以在朋友圈贴个什么文章,说:"只要访问天猫,按下F12并且粘贴以下内容,则可以获得xx元礼品"之类的,那么有的用户真的会去操作,并且自己隐私被暴露了也不知道。
5钓鱼
钓鱼也是一种非常古老的攻击方式了,其实并不太算前端攻击。可毕竟是页面级别的攻击,我们也来一起聊一聊。我相信很多人会有这样的经历,QQ群里面有人发什么兼职啦、什么自己要去国外了房子车子甩卖了,详情在我QQ空间里啦,之类的连接。打开之后发现一个QQ登录框,其实一看域名就知道不是QQ,不过做得非常像QQ登录,不明就里的用户们,就真的把用户名和密码输入了进去,结果没登录到QQ,用户名和密码却给人发过去了。
其实这种方式,在前端也有利用。下面,我们就来试试如果利用前端进行一次逼真的钓鱼。
1 首先,我们在xx空间里分享一篇文章,然后吸引别人去点击。
<head>
<meta charset="utf-8" />
</head>
当前你在xx空间
侯博士的分享
咱们班当年班花,现在长这样:
< a href=" " target="_blank">点我查看</ a>
2 接着,我们在cheat.php这个网站上面,将跳转过来的源网页地址悄悄的进行修改 <!DOCYTPE HTML> 于是,在用户访问了我们的欺骗网站后,之前的tab已经悄然发生了变化,我们将其悄悄的替换为了钓鱼的网站,欺骗用户输入用户名、密码等。
Debounce 和 Throttle 的原理及实现
当我们在处理诸如 resize 、 scroll 、 mousemove 和 keydown/keyup/keypress 等事件的时候,通常我们不希望这些事件太过频繁地触发,尤其是监听程序中涉及到大量的计算或者有非常耗费资源的操作。如果鼠标连续移动,那么浏览器就应该触发多个连续的 mousemove 事件,这意味着浏览器会在其内部计时器允许的情况下,根据用户移动鼠标的速度来触发 mousemove 事件。
Debounce
DOM 事件里的 debounce 概念其实是从机械开关和继电器的“去弹跳”(debounce) 衍生出来的,基本思路就是把多个信号合并为一个信号。
在 JavaScript 中, debounce 函数所做的事情就是, 强制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次 。我们希望在用户停止某个操作一段时间之后才执行相应的监听函数,而不是在用户操作的过程当中,浏览器触发多少次事件,就执行多少次监听函数。比如,在某个 3s 的时间段内连续地移动了鼠标,浏览器可能会触发几十(甚至几百)个 mousemove 事件,不使用 debounce 的话,监听函数就要执行这么多次;如果对监听函数使用 100ms 的“去弹跳”,那么浏览器只会执行一次这个监听函数,而且是在第 3.1s 的时候执行的。
现在,我们就来实现一个 debounce 函数。
/**
- @param fn {Function} 实际要执行的函数
- @param delay {Number} 延迟时间,也就是阈值,单位是毫秒(ms)
- @return {Function} 返回一个“去弹跳”了的函数
*/
function debounce(fn, delay) {
var timer
// 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
return function () {
// 保存函数调用时的上下文和参数,传递给 fn
var context = this
var args = arguments
// 每次这个返回的函数被调用,就清除定时器,以保证不执行 fn
clearTimeout(timer)
// 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
// 再过 delay 毫秒就执行 fn
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
其实思路很简单, debounce 返回了一个闭包,这个闭包依然会被连续频繁地调用,但是在闭包内部,却限制了原始函数 fn 的执行,强制 fn 只在连续操作停止后只执行一次。
debounce 的使用方式如下:
$(document).on(‘mouvemove’, debounce(function(e) {
}, 200))
我们在处理防止用户连续快速点击按钮时就可以采用这种方法解决。
Throttle
throttle 的概念理解起来更容易,就是 固定函数执行的速率 ,即所谓的“节流”。正常情况下, mousemove 的监听函数可能会每 20ms(假设)执行一次,如果设置 200ms 的“节流”,那么它就会 每 200ms 执行一次。比如在 1s 的时间段内,正常的监听函数可能会执行 50(1000/20) 次,“节流” 200ms 后则会执行 5(1000/200) 次。
/**
*
- @param fn {Function} 实际要执行的函数
- @param delay {Number} 执行间隔,单位是毫秒(ms)
- @return {Function} 返回一个“节流”函数
*/
function throttle(fn, threshhold) {
// 记录上次执行的时间
var last
var timer
// 默认间隔为 250ms
threshhold || (threshhold = 250)
// 返回的函数,每过 threshhold 毫秒就执行一次 fn 函数
return function () {
// 保存函数调用时的上下文和参数,传递给 fn
var context = this
var args = arguments
var now = +new Date()
// 如果距离上次执行 fn 函数的时间小于 threshhold,那么就放弃
// 执行 fn,并重新计时
if (last && now < last + threshhold) {
clearTimeout(timer)
// 保证在当前时间区间结束后,再执行一次 fn
timer = setTimeout(function () {
last = now
fn.apply(context, args)
}, threshhold)
// 在时间区间的最开始和到达指定间隔的时候执行一次 fn
} else {
last = now
fn.apply(context, args)
}
}
}
原理也不复杂,相比 debounce ,无非是多了一个时间间隔的判断,其他的逻辑基本一致。 throttle 的使用方式和debounce 一样。
总结
debounce 强制函数在某段时间内只执行一次, throttle 强制函数以固定的速率执行。在处理一些高频率触发的 DOM 事件的时候,它们都能极大提高用户体验。
网络
• OSI网络七层模型介绍?每层模型涉及的协议和理解
• TCP和udp的区别?
TCP面向连接,UDP面向无链接
TCP面向报文,UDP面向字节流
TCP提供可靠传输服务(数据顺序、正确性),UDP传输不可靠
TCP协议传输速度慢,UDP协议传输速度快
TCP协议对系统资源要求多(头部开销大),UDP协议要求少
• TCP建链过程和断开过程?为什么要三次握手,两次行不行?链接建立后传输过程中客户端异常的处理情况?
• 二三层网络转换原理
• http协议的了解
简单快速,无状态,灵活允许任意传输对象
请求行,请求头,请求体
响应状态行,消息报头,响应正文
请求方法:GET、POST的区别
• cookie和session的区别
• 介绍下浏览器输入淘宝网址到浏览器展示网页的过程
数据结构&算法
• 数组和链表的区别?应用场景
• 链表的插入、删除、反转链表
• 不用额外空间判断单链表是否循环链表
1.将所有遍历过的节点用某个结构存储起来,然后遍历每个节点,都在这个结构中查找是否遍历过。如果有遍历过,那就证明存在环。
这个结构,可以是hash,hash中的值存储的是节点的内存地址,
因此查找的操作所需时间O(1)
遍历的操作所需时间O(n)
hash存储空间所需额外的O(n)
【时间复杂度为O(n),空间复杂度为O(n)】
但是:因为使用了某结构存储,所以,违反了题目要求,要叉掉
2.最佳方案
【快慢指针】:需要两个指针,一个快指针:每次走两步,一个慢指针:每次走一步。
如果快慢指针能够相遇(如果快指针能够追上慢指针),就证明有环。
但是!!!这个方法存在问题。如果链表够长,而环又足够小,那么快指针将永远不会追上慢指针
所以,快慢指针只适合用于环出现在链表尾部的情况,也就是单链表环的问题,而无法解决链表存在循环的问题。
• 二叉树?二叉树的遍历?什么是平衡二叉树?二叉树深度?
• 如何判断是否二叉搜索树?
• 介绍一下散列表
数据库
• 乐观锁和悲观锁?使用场景?
• 如何查看SQL性能?
• 数据库索引?
• 数据库事务特性?事务的隔离级别?不同隔离级别对应的问题?
• 有以下表,查询平均成绩>60分的学生学号和平均成绩
student(sno,sname,sage,ssex)学生表
course(cno,cname,tno) 课程表
sc(sno,cno,score) 成绩表
teacher(tno,tname) 教师表
select a.sno as “学号”, avg(a.score) as “平均成绩”
from
(select sno,score from sc) a
group by sno having avg(a.score)>60
C语言
• 能不能在整型中存放32768这个数?
整数类型可以存放从-32768~32767之间的任何数值。但是32768不在这个范围之内。
这个时候modifier是我们需要的东西。Long Int数据类型就可以存放这个数。
• “引用”与指针的区别是什么?
- 引用必须被初始化,指针不必
- 引用初始化以后不能被改变,指针可以改变所指的对象
- 不存在指向空值的引用,但是存在指向空值的指针
• 程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区—存放函数体的二进制代码
• 进程和线程的区别及联系
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程:是进程的一个实体,是cpu调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它可以和同属于一个进程的其他线程共享进程所拥有的的全部资源。
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
相对于进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他进程共享数据,但是拥有自己的栈空间,拥有独立的运行序列。
• 用C编写一个死循环程序
• 什么是面向对象(OOP)
答:面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。
• 什么是多态?
答:多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。
不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。
• 设计模式简单举例
Linux
• 查看/var/log目录下的文件数
• 查看当前目录下所有非.txt后缀文件的最后10行
• 终止tomcat进程
• 建立软链接(快捷方式),以及硬链接的命令
• 文件权限修改
• 网络诊断相关命令
• 文件合并
• 文件最小化/文件压缩
• 使用CDN托管
• 缓存的使用
• 性能优化?
hybrid的原理
Click点击延迟的解决方案
浮点数计算精度丢失问题
javascript的eventloop
全局对象\闭包循环引用导致的内存泄漏\DOM节点子节点释放\timer计时器没有clear等)
前后端分离开发时你们项目怎么发布的
1.手动build后上传资源服务器(如果回答这个 就再问如何保证本地的包是安全的)
2.使用ci工具
常用的有以下几种:
Jenkins :借助 Jenkins 配合 gitlab 的 webhook 来做CI/CD
Circle CI : 强大,对 Github 友好
Travis CI:强大,对 Github 友好
Gitlab CI:Gitlab 自带 CI 环境
你们的项目内用了哪些代码规范工具 配置过哪些rules
1.eslint (例如基于 google ar)
2.prettier
3.stylelint
4.husky(无符合规范就不允许commit)
17.如何进行错误监控
前端错误的分类
• 即时运行错误(代码错误)
• 资源加载错误
错误的捕获方式
即时运行错误的捕获方式:
• try…catch
• window.onerror
资源加载错误:
• object.onerror(如img,script)
• performance.getEntries()
• Error事件捕获
延伸:跨域的js运行错误可以捕获吗,错误提示什么,应该怎么处理?
可以。
Script error
1.在script标签增加crossorigin属性
2.设置js资源响应头Access-Control-Allow-Orgin:*
上报错误的基本原理
采用Ajax通信方式上报
利用Image对象上报
浏览器有哪些缓存机制
1.缓存位置
Service Worker
Memory Cache(内存中的缓存)
Disk Cache(硬盘中的缓存)
2.缓存策略
强缓存(通过设置两种 HTTP Header 实现:Expires 和 Cache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200)
协商缓存(缓存过期了,就需要发起请求验证资源是否有更新。协商缓存可以通过设置两种 HTTP Header 实现:Last-Modified 和 ETag
防抖的原理为触发事件的 n 时间后才执行,如果 n 时间内事件再次被触发,则以新触发的时间为标准,然后 n 事件后再执行
节流的原理为在持续触发事件时候,每隔一段时间执行一次。我们可以使用 time line 或者定时器来实现,或者二者结合实现。
: 如何解决移动端页面300ms延迟问题?
A: FastClick的解决方案,利用touchend时dispatch一个自定义的click事件,并且把浏览器行为的click事件通过event.preventDefault()的方法给阻止掉。
Q: 什么是点击穿透,以及如何解决点击穿透?
A: 假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。 这是因为在移动端浏览器,事件执行的顺序是touchstart > touchend > click。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。解决方案之一就是上面提到的FastClick。
Bug 复现
在一排序 UI 控件中,我们使用了 HTML 5 的 draggable 特性来处理拖拽内部元素使之进行排序的功能。之前都好好的,突然有用户反馈说 drag 的元素在 drop 的时候无论如何都会弹跳到列表的第一号位去。like
收到 Bug 反馈之后,立刻使用各种姿势重现,各种拖拽姿势始终重现不了。后续通过近距离「观察」用户是如何操作的,发现,他用的是「鼠标 🖱」拖拽,我用的是 Tarckpad 拖拽,鼠标拖拽就能够必现了。
硬件设备还会影响拖拽的操作么!?遂为 Mac 接一个鼠标,开始调试 …
Bug 循因
找到 UI 控件代码,在使用 HTML5 draggable 特性的时候,有两个重要的监听事件,一个是 dragover 以及 dragend 。写了大致如下的代码:
// throttle 为 lodash 的 throttle 函数
const onDrag = throttle((e) => {
e.preventDefault();
e.dataTransfer.effectAllowed = ‘move’;
ref = locateNode(e);
placeNodeAt(ref);
}, 200);
const onDragEnd = (e) => {
e.preventDefault();
placeNodeAt(origRef);
sortNodeList(ref ? ref.dataset.id : null);
// …
};
document.addEventListener(‘dragover’, onDrag);
/* events fired on the draggable target /
document.addEventListener(‘drag’, onDrag);
/ events fired on the drop targets /
document.addEventListener(‘dragend’, onDragEnd);
onDrag 是在每次拖拽的时候以及拖拽到可以 drop 的对象上的时候触发的事件回调。由于这个回调的的触发频率是每几百毫秒一次,所以出于性能考虑,做了 200ms 的节流处理。
查出之所以会 locate 并插入到第一个节点的原因,是因为 throttle 回调函数中的 event 参数,它的 clientX 与 clientY 在最后一次始终为 0。而 locateNode 在计算相关排序位置的时候需要依赖这两个参数。
索性 Google 了一下,在 StackOverflow 以及 MDN 上有类似的描述:
/ events fired on the drop targets */
document.addEventListener(“dragover”, function (event) {
// prevent default to allow drop
event.preventDefault();
}, false);
event.preventDefault();
没错,就是 这个行为,如果不调用的话,就会出现最后一次 event 对象上的 clienX 以及 clientY 将为 0。那么为什么呢?查看 HTML Standard 相关文档发现(摘录了部分关键的信息,感兴趣的看这里 全文 ):
The dragover event handler specifies what feedback will be shown to the user, by setting the dropEffect attribute of the DataTransfer associated with the event. This event also needs to be canceled.
During the drag operation, the element directly indicated by the user as the drop target is called the immediate user selection. (Only elements can be selected by the user; other nodes must not be made available as drop targets.) However, the immediate user selection is not necessarily the current target element, which is the element currently selected for the drop part of the drag-and-drop operation.
The immediate user selection changes as the user selects different elements (either by pointing at them with a pointing device, or by selecting them in some other way). The current target element changes when the immediate user selection changes, based on the results of event listeners in the document, as described below.
Initialize event’s mouse and key attributes initialized according to the state of the input devices as they would be for user interaction events. If there is no relevant pointing device, then initialize event’s screenX, screenY, clientX, clientY, and button attributes to 0.
简而言之就是,如果不 preventDefault,最后一次即释放前 event 对象的 traget 对象所关联的 immediate user selection 将会按照默认行为去关联,默认关联的 drop effect 在我没有配置的情况下不存在,这就导致了抛出的事件的 clientX 与 clientY 为 0。而正是 throttle 的时延导致了部分事件 preventDefault() 没有被调用,而出现问题。
那么为什么在 Trackpad 的情况下又是 OK 的呢?怀着侥幸的心理,我缩小了 throttle 函数的第二个参数值,即 throttle 的间隔时间为 10ms。惊奇间发现,Trackpad 同样也出现该问题了。
这样一来,就可以猜测是因为 Trackpad 在触发 dragEnd 的事件时延是大于 200ms 的,即 dragOver 执行时 dragEnd 还未结束,因此 immediate user selection 对应的 target 还存在,clientX / clientY 返回自然就不是 0 了。而之前用鼠标的时候,dragEnd 先执行,dragOver 后执行,dragOver 后执行的时候 target 已经不存在了。
解决方案 & 启迪
• dragOver 的时候每次都调用 preventDefault ,而让 throttle 函数包裹在定位和节点变换的函数之上。
• 至于 trackPad 的对 dragEnd 时延问题,只需要保证 dragEnd 执行之后,dragOver 的回调 cancel 掉即可。
未来在处理这类事件的时候确实得考虑到「设备输入」导致的时延不尽相同的问题,异步事件处理的时候的确需要多加小心,不能够假定先后顺序就一定是如此。
更多详情看这里
12.前端路由的原理
什么是路由?简单的说,路由是根据不同的 url 地址展示不同的内容或页面
使用场景?前端路由更多用在单页应用上, 也就是SPA, 因为单页应用, 基本上都是前后端分离的, 后端自然也就不会给前端提供路由。
前端的路由和后端的路由在实现技术上不一样,但是原理都是一样的。在 HTML5 的 history API 出现之前,前端的路由都是通过 hash 来实现的,hash 能兼容低版本的浏览器。
两种实现前端路由的方式
HTML5 History两个新增的API:history.pushState 和 history.replaceState,两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。
Hash就是url 中看到 # ,我们需要一个根据监听哈希变化触发的事件( hashchange) 事件。我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。
优点
从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。
更多内容请看这里
缺点
使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。
13.Restful API是什么
-
Restful的意思就是表现层状态转化。
- “表现层"其实指的是"资源”(Resources)的"表现层",把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
- 所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在,每一个URI代表一种资源。
- 如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
- Restful就是客户端和服务器之间,传递这种资源的某种表现层
- 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"
Restful API就是符合Restful架构的API设计。
Restful API一些具体实践: -
应该尽量将API部署在专用域名之下。如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
- 应该将API的版本号放入URL。
- 对于资源的具体操作类型,由HTTP动词表示
- 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果
- 如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名
…
浏览器缓存机制剖析
缓存一直是前端优化的主战场, 利用好缓存就成功了一半. 本篇从http请求和响应的头域入手, 让你对浏览器缓存有个整体的概念. 最终你会发现强缓存, 协商缓存 和 启发式缓存是如此的简单 .
浏览器对于请求资源, 拥有一系列成熟的缓存策略. 按照发生的时间顺序分别为存储策略, 过期策略, 协商策略, 其中存储策略在收到响应后应用, 过期策略, 协商策略在发送请求前应用. 流程图如下所示
废话不多说, 我们先来看两张表格.
1.http header中与缓存有关的 key.
2.缓存协商策略用于重新验证缓存资源是否有效, 有关的key 如下 .
下面我们来看下各个头域(key)的作用.
Cache-Control
浏览器缓存里, Cache-Control是金字塔顶尖的规则, 它藐视一切其他设置, 只要其他设置与其抵触, 一律覆盖之.
不仅如此, 它还是一个复合规则, 包含多种值, 横跨 存储策略, 过期策略 两种, 同时在请求头和响应头都可设置.
语法为: “Cache-Control : cache-directive”.
Cache-directive共有如下12种(其中请求中指令7种, 响应中指令 9 种):
假设所请求资源于4月5日缓存, 且在4月12日过期.
当max-age 与 max-stale 和 min-fresh 同时使用时, 它们的设置相互之间独立生效, 最为保守的缓存策略总是有效. 这意味着, 如果max-age=10 days, max-stale=2 days, min-fresh=3 days, 那么:
根据max-age的设置, 覆盖原缓存周期, 缓存资源将在4月15日失效(5+10=15);
根据max-stale的设置, 缓存过期后两天依然有效, 此时响应将返回110(Response is stale)状态码, 缓存资源将在4月14日失效(12+2=14);
根据min-fresh的设置, 至少要留有3天的新鲜期, 缓存资源将在4月9日失效(12-3=9);
由于客户端总是采用最保守的缓存策略, 因此, 4月9日后, 对于该资源的请求将重新向服务器发起验证.
Pragma
http1.0字段, 通常设置为Pragma:no-cache, 作用同Cache-Control:no-cache. 当一个no-cache请求发送给一个不遵循HTTP/1.1的服务器时, 客户端应该包含pragma指令. 为此, 勾选☑️ 上disable cache时, 浏览器自动带上了pragma字段. 如下:
Expires
Expires:Wed, 05 Apr 2017 00:55:35 GMT
即到期时间, 以服务器时间为参考系, 其优先级比 Cache-Control:max-age 低, 两者同时出现在响应头时, Expires将被后者覆盖. 如果Expires, Cache-Control: max-age, 或 Cache-Control:s-maxage 都没有在响应头中出现, 并且也没有其它缓存的设置, 那么浏览器默认会采用一个启发式的算法, 通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间.
如下资源便采取了启发式缓存算法 .
其缓存时间为 (Date_value - Last-Modified_value) * 10%, 计算如下:
const Date_value = new Date(‘Thu, 06 Apr 2017 01:30:56 GMT’).getTime();
const LastModified_value = new Date(‘Thu, 01 Dec 2016 06:23:23 GMT’).getTime();
const cacheTime = (Date_value - LastModified_value) / 10;
const Expires_timestamp = Date_value + cacheTime;
const Expires_value = new Date(Expires_timestamp);
console.log(‘Expires:’, Expires_value); // Expires: Tue Apr 18 2017 23:25:41 GMT+0800 (CST)
可见该资源将于2017年4月18日23点25分41秒过期, 尝试以下两步进行验证:
- 试着把本地时间修改为2017年4月18日23点25分40秒, 迅速刷新页面, 发现强缓存依然有效(依旧是200 OK (from disk cache)).
- 然后又修改本地时间为2017年4月18日23点26分40秒(即往后拨1分钟), 刷新页面, 发现缓存已过期, 此时浏览器重新向服务器发起了验证, 且命中了304协商缓存, 如下所示 .
- 将本地时间恢复正常(即 2017-04-06 09:54:19). 刷新页面, 发现Date依然是4月18日, 如下所示 .
从⚠️ Provisional headers are shown 和Date字段可以看出来, 浏览器并未发出请求, 缓存依然有效, 只不过此时Status Code显示为200 OK. (甚至我还专门打开了charles, 也没有发现该资源的任何请求, 可见这个200 OK多少有些误导人的意味)
可见, 启发式缓存算法采用的缓存时间可长可短, 因此对于常规资源, 建议明确设置缓存时间(如指定max-age 或 expires).
ETag
ETag:“fcb82312d92970bdf0d18a4eca08ebc7efede4fe”
实体标签, 服务器资源的唯一标识符, 浏览器可以根据ETag值缓存数据, 节省带宽. 如果资源已经改变, etag可以帮助防止同步更新资源的相互覆盖. ETag 优先级比 Last-Modified 高.
If-Match
语法: If-Match: ETag_value 或者 If-Match: ETag_value, ETag_value, …
缓存校验字段, 其值为上次收到的一个或多个etag 值. 常用于判断条件是否满足, 如下两种场景:
• 对于 GET 或 HEAD 请求, 结合 Range 头字段, 它可以保证新范围的请求和前一个来自相同的源, 如果不匹配, 服务器将返回一个416(Range Not Satisfiable)状态码的响应.
• 对于 PUT 或者其他不安全的请求, If-Match 可用于阻止错误的更新操作, 如果不匹配, 服务器将返回一个412(Precondition Failed)状态码的响应.
If-None-Match
语法: If-None-Match: ETag_value 或者 If-None-Match: ETag_value, ETag_value, …
缓存校验字段, 结合ETag字段, 常用于判断缓存资源是否有效, 优先级比If-Modified-Since高.
对于 GET 或 HEAD 请求, 如果其etags列表均不匹配, 服务器将返回200状态码的响应, 反之, 将返回304(Not Modified)状态码的响应. 无论是200还是304响应, 都至少返回 Cache-Control, Content-Location, Date, ETag, Expires, and Vary 中之一的字段.
对于其他更新服务器资源的请求, 如果其etags列表匹配, 服务器将执行更新, 反之, 将返回412(Precondition Failed)状态码的响应.
Last-Modified
语法: Last-Modified: 星期,日期 月份 年份 时:分:秒 GMT
Last-Modified: Tue, 04 Apr 2017 10:01:15 GMT
用于标记请求资源的最后一次修改时间, 格式为GMT(格林尼治标准时间). 如可用 new Date().toGMTString()获取当前GMT时间. Last-Modified 是 ETag 的fallback机制, 优先级比 ETag 低, 且只能精确到秒, 因此不太适合短时间内频繁改动的资源. 不仅如此, 服务器端的静态资源, 通常需要编译打包, 可能出现资源内容没有改变, 而Last-Modified却改变的情况.
If-Modified-Since
语法同上 , 如:
If-Modified-Since: Tue, 04 Apr 2017 10:12:27 GMT
缓存校验字段, 其值为上次响应头的Last-Modified值, 若与请求资源当前的Last-Modified值相同, 那么将返回304状态码的响应, 反之, 将返回200状态码响应.
If-Unmodified-Since
缓存校验字段, 语法同上. 表示资源未修改则正常执行更新, 否则返回412(Precondition Failed)状态码的响应. 常用于如下两种场景:
• 不安全的请求, 比如说使用post请求更新wiki文档, 文档未修改时才执行更新.
• 与 If-Range 字段同时使用时, 可以用来保证新的片段请求来自一个未修改的文档.
强缓存
一旦资源命中强缓存, 浏览器便不会向服务器发送请求, 而是直接读取缓存. Chrome下的现象是 200 OK (from disk cache) 或者 200 OK (from memory cache). 如下:
对于常规请求, 只要存在该资源的缓存, 且Cache-Control:max-age 或者expires没有过期, 那么就能命中强缓存.
协商缓存
缓存过期后, 继续请求该资源, 对于现代浏览器, 拥有如下两种做法:
• 根据上次响应中的ETag_value, 自动往request header中添加If-None-Match字段. 服务器收到请求后, 拿If-None-Match字段的值与资源的ETag值进行比较, 若相同, 则命中协商缓存, 返回304响应.
• 根据上次响应中的Last-Modified_value, 自动往request header中添加If-Modified-Since字段. 服务器收到请求后, 拿If-Modified-Since字段的值与资源的Last-Modified值进行比较, 若相同, 则命中协商缓存, 返回304响应.
以上, ETag优先级比Last-Modified高, 同时存在时, 前者覆盖后者. 下面通过实例来理解下强缓存和协商缓存.
如下忽略首次访问, 第二次通过 If-Modified-Since 命中了304协商缓存 .
协商缓存的响应结果, 不仅验证了资源的有效性, 同时还更新了浏览器缓存. 主要更新内容如下:
Age:0
Cache-Control:max-age=600
Date: Wed, 05 Apr 2017 13:09:36 GMT
Expires:Wed, 05 Apr 2017 00:55:35 GMT
Age:0 表示命中了代理服务器的缓存, age值为0表示代理服务器刚刚刷新了一次缓存.
Cache-Control:max-age=600 覆盖 Expires 字段, 表示从Date_value, 即 Wed, 05 Apr 2017 13:09:36 GMT 起, 10分钟之后缓存过期. 因此10分钟之内访问, 将会命中强缓存, 如下所示:
当然, 除了上述与缓存直接相关的字段外, http header中还包括如下间接相关的字段.
Age
出现此字段, 表示命中代理服务器的缓存. 它指的是代理服务器对于请求资源的已缓存时间, 单位为秒. 如下:
Age:2383321
Date:Wed, 08 Mar 2017 16:12:42 GMT
以上指的是, 代理服务器在2017年3月8日16:12:42时向源服务器发起了对该资源的请求, 目前已缓存了该资源2383321秒.
Date
指的是响应生成的时间. 请求经过代理服务器时, 返回的Date未必是最新的, 通常这个时候, 代理服务器将增加一个Age字段告知该资源已缓存了多久.
Vary
对于服务器而言, 资源文件可能不止一个版本, 比如说压缩和未压缩, 针对不同的客户端, 通常需要返回不同的资源版本. 比如说老式的浏览器可能不支持解压缩, 这个时候, 就需要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提升体验. 那么怎么区分这个版本呢, 这个时候就需要Vary了.
服务器通过指定Vary: Accept-Encoding, 告知代理服务器, 对于这个资源, 需要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 通过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬 .
Vary:Accept-Encoding,User-Agent
如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对PC和Mobile返回不同的缓存内容.
怎么让浏览器不缓存静态资源
实际上, 工作中很多场景都需要避免浏览器缓存, 除了浏览器隐私模式, 请求时想要禁用缓存, 还可以设置请求头: Cache-Control: no-cache, no-store, must-revalidate .
当然, 还有一种常用做法: 即给请求的资源增加一个版本号, 如下:
继承 原型
es6
promise实现 手写
https://juejin.cn/post/6844904054796075022
路由实现
深复制
复制函数
函数构造器
svg,iconfont如何实现
http1.0和2.0的区别
http和https的区别 加了一层ssl协议 主要是怎么加密解密的吗
http请求方式 区别
性能优化 :下拉加载, 分页加载 首屏优化 loadign 还有利用缓存啊 尽可能的减少http请求
key值的作用 react 的fiber就是用来对比
箭头函数和普通函数的区别