Skip to content

useToggle 🔄

介绍

想要轻松管理布尔状态的切换吗?useToggle 就像一个智能开关,让你在 truefalse 之间自由切换!🎛️

无论是控制模态框的显示隐藏、切换主题模式,还是管理组件的展开收起状态,这个 Hook 都能让你的状态管理变得简单而优雅!

代码演示

基础用法 🚀

让我们从最简单的开关控制开始:

html
<template>
  <div class="toggle-demo">
    <div class="status-display">
      <span class="status-text">
        当前状态: {{ isOn ? '开启 🟢' : '关闭 🔴' }}
      </span>
    </div>
    
    <div class="control-buttons">
      <button @click="toggle()" class="toggle-btn">
        {{ isOn ? '关闭' : '开启' }}
      </button>
      <button @click="toggle(true)" class="force-btn">
        强制开启
      </button>
      <button @click="toggle(false)" class="force-btn">
        强制关闭
      </button>
    </div>
  </div>
</template>
js
import { useToggle } from '@vant/use';

export default {
  setup() {
    // 创建一个开关状态,默认为关闭
    const [isOn, toggle] = useToggle();

    // 无参数调用:自动切换状态
    const handleAutoToggle = () => {
      toggle();
      console.log('状态已切换:', isOn.value ? '开启' : '关闭');
    };

    // 有参数调用:设置特定状态
    const handleForceOn = () => {
      toggle(true);
      console.log('强制开启!');
    };

    const handleForceOff = () => {
      toggle(false);
      console.log('强制关闭!');
    };

    return {
      isOn,
      toggle,
      handleAutoToggle,
      handleForceOn,
      handleForceOff
    };
  },
};

模态框控制 📱

使用 useToggle 优雅地管理模态框状态:

html
<template>
  <div class="modal-demo">
    <!-- 触发按钮 -->
    <button @click="openModal" class="open-btn">
      打开设置 ⚙️
    </button>
    
    <!-- 模态框 -->
    <div v-if="isModalVisible" class="modal-overlay" @click="closeModal">
      <div class="modal-content" @click.stop>
        <div class="modal-header">
          <h3>设置面板</h3>
          <button @click="closeModal" class="close-btn">✕</button>
        </div>
        
        <div class="modal-body">
          <div class="setting-item">
            <label>
              <input 
                type="checkbox" 
                :checked="isDarkMode" 
                @change="toggleDarkMode"
              />
              深色模式 🌙
            </label>
          </div>
          
          <div class="setting-item">
            <label>
              <input 
                type="checkbox" 
                :checked="isNotificationEnabled" 
                @change="toggleNotification"
              />
              推送通知 🔔
            </label>
          </div>
        </div>
        
        <div class="modal-footer">
          <button @click="resetSettings" class="reset-btn">
            重置设置
          </button>
          <button @click="closeModal" class="confirm-btn">
            确认
          </button>
        </div>
      </div>
    </div>
  </div>
</template>
js
import { useToggle } from '@vant/use';
import { watch } from 'vue';

export default {
  setup() {
    // 模态框显示状态
    const [isModalVisible, toggleModal] = useToggle(false);
    
    // 设置项状态
    const [isDarkMode, toggleDarkMode] = useToggle(false);
    const [isNotificationEnabled, toggleNotification] = useToggle(true);
    
    // 打开模态框
    const openModal = () => {
      toggleModal(true);
      document.body.style.overflow = 'hidden'; // 禁止背景滚动
    };
    
    // 关闭模态框
    const closeModal = () => {
      toggleModal(false);
      document.body.style.overflow = ''; // 恢复滚动
    };
    
    // 重置所有设置
    const resetSettings = () => {
      toggleDarkMode(false);
      toggleNotification(true);
      console.log('设置已重置!');
    };
    
    // 监听深色模式变化
    watch(isDarkMode, (newValue) => {
      document.documentElement.classList.toggle('dark-theme', newValue);
      console.log('深色模式:', newValue ? '已开启' : '已关闭');
    });
    
    // 监听通知设置变化
    watch(isNotificationEnabled, (newValue) => {
      if (newValue) {
        console.log('通知已开启 🔔');
      } else {
        console.log('通知已关闭 🔕');
      }
    });
    
    return {
      isModalVisible,
      isDarkMode,
      isNotificationEnabled,
      openModal,
      closeModal,
      toggleDarkMode,
      toggleNotification,
      resetSettings
    };
  }
};

折叠面板组件 📋

创建一个可展开收起的信息面板:

