Cookie在前端中的实践

对于很多不了解后端的前端er来说,很多涉及后端的知识点都是一道坎,因为不懂后端,所以很多知识都只能在文章上得到,却很少有机会实践,导致很多这块的知识点都是模模糊糊的。在这里,推荐大家去学习一下
Node.js,尝试一下就知道,用 Node.js 搭建一个 mock 服务器是多么简单的一件事情,新世界的大门就此敞开。

废话说太多了,回到本篇文章的主题,来玩玩 Cookie。

环境配置

在开始聊 Cookie 之前,我们需要搭建一个本地服务器,我们将用这个服务器来操作 Cookie。


  1. // 首先打开命令行工具,执行下面一些列命令 
  2.  
  3. mkdir cookie-demo && cd cookie-demo 
  4. npm init 
  5. npm install express --save 
  6. touch main.js  

执行完上面一系列的命令之后,你就能看到如下的文件结构,我们需要操作的文件就是 main.js


  1. cookie-demo 
  2. |- main.js 
  3. |- node_modules 
  4. |- package.json  

打开 main.js,并在 main.js 写入以下代码:


  1. const express = require('express') 
  2. const app = express() 
  3.  
  4. app.listen(3000, err => { 
  5.   if (err) { 
  6.     return console.log(err) 
  7.   } 
  8.   console.log('---- 打开 http://localhost:3000 吧----') 
  9. }) 
  10.  
  11. app.get('/', (req, res) => { 
  12.   res.send('<h1>hello world!</h1>') 
  13. })  

  1. // 在命令行执行 
  2.  
  3. node main.js 
  4.  
  5. // 一个本地服务就跑起来了,现在打开 http://localhost:3000 
  6. // 就可以看到一个大大的 hello world!  

Cookie 是怎样工作的

在介绍 Cookie 是什么之前,我们来看看 Cookie 是如何工作的:

1. 首先,我们假设当前域名下还是没有 Cookie 的

2. 接下来,浏览器发送了一个请求给服务器(这个请求是还没带上 Cookie 的)

3. 服务器设置 Cookie 并发送给浏览器(当然也可以不设置)

4. 浏览器将 Cookie 保存下来

5. 接下来,以后的每一次请求,都会带上这些 Cookie,发送给服务器

验证

我们来验证一下。


  1. // 修改 main.js 
  2.  
  3. app.get('/', (req, res) => { 
  4.   // 服务器接收到请求,在给响应设置一个 Cookie 
  5.   // 这个 Cookie 的 name 为 testName 
  6.   // value 为 testValue 
  7.   res.cookie('testName', 'testValue') 
  8.   res.send('<h1>hello world!</h1>') 
  9. }) 
  10.  
  11. // 保存之后,重启服务器 
  12. // node main.js  

现在打开 http://localhost:3000

  1. 我们看到 Request Headers 并没有 Cookie 这个字段
  2. 但是 Response Headers 有了 Set-Cookie 这个字段

现在我们刷新一下页面,相当于重新向 http://localhost:3000/ 这个地址发起了一次请求。

现在我们就可以看到 Cookie 字段已经带上了,再刷新几次看 Cookie 也还是在的。

document.cookie

JS 提供了获取 Cookie 的方法:document.cookie,我们先去设置多几个 Cookie。


  1. app.get('/', (req, res) => { 
  2.   res.cookie('testName0', 'testValue0') 
  3.   res.cookie('testName1', 'testValue1') 
  4.   res.cookie('testName2', 'testValue2') 
  5.   res.cookie('testName3', 'testValue3') 
  6.   res.send('<h1>hello world!</h1>') 
  7. })  



我们可以看到,Cookie 就是一段字符串。但这个字符串是有格式的,由键值对 key=value 构成,键值对之间由一个分号和一个空格隔开。

什么是 Cookie

说了这么多,大家应该知道 Cookie 是什么吧。整理一下有以下几个点:

  • Cookie 就是浏览器储存在用户电脑上的一小段文本文件
  • Cookie 是纯文本格式,不包含任何可执行的代码
  • Cookie 由键值对构成,由分号和空格隔开
  • Cookie 虽然是存储在浏览器,但是通常由服务器端进行设置
  • Cookie 的大小限制在 4kb 左右

Cookie 的属性选项

每个 Cookie 都有一定的属性,如什么时候失效,要发送到哪个域名,哪个路径等等。在设置任一个 Cookie 时都可以设置相关的这些属性,当然也可以不设置,这时会使用这些属性的默认值。

expires / max-age

expires / max-age 都是控制 Cookie 失效时刻的选项。如果没有设置这两个选项,则默认有效期为 session,即会话 Cookie。这种 Cookie 在浏览器关闭后就没有了。

