2026-week-9st

5 阅读3分钟

window.cookieStore 使用方式和注意事项 尤其注意只能在https或者本地开发中使用!!!

window.cookieStore 提供了一套基于 Promise 的现代、异步方法来操作 Cookie,彻底告别了 document.cookie 繁琐的字符串拼接和解析 。下面详细介绍其使用方式和注意事项。

💻 核心方法使用指南

在开始之前,建议先检查浏览器是否支持,以实现优雅降级:

if ('cookieStore' in window) {
  console.log('支持 CookieStore API');
  // 可以安全地使用 cookieStore 了
} else {
  console.log('不支持,使用 document.cookie 作为降级方案');
}

cookieStore 对象主要提供以下几个方法,所有方法都返回 Promise,推荐结合 async/await 使用。

方法描述
cookieStore.set()设置或修改 Cookie
cookieStore.get()获取单个 Cookie 的详细信息
cookieStore.getAll()获取所有或符合过滤条件的 Cookie 列表
cookieStore.delete()删除指定的 Cookie
cookieStore.addEventListener('change', handler)监听 Cookie 的变化
1. 设置 Cookie: set()

set() 方法有两种调用方式,非常灵活 。

// 方式一:参数分离 (name, value, options)
await cookieStore.set(
  'session_id',        // 键名
  'abc123xyz',         // 键值
  {
    maxAge: 3600,      // 存活时间(秒),优先级高于 expires
    path: '/',         // 作用路径
    domain: 'example.com', // 作用域
    secure: true,      // 仅 HTTPS 下发送
    sameSite: 'lax'    // 可选 'strict' | 'lax' | 'none'
  }
);

// 方式二:对象参数 (更清晰,推荐)
await cookieStore.set({
  name: 'user_pref',
  value: 'dark_mode',
  expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7天后过期
  path: '/',
  secure: true,
  sameSite: 'strict'
});

注意maxAgeexpires 都是定义过期时间的,只需指定其中一个即可 。如果设置一个已存在的 Cookie 键名,其值会被自动覆盖 。

2. 获取 Cookie: get()getAll()

获取到的 Cookie 对象会包含 namevaluedomainpathexpires(时间戳)、secure 等完整属性,无需二次解析 。

// 获取单个 Cookie 的详细信息
const cookie = await cookieStore.get('session_id');
if (cookie) {
  console.log('值:', cookie.value);
  console.log('过期时间戳:', cookie.expires); // 如果存在的话
} else {
  console.log('Cookie 不存在');
}

// 获取所有 Cookie
const allCookies = await cookieStore.getAll();
console.log('所有 Cookie 列表:', allCookies);

// 按路径过滤获取 Cookie
const rootCookies = await cookieStore.getAll({ path: '/' });
3. 删除 Cookie: delete()

删除时需要确保 pathdomain 参数与设置该 Cookie 时完全一致,否则可能删除失败 。

// 简单删除(匹配当前页面的 path 和 domain)
await cookieStore.delete('session_id');

// 精确删除(推荐,避免错删)
await cookieStore.delete({
  name: 'session_id',
  path: '/',
  domain: 'example.com'
});
4. 监听 Cookie 变化: change 事件

这是 CookieStore 最强大的特性之一,可以实时响应 Cookie 的新增、修改和删除,非常适合用来同步登录状态等 。

cookieStore.addEventListener('change', (event) => {
  // 处理新增或修改的 Cookie
  event.changed.forEach(cookie => {
    console.log(`Cookie 新增/更新: ${cookie.name} = ${cookie.value}`);
  });

  // 处理被删除的 Cookie
  event.deleted.forEach(cookie => {
    console.log(`Cookie 被删除: ${cookie.name}`);
  });
});

⚠️ 重要注意事项

在使用这个现代 API 时,有几个关键点需要留意:

  1. 安全上下文cookieStore 只能在安全上下文(HTTPS)中可用。在本地开发时,http://localhost 通常会被视为安全上下文而允许使用 。
  2. 同源策略:和传统的 Cookie 操作一样,你只能操作当前域名下的 Cookie,无法跨域访问 。
  3. 删除参数匹配:删除 Cookie 时,如果当初设置时指定了 pathdomain,那么删除时也必须传入完全相同的参数,否则无法匹配删除 。
  4. SameSite 与 Secure 属性
    • 为了安全起见,建议敏感信息的 Cookie 设置 sameSite: 'lax''strict' 并配合 secure: true
    • 如果你的服务需要跨站共享 Cookie(如单点登录),则必须将 sameSite 设置为 'none',并且同时必须设置 secure: true(即仅在 HTTPS 下工作)。
  5. 兼容性:尽管主流浏览器已广泛支持,但对于需要兼容老旧浏览器的项目,建议使用 polyfill(如 @ungap/cookie-store)或准备 document.cookie 的降级方案 。

decodeURIComponent

decodeURIComponent()方法

适用于解码URL的单个组件(如查询参数),会解码所有非保留字符:

JavaScript
复制
let encodedComponent = "name=%E4%B8%AD%E6%96%87&age=20";
let decodedComponent = decodeURIComponent(encodedComponent);
console.log(decodedComponent); // 输出: name=中文&age=20

