笔记 - VueUse 作者 Anthony Fu 分享可组合的 Vue
Ref & Reactive
能用 Ref 就尽量用
- Ref:
- Pros: 显示调用 + 类型检查
- Cons:
.value
- Reactive:
- Pros:自动 Unwrap
- Cons:在类型上和一般对象没有区别 + ES6解构会失去响应性(toRefs) + 使用watch需要用箭头函数包装
Ref 自动解包
接受 Ref 作为函数参数【模式】
1 2 3
| function add(a: Ref<number>, b: Ref<number>) { return computed(() => a.value + b.value) }
|
- 进阶可以同时接受传入值和Ref
1 2 3 4 5 6 7 8 9 10
| function add( a: Ref<number> | number, b: Ref<number> | number ) { return computed(() => unref(a) + unref(b)) }
const a = ref(1) const c = add(a, 5) c.value = 6
|
MaybeRef【技巧】
1 2 3 4 5 6 7 8
| type MaybeRef<T> = Ref<T> | T
function add( a: MaybeRef<number>, b: MaybeRef<number> ) { return computed(() => unref(a) + unref(b)) }
|
- 不需要管是否嵌套,
ref
和unref
都可以获得想要的值
重复使用已有 Ref【技巧】
1 2 3 4
| const foo = ref(1) const bar = ref(foo)
foo === bar
|
unref【技巧】
- 可以作为重置ref值时使用。∵ 不需要管是否isRef
请求可以写成“同步”形式【技巧】
1 2 3
| const { data } = useFetch(url).json()
const user_url = computed(() => data.value?.user_url)
|
副作用自动清除【模式】
- Vue 原生的
watch
和 computed
API 会在组件销毁时自动解除内部的监听。
- 设计函数时,需要遵循同样的模式
1 2 3 4 5 6 7
| export function unseEventLister(target: EventTarget, name: string, fn: any) { target.addEventListener(name, fn)
onUnmounted(() => { target.removeEventListener(name, fn) }) }
|
类型安全的 Provide / Inject【核心】
- inject时会丢掉上下文共享类型。可以使用
InjectionKey
实现类型安全检查
1 2 3 4 5 6 7 8 9
| import { InjectionKey } from 'vue'
export interface UserInfo { id: number name: string }
export const User: InjectionKey<UserInfo> = Symbol()
|
1 2 3 4 5 6 7 8 9 10
| import { User } from './context' export default { setup() { provide(User, { id: 8, name: 'Wayne' }) } }
|
1 2 3 4 5 6 7 8 9 10 11
| import { User } from './context' export default { setup() { const user = inject(User)
if (user) { console.log(user.name); } } }
|
状态共享【模式】
SPA方案(不兼容SSR)
1 2 3 4 5
| export const state = reactive({ id: 1 name: 'wayne' })
|
1 2 3
| import { state } from './shared' state.id += 1
|
1 2 3
| import { state } from './shared' console.log(state.id);
|
兼容 SSR 的状态共享
- 使用
provide
和 inject
来共享应用层面的状态
- Vue Router v4也是类似的方式:
createRouter()
和 useRouter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export const myStateKey: InjectionKey<MyState> = Symbol()
export function createMyState() { const state = { }
return { install(app: App) { app.provide(myStateKey, state) } } }
export function useMyState(): MyState { return inject(myStateKey)! }
|
1 2 3 4
| const App = create(App)
app.use(createMyState())
|
1 2 3 4
|
const state = useMyState()
|
useVModal【技巧】
1 2 3 4 5 6 7 8 9 10 11 12
| export function useVModal(props, name) { const emit = getCurrentInstance().emit
return computed({ get() { return props[name] }, set(v) { emit(`update:${name}`, v) } }) }
|
1 2 3 4 5 6 7 8 9
| export default defineComponent ({ setup(props) { const value = useVModal(props, 'value')
return { value } } })
|
1 2 3
| <template> <input v-model="value" /> </template>
|