expires

expires 选项用来设置 Cookie 什么时间内有效,expires 其实是 Cookie 失效日期。

expires 必须是 GMT 格式的时间(可以通过 new Date().toGMTString() 或者 new Date().toUTCString() 来获得)


  1. app.get('/', (req, res) => { 
  2.   // 这个 Cookie 设置十秒后失效 
  3.   res.cookie('testName0', 'testValue0', { 
  4.     expires: new Date(Date.now() + 100000) 
  5.   }) 
  6.   // 这个 Cookie 不设置失效时间 
  7.   res.cookie('testName1', 'testValue1') 
  8.   res.send('<h1>hello world!</h1>') 
  9. })  

上面的代码服务器设置了两个 Cookie,一个设置了失效刻,另外一个没有设置,也就是默认的失效时刻 session。现在我们重启服务并且刷新一下页面。

现在响应头部已经加上了响应的设置失效时刻的字段了。在控制台输入下面的代码。


  1. console.log(`现在的 cookie 是:${document.cookie}`) 
  2. setTimeout(() => { 
  3.   console.log(`5 秒后的 cookie 是:${document.cookie}`) 
  4. }, 5000) 
  5. setTimeout(() => { 
  6.   console.log(`10 秒后的 cookie 是:${document.cookie}`) 
  7. }, 10000)  



所以,Cookie 的失效时刻到了之后,通过 document.cookie 就访问不到这个 Cookie 了,当然以后发送请求也不会再带上这个失效的 Cookie 了。

max-age

expires 是 http/1.0 协议中的选项,在新的 http/1.1 协议中 expires 已经由 max-age
选项代替,两者的作用都是限制 Cookie 的有效时间。expires 的值是一个时间点 (Cookie 失效时刻 = expires),而
max-age 的值是一个以秒为单位时间段 (Cookie 失效时刻 = 创建时刻 + max-age)


  1. // 设置 max-age,就是设置从 cookie 创建的时刻算起 
  2. // 再过多少秒 cookie 就会失效 
  3. app.get('/', (req, res) => { 
  4.   res.cookie('testName0', 'testValue0', { 
  5.     // express 这个参数是以毫秒来做单位的 
  6.     // 实际发送给浏览器就会转换为秒 
  7.     // 十秒后失效 
  8.     maxAge: 10000 
  9.   }) 
  10.   res.cookie('testName1', 'testValue1') 
  11.   res.send('<h1>hello world!</h1>') 
  12. })  



优先级

如果同时设置了 max-age 和 expires,以 max-age 的时间为准。


  1. app.get('/', (req, res) => { 
  2.   res.cookie('name0', 'value0') 
  3.   res.cookie('name1', 'value1', { 
  4.     expires: new Date(Date.now() + 30 * 1000), 
  5.     maxAge: 60 * 1000 
  6.   }) 
  7.   res.cookie('name2', 'value2', { 
  8.     maxAge: 60 * 1000 
  9.   }) 
  10.   res.send('<h1>hello world!</h1>') 
  11. })  



domain 和 path

name、domain 和 path 可以标识一个唯一的 Cookie。domain 和 path 两个选项共同决定了 Cookie 何时被浏览器自动添加到请求头部中发送出去。具体是什么原理请看 Cookie 的作用域和作用路径 这个章节。

如果没有设置这两个选项,则会使用默认值。domain 的默认值为设置该 Cookie 的网页所在的域名,path 默认值为设置该 Cookie 的网页所在的目录。

secure

secure 选项用来设置 Cookie 只在确保安全的请求中才会发送。当请求是 HTTPS 或者其他安全协议时,包含 secure 选项的 Cookie 才能被保存到浏览器或者发送至服务器。

默认情况下,Cookie 不会带 secure 选项(即为空)。所以默认情况下,不管是 HTTPS 协议还是 HTTP 协议的请求,Cookie 都会被发送至服务端。

httpOnly

这个选项用来设置 Cookie 是否能通过 js 去访问。默认情况下,Cookie 不会带 httpOnly
选项(即为空),客户端是可以通过 js 代码去访问(包括读取、修改、删除等)这个 Cookie 的。当 Cookie 带 httpOnly
选项时,客户端则无法通过 js 代码去访问(包括读取、修改、删除等)这个 Cookie。

看看代码吧,修改 main.js,保存重启服务,刷新页面。


  1. app.get('/', (req, res) => { 
  2.   res.cookie('notHttpOnly', 'testValue') 
  3.   res.cookie('httpOnlyTest', 'testValue', { 
  4.     httpOnly: true 
  5.   }) 
  6.   res.send('<h1>hello world!</h1>') 
  7. }) 

