在 Web 开发中,我们经常需要处理浏览器 cookies。本文将详细介绍如何使用 JavaScript 编写一个高效的 cookie 解析函数,将 cookie 字符串转换为方便使用的 JavaScript 对象。
理解 Cookie 字符串格式
在实现解析器之前,我们需要了解 cookie 字符串的标准格式:
"name=value; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/; domain=example.com; secure; HttpOnly"
- 每个 cookie 键值对用分号分隔
- 第一个键值对通常是主数据(如
name=value) - 后面的属性是可选参数(如
expires,path,domain等) - 键值对等号左右可能包含空格
最终实现代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Cookie 解析器</title>
<style>
:root {
--primary: #4361ee;
--secondary: #3f37c9;
--accent: #4895ef;
--light: #f8f9fa;
--dark: #212529;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
color: var(--dark);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
margin-bottom: 40px;
padding: 20px;
background: white;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
h1 {
color: var(--secondary);
margin-bottom: 10px;
font-size: 2.5rem;
}
.subtitle {
color: #6c757d;
font-size: 1.2rem;
}
.content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
@media (max-width: 768px) {
.content {
grid-template-columns: 1fr;
}
}
.card {
background: white;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.07);
overflow: hidden;
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card-header {
background: var(--primary);
color: white;
padding: 15px 20px;
}
.card-body {
padding: 20px;
}
.input-container {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--secondary);
}
textarea {
width: 100%;
min-height: 150px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-family: monospace;
font-size: 1rem;
resize: vertical;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: background 0.3s ease;
width: 100%;
}
button:hover {
background: var(--secondary);
}
.result-container {
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 20px;
background: var(--light);
}
pre {
background: #2b2b2b;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
max-height: 400px;
}
.explanation {
margin-top: 40px;
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.07);
}
.explanation h2 {
color: var(--secondary);
margin-bottom: 20px;
border-bottom: 2px solid var(--accent);
padding-bottom: 10px;
}
.explanation-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.explanation-content {
grid-template-columns: 1fr;
}
}
.step {
background: var(--light);
padding: 20px;
border-radius: 10px;
}
.step h3 {
color: var(--primary);
margin-bottom: 10px;
}
.notes {
background: #fff8e1;
padding: 20px;
border-radius: 10px;
margin-top: 20px;
}
.notes h3 {
color: #e65100;
margin-bottom: 10px;
}
.examples {
margin-top: 20px;
}
.example-btn {
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
background: #e9ecef;
color: var(--dark);
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9rem;
}
.example-btn:hover {
background: #dee2e6;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>JavaScript Cookie 解析器</h1>
<p class="subtitle">一个强大的工具,用于将cookie字符串转换为JavaScript对象</p>
</header>
<div class="content">
<div class="card">
<div class="card-header">
<h2>输入 Cookie 字符串</h2>
</div>
<div class="card-body">
<div class="input-container">
<label for="cookieInput">输入您的 cookie 字符串:</label>
<textarea id="cookieInput" placeholder="例如: name=value; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/; secure">user=Jane%20Doe; session=abcd1234; theme=dark; language=en-US; visited=true</textarea>
</div>
<div class="examples">
<strong>示例:</strong><br>
<span class="example-btn" data-example="user=John%20Doe; lang=en; loggedIn=true">简单 cookies</span>
<span class="example-btn" data-example="token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9; expires=Mon, 01 Jan 2024 00:00:00 GMT; secure; HttpOnly">带安全属性</span>
<span class="example-btn" data-example="preferences=theme:dark|layout:compact|notifications:off; sessionId=abcd1234efgh5678">复杂值</span>
</div>
<button id="parseButton">解析 Cookies</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>解析结果 (JavaScript 对象)</h2>
</div>
<div class="card-body">
<div class="result-container">
<pre id="result"></pre>
</div>
</div>
</div>
</div>
<div class="explanation">
<h2>实现解析器的详细步骤</h2>
<div class="explanation-content">
<div class="step">
<h3>1. 准备解析函数</h3>
<p>创建一个函数,接受 cookie 字符串作为输入,返回解析后的对象:</p>
<pre>function parseCookies(cookieString) {
// 解析逻辑将放在这里
}</pre>
</div>
<div class="step">
<h3>2. 初始化结果对象</h3>
<p>创建一个空对象用于存储解析结果:</p>
<pre>const cookies = {};</pre>
</div>
<div class="step">
<h3>3. 处理空字符串</h3>
<p>检查输入字符串是否为空:</p>
<pre>if (!cookieString || cookieString.trim() === '') {
return cookies;
}</pre>
</div>
<div class="step">
<h3>4. 分割 cookie 条目</h3>
<p>使用分号分隔符分割 cookie 字符串:</p>
<pre>const items = cookieString.split(';');</pre>
</div>
<div class="step">
<h3>5. 处理每个 cookie</h3>
<p>遍历所有 cookie 条目:</p>
<pre>items.forEach(item => {
// 处理每个条目
});</pre>
</div>
<div class="step">
<h3>6. 分割键值对</h3>
<p>将每个条目按第一个等号分割为键和值:</p>
<pre>const [key, ...values] = item.split('=');
const value = values.join('=');</pre>
</div>
<div class="step">
<h3>7. 解码和清理</h3>
<p>去除键值两端的空格,并解码 URL 编码的值:</p>
<pre>const cleanedKey = key.trim();
// 尝试解码值,失败时使用原值
let cleanedValue;
try {
cleanedValue = decodeURIComponent(value.trim());
} catch (e) {
cleanedValue = value.trim();
}</pre>
</div>
<div class="step">
<h3>8. 添加到结果对象</h3>
<p>将解析后的键值对添加到结果对象:</p>
<pre>if (cleanedKey && cleanedValue !== undefined) {
cookies[cleanedKey] = cleanedValue;
}</pre>
</div>
</div>
<div class="notes">
<h3>重要注意事项</h3>
<ul>
<li><strong>URL 解码</strong>: 对值使用 <code>decodeURIComponent()</code> 方法</li>
<li><strong>错误处理</strong>: 当解码无效编码值时回退到原始值</li>
<li><strong>空格处理</strong>: 正确处理键值前后的空格</li>
<li><strong>多个等号</strong>: 值中可能包含等号,因此只按第一个等号分割</li>
<li><strong>Cookie 属性</strong>: 只保留主键值对,忽略如 expires、path 等属性</li>
</ul>
</div>
</div>
</div>
<script>
// 功能完整的 cookie 解析函数
function parseCookies(cookieString) {
// 如果没有输入或为空字符串,返回空对象
if (!cookieString || cookieString.trim() === '') {
return {};
}
// 创建结果对象
const cookies = {};
// 按分号分割字符串
const items = cookieString.split(';');
// 处理每个 cookie 条目
items.forEach(item => {
// 按第一个等号分割键值对
const [key, ...values] = item.split('=');
// 如果没有值,则跳过
if (values.length === 0) return;
// 合并值部分(可能包含等号)
const value = values.join('=');
// 清理键和值中的空格
const cleanedKey = key.trim();
const trimmedValue = value.trim();
// 尝试解码 URL 编码的值
let cleanedValue;
try {
cleanedValue = decodeURIComponent(trimmedValue);
} catch (e) {
// 如果解码失败,使用原始值
cleanedValue = trimmedValue;
}
// 将有效的键值对添加到结果对象
if (cleanedKey && cleanedValue !== undefined) {
cookies[cleanedKey] = cleanedValue;
}
});
return cookies;
}
// DOM 操作和事件绑定
document.addEventListener('DOMContentLoaded', () => {
const cookieInput = document.getElementById('cookieInput');
const parseButton = document.getElementById('parseButton');
const resultElement = document.getElementById('result');
const exampleButtons = document.querySelectorAll('.example-btn');
// 初始解析
updateResult();
// 解析按钮点击事件
parseButton.addEventListener('click', updateResult);
// 回车键触发解析
cookieInput.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
updateResult();
}
});
// 示例按钮事件
exampleButtons.forEach(button => {
button.addEventListener('click', () => {
cookieInput.value = button.dataset.example;
updateResult();
});
});
// 更新结果显示
function updateResult() {
const cookieStr = cookieInput.value;
const result = parseCookies(cookieStr);
// 美化输出格式
resultElement.innerHTML = JSON.stringify(result, null, 2)
.replace(/ /g, ' ')
.replace(/\n/g, '<br>');
}
});
</script>
</body>
</html>
解析器的核心实现
以上页面展示了一个完整的 cookie 解析器实现。以下是核心代码:
function parseCookies(cookieString) {
// 如果输入为空,返回空对象
if (!cookieString || cookieString.trim() === '') {
return {};
}
const cookies = {};
// 用分号分割字符串
const items = cookieString.split(';');
items.forEach(item => {
// 按第一个等号分割键值对
const [key, ...values] = item.split('=');
// 如果没有值,跳过此项
if (values.length === 0) return;
// 合并值部分(可能包含等号)
const value = values.join('=');
// 清理字符串两端空格
const cleanedKey = key.trim();
const trimmedValue = value.trim();
// 尝试解码URL编码的值
let cleanedValue;
try {
cleanedValue = decodeURIComponent(trimmedValue);
} catch (e) {
// 解码失败,使用原始值
cleanedValue = trimmedValue;
}
// 将有效的键值对添加到结果对象
if (cleanedKey && cleanedValue !== undefined) {
cookies[cleanedKey] = cleanedValue;
}
});
return cookies;
}
关键设计考虑
- URL 解码:使用
decodeURIComponent()处理 URL 编码的值 - 错误处理:在解码失败时回退到原始值
- 空格处理:删除键值前后的空白字符
- 等号支持:正确处理值中的等号字符
- 空值处理:跳过无效数据条目
- 性能优化:简洁高效的遍历算法
应用场景
这个解析器适用于:
- 浏览器端处理document.cookie
- 服务器端处理HTTP请求中的Cookie头
- 调试和分析应用中的Cookie数据
- 在无法使用document.cookie API时手动处理Cookies