html
<template>
  <div class="accordion-demo">
    <div class="accordion-item" v-for="item in accordionItems" :key="item.id">
      <div 
        class="accordion-header" 
        @click="item.toggle()"
        :class="{ active: item.isExpanded }"
      >
        <h4>{{ item.title }}</h4>
        <span class="accordion-icon">
          {{ item.isExpanded ? '▼' : '▶' }}
        </span>
      </div>
      
      <transition name="accordion">
        <div v-if="item.isExpanded" class="accordion-content">
          <p>{{ item.content }}</p>
        </div>
      </transition>
    </div>
  </div>
</template>
js
import { useToggle } from '@vant/use';
import { reactive } from 'vue';

export default {
  setup() {
    // 创建多个折叠面板项
    const accordionItems = reactive([
      {
        id: 1,
        title: '什么是 Vue 3?',
        content: 'Vue 3 是一个渐进式 JavaScript 框架,用于构建用户界面。它具有更好的性能、更小的包体积和更强的 TypeScript 支持。',
        ...useToggle(false)
      },
      {
        id: 2,
        title: 'Composition API 的优势',
        content: 'Composition API 提供了更好的逻辑复用、更清晰的代码组织和更强的类型推导能力,让大型项目的维护变得更加容易。',
        ...useToggle(false)
      },
      {
        id: 3,
        title: '如何开始学习?',
        content: '建议从官方文档开始,然后通过实际项目练习。可以先学习基础概念,再逐步深入到高级特性和生态系统。',
        ...useToggle(false)
      }
    ]);
    
    // 展开所有面板
    const expandAll = () => {
      accordionItems.forEach(item => item.toggle(true));
    };
    
    // 收起所有面板
    const collapseAll = () => {
      accordionItems.forEach(item => item.toggle(false));
    };
    
    return {
      accordionItems,
      expandAll,
      collapseAll
    };
  }
};

功能开关管理 🎛️

实现一个功能开关面板,管理应用的各种特性:

html
<template>
  <div class="feature-toggle-panel">
    <h3>功能开关面板 🎛️</h3>
    
    <div class="feature-grid">
      <div 
        v-for="feature in features" 
        :key="feature.key"
        class="feature-card"
        :class="{ enabled: feature.isEnabled }"
      >
        <div class="feature-header">
          <span class="feature-icon">{{ feature.icon }}</span>
          <h4>{{ feature.name }}</h4>
        </div>
        
        <p class="feature-description">{{ feature.description }}</p>
        
        <div class="feature-controls">
          <label class="toggle-switch">
            <input 
              type="checkbox" 
              :checked="feature.isEnabled"
              @change="feature.toggle()"
            />
            <span class="slider"></span>
          </label>
          <span class="status-text">
            {{ feature.isEnabled ? '已启用' : '已禁用' }}
          </span>
        </div>
      </div>
    </div>
    
    <div class="panel-actions">
      <button @click="enableAllFeatures" class="action-btn primary">
        启用全部功能
      </button>
      <button @click="disableAllFeatures" class="action-btn secondary">
        禁用全部功能
      </button>
      <button @click="resetToDefaults" class="action-btn">
        恢复默认设置
      </button>
    </div>
  </div>
</template>
js
import { useToggle } from '@vant/use';
import { reactive, watch } from 'vue';

export default {
  setup() {
    // 定义功能开关列表
    const features = reactive([
      {
        key: 'notifications',
        name: '推送通知',
        icon: '🔔',
        description: '接收应用内消息和提醒',
        defaultValue: true,
        ...useToggle(true)
      },
      {
        key: 'darkMode',
        name: '深色模式',
        icon: '🌙',
        description: '切换到深色主题界面',
        defaultValue: false,
        ...useToggle(false)
      },
      {
        key: 'autoSave',
        name: '自动保存',
        icon: '💾',
        description: '自动保存用户输入的内容',
        defaultValue: true,
        ...useToggle(true)
      },
      {
        key: 'analytics',
        name: '数据分析',
        icon: '📊',
        description: '收集使用数据以改善体验',
        defaultValue: false,
        ...useToggle(false)
      },
      {
        key: 'betaFeatures',
        name: '测试功能',
        icon: '🧪',
        description: '启用实验性的新功能',
        defaultValue: false,
        ...useToggle(false)
      },
      {
        key: 'offlineMode',
        name: '离线模式',
        icon: '📱',
        description: '在无网络时继续使用应用',
        defaultValue: true,
        ...useToggle(true)
      }
    ]);
    
    // 启用所有功能
    const enableAllFeatures = () => {
      features.forEach(feature => feature.toggle(true));
      console.log('所有功能已启用!');
    };
    
    // 禁用所有功能
    const disableAllFeatures = () => {
      features.forEach(feature => feature.toggle(false));
      console.log('所有功能已禁用!');
    };
    
    // 恢复默认设置
    const resetToDefaults = () => {
      features.forEach(feature => {
        feature.toggle(feature.defaultValue);
      });
      console.log('已恢复默认设置!');
    };
    
    // 监听功能状态变化
    features.forEach(feature => {
      watch(() => feature.isEnabled, (newValue) => {
        console.log(`${feature.name} ${newValue ? '已启用' : '已禁用'}`);
        
        // 根据功能类型执行相应操作
        switch (feature.key) {
          case 'darkMode':
            document.documentElement.classList.toggle('dark', newValue);
            break;
          case 'notifications':
            if (newValue && 'Notification' in window) {
              Notification.requestPermission();
            }
            break;
          case 'analytics':
            // 启用/禁用分析代码
            break;
        }
      });
    });
    
    return {
      features,
      enableAllFeatures,
      disableAllFeatures,
      resetToDefaults
    };
  }
};

