跟着 ahooks 写 Hook 之 useCookieState

285 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

💻文档地址:ahooks.js.org/zh-CN/

👨‍💻github地址:github.com/alibaba/hoo…


Cookie 介绍

由于 HTTP 是无状态的协议,不能保存每一次请求的状态,所以需要给客户端增加 Cookie 来保存客户端的状态。

Cookie 的作用主要用于 用户识别状态管理

// 请求首部字段 
Cookie: key=value; expires=true, 05 Jul 2016 07:23:31 GMT; path=/; domain=.baidu.com 

// 响应首部字段 
Set-Cookie:key = value;
  • key-value: Cookie 值
  • expires=date : Cookie 的有效期(默认的是浏览器关闭之前)

有效期

Cookie 过期之后,浏览器删除且不再携带 Cookie

  • Expires :具体的过期时间
  • Max-age :过期的时间范围,浏览器接受 Cookie 开始计时

作用域

浏览器在发送请求之前,检查 Cookie 的 domain 和 path,如果不一致,则不携带 Cookie。

  • path :将服务器的文件目录作为 Cookie 的适用对象
  • domain = 域名 : 作为 Cookie 适用对象的域名。如:naiyou.com,则 www.naiyou.comwww.naiyou2.com 也可以访问。

安全性

在 Cookie 传输和管理的时候,要确保 Cookie 的安全性,不被窃取。

  • Secure :仅在 HTTPS 安全通信时才会发送 Cookie 。
  • HttpOnly :使用 Cookie 不能被 Javascript 脚本访问(防止跨站脚本攻击 XSS 对 Cookie 信息的窃取)。
  • SameSite :防止跨站伪造 CORF 的攻击
    • Strict :浏览器完全禁止第三方请求携带 Cookie。www.naiyou.com 只能在 www.naiyou.com 下请求。
    • Lax :只能在 get 方法提交表单情况或者 a 标签发送 get 请求的情况下可以携带 Cookie。
    • None :默认,请求会自动携带上 Cookie。

局限性

  • 大小:只有 4KB,只能存储少量信息
  • 性能:如果不设置 Cookie 的 Domain 和 path ,Cookie 就会被发送到各个域名下,随着请求数量的增多,性能也会出现很大的问题。
  • 安全: Cookie 明文传输,被攻击劫持之后,服务器的资源会被窃取。且如果不设置 HttpOnly 攻击者会通过 JS 获取到 Cookie 。

useCookieState

useCookieState 的 setState 可以接收 function updater,就像 useState 那样。

function useCookieState(cookieKey: string, options: Options = {}) {
  // 接收字符串 + 函数
  const [state, setState] = useState<State>(() => {
    const cookieValue = Cookies.get(cookieKey);

    if (isString(cookieValue)) return cookieValue;

    if (isFunction(options.defaultValue)) {
      return options.defaultValue();
    }

    return options.defaultValue;
  });

  const updateState = useMemoizedFn(
    (
      newValue: State | ((prevState: State) => State),
      newOptions: Cookies.CookieAttributes = {},
    ) => {
      const { defaultValue, ...restOptions } = { ...options, ...newOptions };
      setState((prevState) => {
        const value = isFunction(newValue) ? newValue(prevState) : newValue;
        if (value === undefined) {
          Cookies.remove(cookieKey);
        } else {
          Cookies.set(cookieKey, value, restOptions);
        }
        return value;
      });
    },
  );

  return [state, updateState] as const;
}