学习:VueUse (1)

useFullscreen | VueUse 中文网

VueUse简介

VueUse是一个Vue组合式函数(Composables)的集合,为Vue开发者提供了大量实用的工具函数,可以极大地提高开发效率。

  • VueUse是一个Vue组合式函数的集合
  • 提供了大量可重用的逻辑
  • 适用于Vue 2和Vue 3
  • 无需安装整个库,可以按需导入
  • 完全使用TypeScript编写,提供优秀的类型支持

安装VueUse

复制代码
# 使用npm
npm i @vueuse/core

# 使用yarn
yarn add @vueuse/core

# 使用pnpm
pnpm add @vueuse/core

使用VueUse:

javascript 复制代码
// 按需导入
import { useMouse, useLocalStorage } from '@vueuse/core'

export default {
  setup() {
    // 使用组合式函数
    const { x, y } = useMouse()
    const storage = useLocalStorage('my-key', 'default-value')
    
    return {
      x, y,
      storage
    }
  }
}

VueUse生态系统:

javascript 复制代码
@vueuse/core        # 核心库,包含最常用的组合式函数
@vueuse/components  # 组件库
@vueuse/motion      # 动画相关
@vueuse/rxjs        # RxJS集成
@vueuse/firebase    # Firebase集成
@vueuse/math        # 数学相关工具
@vueuse/sound       # 声音相关工具
@vueuse/router      # Vue Router集成
@vueuse/head        # 标题和元信息管理
@vueuse/integrations #为工具库提供集成封装器

浏览器API相关的Composables

useWindowScroll - 监听窗口滚动
javascript 复制代码
import { useWindowScroll } from '@vueuse/core'

export default {
  setup() {
    const { x, y, isScrolling, arrivedState, directions } = useWindowScroll()
    
    console.log(x.value)     // 水平滚动位置
    console.log(y.value)     // 垂直滚动位置
    console.log(isScrolling.value)  // 是否正在滚动
    console.log(arrivedState)  // { top: true, bottom: false, left: true, right: false }
    console.log(directions)  // { top: false, bottom: true, left: false, right: false }
    
    return {
      x, y, isScrolling,
      // 可以使用计算属性创建逻辑
      isAtBottom: computed(() => arrivedState.bottom)
    }
  }
}
useWindowSize - 监听窗口大小
javascript 复制代码
import { useWindowSize } from '@vueuse/core'

export default {
  setup() {
    const { width, height } = useWindowSize()
    
    console.log(width.value)  // 窗口宽度
    console.log(height.value) // 窗口高度
    
    // 响应式逻辑
    const isMobile = computed(() => width.value < 768)
    const isTablet = computed(() => width.value >= 768 && width.value < 1024)
    const isDesktop = computed(() => width.value >= 1024)
    
    return {
      width, height,
      isMobile, isTablet, isDesktop
    }
  }
}
useDark - 暗黑模式切换
javascript 复制代码
import { useDark, useToggle } from '@vueuse/core'

export default {
  setup() {
    // 使用暗黑模式
    const isDark = useDark()
    
    // 创建切换函数
    const toggleDark = useToggle(isDark)
    
    return {
      isDark,
      toggleDark
    }
  }
}
useClipboard - 剪贴板操作
javascript 复制代码
import { useClipboard } from '@vueuse/core'

export default {
  setup() {
    const source = ref('Hello VueUse!')
    const { copy, copied, isSupported } = useClipboard({ source })
    
    // 手动复制
    const copyToClipboard = (text) => {
      source.value = text
      copy()
    }
    
    return {
      source,
      copy,
      copied,
      isSupported,
      copyToClipboard
    }
  }
}
useLocalStorage - 本地存储
javascript 复制代码
import { useLocalStorage } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const storedValue = useLocalStorage('my-key', 'default-value')
    
    // 存储对象
    const user = useLocalStorage('user', {
      name: '',
      email: '',
      preferences: {
        theme: 'light',
        language: 'en'
      }
    })
    
    // 监听变化
    watch(
      () => user.value,
      (newValue) => {
        console.log('用户数据已更新:', newValue)
      },
      { deep: true }
    )
    
    return {
      storedValue,
      user
    }
  }
}
useGeolocation - 地理位置API
javascript 复制代码
import { useGeolocation } from '@vueuse/core'

