vue3

15 阅读6分钟

el-switch

const appStore = useAppStore()
const changeThemeType = (value) => {
    appStore.toggleThemeDark(value);
}
<el-switch size="default" v-model="appStore.appThemeDark" @change="changeThemeType"/>

这里夜间开关变动后,会用调用changeThemeType函数并传参最新的appThemeDark值

@change

vue中,@change是一个事件监听器,用于监听表单元素(例如 <input><select><textarea> 等)发生“值变化并失去焦点”时触发的事件

<input @change="handleChange" />
<!-- 等价于 -->
<input v-on:change="handleChange" />

@change触发时机:

普通输入框: 当输入内容后离开焦点时才会触发, 当输入内容后离开焦点(blur)时才会触发;

复选框:当选中状态改变时立即触发(不需要失焦)

下拉框:当选项改变时立即触发

结合v-model使用

v-model默认会自动监听input事件来更新数据

如果你希望在值确认时再更新,可以结合@change使用:

<template>
  <input v-model="text" @change="onConfirm" />
</template>

<script setup>
import { ref } from 'vue'

const text = ref('')

function onConfirm() {
  console.log('确认输入:', text.value)
}
</script>

appSetting.js

const appThemeDark = ref(dbUtils.get('appThemeDark') === null ? true : dbUtils.get('appThemeDark') === 'light');

const toggleThemeDark = (type) => {

        if (!type) {
            dbUtils.set('appThemeDark', 'dark')
            appThemeDark.value = false
            document.documentElement.classList.add("dark");
        } else {
            dbUtils.set('appThemeDark', 'light')
            appThemeDark.value = true
            document.documentElement.classList.remove("dark");
        }

}

dbUtils

set(key, value) {

        if (typeof value === 'object') {
            // 如果值是对象或数组,先将其转换为 JSON 字符串再存储
            value = JSON.stringify(value);
        }

        localStorage.setItem(`${t}-${v}-` + key, value)

    },

    get(key) {
    
        const value = localStorage.getItem(`${t}-${v}-` + key)
        console.log(value)
        try {
            //尝试解析JSON字符串
            return JSON.parse(value)

        } catch (e) {
            return value
        }
    }

这段代码的作用是:当appThemeDark的值改变时,将localStorage中存储的值也同步更改,并改变页面的css -> 通过在html中加或删除dark类的方式,ElementUI可以一键切换全局模式,也可以自定义:

html.dark {
  --main-background: var(--el-bg-color-overlay)
}

这里--el-bg-color-overlay是ElementUI提供的可配置变量颜色,可能由:

  • 框架默认值(内置CSS)
  • 用户主题定制
  • 动态暗黑切换

来共同决定

也就是,可以实时改变的,当你在页面上加上 .dark 类时,CSS 层会生效下面这一条定义:

html.dark {
  --el-bg-color-overlay: #1d1e1f;
}

改完后,所有引用 var(--el-bg-color-overlay) 的组件(如弹窗、菜单等)都会立即更新颜色
这也是为什么 Element Plus 可以实现“实时切换主题”的原因

想要使用--el-bg-color-overlay,需要引入elementUI的相关组件 import 'element-plus/theme-chalk/dark/css-vars.css'

localStorge

localStorage是浏览器提供的一种本地存储机制

用于在浏览器中持久保存少量数据,可以理解成浏览器里的小型数据库,可以保存一些前端需要长期使用的数据

  • 位置:保存在用户浏览器本地(不会上传到服务器)
  • 生命周期:永久保存,除非主动删除或用户清理浏览器缓存
  • 容量:一般每个域名可存 5MB 左右(比 Cookie 大得多)
  • 数据类型:只能存字符串(非字符串要序列化)

localStorge和Cookie的区别

Cookie是浏览器保存的一小段数据,主要用于:

  • 标识用户身份(例如登录状态)
  • 在请求中自动携带数据(例如会话ID,语言偏好)
  • 在不同页面间共享信息

它是浏览器和服务器之间的“通信型”存储机制

对比项localStoragesessionStorageCookie
存储位置浏览器本地浏览器本地浏览器本地
生命周期永久保存页面会话结束清除可设置过期时间
容量~5MB~5MB~4KB
是否随请求发送❌ 否❌ 否✅ 是
操作接口简单(API形式)简单(API形式)复杂(要解析字符串)
典型用途用户设置、缓存数据页面临时数据登录状态、会话信息

