JS前端操作 Cookie源码示例解析

目录
  • 引言
  • 源码分析
    • 使用
    • 源码
    • 分析
    • set
    • get
    • remove
    • withAttributes & withConverter
  • 总结

引言

前端操作Cookie的场景其实并不多见,Cookie也因为各种问题被逐渐淘汰,但是我们不用Cookie也可以学习一下它的思想,或者通过这次的源码来学习其他的一些知识。

今天带来的是:js-cookie

源码分析

使用

根据README,我们可以看到js-cookie的使用方式:

// 设置
Cookies.set('name', 'value');
// 设置过期时间
Cookies.set('name', 'value', { expires: 7 })
// 获取
Cookies.get('name') // => 'value'
// 获取所有
Cookies.get() // => { name: 'value' }
// 获取指定域名下
Cookies.get('foo', { domain: 'sub.example.com' })
// 删除
Cookies.remove('name')

还有很多其他用和配置说明,大家可以自己去看看。

源码

js-cookie的源码并不多,src目录下的api.mjs就是我们要分析的源码,只有一百行左右。

/* eslint-disable no-var */
import assign from './assign.mjs'
import defaultConverter from './converter.mjs'
function init (converter, defaultAttributes) {
  function set (name, value, attributes) {
    if (typeof document === 'undefined') {
      return
    }
    attributes = assign({}, defaultAttributes, attributes)
    if (typeof attributes.expires === 'number') {
      attributes.expires = new Date(Date.now() + attributes.expires * 864e5)
    }
    if (attributes.expires) {
      attributes.expires = attributes.expires.toUTCString()
    }
    name = encodeURIComponent(name)
      .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
      .replace(/[()]/g, escape)
    var stringifiedAttributes = ''
    for (var attributeName in attributes) {
      if (!attributes[attributeName]) {
        continue
      }
      stringifiedAttributes += '; ' + attributeName
      if (attributes[attributeName] === true) {
        continue
      }
      // Considers RFC 6265 section 5.2:
      // ...
      // 3.  If the remaining unparsed-attributes contains a %x3B (";")
      //     character:
      // Consume the characters of the unparsed-attributes up to,
      // not including, the first %x3B (";") character.
      // ...
      stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]
    }
    return (document.cookie =
      name + '=' + converter.write(value, name) + stringifiedAttributes)
  }
  function get (name) {
    if (typeof document === 'undefined' || (arguments.length && !name)) {
      return
    }
    // To prevent the for loop in the first place assign an empty array
    // in case there are no cookies at all.
    var cookies = document.cookie ? document.cookie.split('; ') : []
    var jar = {}
    for (var i = 0; i < cookies.length; i++) {
      var parts = cookies[i].split('=')
      var value = parts.slice(1).join('=')
      try {
        var found = decodeURIComponent(parts[0])
        jar[found] = converter.read(value, found)
        if (name === found) {
          break
        }
      } catch (e) {}
    }
    return name ? jar[name] : jar
  }
  return Object.create(
    {
      set: set,
      get: get,
      remove: function (name, attributes) {
        set(
          name,
          '',
          assign({}, attributes, {
            expires: -1
          })
        )
      },
      withAttributes: function (attributes) {
        return init(this.converter, assign({}, this.attributes, attributes))
      },
      withConverter: function (converter) {
        return init(assign({}, this.converter, converter), this.attributes)
      }
    },
    {
      attributes: { value: Object.freeze(defaultAttributes) },
      converter: { value: Object.freeze(converter) }
    }
  )
}
export default init(defaultConverter, { path: '/' })
/* eslint-enable no-var */

分析

js-cookie的源码并不多,我们先来看看导出的是什么:

export default init(defaultConverter, { path: '/' })

这里是直接导出了init函数的返回值,我们来看看init函数的返回值:

function init (converter, defaultAttributes) {
  // ...
  return Object.create(
    {
      set: set,
      get: get,
      remove: function (name, attributes) {
        set(
          name,
          '',
          assign({}, attributes, {
            expires: -1
          })
        )
      },
      withAttributes: function (attributes) {
        return init(this.converter, assign({}, this.attributes, attributes))
      },
      withConverter: function (converter) {
        return init(assign({}, this.converter, converter), this.attributes)
      }
    },
    {
      attributes: { value: Object.freeze(defaultAttributes) },
      converter: { value: Object.freeze(converter) }
    }
  )
}

这里是使用Object.create创建了一个对象,这个对象有setgetremovewithAttributeswithConverter这几个方法,这几个方法都是在init函数内部定义的,我们来看看这几个方法的实现:

set