export default {
  setup() {
    const { 
      coords, 
      locatedAt, 
      error, 
      pause, 
      resume, 
      isLoading 
    } = useGeolocation()
    
    // coords包含经纬度信息
    console.log(coords.value.latitude)  // 纬度
    console.log(coords.value.longitude) // 经度
    
    return {
      coords,
      locatedAt,
      error,
      isLoading,
      pause,    // 暂停位置更新
      resume    // 恢复位置更新
    }
  }
}
useFullscreen - 全屏API
javascript 复制代码
import { useFullscreen } from '@vueuse/core'

export default {
  setup() {
    const { isFullscreen, enter, exit, toggle } = useFullscreen()
    
    // 也可以指定元素
    const el = ref()
    const { isFullscreen: elFullscreen } = useFullscreen(el)
    
    return {
      isFullscreen,
      elFullscreen,
      enter,
      exit,
      toggle,
      el
    }
  }
}

状态管理相关的Composables

useStorage - 通用存储API
javascript 复制代码
import { useStorage } from '@vueuse/core'

export default {
  setup() {
    // 使用localStorage (默认)
    const localData = useStorage('local-key', { foo: 'bar' })
    
    // 使用sessionStorage
    const sessionData = useStorage('session-key', { foo: 'bar' }, sessionStorage)
    
    // 自定义存储实现
    const customStorage = {
      getItem: (key) => myStorageSystem.get(key),
      setItem: (key, value) => myStorageSystem.set(key, value)
    }
    const customData = useStorage('custom-key', { foo: 'bar' }, customStorage)
    
    return {
      localData,
      sessionData,
      customData
    }
  }
}
useTimeoutFn - 延时执行函数
javascript 复制代码
import { useTimeoutFn } from '@vueuse/core'

export default {
  setup() {
    const { 
      start, 
      stop, 
      isPending 
    } = useTimeoutFn(() => {
      console.log('3秒后执行')
    }, 3000)
    
    // 立即启动
    start()
    
    return {
      start,
      stop,
      isPending
    }
  }
}
useIntervalFn - 定时执行函数
javascript 复制代码
import { useIntervalFn } from '@vueuse/core'

export default {
  setup() {
    const count = ref(0)
    
    const { 
      pause, 
      resume, 
      isActive 
    } = useIntervalFn(() => {
      count.value++
      console.log(`计数: ${count.value}`)
    }, 1000)
    
    return {
      count,
      pause,
      resume,
      isActive
    }
  }
}
useDebounceFn - 防抖函数
javascript 复制代码
import { useDebounceFn } from '@vueuse/core'

export default {
  setup() {
    const searchQuery = ref('')
    const results = ref([])
    
    // 创建防抖函数,延迟500ms
    const debouncedSearch = useDebounceFn(() => {
      console.log('执行搜索:', searchQuery.value)
      // 模拟API请求
      fetch(`/api/search?q=${searchQuery.value}`)
        .then(res => res.json())
        .then(data => {
          results.value = data
        })
    }, 500)
    
    // 监听搜索词变化
    watch(searchQuery, () => {
      if (searchQuery.value.trim()) {
        debouncedSearch()
      }
    })
    
    return {
      searchQuery,
      results
    }
  }
}
useThrottleFn - 节流函数
javascript 复制代码
import { useThrottleFn } from '@vueuse/core'

export default {
  setup() {
    const position = ref({ x: 0, y: 0 })
    
    // 创建节流函数,每200ms最多执行一次
    const throttledMouseMove = useThrottleFn((event) => {
      position.value = { x: event.clientX, y: event.clientY }
      console.log('鼠标位置:', position.value)
    }, 200)
    
    const handleMouseMove = (event) => {
      throttledMouseMove(event)
    }
    
    onMounted(() => {
      window.addEventListener('mousemove', handleMouseMove)
    })
    
    onUnmounted(() => {
      window.removeEventListener('mousemove', handleMouseMove)
    })
    
    return {
      position
    }
  }
}

用户交互相关的Composables