三种常见存储方式的应用场景

Cookie

Cookie是最早的前端存储方式,最初是为了在HTTP请求间保存用户状态(如登录状态)

  • 每次请求同源接口时,浏览器会自动携带 cookie(除非被禁用)。
  • 大小一般只有 4KB 左右
  • 可以设置 过期时间

常见应用场景:

  • 登录状态/session 管理(例如服务器根据 cookie 中的 session_id 知道你是谁)
  • 跨请求的用户识别(如跟踪用户行为、广告分析)
  • 需要后端读取的状态信息

⚠ 缺点:容易被滥用(追踪用户)、空间小、携带到服务器增加请求负担。

localStorage

  • 大约 5MB 容量
  • 永久存储(除非用户主动清理或 JS 调用 removeItem() / clear())。
  • 只存在于浏览器端,不会随着 HTTP 请求自动发送到服务器。
  • 同源策略:只能在相同的协议 + 域名 + 端口下访问。

常见应用场景

  • 保存用户的偏好设置(例如主题、语言、字体大小)
  • 缓存不太敏感的数据(例如上次打开的页面、草稿内容)
  • 前端状态持久化(比如单页应用保存临时表单内容或上次的滚动位置)

⚠ 缺点:无法在不同浏览器或设备间共享;对隐私敏感内容不安全。

sessionStorage

与 localStorage 相似,但:

  • 生命周期更短:只在当前 标签页(tab)会话 中有效;
  • 关闭标签页就清空;
  • 不会随请求发送;
  • 不同标签页之间互不共享。

常见应用场景

  • 表单填写过程中的临时数据保存(防止误刷新)
  • 单次会话内的数据共享(例如某个页面跳转前后传递信息)
  • SPA 应用中短期状态缓存(例如 API 请求结果暂存)

⚠缺点:标签页一关就没了,无法持久化。

初始化函数

如果只有之前的代码,主题颜色只会在颜色按钮改变时改变,那么应该如何通过localStorage内存储的变量实现初始化

export const useAppStore = defineStore("AppSetting", ()=>{
    const initThemeDark = () => {
            if (!appThemeDark.value) {
                dbUtils.set('appThemeDark', 'dark');
                document.documentElement.classList.add('dark');
            } else {
                dbUtils.set('appThemeDark', 'light');
                document.documentElement.classList.remove('dark');
            }
    }
    
    initThemeDark();
}

在这里,initThemeDark() 的执行时机,取决于 Pinia 的 defineStore如何创建和使用 store 的

  1. defineStore本身不会立即执行函数体
export const useAppStore = defineStore("AppSetting", () => {
  initThemeDark()
})

这里的defineStore并不会马上执行initThemeDark()

它只是定义了一个工厂函数(也就是 useAppStore),还没有真正创建 store 实例。

Pinia 内部大概相当于做了这样的事:

function defineStore(name, setupFn) {
  return function useStore() {
    // 当你第一次调用 useAppStore() 时
    // 才执行 setupFn()
    return setupFn()
  }
}
  1. initThemeDark() 的执行时机

真正调用的时机是:

const appStore = useAppStore()

就在这行代码执行时:

  • setupFn(也就是你传给 defineStore 的回调)会执行;
  • 也就是 initThemeDark() 会在这时运行。
阶段发生的事
加载模块时defineStore 定义了一个名为 "AppSetting" 的 store(函数没执行)
第一次调用 useAppStore()initThemeDark() 在这里被调用
后续调用 useAppStore()因为 Pinia 会缓存 store 实例,不会再执行第二次

为什么不会执行第二次

export const useAppStore = defineStore("AppSetting", () => {
  initThemeDark()
})

Pinia 的defineStore返回一个函数(也就是useAppStore)

当你在组件或代码里调用:

const appStore = useAppStore()

这时候Pinia会:

  1. 检查这个store名字(这里是"AppSetting")是否已经存在
  2. 如果没有,就执行一次传进去的函数(也就是 () => { initThemeDark() })来创建 store;
  3. 然后把这个 store 缓存起来
  4. 以后再调用 useAppStore() 时,Pinia 直接返回缓存好的那个实例,不会再执行 initThemeDark()

设计原因

为了保证:

  • 所有组件共享同一个状态(单例store)
  • 避免重复初始化逻辑(例如主题初始化、接口请求、localStorage恢复等)

因此initThemeDark()这个初始化方法只会在第一次创建store时执行一次