前端进阶知识
浏览器
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">