Skip to main content

Command Palette

Search for a command to run...

VueUse Learn —— state

Published
4 min read
T

A simple developer.

State 状态相关hook

1. createGlobalState 可跨实例复用的全局状态

Type


// 接收一个state构造函数(一般都是接收一个() => useStorage())
export declare function createGlobalState<T>(
  stateFactory: () => T
): CreateGlobalStateReturn<T> // 返回stateFactory函数的执行结果 T

Usage

// Store.ts
import { createGlobalState, useStorage } from '@vueuse/core'
import { ref } from 'vue'

export const useGlobalState = createGlobalState(
    () => ref(1)
)

// component1.vue
import { useGlobalState } from 'Store'

const state1 = useGlobalState()

window.s1 = state1

// component2.vue
import { useGlobalState } from 'Store'

const state2 = useGlobalState()

window.s2 = state2


// 说明:此时state1的变化也会相应到state2中,即该状态为全局响应性的指向同一个实例(window.s1 === window.s2)
window.s1.value = 2

window.s2.value // ==> 2

2. createInjectionState 创建可注入管理的全局状态

  1. 在父级provide注入后,子级中的所有对应useState同步实例

  2. 组件只有在其父级(父/祖父/...)调用了provide注入后,才能狗调用useState获取实例

Type

export declare function createInjectionState<
  Arguments extends Array<any>,
  Return
>(
  composable: (...args: Arguments) => Return
): readonly [
  useProvidingState: (...args: Arguments) => void, // 注入函数,在父级调用后即调用输入参数 composable 构建状态实例,后续子节点内useInjectedState公用该实例
  useInjectedState: () => Return | undefined
]

Usage

// useCounterStore.ts
import { computed, ref } from 'vue-demi'
import { createInjectionState } from '@vueuse/shared'

const [useProvideCounterStore, useCounterStore] = createInjectionState((initialValue: number) => {
  // state
  const count = ref(initialValue)

  // getters
  const double = computed(() => count.value * 2)

  // actions
  function increment() {
    count.value++
  }

  return { count, double, increment }
})

export {
    useProvideCounterStore,
    useCounterStore,
}

// RootComponent.vue
<script>
import { useProvideCounterStore } from 'useCounterStore'
import child1 from 'child1'
import child2 from 'child2'

useProvideCounterStore(20)  
</script>

<template>
    <div>
        <child1 />
        <child2 />
    </div>
</template>

// child1.vue
<script>
import { useCounterStore } from 'useCounterStore'

const { count, double, increment } = useCounterStore()
</script>
<template>
    count: {{ count }}
    double: {{ double }}
    <button @click="increment">+</button>
</template>

// child2.vue
<script>
import { useCounterStore } from 'useCounterStore'

const { count, double, increment } = useCounterStore()
</script>
<template>
    count: {{ count }}
    double: {{ double }}
    <button @click="increment">+</button>
</template>

// 说明:根本上child1的实例 (count, double) 与child2的为同一个,child1进行 + ,与child2进行 + 都会影响到对方

// 而在此创建RootComponent,进行了新的Provide,两个Root之间内部的useCountState是隔离的互不影响(分别进行了各自的provide生成两份不同实例)

3. createSharedComposable 与 createGlobalState 类似,但其接收参数为一个composable function,返回一个共享的该composable function

这里主要比直接用 composable fn 更优点在于,调用createSharedComposable后,我们在多处使用的 composable fn 仅会被实例化一次,可以节约不必要的空间开销,如下面usage,多处使用useMouse,如果没有createSharedComposable,则会创建多个useMouse实例,多次对document.body进行鼠标移动事件监听,造成不必要的性能消耗。

Type

export declare function createSharedComposable<
  Fn extends (...args: any[]) => any
>(composable: Fn): Fn // 返回传入的 Fn 实例本身(这点与createGlobalState不同)

Usage

// Store
import { createSharedComposable, useMouse } from '@vueuse/core'

export const useSharedMouse = createSharedComposable(useMouse)

// component1
import { useSharedMouse } from 'Store'

const mouse1 = useSharedMouse()

// component2
import { useSharedMouse } from 'Store'

const mouse2 = useSharedMouse()

// 说明:上面介绍中说了,该hook最大优势即在于针对多处调用的相通的composable fn,共享其实例,节约性能。故此下面等式成立

mouse1 === mouse2 // true

4. useAsyncState 异步响应式状态,其主要功能为,将在promise完成后自动将promise结果赋给reactiveState

Type


// 返回值:响应性状态,是否准备好可以执行Promise(与isLoading互反),是否正在Promise Pending,Promise错误时信息,execute重新执行该Promise(类似刷新一下数据之类的)
export interface UseAsyncStateReturn<Data, Shallow extends boolean> {
  state: Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>
  isReady: Ref<boolean>
  isLoading: Ref<boolean>
  error: Ref<unknown>
  execute: (delay?: number, ...args: any[]) => Promise<Data>
}