function set(name, value, attributes) {
    if (typeof document === 'undefined') {
        return
    }
    attributes = assign({}, defaultAttributes, attributes)
    if (typeof attributes.expires === 'number') {
        attributes.expires = new Date(Date.now() + attributes.expires * 864e5)
    }
    if (attributes.expires) {
        attributes.expires = attributes.expires.toUTCString()
    }
    name = encodeURIComponent(name)
        .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
        .replace(/[()]/g, escape)
    var stringifiedAttributes = ''
    for (var attributeName in attributes) {
        if (!attributes[attributeName]) {
            continue
        }
        stringifiedAttributes += '; ' + attributeName
        if (attributes[attributeName] === true) {
            continue
        }
        // Considers RFC 6265 section 5.2:
        // ...
        // 3.  If the remaining unparsed-attributes contains a %x3B (";")
        //     character:
        // Consume the characters of the unparsed-attributes up to,
        // not including, the first %x3B (";") character.
        // ...
        stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]
    }
    return (document.cookie =
        name + '=' + converter.write(value, name) + stringifiedAttributes)
}

一行一行来看:

if (typeof document === 'undefined') {
    return
}

首先判断是否有document对象,如果没有则直接返回,这说明js-cookie只能在浏览器环境下使用。

attributes = assign({}, defaultAttributes, attributes)

然后合并配置项,将defaultAttributes和传入的attributes合并,这里的assign大家直接理解为Object.assign就好了。

if (typeof attributes.expires === 'number') {
    attributes.expires = new Date(Date.now() + attributes.expires * 864e5)
}
if (attributes.expires) {
    attributes.expires = attributes.expires.toUTCString()
}

然后判断expires是否是一个数字,如果是数字则将其转换为一个Date对象;

这里的864e5是一个常量,结尾的e5代表后面加5个0,也就是86400000表示一天的毫秒数。

然后判断expires是否存在,如果存在则将其转换为UTC时间。

name = encodeURIComponent(name)
    .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
    .replace(/[()]/g, escape)

这里对name进行了编码,然后将name中的%()进行了转义。

escape是一个内置函数,它的作用是将一个字符串转换为UTF-8编码的字符串,这里的escape是将()转换为%28%29

参考:escape()

var stringifiedAttributes = ''
for (var attributeName in attributes) {
    if (!attributes[attributeName]) {
        continue
    }
    stringifiedAttributes += '; ' + attributeName
    if (attributes[attributeName] === true) {
        continue
    }
    // Considers RFC 6265 section 5.2:
    // ...
    // 3.  If the remaining unparsed-attributes contains a %x3B (";")
    //     character:
    // Consume the characters of the unparsed-attributes up to,
    // not including, the first %x3B (";") character.
    // ...
    stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]
}

这里是将attributes转换为字符串,这里的stringifiedAttributes是一个字符串,最后的结果是这样的:

stringifiedAttributes = '; path=/; expires=Wed, 21 Oct 2015 07:28:00 GMT'

最后将namevaluestringifiedAttributes拼接起来,然后赋值给document.cookie

get

function get(name) {
    if (typeof document === 'undefined' || (arguments.length && !name)) {
        return
    }
    // To prevent the for loop in the first place assign an empty array
    // in case there are no cookies at all.
    var cookies = document.cookie ? document.cookie.split('; ') : []
    var jar = {}
    for (var i = 0; i < cookies.length; i++) {
        var parts = cookies[i].split('=')
        var value = parts.slice(1).join('=')
        try {
            var found = decodeURIComponent(parts[0])
            jar[found] = converter.read(value, found)
            if (name === found) {
                break
            }
        } catch (e) {
        }
    }
    return name ? jar[name] : jar
}

get方法的实现比较简单,主要是解析document.cookie,然后将其转换为一个对象,来逐行解析:

if (typeof document === 'undefined' || (arguments.length && !name)) {
    return
}

对比于set方法,这里多了一个(arguments.length && !name)的判断,这里是防止传入空字符串的name

var cookies = document.cookie ? document.cookie.split('; ') : []

这里是将document.cookie分割为一个数组,每一项是一个cookie

var jar = {}
for (var i = 0; i < cookies.length; i++) {
    var parts = cookies[i].split('=')
    var value = parts.slice(1).join('=')
}

这一步是只要cookienamevalue,其他的一些额外附加信息都不需要。

try {
    var found = decodeURIComponent(parts[0])
    jar[found] = converter.read(value, found)
    if (name === found) {
        break
    }
} catch (e) {
}

这里是将name进行了解码,然后将namevalue存储到jar对象中,如果传入了name,则在找到对应的name后就跳出循环。

return name ? jar[name] : jar

