1. 前言
-
本文参加了由 公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
-
这是源码共读的第17期,链接:js-cookie
2. js-cookie介绍
js-cookie是为了解决原生读写cookie操作不便利, cookie及其他存储对比。
3. 源码解读
3.1 主要函数解释
assign
: 将多个对象中的属性合并到一个目标对象中
js
export default function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i]
for (var key in source) {
target[key] = source[key]
}
}
return target
}
converter
: 提供两个方法read和write,分别用于读取和写入URL编码。
- read方法用于将URL编码的字符串转换为可读的字符串,
- write方法则用于将字符串转换为URL编码的字符串。
js
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
)
}
}
例如:

init
:用于初始化和创建一个Cookie对象。
function init(converter, defaultAttributes)
是一个初始化函数,它接收一个转换器对象和默认属性对象作为参数。
-
set(name, value, attributes)
方法用于设置Cookie。- 首先判断是否在浏览器环境下,如果不是就直接返回。
- 使用
assign
方法将默认属性和传入的属性合并。 - 如果属性中有
expires
并且类型是数字,将它转换为表示过期日期的字符串。 - 将
name
进行编码,并替换%XX
形式的编码和括号字符。 - 遍历属性对象,根据规则构建字符串形式的属性。
- 最后将Cookie设置到document.cookie中。
-
get(name)
方法用于获取Cookie。- 首先判断是否在浏览器环境下或者是否传入了name,如果不满足条件则返回。
- 将document.cookie拆分成数组,遍历每一个Cookie。
- 根据Cookie中的键值对,解码键名,然后使用转换器读取对应的值。
- 如果传入了name,则返回对应的值;否则,返回整个Cookie对象。
-
remove(name, attributes)
方法用于移除Cookie。- 调用
set
方法,并将值设置为空字符串以及过期时间设置为-1。
- 调用
-
withAttributes(attributes)
使用自定义的属性,创建新的Cookie -
withConverter(converter)
使用自定义的转换器,创建新的Cookie
js
import assign from './assign.mjs'
import defaultConverter from './converter.mjs'
function init(converter, defaultAttributes) {
//首先判断是否在浏览器环境下,如果不是就直接返回。 传入{name:'jack'}
function set(name, value, attributes) {
if (typeof document === 'undefined') {
return
}
//使用 `assign` 方法将默认属性和传入的属性合并。
attributes = assign({}, defaultAttributes, attributes)
//如果属性中有 `expires` 并且类型是数字,将它转换为表示过期日期的字符串。
if (typeof attributes.expires === 'number') {
attributes.expires = new Date(Date.now() + attributes.expires * 864e5)
}
if (attributes.expires) {
attributes.expires = attributes.expires.toUTCString()
}
//将 `name` 进行编码,并替换 `%XX` 形式的编码和括号字符。
name = encodeURIComponent(name)
.replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
.replace(/[()]/g, escape)
var stringifiedAttributes = ''
// 遍历传入的对象,根据规则构建字符串形式的属性。 例如 {httpOnly:true,Priority:Medium,Partition Key:''}
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue
}
//如果值存在则拼接属性
stringifiedAttributes += '; ' + attributeName
// 如果值是true,进行下一个
if (attributes[attributeName] === true) {
continue
}
/**
根据 RFC 6265 规范中的这个处理字符的规定,有以下几个好处和用途:
1. 约定分隔符:规范明确指定使用分号(`;`)作为 Cookie 属性之间的分隔符。通过约定分隔符,可以方便地将多个属性放在一个字符串中,并通过分号进行分隔。
2. 处理属性值中的特殊字符:如果属性值中包含分号(`;`)等特殊字符,按照规范,应该在第一个分号之前将其转义或截断。这样做可以确保在解析 Cookie 属性时不会产生歧义或错误。
3. 兼容性:遵循规范有助于提高代码的兼容性。各种浏览器和服务器都应该遵守 RFC 6265 的规范,以确保在 Cookie 的设置、解析和传输过程中得到一致的行为。
*/
// 最终结果 ;httpOnly;Priority=Medium;
stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]
}
// name=jack;httpOnly;Priority=Medium;
return (document.cookie =
name + '=' + converter.write(value, name) + stringifiedAttributes)
}
function get(name) {
//首先判断是否在浏览器环境下或者是否传入了name,如果不满足条件则返回。
if (typeof document === 'undefined' || (arguments.length && !name)) {
return
}
// 将document.cookie拆分成数组,遍历每一个Cookie。
var cookies = document.cookie ? document.cookie.split('; ') : []
var jar = {}
// 根据Cookie中的键值对,解码键名,然后使用转换器读取对应的值。
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]) // 解码 属性
// 如果存在则不进行读取
if (!(found in jar)) jar[found] = converter.read(value, found)
if (name === found) {
break
}
} catch (e) {
// Do nothing...
}
}
// 如果传入了name,则返回对应的值;否则,返回整个Cookie对象。
return name ? jar[name] : jar
}
return Object.create(
{
set,
get,
// 调用 `set` 方法,并将值设置为空字符串以及过期时间设置为-1。
remove: function (name, attributes) {
set(
name,
'',
assign({}, attributes, {
expires: -1
})
)
},
// 使用自定义的属性,创建新的Cookie
withAttributes: function (attributes) {
return init(this.converter, assign({}, this.attributes, attributes))
},
// 使用自定义的转换器,创建新的Cookie
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: '/' })
expires进行日期转换:
可以查看cookie设置的列表,理解默认属性,查看过期时间的设置

4. 总结
最后总结一下:

通过本次js-cookie主要学习了:
- 学习cookie的设置
- encodeURIComponent、decodeURIComponent的用法
- Object.create使用
- Object.freeze使用
不足是没有深究测试用例,主要是源码学习,希望对大家有点帮助O^O。