无论是 Vue/React/Svelte/Angular/Solid.js 都有对应的状态管理工具
Pinia
@vue/reactivity / Vue2.x / MobX
import { reactive, ref, effect } from '@vue/reactivity' const state = reactive({ account: '', id: '', }) const show = ref(false) // 会立即执行一次 effect(() => { console.log(show.value) // false })
import { reactive, ref, effect } from '@vue/reactivity' const state = reactive({ account: '', id: '', }) const show = ref(false) // 会立即执行一次 effect(() => { console.log(show.value) // false })
@vue/reactivity 是一个框架无关(framework agnostic)的包,只包含响应式的核心。与此类似的还有流式数据管理 Rxjs/Xstream;他们都可以在任何应用中使用。
部分代码参考 reactivue
import { useEffect, useState } from 'react' import { ref, effect } from '@vue/reactivity' function App() { const [count, setCount] = useState(0) const _count = ref(0) useEffect(() => { const stop = effect(() => { setCount(_count) return () => stop() }) }, []) return ( <div> <span>count is: {count}</span> <button type="button" onClick={() => _count.value++}> + </button> </div> ) }
import { useEffect, useState } from 'react' import { ref, effect } from '@vue/reactivity' function App() { const [count, setCount] = useState(0) const _count = ref(0) useEffect(() => { const stop = effect(() => { setCount(_count) return () => stop() }) }, []) return ( <div> <span>count is: {count}</span> <button type="button" onClick={() => _count.value++}> + </button> </div> ) }
import { useEffect, useState } from 'react' import { ref, effect } from '@vue/reactivity' const _count = ref(0) function App() { const [count, setCount] = useState(0) useEffect(() => { const stop = effect(() => { setCount(_count) return () => stop() }) }, []) return ( <div> <span>count is: {count}</span> <button type="button" onClick={() => _count.value++}> + </button> </div> ) }
import { useEffect, useState } from 'react' import { ref, effect } from '@vue/reactivity' const _count = ref(0) function App() { const [count, setCount] = useState(0) useEffect(() => { const stop = effect(() => { setCount(_count) return () => stop() }) }, []) return ( <div> <span>count is: {count}</span> <button type="button" onClick={() => _count.value++}> + </button> </div> ) }
封装一个简单 store
import { ref, Ref, effect } from '@vue/reactivity' import { useState } from 'react' export const useAtom = <T>(config: T ) => ref(config) export const useAtomValue = <T>(atom: ReturnType<typeof useAtom>, select?: (value: T) => T) => { const [_atom, setAtom] = useState<T>(atom.value) effect(() => { setAtom(atom.value) }) return select ? select(_atom) : _atom } export const useSetAtom = <T>(atom: ReturnType<typeof useAtom<T>>) => { return (value: T) => atom.value = value }
import { ref, Ref, effect } from '@vue/reactivity' import { useState } from 'react' export const useAtom = <T>(config: T ) => ref(config) export const useAtomValue = <T>(atom: ReturnType<typeof useAtom>, select?: (value: T) => T) => { const [_atom, setAtom] = useState<T>(atom.value) effect(() => { setAtom(atom.value) }) return select ? select(_atom) : _atom } export const useSetAtom = <T>(atom: ReturnType<typeof useAtom<T>>) => { return (value: T) => atom.value = value }
使用 watch
const watch = (source: unknown, fn: (value?: unknown) => void) => { const stop = effect(() => fn(source), { lazy: true, }) return () => stop() }
const watch = (source: unknown, fn: (value?: unknown) => void) => { const stop = effect(() => fn(source), { lazy: true, }) return () => stop() }
硬编码期
import { onBeforeUnmount } from 'vue' export default { setup() { window!.jsBridge!.onPullDown = () => { // do something fetch('www.baidu.com').then(res => { res.json().then(data => console.log(data)) }) } onBeforeUnmount(() => { window!.jsBridge!.onPullDown = null }) } }
import { onBeforeUnmount } from 'vue' export default { setup() { window!.jsBridge!.onPullDown = () => { // do something fetch('www.baidu.com').then(res => { res.json().then(data => console.log(data)) }) } onBeforeUnmount(() => { window!.jsBridge!.onPullDown = null }) } }
流式调用
import xs, { listener } from 'xstream' const pullDownProducer = { start: (listener: Listener<undefined>) => { window!.jsBridge!.onPullDown = () => { listener.next(undefined) } }, stop: () => {}, } export const pullDownStream = xs.create(pullDownProducer)
import xs, { listener } from 'xstream' const pullDownProducer = { start: (listener: Listener<undefined>) => { window!.jsBridge!.onPullDown = () => { listener.next(undefined) } }, stop: () => {}, } export const pullDownStream = xs.create(pullDownProducer)
import { onBeforeUnmount } from 'vue' import { pullDownStream } from './streams' import { fetchApi } from 'api' export default { setup() { const sub = pullDownStream.subscribe(fetchApi) onBeforeUnmount(() => { sub.unsubscribe() }) } }
import { onBeforeUnmount } from 'vue' import { pullDownStream } from './streams' import { fetchApi } from 'api' export default { setup() { const sub = pullDownStream.subscribe(fetchApi) onBeforeUnmount(() => { sub.unsubscribe() }) } }
响应式管理
import { ref, watch, onBeforeUnmount } from 'vue' const pullDownStatus = ref(false) const window!.jsBridge!.onPullDown = () => { pullDownStatus.value = true Promise.resolve().then(() => { pullDownStatus.value = false }) } export const usePullDown = (cb) => { const stop = watch(pullDownStatus, (val) => { val && cb() }) return () => stop() }
import { ref, watch, onBeforeUnmount } from 'vue' const pullDownStatus = ref(false) const window!.jsBridge!.onPullDown = () => { pullDownStatus.value = true Promise.resolve().then(() => { pullDownStatus.value = false }) } export const usePullDown = (cb) => { const stop = watch(pullDownStatus, (val) => { val && cb() }) return () => stop() }
import { onBeforeUnmount } from 'vue' import { usePullDown } from './usePullDown' import { fetchApi } from 'api' export default { setup() { const stop = usePullDown(fetchApi) return { stop, } } }
import { onBeforeUnmount } from 'vue' import { usePullDown } from './usePullDown' import { fetchApi } from 'api' export default { setup() { const stop = usePullDown(fetchApi) return { stop, } } }
抄一抄 SWR
import { reactive, toRefs } from '@vue/reactivity' const useFetch = <D = {}, Error = any>(url: string, fetcher?: (url: string) => Promise<D>) => { const stateRef = reactive<{ data: undefined | D, error: undefined | Error, loading: boolean, }>({ data: undefined, error: undefined, loading: false, }) const startFetch = async () => { // } startFetch() return toRefs(stateRef) }
import { reactive, toRefs } from '@vue/reactivity' const useFetch = <D = {}, Error = any>(url: string, fetcher?: (url: string) => Promise<D>) => { const stateRef = reactive<{ data: undefined | D, error: undefined | Error, loading: boolean, }>({ data: undefined, error: undefined, loading: false, }) const startFetch = async () => { // } startFetch() return toRefs(stateRef) }
const startFetch = async () => { try { if (fetcher) { stateRef.loading = true stateRef.data = await fetcher(url) stateRef.loading = false } else { stateRef.loading = true const res = await fetch(key) stateRef.data = await res.json() stateRef.loading = false } } catch (error) { stateRef.error = error } }
const startFetch = async () => { try { if (fetcher) { stateRef.loading = true stateRef.data = await fetcher(url) stateRef.loading = false } else { stateRef.loading = true const res = await fetch(key) stateRef.data = await res.json() stateRef.loading = false } } catch (error) { stateRef.error = error } }
<!-- 使用 --> <script lang="ts" setup> const { data } = useFetch('/api/test') </script>
<!-- 使用 --> <script lang="ts" setup> const { data } = useFetch('/api/test') </script>