最后返回jar对象,如果传入了name,则返回对应的value,否则返回整个jar对象。

这里的核心是converter.read,这个方法是用来解析value的,这里的converter是一个对象,它有两个方法:

/* eslint-disable no-var */
export default {
  read: function (value) {
    if (value[0] === '"') {
      value = value.slice(1, -1)
    }
    return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent)
  },
  write: function (value) {
    return encodeURIComponent(value).replace(
      /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,
      decodeURIComponent
    )
  }
}
/* eslint-enable no-var */

read方法是将value进行解码,write方法是将value进行编码。

remove

function remove(name, attributes) {
    set(
        name,
        '',
        assign({}, attributes, {
            expires: -1
        })
    )
}

remove方法就是使用set方法将value设置为空字符串,然后将expires设置为-1,这样就相当于删除了cookie

withAttributes & withConverter

Object.create({
    withAttributes: function (attributes) {
        return init(assign({}, defaultAttributes, attributes))
    },
    withConverter: function (converter) {
        return init(assign({}, defaultConverter, converter))
    }
})

这两个方法就是用来设置defaultAttributesdefaultConverter的,这两个对象是用来设置cookie的默认属性和默认的converter

总结

通过学习js-cookie的源码,我们可以了解到cookie的基本使用,如果想深入了解cookie,可以参考MDN

同时我们也学会了很多字符串的处理方法,比如encodeURIComponentdecodeURIComponentsplitjoin等等。

