js-cookie源码阅读记录
js-cookie是一个javascript 设置cookie的库
1. 基本功能与使用
- 设置cookie
Cookies.set('test', '123', { expires: 1 })
- 获取cookie
Cookies.get('test')
- 删除cookie
Cookies.remove('test')
2. 方法实现原理
set方法 原理:document.cookie = test=123; expires=1,结构为 以;空格分隔的键值对
function set (key, value, attributes) {
// 接收三个参数
// key: cookie的键
// value: cookie的值
// attributes: cookie的属性值
// 如果document是undefined 说明不是浏览器 则代码不会往下执行
if (typeof document === 'undefined') {
return
}
// 合并传入的属性值与默认值
attributes = assign({}, defaultAttributes, attributes);
// expires 传入的数字表示的是天数
// 如果传入的有效期属性是数字 则有效期=当前时间戳+传入的天数的毫秒数
// 比如传入1 则表示cookie 1天后过期
if (typeof attributes.expires === 'number') {
attributes.expires = new Date(Date.now() + attributes.expires * 864e5);
}
// 转换为UTC格式的字符串
if (attributes.expires) {
attributes.expires = attributes.expires.toUTCString();
}
// key先编码 再解码
key = encodeURIComponent(key)
.replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
.replace(/[()]/g, escape);
// 遍历属性
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue
}
// 得到类似这样的结构 '; expires'
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue
}
// 得到类似这样的结构 '; expires=1'
stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
}
// 最终得到 'test=124; expries=1'
return (document.cookie =
key + '=' + converter.write(value, key) + stringifiedAttributes)
}
get方法 原理: 解析document.cookie的返回值
比如:
'test2=%E4%BD%A0%E5%A5%BD; esmodule=EsModule; es=[(123)]'
function get (key) {
// 判断是不是浏览器 有没有传key
if (typeof document === 'undefined' || (arguments.length && !key)) {
return
}
// 有cookie则以;空格分割 否则为空数组
var cookies = document.cookie ? document.cookie.split('; ') : [];
var jar = {};
for (var i = 0; i < cookies.length; i++) {
// 得到每一项以=分割的数组 例如 [test, 123]
var parts = cookies[i].split('=');
// 得到value值 join处理 cookie值中有=的情况 比如 test=123=cos 的情况
var value = parts.slice(1).join('=');
try {
// 解码key值
var foundKey = decodeURIComponent(parts[0]);
// 解码value值
// 传入foundKey是为了自定义解码时使用
jar[foundKey] = converter.read(value, foundKey);
// 如果当前变量的key等于获取的key 则跳出循环
if (key === foundKey) {
break
}
} catch (e) {}
}
// 返回cookie的值 或者jar对象
return key ? jar[key] : jar
}
remove方法 原理: 设置属性{ expires: -1 }让cookie直接过期
remove: function (key, attributes) {
set(
key,
'',
assign({}, attributes, {
expires: -1
})
);
},
3. 解释下上文的Cookies
上文的Cookies.set中的Cookies其实是js-cookie导出的全局属性,指向的是源码中的init方法
init方法返回一个对象,对象包含set;get;remove等方法
function init (converter, defaultAttributes) {
function set (key, value, attributes) {
console.log(key, value, attributes)
if (typeof document === 'undefined') {
return
}
attributes = assign({}, defaultAttributes, attributes);
... // 此处为省略代码
return (document.cookie =
key + '=' + converter.write(value, key) + stringifiedAttributes)
}
function get (key) {
if (typeof document === 'undefined' || (arguments.length && !key)) {
return
}
... // 此处为省略代码
return key ? jar[key] : jar
}
return Object.create(
{
set: set,
get: get,
remove: function (key, attributes) {
set(
key,
'',
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) }
}
)
}
var api = init(defaultConverter, { path: '/' });
return api;
返回对象使用了Object.create()方法
我们看到上面还返回了withAttributes和withConverter方法
withAttributes传入自定义的属性,返回新的实例api,也就是init函数,只不过设置过后,当再次设置cookie时,默认会带上传入的自定义属性
const api = Cookies.withAttributes({ path: '/', domain: '.example.com' })
withConverter 是创建覆盖默认解码实现的 api 的新实例,如果要单独为cookie解码与编码,则可以使用此方法
官网例子
// 自定义解码
document.cookie = 'escaped=%u5317'
document.cookie = 'default=%E5%8C%97'
var cookies = Cookies.withConverter({
read: function (value, name) {
if (name === 'escaped') {
return unescape(value)
}
// Fall back to default for all other cookies
return Cookies.converter.read(value, name)
}
})
cookies.get('escaped') // 北
cookies.get('default') // 北
cookies.get() // { escaped: '北', default: '北' }
// 自定义编码
Cookies.withConverter({
write: function (value, name) {
return value.toUpperCase()
}
})
Object.freeze方法用来冻结默认属性与方法,防止被修改
4. 实现一个兼容UMD,CMD,AMD规范,浏览器全局属性的库的写法
; (function (context, factory) {
if (typeof exports === 'object' && typeof module !== undefined) {
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
define(factory)
} else {
context['ZKK'] = factory()
}
}(
this, (function () {
return 'hello'
})
))
js-cookie源码中的写法
;
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, (function () {
var current = global.Cookies;
var exports = global.Cookies = factory();
exports.noConflict = function () { global.Cookies = current; return exports; };
}()));
}(this, (function () { 'use strict';
......
5. init函数默认的解码,编码对象
var defaultConverter = {
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
)
}
};
6. init函数
init函数返回了一个对象,对象的属性上有对应的方法,这些方法又在init的函数作用域里。
参考资料