useMouse - 鼠标位置追踪
javascript 复制代码
import { useMouse } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const { x, y, sourceType } = useMouse()
    
    // 相对于特定元素
    const el = ref()
    const { x: elX, y: elY } = useMouse({ target: el })
    
    return {
      x, y, sourceType,
      elX, elY,
      el
    }
  }
}
usePointer - 指针位置追踪(支持触摸)
javascript 复制代码
import { usePointer } from '@vueuse/core'

export default {
  setup() {
    const { 
      x, 
      y, 
      pressure, 
      pointerType, 
      isInside 
    } = usePointer()
    
    // 检测设备类型
    const isMouse = computed(() => pointerType.value === 'mouse')
    const isTouch = computed(() => pointerType.value === 'touch')
    const isPen = computed(() => pointerType.value === 'pen')
    
    return {
      x, y, pressure, pointerType, isInside,
      isMouse, isTouch, isPen
    }
  }
}
useKeyboard - 键盘事件监听
javascript 复制代码
import { useKeyboard } from '@vueuse/core'

export default {
  setup() {
    // 监听特定按键
    const pressed = useKeyboard('Enter')
    
    // 监听多个按键
    const ctrlPressed = useKeyboard('Control')
    const spacePressed = useKeyboard(' ')
    
    // 组合键
    const ctrlAPressed = useKeyboard('Control+a')
    const ctrlSPressed = useKeyboard('Control+s')
    
    // 处理组合键
    watch(ctrlSPressed, (newValue) => {
      if (newValue) {
        console.log('Ctrl+S 被按下,执行保存操作')
        // 保存操作
      }
    })
    
    return {
      pressed,
      ctrlPressed,
      spacePressed,
      ctrlAPressed,
      ctrlSPressed
    }
  }
}
useDraggable - 拖拽功能
javascript 复制代码
import { useDraggable } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const el = ref()
    const { x, y, style } = useDraggable(el)
    
    // 限制拖拽范围
    const container = ref()
    const { 
      x: boundedX, 
      y: boundedY, 
      style: boundedStyle 
    } = useDraggable(el, {
      containerElement: container,
      initialValue: { x: 100, y: 100 }
    })
    
    // 只允许水平拖拽
    const { 
      x: horizontalX, 
      style: horizontalStyle 
    } = useDraggable(el, {
      axis: 'x'
    })
    
    return {
      el,
      x, y, style,
      container,
      boundedX, boundedY, boundedStyle,
      horizontalX, horizontalStyle
    }
  }
}
useDropZone - 拖放区域
javascript 复制代码
import { useDropZone } from '@vueuse/core'

export default {
  setup() {
    const dropZoneRef = ref()
    const files = ref([])
    
    const { isOverDropZone } = useDropZone(dropZoneRef, {
      onDrop,
      onOver,
      onLeave,
    })
    
    function onDrop(files) {
      console.log('拖放的文件:', files)
      files.value = Array.from(files)
    }
    
    function onOver(files) {
      console.log('文件进入拖放区域:', files)
    }
    
    function onLeave(files) {
      console.log('文件离开拖放区域:', files)
    }
    
    return {
      dropZoneRef,
      files,
      isOverDropZone
    }
  }
}

动画与过渡相关的Composables

useTransition - 过渡值
javascript 复制代码
import { useTransition } from '@vueuse/core'

export default {
  setup() {
    const source = ref(0)
    const output = useTransition(source)
    
    // 更新源值,会平滑过渡到目标值
    source.value = 100
    
    // 自定义过渡
    const customOutput = useTransition(source, {
      duration: 1000,
      transition: [0.25, 0.1, 0.25, 1] // 贝塞尔曲线
    })
    
    return {
      source,
      output,
      customOutput
    }
  }
}
useTween - 补间动画
javascript 复制代码
import { useTween } from '@vueuse/core'

export default {
  setup() {
    // 从0到100的补间动画,持续1秒
    const tween = useTween(100, 1000)
    
    console.log(tween.value) // 当前值(0-100)
    
    // 使用回调
    const animated = useTween(100, 1000, {
      onUpdate: (value) => {
        console.log('当前值:', value)
      },
      onComplete: () => {
        console.log('动画完成')
      }
    })
    
    return {
      tween,
      animated
    }
  }
}
useMotion - 运动动画
javascript 复制代码
import { useMotion } from '@vueuse/core'
import { ref } from 'vue'