看图,设置了 httpOnly 的 Cookie 多了一个勾。而且通过 document.cookie 无法访问到那个 Cookie。

在客户端是不能通过 js 代码去设置 一个 httpOnly 类型的 Cookie 的,这种类型的 Cookie 只能通过服务端来设置,发送请求的时候,我们看到请求头还是会带上这个设置了 httpOnly 的 Cookie,如下图。



设置 Cookie

明确一点:Cookie 可以由服务端设置,也可以由客户端设置。看到这里相信大家都可以理解了吧。

服务端设置 Cookie

看回刚刚的那张图,我们设置了很多个 Cookie。



一个 Set-Cookie 字段只能设置一个 Cookie,当你要想设置多个 Cookie,需要添加同样多的 Set-Cookie 字段

服务端可以设置 Cookie 的所有选项:expires、domain、path、secure、HttpOnly

客户端设置 Cookie

在网页即客户端中我们也可以通过 js 代码来设置 Cookie。

设置


  1. document.cookie = 'name=value' 

可以设置 Cookie 的下列选项:expires、domain、path,各个键值对之间都要用 ; 和 空格 隔开


  1. document.cookie='name=value; expires=Thu, 26 Feb 2116 11:50:25 GMT; domain=sankuai.com; path=/'; 

secure

只有在 https 协议的网页中,客户端设置 secure 类型的 Cookie 才能成功

HttpOnly

客户端中无法设置 HttpOnly 选项

删除 Cookie

Cookie 的 name、path 和 domain 是唯一标识一个 Cookie 的。我们只要将一个 Cookie 的 max-age 设置为 0,就可以删除一个 Cookie 了。


  1. let removeCookie = (name, path, domain) => { 
  2.   document.cookie = `${name}=; path=${path}; domain=${domain}; max-age=0` 
  3. }  

Cookie 的作用域和作用路径

作用域

在说这个作用域之前,我们先来对域名做一个简单的了解。

子域,是相对父域来说的,指域名中的每一个段。各子域之间用小数点分隔开。放在域名最后的子域称为最高级子域,或称为一级域,在它前面的子域称为二级域。

以下图为例,news.163.com 和 sports.163.com 是子域,163.com 是父域。

当 Cookie 的 domain 为 news.163.com,那么访问 news.163.com 的时候就会带上 Cookie;

当 Cookie 的 domain 为 163.com,那么访问 news.163.com 和 sports.163.com 就会带上 Cookie

作用路径

当 Cookie 的 domain 是相同的情况下,也有是否带上 Cookie 也有一定的规则。

在子路径内可以访问访问到父路径的 Cookie,反过来就不行。

看看例子,还是先修改 main.js


  1. app.get('/parent', (req, res) => { 
  2.   res.cookie('parent-name', 'parent-value', { 
  3.     path: '/parent' 
  4.   }) 
  5.   res.send('<h1>父路径!</h1>') 
  6. }) 
  7.  
  8. app.get('/parent/childA', (req, res) => { 
  9.   res.cookie('child-name-A', 'child-value-A', { 
  10.     path: '/parent/childA' 
  11.   }) 
  12.   res.send('<h1>子路径A!</h1>') 
  13. }) 
  14.  
  15. app.get('/parent/childB', (req, res) => { 
  16.   res.cookie('child-name-B', 'child-value-B', { 
  17.     path: '/parent/childB' 
  18.   }) 
  19.   res.send('<h1>子路径B!</h1>') 
  20. })  

下面这里的 “域” 应该改为路径

作者:Mertens

来源:51CTO

时间: 2024-11-02 09:06:56

Cookie在前端中的实践的相关文章

Web前端优化最佳实践之Cookie篇

Web 前端优化最佳实践第三部分面向 Cookie .目前只有 2 条实践规则. 1. 缩小 Cookie (Reduce Cookie Size) Cookie 是个很有趣的话题.根据 RFC 2109 的描述,每个客户端最多保持 300 个 Cookie,针对每个域名最多 20 个 Cookie (实际上多数浏览器现在都比这个多,比如 Firefox 是 50 个) ,每个 Cookie 最多 4K,注意这里的 4K 根据不同的浏览器可能不是严格的 4096 .别扯远了,对于 Cookie

Web前端优化最佳实践之Mobile(iPhone)篇