游戏状态控制 🎮

使用 useToggle 管理游戏的各种状态:

html
<template>
  <div class="game-control-panel">
    <h3>游戏控制面板 🎮</h3>
    
    <div class="game-status">
      <div class="status-item">
        <span class="label">游戏状态:</span>
        <span class="value" :class="{ active: isGameRunning }">
          {{ isGameRunning ? '运行中 ▶️' : '已暂停 ⏸️' }}
        </span>
      </div>
      
      <div class="status-item">
        <span class="label">音效:</span>
        <span class="value" :class="{ active: isSoundEnabled }">
          {{ isSoundEnabled ? '开启 🔊' : '关闭 🔇' }}
        </span>
      </div>
      
      <div class="status-item">
        <span class="label">调试模式:</span>
        <span class="value" :class="{ active: isDebugMode }">
          {{ isDebugMode ? '开启 🐛' : '关闭' }}
        </span>
      </div>
    </div>
    
    <div class="game-controls">
      <button 
        @click="toggleGame" 
        class="control-btn"
        :class="{ primary: !isGameRunning, danger: isGameRunning }"
      >
        {{ isGameRunning ? '暂停游戏' : '开始游戏' }}
      </button>
      
      <button @click="toggleSound" class="control-btn">
        {{ isSoundEnabled ? '关闭音效' : '开启音效' }}
      </button>
      
      <button @click="toggleDebug" class="control-btn">
        {{ isDebugMode ? '关闭调试' : '开启调试' }}
      </button>
      
      <button @click="resetGame" class="control-btn secondary">
        重置游戏
      </button>
    </div>
    
    <div v-if="isDebugMode" class="debug-panel">
      <h4>调试信息 🔍</h4>
      <pre>{{ debugInfo }}</pre>
    </div>
  </div>
</template>
js
import { useToggle } from '@vant/use';
import { computed, watch } from 'vue';

export default {
  setup() {
    // 游戏状态管理
    const [isGameRunning, toggleGame] = useToggle(false);
    const [isSoundEnabled, toggleSound] = useToggle(true);
    const [isDebugMode, toggleDebug] = useToggle(false);
    
    // 调试信息
    const debugInfo = computed(() => ({
      gameRunning: isGameRunning.value,
      soundEnabled: isSoundEnabled.value,
      debugMode: isDebugMode.value,
      timestamp: new Date().toLocaleTimeString()
    }));
    
    // 重置游戏
    const resetGame = () => {
      toggleGame(false);
      toggleSound(true);
      toggleDebug(false);
      console.log('游戏已重置!');
    };
    
    // 监听游戏状态变化
    watch(isGameRunning, (running) => {
      if (running) {
        console.log('游戏开始!🎮');
        // 启动游戏循环
        startGameLoop();
      } else {
        console.log('游戏暂停!⏸️');
        // 停止游戏循环
        stopGameLoop();
      }
    });
    
    // 监听音效状态变化
    watch(isSoundEnabled, (enabled) => {
      if (enabled) {
        console.log('音效已开启!🔊');
        // 启用音效系统
      } else {
        console.log('音效已关闭!🔇');
        // 禁用音效系统
      }
    });
    
    // 监听调试模式变化
    watch(isDebugMode, (debug) => {
      if (debug) {
        console.log('调试模式已开启!🐛');
        // 显示调试信息
      } else {
        console.log('调试模式已关闭!');
        // 隐藏调试信息
      }
    });
    
    // 游戏循环控制
    let gameLoopId = null;
    
    const startGameLoop = () => {
      if (gameLoopId) return;
      
      gameLoopId = setInterval(() => {
        // 游戏逻辑更新
        if (isDebugMode.value) {
          console.log('游戏循环更新...');
        }
      }, 16); // 60 FPS
    };
    
    const stopGameLoop = () => {
      if (gameLoopId) {
        clearInterval(gameLoopId);
        gameLoopId = null;
      }
    };
    
    return {
      isGameRunning,
      isSoundEnabled,
      isDebugMode,
      debugInfo,
      toggleGame,
      toggleSound,
      toggleDebug,
      resetGame
    };
  }
};