export default {
  setup() {
    const el = ref()
    
    const { apply, stop, instance } = useMotion(el, {
      initial: {
        opacity: 0,
        y: 100
      },
      enter: {
        opacity: 1,
        y: 0,
        transition: {
          duration: 800,
          type: 'spring'
        }
      },
      leave: {
        opacity: 0,
        y: -100,
        transition: {
          duration: 300,
          ease: 'easeIn'
        }
      }
    })
    
    // 手动应用动画
    const applyAnimation = () => {
      apply('enter')
    }
    
    return {
      el,
      applyAnimation
    }
  }
}

网络请求相关的Composables

useFetch - HTTP请求
javascript 复制代码
import { useFetch } from '@vueuse/core'

export default {
  setup() {
    // GET请求
    const { data, error, pending, execute } = useFetch('/api/users')
    
    console.log(data.value)    // 响应数据
    console.log(error.value)  // 错误信息
    console.log(pending.value) // 是否正在请求
    
    // POST请求
    const { data: postData, execute: postExecute } = useFetch('/api/users').post({
      name: 'John Doe',
      email: 'john@example.com'
    }).json()
    
    // 手动执行请求
    const manualRequest = async () => {
      await execute()
      console.log('请求完成:', data.value)
    }
    
    return {
      data,
      error,
      pending,
      execute,
      postData,
      postExecute,
      manualRequest
    }
  }
}
useAsyncState - 异步状态管理
javascript 复制代码
import { useAsyncState } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const { state, isReady, isLoading, error } = useAsyncState(
      fetch('https://api.example.com/data').then(res => res.json()),
      { id: 1, name: 'Loading...' }  // 初始状态
    )
    
    // 使用异步函数
    const fetchUserData = async (userId) => {
      const response = await fetch(`https://api.example.com/users/${userId}`)
      return response.json()
    }
    
    const { 
      state: userState, 
      execute: fetchUser, 
      refresh 
    } = useAsyncState(fetchUserData, null, {
      immediate: false  // 不立即执行
    })
    
    // 获取用户数据
    const getUser = (id) => {
      fetchUser(1001)  // 传入参数
    }
    
    return {
      state,
      isReady,
      isLoading,
      error,
      userState,
      getUser,
      refresh
    }
  }
}

时间与日期相关的Composables

useNow - 当前时间
javascript 复制代码
import { useNow } from '@vueuse/core'

export default {
  setup() {
    // 基本用法 - 每秒更新一次
    const now = useNow()
    
    // 自定义更新间隔
    const slowNow = useNow({ 
      interval: 5000  // 每5秒更新一次
    })
    
    // 计算属性
    const formattedTime = computed(() => {
      return new Intl.DateTimeFormat().format(now.value)
    })
    
    return {
      now,
      slowNow,
      formattedTime
    }
  }
}
useDateFormat - 日期格式化
javascript 复制代码
import { useDateFormat, useNow } from '@vueuse/core'

export default {
  setup() {
    const now = useNow()
    
    // 基本格式化
    const formatted = useDateFormat(now, 'YYYY-MM-DD HH:mm:ss')
    
    // 使用预设格式
    const isoFormat = useDateFormat(now, 'iso')
    const usFormat = useDateFormat(now, 'MM/DD/YYYY')
    
    // 自定义格式
    const customFormat = useDateFormat(now, 'dddd, MMMM Do YYYY')
    
    // 格式化固定日期
    const birthday = useDateFormat('1990-01-01', 'YYYY-MM-DD')
    
    return {
      formatted,
      isoFormat,
      usFormat,
      customFormat,
      birthday
    }
  }
}
useCountdown - 倒计时
javascript 复制代码
import { useCountdown } from '@vueuse/core'

export default {
  setup() {
    // 基本用法 - 从60秒开始倒计时
    const { 
      countdown, 
      isRunning, 
      start, 
      stop, 
      reset 
    } = useCountdown(60, {
      interval: 1000,  // 每秒更新一次
      onFinished: () => {
        console.log('倒计时结束')
      }
    })
    
    // 格式化显示
    const formattedTime = computed(() => {
      const minutes = Math.floor(countdown.value / 60)
      const seconds = countdown.value % 60
      return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
    })
    
    return {
      countdown,
      isRunning,
      start,
      stop,
      reset,
      formattedTime
    }
  }
}

