vue2/vue3 Composition Api 常用工具集, @vueuse/core 之核心函数 state 实践
VueUse是一款基于组合式API的函数集合。
VueUse不是Vue.use,它是为Vue 2和3服务的一套Vue Composition API的常用工具集,是目前世界上Star最高的同类型库之一。它的初衷就是将一切原本并不支持响应式的JS API变得支持响应式,省去程序员自己写相关代码。
createGlobalState
将状态保存全局作用域中,以便跨Vue实例复用。
该状态存储在内存中,刷新页面就丢失,如果希望刷新页面存在,可以与localstorage 一起用。
// stores.ts
import { ref, computed } from 'vue'
import { createGlobalState } from '@vueuse/core'
const useGlobalState = createGlobalState(
() => {
// state
const count = ref(0)
// getters
const doubleCount = computed(() => count.value * 2)
// actions
function increment() {
count.value++
}
function decrement() {
count.value--
}
return {
count,
doubleCount,
increment,
decrement
}
}
)
export default useGlobalState
// Index.vue
<script lang="ts" setup>
import useGlobalState from './store.js'
import Controls from './Controls.vue'
const { count, doubleCount} = useGlobalState()
</script>
<template>
<div class="count">
{{ count }}
double: {{ doubleCount }}
<Controls></Controls>
</div>
</template>
// Controls.vue
<script lang="ts" setup>
import useGlobalState from './store.js'
const { increment, decrement } = useGlobalState()
</script>
<template>
<div class="btn">
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
createInjectionState
创建可以注入到组件中的全局状态。
// useCounterStore.ts
import { computed, ref } from 'vue'
import { createInjectionState } from '@vueuse/core'
const [useProvideCounterStore, useCounterStore] = createInjectionState((initialValue: number) => {
// state
const count = ref(initialValue)
// getters
const double= computed(() => count.value * 2)
// actions
const increment = () => count.value++
const decrement = () => count.value--
return {
count,
double,
increment,
decrement,
}
})
export { useProvideCounterStore, useCounterStore }
// Index.vue
<script setup lang="ts">
import Root from './Root.vue'
import Counter from './Counter.vue'
import Controls from './Controls.vue'
</script>
<template>
<div>
<Root>
<Counter />
<Controls />
</Root>
</div>
</template>
// Root.vue
<script setup lang="ts">
import { useProvideCounterStore } from './useCounterStore'
useProvideCounterStore(0)
</script>
<template>
<div>
<slot />
</div>
</template>
// Counter.vue
<script lang="ts" setup>
import { useCounterStore } from './useCounterStore'
const { count, double } = useCounterStore()!
</script>
<template>
<ul>
<li>
count: {{ count }}
</li>
<li>
double: {{ double }}
</li>
</ul>
</template>
// Controls.vue
<script setup lang="ts">
import { useCounterStore } from './useCounterStore'
const { increment, decrement } = useCounterStore()!
</script>
<template>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</template>
createSharedComposable
让一个钩子函数可用于多个Vue实例中。
// useSharedMouse.ts
import { createSharedComposable, useMouse } from '@vueuse/core'
const useSharedMouse = createSharedComposable(useMouse)
export default useSharedMouse
// Index.vue
<script lang="ts" setup>
import CompA from './CompA.vue'
import CompB from './ComB.vue'
</script>
<template>
<div>
<CompA />
<CompB />
</div>
</template>
// CompA.vue
<script lang="ts" setup>
import useSharedMouse from './useSharedMouse'
const { x, y } = useSharedMouse()
</script>
<template>
<div>
ComA组件
x:{{ x }},
y:{{ y }}
</div>
</template>
// CompB.vue
<script lang="ts" setup>
import useSharedMouse from './useSharedMouse'
const { x, y } = useSharedMouse()
</script>
<template>
<div>
ComB组件
x:{{ x }},
y:{{ y }}
</div>
</template>
useAsyncState
响应式获取异步状态。不会阻塞setup 函数,在promise完成后,将自动触发。
<script lang="ts" setup>
import { onMounted, reactive } from 'vue'
import { useAsyncState } from '@vueuse/core'
const ajaxData = reactive({
state: '',
isReady: false,
isLoading: false,
})
const fetchUser = async(id: number) => {
const resp = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
return resp.json()
}
const loadData = () => {
ajaxData.state = ''
ajaxData.isLoading = true
ajaxData.isReady = false
const { execute }= useAsyncState(fetchUser, null, {
immediate: false,
delay: 2000,
onSuccess: (data) => {
ajaxData.state = data
ajaxData.isLoading = false
ajaxData.isReady = true
}
})
execute(2000, 1)
}
onMounted(() => {
loadData()
})
</script>
<template>
<div class="use-async-state">
Ready: {{ ajaxData.isReady }}
Loading: {{ ajaxData.isLoading }}
State: {{ ajaxData.state }}
<br />
<button @click="loadData">加载数据</button>
</div>
</template>
useRefHistory
跟踪 ref 的更改历史,提供撤消和重做功能
<script lang="ts" setup>
import { ref } from 'vue'
import { useRefHistory } from '@vueuse/core'
const count = ref(0)
const { history, undo, redo } = useRefHistory(count)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
</script>
<template>
<div class="use-ref-history">
count: {{ count }} <br />
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="undo">undo</button>
<button @click="redo">redo</button>
<ul>
<li v-for="item in history" :key="item.timestamp">
{{ item.timestamp }}: {{ item.snapshot }}
</li>
</ul>
{{ history }}
</div>
</template>
useDebouncedRefHistory
useRefHistory
的简写,带有防抖过滤器。
<script lang="ts" setup>
import { ref } from 'vue'
import { useDebouncedRefHistory } from '@vueuse/core'
const count = ref(0)
const { history, undo, redo } = useDebouncedRefHistory(count, {
deep: true,
debounce: 100
})
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
</script>
<template>
<div class="use-debounced-ref-history">
count: {{ count }} <br />
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="undo">undo</button>
<button @click="redo">redo</button>
<ul>
<li v-for="item in history" :key="item.timestamp">
{{ item.timestamp }}: {{ item.snapshot }}
</li>
</ul>
{{ history }}
</div>
</template>
useThrottledRefHistory
带节流过滤器的 useRefHistory
的简写。
<script lang="ts" setup>
import { ref } from 'vue'
import { useDebouncedRefHistory } from '@vueuse/core'
const count = ref(0)
const { history, undo, redo } = useThrottledRefHistory(count, {
deep: true,
debounce: 100
})
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
</script>
<template>
<div class="use-throttled-ref-history">
count: {{ count }} <br />
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="undo">undo</button>
<button @click="redo">redo</button>
<ul>
<li v-for="item in history" :key="item.timestamp">
{{ item.timestamp }}: {{ item.snapshot }}
</li>
</ul>
{{ history }}
</div>
</template>
useManualRefHistory
调用 commit()
时,手动跟踪ref的更改历史,提供撤消和重做功能
必须调用了commit 方法才会产生记录
<script lang="ts" setup>
import { ref } from 'vue'
import { useManualRefHistory } from '@vueuse/core'
const count = ref(0)
const { history, commit, undo, redo } = useManualRefHistory(count)
const increment = () => {
count.value++
commit()
}
const decrement = () => {
count.value--
commit()
}
</script>
<template>
<div class="use-manual-ref-history">
count: {{ count }} <br />
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="undo">undo</button>
<button @click="redo">redo</button>
<ul>
<li v-for="item in history" :key="item.timestamp">
{{ item.timestamp }}: {{ item.snapshot }}
</li>
</ul>
{{ history }}
</div>
</template>
useLastChanged
记录最后一次更改的时间戳
<script lang="ts" setup>
import { ref } from 'vue'
import { useLastChanged } from '@vueuse/core'
const input = ref(0)
const lastChanged = useLastChanged(input)
</script>
<template>
<div class="use-last-changed">
<input v-model="input" /> <br />
lastChanged: {{ lastChanged }}
</div>
</template>
useStorage
响应式的 LocalStorage/SessionStorage
<script lang="ts" setup>
import { useStorage } from '@vueuse/core'
const state = useStorage('my-store', 'hello @vueuse/core')
const edit = () => {
state.value = 'edit store'
}
const clear = () => {
state.value = null
}
</script>
<template>
<div class="use-storage">
storage: {{ state }} <br />
<button @click="edit">修改</button>
<button @click="clear">清除</button>
</div>
</template>
useStorage 传入第三个参数(localStorage、sessionStorage),第二个参数默认值就不起做用
localStorage.setItem('my-store', '{"a": "1", "b": "2"}')
sessionStorage.setItem('my-store', '{"a": "1", "b": "2"}')
const state = useStorage('my-store', { c: 3, d: 4, a: 10}, sessionStorage)
合并默认值
const options = {mergeDefaults: true}
const state = useStorage('my-store', { c: 3, d: 4, a: 10}, sessionStorage, options)
自定义合入规则
{
mergeDefaults: (storageValue, defaults) => deepMerge(defaults, storageValue)
}
自定义序列化
const state = useStorage('key', '123', undefined, {
serializer: {
read: (v: any) => v ? JSON.parse(v) : null,
write: (v: any) => JSON.parse(v),
}
})
useStorageAsync
支持异步的响应式Storage
useLocalStorage
响应式的 LocalStorage,方法是基于useStorage,使用方式同useStorage。
useSessionStorage
响应式的 SessionStorage,方法是基于useStorage,使用方式同useStorage。