Web 前端优化最佳实践最后一部分是针对移动应用的,其实只是针对 iPhone 的,目前只有两条规则. 1. 单个数据对象小于 25K (Keep Components under 25K) 这个似乎只是针对 iPhone 研究的.建议保持单个 Web 数据对象在 25 K 以下.为什么是 25K? Apple 官方信息指出可缓存到内存中的 Web 对象最大支持到 10M,但经过测试,发现也就是 25K 左右. iPhone 在市场上的优异表现,让 Web 人员不得不考虑如何针对其进行优化.相信

Web前端优化最佳实践之图象篇

Web 前端优化最佳实践第六部分面向 图片(Image),这部分目前有 4 条规则.在最近的 Velocity 2008 技术大会上,Yahoo! 的 Stoyan Stefanov 做的 Image Optimization: How Many of These 7 Mistakes Are You Making 也非常有参考价值.结合一起说一下. 1. 优化图片 (Optimize Images) 使用 GIF .JPG 还是 PNG 格式的图片? 尽可能的使用 PNG 格式的图片,更多的功

Web前端优化最佳实践之CSS篇

Web 前端优化最佳实践第四部分面向 CSS.目前共计有 6 条实践规则.另请参见 Mozilla 开发者中心的文章:Writing Efficient CSS 1. 把 CSS 放到代码页上端 (Put Stylesheets at the Top) 官方的解释我觉得多少有点语焉不详.这一条其实和用户访问期望有关.CSS 放到最顶部,浏览器能够有针对性的对 HTML 页面从顶到下进行解析和渲染.没有人喜欢等待,而浏览器已经考虑到了这一点. 2. 避免 CSS 表达式 (Avoid CSS Ex

《Web前端开发最佳实践》——2.2 前端代码重构

2.2 前端代码重构 代码重构是业内经常讨论的一个热门话题.重构指的是在不改变代码外部行为的情况下进行源代码修改,重构之前需要考虑的是重构后如何才能保证外部行为不改变.对于后端代码来说,可以通过大量的自动化测试来确保重构后的代码逻辑,可对于普遍缺乏自动化测试的前端代码来说,重构之前一定要考虑再三才能下手.我曾经有一次不算太成功的前端重构经历,所幸的是没有导致太大的问题,但教训是惨痛的.此次重构的项目本身没有足够的自动化测试,尤其是针对前端的自动化测试,其实在重构之前也预想到了重构的风险.先来介绍

Web前端优化最佳实践之Server篇

Web 前端优化最佳实践第二部分面向 Server .目前共计有 6 条实践规则.[注,这最多算技术笔记,查看最原始内容,还请访问:Exceptional Performance : Best Practices for Speeding Up Your Web Site ] 1. 使用 CDN (Use a Content Delivery Network) 国内 CDN 的普及还不够.不过我们有独特的电信.网通之间的问题,如果针对这个作优化,基本上也算能收到 CDN 或类似的效果吧(假装如此

Web前端优化最佳实践之JavaScript篇

Web 前端优化最佳实践之 JavaScript 篇,这部分有 6 条规则,和 CSS 篇 重复的有几条.前端优化最佳实践,最重要的还是"实践",要理解这东西容易得很,关键是要去"实践",去"执行",去"反馈",去获取受益. 1. 脚本放到 HTML 代码页底部 (Put Scripts at the Bottom) 当一个脚本在下载的时候,浏览器干不了其它的事儿(串行了).所以,把它扔到最后面去处理.对于一些功能性的脚本,可

Docker在英雄联盟游戏中的实践探索(二)

本文讲的是Docker在英雄联盟游戏中的实践探索(二),[编者的话]这篇博客是Riot的Docker实践系列博客的第二篇,主要内容是:基于Cloudbees镜像创建新的Dockerfile,设置了一个日志目录,并介绍了如何使用docker exec命令查看日志文件. 当我一年前开始学习Docker的时候,发现很难找到好的文档和实例,即使是今天,也只能找到一些简单的用例,完全不能用作真正的产品.使用Docker容器来产品化应用,需要适应容器的短暂性和单一进程的特性.这对于需要数据持久化和多进程的应

《Web前端开发最佳实践》——2.6 前端代码基本命名规范和格式规范

2.6 前端代码基本命名规范和格式规范 命名规范和格式规范是代码规范中最基本的规范,任何代码的混乱都是从命名和格式的混乱开始的,而意义明确的命名和规整的代码格式则提高了代码的可读性与可维护性,给代码的阅读者和维护者留下了良好的第一印象.命名规范和格式规范没有一个统一的标准,不同的人可能有不同的认识,但是在同一个项目中,必须严格遵守统一的命名和格式规范.以下推荐的规范是在实际项目中认同度较高的代码规范,供读者参考.2.6.1 HTML命名规范及格式规范 HTML代码所有的标签名和属性应该都为小写,