实用工具类Composables

useToggle - 切换布尔值
javascript 复制代码
import { useToggle } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const [value, toggle] = useToggle(true)
    
    // 手动切换
    const flip = () => {
      toggle()
    }
    
    // 直接设置值
    const setTrue = () => {
      toggle(true)
    }
    
    const setFalse = () => {
      toggle(false)
    }
    
    return {
      value,
      flip,
      setTrue,
      setFalse
    }
  }
}
useArrayFind - 数组查找
javascript 复制代码
import { useArrayFind } from '@vueuse/core'

export default {
  setup() {
    const items = ref([
      { id: 1, name: 'Item 1', category: 'A' },
      { id: 2, name: 'Item 2', category: 'B' },
      { id: 3, name: 'Item 3', category: 'A' }
    ])
    
    // 查找第一个符合条件的项
    const { 
      find, 
      result: foundItem 
    } = useArrayFind(items, item => item.category === 'A')
    
    // 手动触发查找
    const findFirstItem = () => {
      find()
    }
    
    // 带默认值
    const { 
      find: findWithDefault, 
      result: foundItemWithDefault 
    } = useArrayFind(
      items, 
      item => item.category === 'C', 
      { id: -1, name: 'Not Found' }
    )
    
    return {
      items,
      foundItem,
      findFirstItem,
      foundItemWithDefault
    }
  }
}
useTitle - 页面标题管理
javascript 复制代码
import { useTitle } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const title = useTitle('My Awesome App')
    
    // 更新标题
    const updateTitle = (newTitle) => {
      title.value = newTitle
    }
    
    // 恢复原始标题
    const restoreTitle = () => {
      title.value = null
    }
    
    return {
      title,
      updateTitle,
      restoreTitle
    }
  }
}
useFavicon - 网站图标管理
javascript 复制代码
import { useFavicon } from '@vueuse/core'

export default {
  setup() {
    // 基本用法
    const favicon = useFavicon()
    
    // 设置特定图标
    const setLightFavicon = () => {
      favicon.value = '/light-favicon.ico'
    }
    
    const setDarkFavicon = () => {
      favicon.value = '/dark-favicon.ico'
    }
    
    // 使用表情符号作为图标
    const setEmojiFavicon = () => {
      favicon.value = '🚀'
    }
    
    return {
      favicon,
      setLightFavicon,
      setDarkFavicon,
      setEmojiFavicon
    }
  }
}
useToggle - 状态切换
javascript 复制代码
import { useToggle } from '@vueuse/core'

export default {
  setup() {
    // 切换布尔值
    const [isOpen, toggleOpen] = useToggle(false)
    
    // 切换多个状态
    const options = ['Option A', 'Option B', 'Option C']
    const [currentOption, toggleOption] = useToggle(options)
    
    // 自定义初始值
    const [isDark, toggleDark] = useToggle(true)
    
    return {
      isOpen,
      toggleOpen,
      currentOption,
      toggleOption,
      isDark,
      toggleDark
    }
  }
}

创建自定义Composables

VueUse的设计理念鼓励我们创建自己的可重用组合式函数。下面是一些自定义Composables的例子:

自定义API状态管理
javascript 复制代码
// composables/useApiState.js
import { ref, computed } from 'vue'
import { useAsyncState } from '@vueuse/core'

export function useApiState(apiCall, initialData = null, options = {}) {
  const {
    state: data,
    isReady,
    isLoading,
    error,
    execute,
    refresh
  } = useAsyncState(apiCall, initialData, {
    resetOnExecute: false,
    ...options
  })
  
  const hasData = computed(() => !!data.value)
  const hasError = computed(() => !!error.value)
  
  const errorMessage = computed(() => {
    return hasError.value ? error.value.message : ''
  })
  
  return {
    data,
    isReady,
    isLoading,
    error,
    errorMessage,
    hasData,
    hasError,
    execute,
    refresh
  }
}

// 使用自定义组合式函数
export default {
  setup() {
    const fetchUsers = async () => {
      const response = await fetch('/api/users')
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      return response.json()
    }
    
    const {
      data: users,
      isLoading,
      errorMessage,
      hasError,
      refresh
    } = useApiState(fetchUsers, [])
    
    return {
      users,
      isLoading,
      errorMessage,
      hasError,
      refresh
    }
  }
}
自定义验证功能
javascript 复制代码
// composables/useValidation.js
import { ref, computed } from 'vue'