// 接收参数(可选)
export interface UseAsyncStateOptions<Shallow extends boolean> {
  /**
   * execute Promise的延迟时间,毫秒
   *
   * @default 0
   */
  delay?: number
  /**
   * 在useAsyncState hook调用后立即执行Promise(默认即为true)
   * 也会应用上面设置的delay
   *
   * @default true
   */
  immediate?: boolean
  /**
   * Callback when error is caught.
   */
  onError?: (e: unknown) => void
  /**
   * 在每次 execute Promise 之前,将state重置为我们传入的 initialState
   *
   * 这在多次调用execute函数(例如,刷新数据)时非常有用。设置为false时,当前状态保持不变,直到promise解析为止。
   *
   * @default true
   */
  resetOnExecute?: boolean
  /**
   * Use shallowRef.
   *
   * @default true
   */
  shallow?: Shallow
  /**
   *
   * 将execute Promise的错误直接抛出,内部不做处理
   *
   * @default false
   */
  throwError?: boolean
}

export declare function useAsyncState<Data, Shallow extends boolean = true>(
  promise: Promise<Data> | ((...args: any[]) => Promise<Data>),
  initialState: Data,
  options?: UseAsyncStateOptions<Shallow>
): UseAsyncStateReturn<Data, Shallow>

Usage

const { state, isReady, isLoading, error, execute } = useAsyncState(
  axios
    .get('https://jsonplaceholder.typicode.com/todos/1')
    .then(t => t.data),
  { id: null },
)

5. useRefHistory 记录Ref响应式数据的变化历史,提供 【撤销】【重做】功能

  1. Deep Track:在跟踪Object/Array等复杂类型时,由于修改属性并不会更改引用,无法直接触发历史提交,这里我们需要传入 { deep: true } 令其深入跟踪 useRefHistory(state, { deep: true })

  2. 可自定义克隆函数:useRefHistory内部使用的 (x) => JSON.parse(JSON.stringify(x)) 来对传入的state进行克隆,这样“直接”的处理会使得对如 Set/Map/Date 等高级类型失真(降级为简单的Object/字符串等)。故此提供了配置项 dump,可让我们自定义一个克隆函数 useRefHistory(state, { dump: _.cloneDeep }) (引入lodash的克隆函数作为useRefHistory的克隆函数)

  3. Custom parse and serialization:如果不想使用自定义克隆函数,对数据直接进行克隆的话(history原样照搬传入的targetState),我们还可以自定义 序列化 & 解析 函数,比如,在track一个对象时,我们只希望history存储其 String 化的对象而不是对象本身。使用 dump 设置序列化函数,parse 设置解析函数 useRefHistory(state, { dump: JSON.stringify, parse: JSON.parse })

  4. 历史记录容量:默认情况下,我们将保留所有历史记录(无数量限制),直到明确清除它们,可以通过容量选项 capacity 设置要保留的最大历史记录量。useRefHistory(target, { capacity: 15 })

  5. History Flush Timing:与响应式系统相同,

Type


Usage

import { ref } from 'vue'
import { useRefHistory } from '@vueuse/core'

const counter = ref(0)
const { history, undo, redo } = useRefHistory(counter)

6. useDebouncedRefHistory 记录Ref响应式数据的变化历史,with Debounce 防抖

在防抖时间内的多次update,仅记录最后一次update历史

Type

export declare function useDebouncedRefHistory<Raw, Serialized = Raw>(
  source: Ref<Raw>,
  options?: Omit<UseRefHistoryOptions<Raw, Serialized>, "eventFilter"> & {
    debounce?: MaybeComputedRef<number>
  }
): UseRefHistoryReturn<Raw, Serialized>

Usage

import { ref } from 'vue'
import { useDebouncedRefHistory } from '@vueuse/core'

const counter = ref(0)
const { history, undo, redo } = useDebouncedRefHistory(counter, { deep: true, debounce: 1000 })

More from this blog

Git 无法正常访问问题解决 —— DNS无法解析git地址

在国内网络经常会无法访问github,一般呈现错误为: github.com 打不开 git 操作 clone/push/pull/fetch 超时等 这普遍由于国内DNS解析github相关域名的问题,国内DNS解析污染会导致,无法解析github相关域名,使得我们无法获悉正确ip而无法访问。 偏方一剂:修改Host,固定绑定 domain - ip 查询相关域名的解析 IPAddress github.com github.global.ssl.fastly.net asse...

Sep 22, 20221 min read

Vue SSR实战小练

思想 开发环境: 在webpack dev与pro的前端打包构建基础上, 添加webpack server compiler的服务,其为单独创建的node服务,用于渲染html代码并返回给客户端。(其他JS...则仍旧交由webpack dev server来构建) 所以在获取html后要再自行将dev客户端渲染的js加入到html中 在生产环境则不需如此: 由dev与server打包好的文件,将其组合 客户端则访问node服务来获取文件 基本用法 安装:npm install v...

Sep 5, 20229 min read

Jsonp 跨域解决方案解析

由来背景 As we all know,浏览器同源策略会将非同源请求(跨域)抛弃,而许多时候我们可能并未将前端资源与服务端服务放在一个服务器,此时就需要一个跨域的手段了。 基于此,针对同源策略,衍生了一种跨域的方法Jsonp。 Jsonp简单讲,即利用了script标签不受浏览器同源策略影响的特性,从而利用script想服务端跨域请求的方式。 原理解析 图下图所示 封装一个Jsonp函数,Promise化 // jsonp.ts type JsonpOptions<T extends Reco...

Sep 2, 20222 min read
Jsonp 跨域解决方案解析

唯在一心

15 posts

A simple developer.