js-cookie源码学习

235 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

仓库地址

js-cookie

学习目标

  1. 学习js-cookie源码
  2. 总结源码中的知识点

学习结论

  1. init()函数,返回创建的对象。包括一下方法和属性
  2. get函数:将 document.cookie 字符串转化为 Object 形式,转化过程中判断是否在存 key,如果有就返回对应 value,value值是解码之后的值。
  3. set函数:把 attributes stringify,然后追加到 key=value 后, document.cookie = {key}=${value}${attrStr}。对=后边的值,进行编码。
  4. del函数:调用 set,把 expires 设置为 -1 天,cookie 直接过期被删除。

源码注释

/**
 * 获取单个 Cookie
 */
import {defaultAttributes, defaultConverter, TWENTY_FOUR_HOURS} from './constants'
import {Attributes, Converter} from './types'

interface Cookie {
  get: (key: string) => string | null
  set: (key: string, value: string) => void
  del: (key: string) => void
  withConverter: (converter: Converter) => Cookie
  withAttributes: (attributes: Partial<Attributes>) => Cookie // Partial<T> 可以快速把某个接口类型中定义的属性变成可选的(Optional):
}

function init(initConverter: Converter, initAttributes: Attributes): Cookie {
  function get(key: string): string | null {
    if (typeof document === 'undefined') return null

    const cookiePairs = document.cookie ? document.cookie.split('; ') : [] // pair:对,一对

    const cookieStore: Record<string, string> = {} // store:存储
    // Record ts类型,定义对象的<key,value>

    // some 快速查找。如果是自己处理的话,会用map循环,存对应的key;然后再根据key取值
    // 问题: 重复的key会覆盖,那最后的存储的值。
    // 循环浪费
    cookiePairs.some(pair => { 
      const [curtKey, ...curtValue] = pair.split('=') // 数组直接解构,下次也可以使用

      try {
        // 有可能 value 存在 '='
        cookieStore[curtKey] = initConverter.decode(curtValue.join('=')) // join('='),有可能存在这样的数据,自己还真没考虑到。
      } catch (e) {}

      return curtKey === key // 如果相等时,就会 break
    })

    return key ? cookieStore[key] : null
  }

  /**
   * 设置 Cookie key-val 对
   */
  function set(key: string, value: string, attributes = initAttributes) {
    if (typeof document === 'undefined') return null // 确保是浏览器环境

    attributes = {...initAttributes, ...attributes}

    if (attributes.expires) {
      // 将过期天数转为 UTC string
      if (typeof attributes.expires === 'number') {
        attributes.expires = new Date(Date.now() + attributes.expires * TWENTY_FOUR_HOURS)
        attributes.expires = attributes.expires.toUTCString() // toUTCString 根据世界时 (UTC) 把 Date 对象转换为字符串:
      }
    }

    value = initConverter.encode(value)// 加密

    // 获取 Cookie 其它属性的字符串形式
    const attrStr = Object.entries(attributes).reduce((prevStr, attrPair) => {
      // entries使用的较少,返回一个【key,value】的键值对数组
      // reduce 参数:总值,当前val,索引,self,    初始值
      const [attrKey, attrValue] = attrPair

      if (!attrValue) return prevStr

      prevStr += `; ${attrKey}`

      // attrValue 有可能为 truthy(真值),所以要排除 true 值的情况
      if (attrValue === true) return prevStr

      // 排除 attrValue 存在 ";" 号的情况
      prevStr += `=${attrValue.split('; ')[0]}`// split,参数: 分隔符,一个整数,限定返回的分割片段数量

      return prevStr
    }, '') // 初始值

    return document.cookie = `${key}=${value}${attrStr}`
  }

  /**
   * 删除某个 Cookie
   */
  function del(key: string, attributes = initAttributes) {
    // 将 expires 减 1 天,Cookie 自动失败
    set(key, '', {...attributes, expires: -1})
  }

  /**
   * 添加自定义 converter
   */
  function withConverter(customConverter: Converter) {
    return init({...this.converter, ...customConverter}, this.attributes)
  }

  /**
   * 添加自定义 attributes
   */
  function withAttributes(customAttributes: Attributes) {
    return init(this.converter, {...this.attributes, ...customAttributes})
  }

  return Object.create( // 创建一个对象,并对属性做设置。
    {get, set, del, withConverter, withAttributes},
    {
      converter: {value: Object.freeze(initConverter)},// 冻结对象
      attributes: {value: Object.freeze(initAttributes)},
    }
  )
}

export default init(defaultConverter, defaultAttributes)

学习收获

  1. 学习命名【也当学习英语了^-^】
    1. Pair: 对,一对
    2. stored:存储
    3. expires:到期,过期
    4. attributes:属性集
    5. Converter:转化器
    6. with:与,和,一起
  1. ts类型
    1. Partial 可以快速把某个接口类型中定义的属性变成可选的(Optional):
    2. Record ts类型,定义对象的<key,value>
  1. Object相关的方法
    1. Object.create()
    2. Object.freeze()
    3. Object方法
  1. 反思
    1. 自己在实现的时候,不会考虑到编码和解码的问题
    2. 对于封装来说,让用户使用的很爽重要,但也要控制好修改的度。【避免全局的变量,对象冻结】
    3. 每一个都是单独的对象,互不影响。
  1. 相关联的知识点
    1. session
    2. localStorage
    3. sessionStorage
    4. indexedDB
    5. jwt[鉴权]
    6. vuex

参考文章

github.com/haixiangyan…