前端进阶知识
浏览器
cookie sessionStorage localStorage 区别
共同点:都是保存在浏览器端、且同源的
区别:
cookie
数据始终在同源的http
请求中携带(即使不需要),即cookie在
浏览器和服务器间来回传递,而sessionStorage
和localStorage
不会自动把数据发送给服务器,仅在本地保存。cookie
数据还有路径 (path
)的概念,可以限制cookie
只属于某个路径下- 存储大小限制也不同
cookie
数据不能超过4K
,同时因为每次http
请求都会携带cookie
、所以cookie
只适合保存很小的数据,如会话标识。sessionStorage
和localStorage
虽然也有存储大小的限制,但比cookie
大得多,可以达到5M
或更大
- 数据有效期不同
sessionStorage
:仅在当前浏览器窗口关闭之前有效localStorage
:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据cookie
:只在设置的cookie
过期时间之前有效,即使窗口关闭或浏览器关闭
- 作用域不同
sessionStorage
不在不同的浏览器窗口中共享,即使是同一个页面localstorage
在所有同源窗口中都是共享的cookie
也是在所有同源窗口中都是共享的
web Storage
支持事件通知机制,可以将数据更新的通知发送给监听者web Storage
的api
接口使用更方便
写一个会过期的 localStorage
惰性删除
惰性删除是指,某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。我们先来简单实现一下:
var lsc = (function (self) {
var prefix = 'one_more_lsc_'
/**
* 增加一个键值对数据
* @param key 键
* @param val 值
* @param expires 过期时间,单位为秒
*/
self.set = function (key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
};
/**
* 读取对应键的值数据
* @param key 键
* @returns {null|*} 对应键的值
*/
self.get = function (key){
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
}
val = JSON.parse(val);
if (val.expires < new Date().getTime()){
localStorage.removeItem(key);
return null;
}
return val.val;
};
return self;
}(lsc || {}));
缺点:如果一个key一直没有被用到,即使它已经过期了也永远存放在localStorage
。
定时删除
定时删除是指,每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU
的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage
空间的浪费。
每隔一秒执行一次定时删除,操作如下:
- 随机测试20个设置了过期时间的key。
- 删除所有发现的已过期的key。
- 若删除的key超过5个则重复步骤1,直至重复500次。
var lsc = (function (self) {
var prefix = 'one_more_lsc_'
var list = [];
//初始化list
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍历所有localStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可过期缓存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
}
}
list = temp;
};
self.init();
self.check = function () {
if (!list || list.length == 0){
return;
}
var checkCount = 0;
while (checkCount < 500){
var expireCount = 0;
//随机测试20个设置了过期时间的key
for (var i = 0; i < 20; i++){
if (list.length == 0) {
break;
}
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
//从list中删除被惰性删除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
}
val = JSON.parse(val);
//删除所有发现的已过期的key
if (val.expires < new Date().getTime()){
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
}
}
//若删除的key不超过5个则跳出循环
if (expireCount <= 5 || list.length == 0){
break;
}
checkCount++;
}
}
//每隔一秒执行一次定时删除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));
localStorage跨域解决方案
通过postMessage
来实现跨源通信
- 可以实现一个公共的
iframe
部署在某个域名中,作为共享域 - 将需要实现
localStorage
跨域通信的页面嵌入这个iframe
- 接入对应的
SDK
操作共享域,从而实现localStorage
的跨域存储
memory cache 如何开启
memory cache
如何开启是一种比较特殊的缓存,他不受max-age
、no-cache
等配置的影响,即使我们不设置缓存,如果当前的内存空间比较充裕的话,一些资源还是会被缓存下来。但这种缓存是暂时的,一旦关闭了浏览器,这一部分用于缓存的内存空间就会被释放掉。如果真的不想使用缓存,可以设置no-store
,这样,即便是内存缓存,也不会生效。
localstorage的限制
- 浏览器的大小不统一,并且在IE8以上的IE版本才支持
localStorage
这个属性 - 目前所有的浏览器中都会把
localStorage
的值类型限定为string
类型,这个在对我们日常比较常见的JSON
对象类型需要一些转换 localStorage
在浏览器的隐私模式下面是不可读取的localStorage
本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡localStorage
不能被爬虫抓取到
浏览器输入URL发生了什么
- URL 解析
- DNS 查询
- TCP 连接
- 处理请求
- 接受响应
- 渲染页面
浏览器如何渲染页面的?
HTML
被HTML
解析器解析成DOM
树;CSS
被CSS
解析器解析成CSSOM
树;- 结合
DOM
树和CSSOM
树,生成一棵渲染树(Render Tree
),这一过程称为Attachment
; - 生成布局(
flow
),浏览器在屏幕上“画”出渲染树中的所有节点; - 将布局绘制(
paint
)在屏幕上,显示出整个页面。
重绘、重排区别如何避免
重排(Reflow):当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。
重绘(Repaint):是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。比如改变某个元素的背景色、文字颜色、边框颜色等等
区别:重绘不一定需要重排(比如颜色的改变),重排必然导致重绘(比如改变网页位置)
引发重排的情况:
- 添加、删除可见的dom
- 元素的位置改变
- 元素的尺寸改变(外边距、内边距、边框厚度、宽高、等几何属性)
- 页面渲染初始化
- 浏览器窗口尺寸改变
- 获取某些属性。当获取一些属性时,浏览器为取得正确的值也会触发重排,它会导致队列刷新,这些属性包括:
offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle inIE)
。所以,在多次使用这些值时应进行缓存。
浏览器自己的优化:
浏览器会维护1个队列,把所有会引起重排,重绘的操作放入这个队列,等队列中的操作到一定数量或者到了一定时间间隔,浏览器就会flush队列,进行一批处理,这样多次重排,重绘变成一次重排重绘
减少 reflow/repaint:
- 不要一条一条地修改 DOM 的样式。可以先定义好 css 的 class,然后修改DOM 的 className。
- 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
- 为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
- 千万不要使用 table 布局。 因为可能很小的一个小改动会造成整个 table 的重新布局。(table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。)
- 不要在布局信息改变的时候做查询(会导致渲染队列强制刷新)
浏览器垃圾回收机制
浏览器的 Javascript
具有自动垃圾回收机制(GC
:Garbage Collecation
),也就是说,执行环境会负责管理代码执行过程中使用的内存。
其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量
- 全局变量的生命周期直至浏览器卸载页面才会结束。
- 局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束
- 而闭包中由于内部函数的原因,外部函数并不能算是结束。
用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。
标记清除
js中最常用的垃圾回收方式就是标记清除。
当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。
垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
token 能放在cookie中吗
能
token 是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,token 便应运而生。
「简单 token 的组成」:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
token认证流程:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端签发一个 token ,并把它发送给客户端
- 客户端接收 token 以后会把它存储起来,比如放在 cookie 里或者 localStorage 里
- 客户端每次发送请求时都需要带着服务端签发的 token(把 token 放到 HTTP 的 Header 里)
- 服务端收到请求后,需要验证请求里带有的 token ,如验证成功则返回对应的数据
cookie
- cookie 是什么?
- cookie 是存储于访问者计算机中的变量。每当一台计算机通过浏览器来访问某个页面时,那么就可以通过 JavaScript 来创建和读取 cookie。
- 实际上 cookie 是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
- 不同浏览器对 cookie 的实现也不一样。即保存在一个浏览器中的 cookie 到另外一个浏览器是不能获取的。
cookie
和 session
都能保存计算机中的变量,但是 session
是运行在服务器端的,而客户端我们只能通过 cookie
来读取和创建变量
- cookie 能做什么?
- 用来保存用户名密码
实际操作中,这种方法很少用了,基本上都是将这些信息存储在数据库中。
- 怎么使用 cookie?
- 语法:
document.cookie = "name=value;expires=evalue; path=pvalue; domain=dvalue; secure;”
- name=value 必选参数
- 这是一个键值对,分别表示要存入的 属性 和 值。
document.cookie="name=中文";
// 为了防止中文乱码,我们可以使用encodeURIComponent()编码;decodeURIComponent()解码
document.cookie = encodeURIComponent("name")+"="+encodeURIComponent("中文");- path=pvalue 可选参数
- 限制访问 cookie 的目录,默认情况下对于当前网页所在的同一目录下的所有页面有效
- domain=dvalue 可选参数
- 用于限制只有设置了的域名才可以访问;如果没有设置,则默认在当前域名访问
- secure=true|false 可选参数,默认是 true 不安全传输
- true,默认值;
- false,必须通过 https 来访问
- 语法:
- cookie可能被禁用。当用户非常注重个人隐私保护时,他很可能禁用浏览器的cookie功能
- cookie是与浏览器相关的。这意味着即使访问的是同一个页面,不同浏览器之间所保存的cookie也是不能互相访问的
- cookie可能被删除。因为每个cookie都是硬盘上的一个文件,因此很有可能被用户删除
- cookie安全性不够高。所有的cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。
- cookie 在保存时,只要后面保存的 name 相同,那么就会覆盖前面的 cookie,注意是完全覆盖,包括失效时间
移动端
移动端适配方案
viewport 适配
例如:根据设计稿标准(750px 宽度)开发页面,写完后页面及元素自动缩小,适配 375 宽度的屏幕。可以在 head 里设置如下代码 initial-scale = 屏幕的宽度 / 设计稿的宽度
<meta name="viewport" content="width=750,initial-scale=0.5">
为了适配其他屏幕,需要动态的设置 initial-scale 的值:
<head>
<script>
const WIDTH = 750
const mobileAdapter = () => {
let scale = screen.width / WIDTH
let content = `width=${WIDTH}, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}`
let meta = document.querySelector('meta[name=viewport]')
if (!meta) {
meta = document.createElement('meta')
meta.setAttribute('name', 'viewport')
document.head.appendChild(meta)
}
meta.setAttribute('content',content)
}
mobileAdapter()
window.onorientationchange = mobileAdapter //屏幕翻转时再次执行
</script>
</head>
缺点就是边线问题,不同尺寸下,边线的粗细是不一样的(等比缩放后),全部元素都是等比缩放,实际显示效果可能不太好
vw 适配(部分等比缩放)
- 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标注是基于此宽度标注)
- 开始开发,对设计稿的标注进行转换,把px换成vw。比如页面元素字体标注的大小是32px,换成vw为(100/750)*32 vw (100vw == 750px)
- 对于需要等比缩放的元素,CSS使用转换后的单位
- 对于不需要缩放的元素,比如边框阴影,使用固定单位px
:root {
--width: 0.133333
}
rem 适配
- 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标是基于此宽度标注)
- 开始开发,对设计稿的标注进行转换
- 对于需要等比缩放的元素,CSS使用转换后的单位
- 对于不需要缩放的元素,比如边框阴影,使用固定单位px
弹性盒适配(合理布局)
<meta name="viewport" content="width=device-width">