以上就是JS前端操作 Cookie源码示例解析的详细内容,更多关于JS前端操作Cookie的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解javascript如何在跨域请求中携带cookie

    目录 1.搭建环境 2.测试同源cookie 3.跨域请求携带cookie 4.总结 5.知识点 1. 搭建环境 1.生成工程文件 npm init 2.安装 express npm i express --save 3.新增app1.js,开启服务器1 端口:3001 const express = require('express') const app = express() const port = 3001 // 设置`cookie` app.get("/login", (r

  • 利用js-cookie实现前端设置缓存数据定时失效

    一.背景 业务需要在前端进行数据的缓存,到期就删除再进行获取新数据. 二.实现过程 前端设置数据定时失效的可以有下面2种方法: 1.当数据较大时,可以利用localstorage,存数据时候写入一个时间,获取的时候再与当前时间进行比较 2.如果数据不超过cookie的限制大小,可以利用cookie比较方便,直接设置有效期即可. 3.更多(请大神指点) 利用localstorage实现 localstorage实现思路: 1.存储数据时加上时间戳 在项目开发中,我们可以写一个公用的方法来进行存储的

  • JavaScript中Cookie的使用之如何设置失效时间

    目录 1.什么是Cookie? 1.1简介 1.2特点 2.JavaScript操作Cookie 2.1基础操作 2.2设置失效时间 总结 1.什么是Cookie? 1.1简介 主要用于存储访问过的网站数据,存储浏览器的信息到本地计算机中,用于客户端和服务器端的通讯 Cookie 是为了解决“如何记住用户信息”而发明的: 当用户访问网页时,他的名字可以存储在 cookie 中. 下次用户访问该页面时,cookie 会“记住”他的名字. 注意:如果浏览器完全禁止cookie,大多数网站的基本功能都

  • JavaScript实现cookie的操作

    cookie 用于存储 web 页面的用户信息. 一.什么是 Cookie? Cookie 是一些数据, 存储于你电脑上的文本文件中. 当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息. Cookie 的作用就是用于解决 "如何记录客户端的用户信息": 当用户访问 web 页面时,他的名字可以记录在 cookie 中. 在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录. Cookie 以名/值对形式存储,如下所示: usernam

  • JS+cookie实现购物评价五星好评功能

    本文实例为大家分享了JS+cookie实现购物评价五星好评功能的具体代码,供大家参考,具体内容如下 案例实现的是购物评价中五星点评功能. 通过JS面向对象方法实现 利用cookie实现历史点评保存的功能,在下一次打开页面仍保存上一次点评效果. 具体html,js代码如下: <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <

  • JavaScript cookie与session的使用及区别深入探究

    目录 1. cookie 1.1 什么是cookie 1.2 KOA中使用cookie 1.3 expires和maxAge 1.4 浏览器端设置和删除cookie 2. session 2.1 什么是session 2.2 koa中使用session 3. cookie和session的区别 4. cookie和session的使用场景 1. cookie 1.1 什么是cookie cookie是解决http无状态的一种方案. 服务端与服务端经过三次握手后建立连接,数据发送完后连接关闭,在之

  • JS前端操作 Cookie源码示例解析

    目录 引言 源码分析 使用 源码 分析 set get remove withAttributes & withConverter 总结 引言 前端操作Cookie的场景其实并不多见,Cookie也因为各种问题被逐渐淘汰,但是我们不用Cookie也可以学习一下它的思想,或者通过这次的源码来学习其他的一些知识. 今天带来的是:js-cookie 源码分析 使用 根据README,我们可以看到js-cookie的使用方式: // 设置 Cookies.set('name', 'value'); //

  • Flink 侧流输出源码示例解析

    目录 Flink 侧流输出源码解析 源码解析 TimestampedCollector#collect CountingOutput#collect BroadcastingOutputCollector#collect RecordWriterOutput#collect ProcessOperator#ContextImpl#output CountingOutput#collect BroadcastingOutputCollector#collect RecordWriterOutput

  • OpenMP task construct 实现原理及源码示例解析

    目录 前言 从编译器角度看 task construct Task Construct 源码分析 总结 前言 在本篇文章当中主要给大家介绍在 OpenMP 当中 task 的实现原理,以及他调用的相关的库函数的具体实现. 在本篇文章当中最重要的就是理解整个 OpenMP 的运行机制. 从编译器角度看 task construct 在本小节当中主要给大家分析一下编译器将 openmp 的 task construct 编译成什么样子,下面是一个 OpenMP 的 task 程序例子: #inclu

  • Django动态随机生成温度前端实时动态展示源码示例

    目录 随机生成温度 前端动态实时 一.django APScheduler定时任务 简介 安装 使用步骤 基础组件 二.dwebsocket 简介 安装 使用方法 属性和方法 为了模拟随机的温度显示,在models中的表中的数据 views 路由 VUE中的代码 随机生成温度 前端动态实时 一.django APScheduler定时任务 简介 APScheduler的全称是Advanced Python Scheduler. 它是一个轻量级的 Python 定时任务调度框架. APSchedu

  • MyBatis SqlSource源码示例解析

    目录 正文 SqlNode SqlNode接口定义 BoundSql SqlSource SqlSource解析时机 SqlSource调用时机 总结 正文 MyBatis版本:3.5.12. 本篇讲从mybatis的角度分析SqlSource.在xml中sql可能是带?的预处理语句,也可能是带$或者动态标签的动态语句,也可能是这两者的混合语句. SqlSource设计的目标就是封装xml的crud节点,使得mybatis运行过程中可以直接通过SqlSource获取xml节点中解析后的SQL.

  • Flutter加载图片流程之ImageProvider源码示例解析

    目录 加载网络图片 ImageProvider resolve obtainKey resolveStreamForKey loadBuffer load(被废弃) evict 总结 困惑解答 加载网络图片 Image.network()是Flutter提供的一种从网络上加载图片的方法,它可以从指定的URL加载图片,并在加载完成后将其显示在应用程序中.本节内容,我们从源码出发,探讨下图片的加载流程. ImageProvider ImageProvider是Flutter中一个抽象类,它定义了一种

  • Python 装饰器常用的创建方式及源码示例解析

    目录 装饰器简介 基础通用装饰器 源码示例 执行结果 带参数装饰器 源码示例 源码结果 源码解析 多装饰器执行顺序 源码示例 执行结果 解析 类装饰器 源码示例 执行结果 解析 装饰器简介 装饰器(decorator)是一种高级Python语法.可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用.修饰器经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理

  • React Refs 的使用forwardRef 源码示例解析

    目录 三种使用方式 1. String Refs 2. 回调 Refs 3. createRef 两种使用目的 Refs 转发 createRef 源码 forwardRef 源码 三种使用方式 React 提供了 Refs,帮助我们访问 DOM 节点或在 render 方法中创建的 React 元素. React 提供了三种使用 Ref 的方式: 1. String Refs class App extends React.Component { constructor(props) { su

  • Flutter加载图片流程之ImageCache源码示例解析

    目录 ImageCache _pendingImages._cache._liveImages maximumSize.currentSize clear evict _touch _checkCacheSize _trackLiveImage putIfAbsent clearLiveImages 答疑解惑 ImageCache const int _kDefaultSize = 1000; const int _kDefaultSizeBytes = 100 << 20; // 100 M

  • solid.js响应式createSignal 源码解析

    目录 正文 createSignal readSignal writeSignal 案例分析 总结 正文 www.solidjs.com/docs/latest… createSignal 用来创建响应式数据,它可以跟踪单个值的变化. solid.js 的响应式实现参考了 S.js,它是一个体积超小的 reactive 库,支持自动收集依赖和简单的响应式编程. createSignal createSignal 首先我们来看下 createSignal 的声明: // packages/soli

随机推荐