API 参考 📚

类型定义

ts
function useToggle(
  defaultValue?: boolean,
): [Ref<boolean>, (newValue?: boolean) => void];

参数

参数说明类型默认值
defaultValue初始状态值booleanfalse

返回值

参数说明类型
state当前的布尔状态值Ref<boolean>
toggle切换状态的函数,可传入具体值或不传参自动切换(newValue?: boolean) => void

实际应用场景 🎯

1. 界面控制

  • 模态框管理: 控制弹窗、抽屉、对话框的显示隐藏
  • 折叠面板: 管理手风琴组件的展开收起状态
  • 侧边栏: 控制导航栏的显示隐藏

2. 功能开关

  • 主题切换: 深色模式与浅色模式的切换
  • 功能启用: 控制应用功能的开启关闭
  • 设置管理: 用户偏好设置的状态管理

3. 游戏开发

  • 游戏状态: 暂停/继续、音效开关、调试模式
  • 玩家状态: 生命值、技能状态、装备状态
  • 关卡控制: 关卡解锁、完成状态

4. 表单交互

  • 复选框: 单个选项的选中状态
  • 开关按钮: 设置项的启用禁用
  • 条件显示: 根据选择显示不同内容

最佳实践 💡

1. 语义化命名

js
// ✅ 推荐:使用有意义的变量名
const [isModalVisible, toggleModal] = useToggle(false);
const [isDarkMode, toggleDarkMode] = useToggle(false);

// ❌ 避免:使用无意义的变量名
const [flag, setFlag] = useToggle(false);

2. 合理设置默认值

js
// ✅ 推荐:根据业务需求设置合理的默认值
const [isNotificationEnabled, toggleNotification] = useToggle(true);  // 通知默认开启
const [isDebugMode, toggleDebug] = useToggle(false);  // 调试模式默认关闭

3. 状态变化监听

js
// ✅ 推荐:监听状态变化执行副作用
watch(isDarkMode, (newValue) => {
  document.documentElement.classList.toggle('dark', newValue);
  localStorage.setItem('darkMode', String(newValue));
});

4. 组合使用

js
// ✅ 推荐:多个相关状态的组合管理
const [isLoading, toggleLoading] = useToggle(false);
const [isError, toggleError] = useToggle(false);
const [isSuccess, toggleSuccess] = useToggle(false);

const resetStatus = () => {
  toggleLoading(false);
  toggleError(false);
  toggleSuccess(false);
};

调试技巧 🔧

1. 状态变化日志

js
// 添加状态变化日志
const [isVisible, toggleVisible] = useToggle(false);

watch(isVisible, (newValue, oldValue) => {
  console.log(`状态变化: ${oldValue} -> ${newValue}`);
}, { immediate: true });

2. 开发工具集成

js
// 在开发环境中添加调试信息
if (process.env.NODE_ENV === 'development') {
  window.debugToggles = {
    isModalVisible,
    isDarkMode,
    isDebugMode
  };
}

3. 状态持久化

js
// 将状态保存到 localStorage
const [isDarkMode, toggleDarkMode] = useToggle(
  JSON.parse(localStorage.getItem('darkMode') || 'false')
);

watch(isDarkMode, (newValue) => {
  localStorage.setItem('darkMode', JSON.stringify(newValue));
});

性能优化 ⚡

1. 避免不必要的重新渲染

js
// ✅ 推荐:使用 computed 计算衍生状态
const statusText = computed(() => 
  isLoading.value ? '加载中...' : '加载完成'
);

2. 批量状态更新

js
// ✅ 推荐:使用 nextTick 批量更新
import { nextTick } from 'vue';

const resetAllStates = async () => {
  toggleLoading(false);
  toggleError(false);
  toggleSuccess(false);
  
  await nextTick();
  console.log('所有状态已重置');
};

浏览器兼容性 🌐

useToggle 基于 Vue 3 的响应式系统,支持所有 Vue 3 兼容的环境:

  • Chrome 60+
  • Firefox 60+
  • Safari 12+
  • Edge 79+

相关文档 📖

核心概念

相关 Hooks

实际应用

设计模式

进阶主题

基於Vant構建的企業級移動端解決方案