关键特性

  • 会解码所有非保留字符,包括原本应保留的字符
  • 适用于处理URL的片段(如查询字符串、路径参数)

pinia 持久化插件pinia-plugin-persistedstate高级用法

import { getUserInfo, siteLogout, siteRefresh } from "@/api/user";
import { defineStore, storeToRefs } from "pinia";
import { clearSession, CKKEY } from "@/utils/auth";
import { Message } from "@arco-design/web-vue";
import Cookies from "js-cookie";
const EXPIRETIME = 86400;
const MILLISECOND = 1000;

export default defineStore("user", {
  state: () => {
    return {
      token: "",
      appId: null, // 应用ID
      userId: 0,
      userInfo: null,
      expire_in: EXPIRETIME * MILLISECOND, // token过期时间
      timeId: null,
      permissions: [],
      noAuthLoginout: false
    };
  },
  actions: {
    toRefs() {
      return storeToRefs(this);
    },
    setAppId(appId) {
      this.appId = appId;
    },
    setToken(token) {
      this.token = token;
    },
    setPermissions(permissions) {
      this.permissions = permissions;
    },
    setUserInfo(userInfo) {
      this.userInfo = userInfo;
      this.userId = userInfo.id;
      if (this.token) {
        Cookies.set(CKKEY + `-${userInfo.id}`, this.token, {
          expires: EXPIRETIME / (24 * 60 * 60),
          path: "/"
        });
      }
    },
    setRedirect(url) {
      this.redirectUrl = url;
    },
    async getUserInfo() {
      return new Promise((resolve) => {
        getUserInfo()
          .then((res) => {
            resolve(res.data);
            this.setUserInfo(res.data);
          })
          .finally(() => {
            resolve();
          });
      });
    },
    getUserPermissions() {
      return new Promise((resolve) => {
        resolve();
      });
    },
    removeToken() {
      this.token = null;
    },
    // 刷新token
    refreshToken(time) {
      this.expire_in = time * MILLISECOND;
      this.timeId = setTimeout(
        () => {
          siteRefresh().then((res) => {
            this.setToken(res.data.access_token);
            this.refreshToken(res.data.expire_in);
          });
        },
        this.expire_in - 10 * MILLISECOND
      ); // 过去10秒前刷新token
    },

    // 登出
    logout() {
      return new Promise((resolve) => {
        siteLogout().then(() => {
          this.removeToken();
          Message.success("退出成功");
          clearSession();
          resolve();
        });
      });
    }
  },
  //
  persist: [
    {
      key: "dgig-user",
      storage: sessionStorage,
      paths: ["token", "userInfo", "permissions", "noAuthLoginout"]
    }
    // 这里可以如下使用cookie 来持久化  store state, 插件文档只写了local 和session 这俩storage
    // {
    //   key: CKKEY,
    //   storage: {
    //     getItem: (key) => Cookies.get(key),
    //     setItem: (key, value) => {
    //       const jsonVal = JSON.parse(value);
    //       if (jsonVal.token !== null) {
    //         Cookies.set(key, value, {
    //           expires: EXPIRETIME / (24 * 60 * 60),
    //           path: "/"
    //         });
    //       } else {
    //         Cookies.remove(key, { path: "/" });
    //       }
    //     },
    //     removeItem: (key) => {
    //       Cookies.remove(key, { path: "/" });
    //     }
    //   },
    //   paths: ["token", "userId"]
    // }
  ]
});

echarts 图例

legend: {
      icon: props.l_icon,
      type: props.l_type,
      orient: props.l_orient, // 水平或者竖直排列
      left: props.position_l[0],
      top: props.position_l[1],
      width: props.l_width,
      height: props.l_height || 120,
      itemWidth: 8,
      itemHeight: 8,
      itemGap: props.l_gap,
      pageIconColor: "#3c7dfc",
      pageTextStyle: {
        color: props.textcolor
      },
      tooltip: {
        show: true,
        formatter: ({ name }) => {
          const item = props.list.find((i) => {
            return i.name === name;
          });
          return `${name}   ${item.value} | ${item.rate}%`;
        }
      },

      textStyle: {
        color: props.textcolor,
        rich: { // 图例文字富文本
          name: {
            //这里是下面格式化的key 以及具体样式
            verticalAlign: "right",
            align: "left",
            fontSize: 14,
            color: props.textcolor,
            padding: props.lconfig.np,
            width: props.lconfig.nw
          },
          percent: {
            align: "left",
            color: props.textcolor,
            fontSize: 14,
            width: props.lconfig.pw
          },
          line: {
            color: props.llcolor,
            fontSize: 14,
            padding: props.lconfig.lp
          },
          ratio: {
            align: "left",
            color: props.textcolor,
            fontSize: 14
          }
        }
      },
      formatter: (name) => {
        const item = props.list.find((i) => {
          return i.name === name;
        });

        const showName = echarts.format.truncateText(
          name,
          props.lconfig.nw,
          "14px",
          "…"
        );
        const showValue = echarts.format.truncateText(
          item.value,
          props.lconfig.sw,
          "14px",
          "…"
        );
        // 下面return 加上\n 可以实现换行!!!
        return `{name|${showName}}{percent|${showValue}}{line||}{ratio|${item.rate}%}`;
      }
    },