Skip to main content

前端进阶知识

浏览器

共同点:都是保存在浏览器端、且同源的

区别:

  1. cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStoragelocalStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径 (path)的概念,可以限制cookie只属于某个路径下
  2. 存储大小限制也不同
    • cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。
    • sessionStoragelocalStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  3. 数据有效期不同
    • sessionStorage:仅在当前浏览器窗口关闭之前有效
    • localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据
    • cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
  4. 作用域不同
    • sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面
    • localstorage在所有同源窗口中都是共享的
    • cookie也是在所有同源窗口中都是共享的
  5. web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
  6. web Storageapi接口使用更方便

写一个会过期的 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空间的浪费。
每隔一秒执行一次定时删除,操作如下:

  1. 随机测试20个设置了过期时间的key。
  2. 删除所有发现的已过期的key。
  3. 若删除的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-ageno-cache等配置的影响,即使我们不设置缓存,如果当前的内存空间比较充裕的话,一些资源还是会被缓存下来。但这种缓存是暂时的,一旦关闭了浏览器,这一部分用于缓存的内存空间就会被释放掉。如果真的不想使用缓存,可以设置no-store,这样,即便是内存缓存,也不会生效。

localstorage的限制

  1. 浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
  2. 目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
  3. localStorage在浏览器的隐私模式下面是不可读取的
  4. localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
  5. localStorage不能被爬虫抓取到

浏览器输入URL发生了什么

  1. URL 解析
  2. DNS 查询
  3. TCP 连接
  4. 处理请求
  5. 接受响应
  6. 渲染页面

浏览器如何渲染页面的?

  1. HTMLHTML 解析器解析成 DOM 树;
  2. CSSCSS 解析器解析成 CSSOM 树;
  3. 结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment
  4. 生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点;
  5. 将布局绘制(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做布局的一个原因。)
  • 不要在布局信息改变的时候做查询(会导致渲染队列强制刷新)

浏览器垃圾回收机制

info

浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。
其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量

  • 全局变量的生命周期直至浏览器卸载页面才会结束。
  • 局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束
  • 而闭包中由于内部函数的原因,外部函数并不能算是结束。

用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

标记清除

js中最常用的垃圾回收方式就是标记清除。
当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。

垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。

token 能放在cookie中吗

token 是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,token 便应运而生。
「简单 token 的组成」:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

token认证流程:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端签发一个 token ,并把它发送给客户端
  4. 客户端接收 token 以后会把它存储起来,比如放在 cookie 里或者 localStorage 里
  5. 客户端每次发送请求时都需要带着服务端签发的 token(把 token 放到 HTTP 的 Header 里)
  6. 服务端收到请求后,需要验证请求里带有的 token ,如验证成功则返回对应的数据
  1. cookie 是什么?
    • cookie 是存储于访问者计算机中的变量。每当一台计算机通过浏览器来访问某个页面时,那么就可以通过 JavaScript 来创建和读取 cookie。
    • 实际上 cookie 是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
    • 不同浏览器对 cookie 的实现也不一样。即保存在一个浏览器中的 cookie 到另外一个浏览器是不能获取的。
PS

cookiesession 都能保存计算机中的变量,但是 session 是运行在服务器端的,而客户端我们只能通过 cookie 来读取和创建变量

  1. cookie 能做什么?
    • 用来保存用户名密码
PS

实际操作中,这种方法很少用了,基本上都是将这些信息存储在数据库中。

  1. 怎么使用 cookie?
    • 语法: document.cookie = "name=value;expires=evalue; path=pvalue; domain=dvalue; secure;”
    1. name=value 必选参数
      • 这是一个键值对,分别表示要存入的 属性
    document.cookie="name=中文";
    // 为了防止中文乱码,我们可以使用encodeURIComponent()编码;decodeURIComponent()解码
    document.cookie = encodeURIComponent("name")+"="+encodeURIComponent("中文");
    1. path=pvalue 可选参数
      • 限制访问 cookie 的目录,默认情况下对于当前网页所在的同一目录下的所有页面有效
    2. domain=dvalue 可选参数
      • 用于限制只有设置了的域名才可以访问;如果没有设置,则默认在当前域名访问
    3. 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 适配(部分等比缩放)

  1. 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标注是基于此宽度标注)
  2. 开始开发,对设计稿的标注进行转换,把px换成vw。比如页面元素字体标注的大小是32px,换成vw为(100/750)*32 vw (100vw == 750px)
  3. 对于需要等比缩放的元素,CSS使用转换后的单位
  4. 对于不需要缩放的元素,比如边框阴影,使用固定单位px
:root { 
--width: 0.133333
}

rem 适配

  1. 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标是基于此宽度标注)
  2. 开始开发,对设计稿的标注进行转换
  3. 对于需要等比缩放的元素,CSS使用转换后的单位
  4. 对于不需要缩放的元素,比如边框阴影,使用固定单位px

弹性盒适配(合理布局)

<meta name="viewport" content="width=device-width">