export function useValidation(rules, initialValue = '') {
  const value = ref(initialValue)
  const error = ref('')
  const isDirty = ref(false)
  
  const validate = () => {
    isDirty.value = true
    error.value = ''
    
    for (const rule of rules) {
      const result = rule(value.value)
      if (result !== true) {
        error.value = result
        return false
      }
    }
    
    return true
  }
  
  const isValid = computed(() => {
    if (!isDirty.value) return true
    return !error.value
  })
  
  // 常用验证规则
  const requiredRule = (val) => !!val || '此字段为必填项'
  const emailRule = (val) => {
    const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return pattern.test(val) || '请输入有效的邮箱地址'
  }
  const minLengthRule = (min) => (val) => val.length >= min || `最少需要${min}个字符`
  
  return {
    value,
    error,
    isDirty,
    isValid,
    validate,
    rules: {
      required: requiredRule,
      email: emailRule,
      minLength: minLengthRule
    }
  }
}

// 使用自定义验证
export default {
  setup() {
    const {
      value: email,
      error: emailError,
      isDirty: emailDirty,
      isValid: emailValid,
      validate: validateEmail
    } = useValidation([
      value => !!value || '邮箱是必填项',
      value => {
        const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
        return pattern.test(value) || '请输入有效的邮箱地址'
      }
    ])
    
    const submitForm = () => {
      validateEmail()
      if (emailValid.value) {
        console.log('表单提交:', email.value)
      }
    }
    
    return {
      email,
      emailError,
      emailDirty,
      emailValid,
      submitForm
    }
  }
}

集成

VueUse最佳实践

  1. 按需导入:只导入需要的功能,减少打包体积

    复制代码
    // ✅ 推荐 - 按需导入
    import { useMouse } from '@vueuse/core'
    
    // ❌ 不推荐 - 导入整个库
    import VueUse from '@vueuse/core'
  2. 合理使用响应式:注意哪些返回值是响应式的,哪些不是

    javascript 复制代码
    const { x, y } = useMouse()  // x和y都是响应式的ref
    const source = ref('text')   // source是响应式的ref
  3. 清理资源:某些Composables需要手动清理资源

    javascript 复制代码
    const { pause, resume } = useIntervalFn(callback, 1000)
    
    onUnmounted(() => {
      pause() // 清理定时器
    })
  4. 组合使用:将多个Composables组合使用,创建更复杂的功能

    javascript 复制代码
    const { x, y } = useMouse()
    const isDark = useDark()
    
    // 根据鼠标位置和主题模式改变样式
    const cursorStyle = computed(() => ({
      left: `${x.value}px`,
      top: `${y.value}px`,
      opacity: isDark.value ? 0.8 : 0.5
    }))
  5. 错误处理:处理Composables可能抛出的错误

    javascript 复制代码
    const { data, error } = useFetch('/api/data')
    
    if (error.value) {
      console.error('请求失败:', error.value)
    }
  6. 性能优化:对于计算密集型的操作,考虑使用防抖或节流

    javascript 复制代码
    const debouncedSearch = useDebounceFn(searchAPI, 500)
相关推荐
Li.CQ2 小时前
SQL学习笔记(二)
笔记·sql·学习
Huangxy__2 小时前
指针的补充学习
学习
Smartdaili China3 小时前
掌握Java网页抓取:技术与示例完整指南
java·网络·学习·指南·网页·住宅ip·爬虫api
charlie1145141915 小时前
如何快速在 VS2026 上使用 C++ 模块 — 完整上手指南
开发语言·c++·笔记·学习·现代c++
炽烈小老头6 小时前
【每天学习一点算法 2025/12/15】环形链表
学习·算法·链表
白帽子凯哥哥8 小时前
转行网络安全学习计划与报班建议
学习·安全·web安全·网络安全·渗透测试·漏洞挖掘·网安培训
ReaF_star9 小时前
【基线】关于Debian的一些简单安全配置及验证
学习·安全·debian
理人综艺好会9 小时前
Redis学习之go-redis
redis·学习·golang