предисловие
vue-nextЭто хранилище исходного кода Vue 3. Vue 3 использует lerna для разделения пакета и возможности реагирования@vue/reactivityделится на один пакет.
Если мы хотим интегрировать его в React, возможно ли это? Приходите попробовать.
Пример использования
Без дальнейших церемоний, давайте посмотрим, как использовать его, чтобы уменьшить тягу.
// store.ts
import { reactive, computed, effect } from '@vue/reactivity';
export const state = reactive({
count: 0,
});
const plusOne = computed(() => state.count + 1);
effect(() => {
console.log('plusOne changed: ', plusOne);
});
const add = () => (state.count += 1);
export const mutations = {
// mutation
add,
};
export const store = {
state,
computed: {
plusOne,
},
};
export type Store = typeof store;
// Index.tsx
import { Provider, useStore } from 'rxv'
import { mutations, store, Store } from './store.ts'
function Count() {
const countState = useStore((store: Store) => {
const { state, computed } = store;
const { count } = state;
const { plusOne } = computed;
return {
count,
plusOne,
};
});
return (
<Card hoverable style={{ marginBottom: 24 }}>
<h1>计数器</h1>
<div className="chunk">
<div className="chunk">store中的count现在是 {countState.count}</div>
<div className="chunk">computed值中的plusOne现在是 {countState.plusOne.value}</div>
<Button onClick={mutations.add}>add</Button>
</div>
</Card>
);
}
export default () => {
return (
<Provider value={store}>
<Count />
</Provider>
);
};
Как можно заметить,storeВ определении используется только@vue/reactivity,а такжеrxvПросто создайте слой моста в компоненте для соединения Vue3 и React, и тогда мы сможем в полной мере использовать адаптивные возможности Vue3.
предварительный просмотр
Видно, что мощные возможности реактивного и вычисляемого используются прекрасно.
анализировать
Анализ нескольких основных API, предоставляемых этим пакетом:
эффект (акцент)
Эффект на самом деле является общей концепцией реактивных библиотек:观察函数, как в Vue2Watcher, в мобксеautorun,observerвроде так и есть收集依赖.
Это приемлемо для функции, это поможет вам выполнить эту функцию и открыто в зависимости от коллекции,
Доступ к реагирующим данным внутри этой функции может собирать зависимости, поэтому при изменении реагирующих данных будет запущено обновление.
Простейшее использование
const data = reactive({ count: 0 })
effect(() => {
// 就是这句话 访问了data.count
// 从而收集到了依赖
console.log(data.count)
})
data.count = 1
// 控制台打印出1
Тогда, если в этом простом примере
() => {
// 就是这句话 访问了data.count
// 从而收集到了依赖
console.log(data.count)
}
Может ли эта функция, замененная рендерингом компонентов React, достичь цели оперативного обновления компонентов?
реактивный (ударение)
Основной API отзывчивых данных, этот API возвращаетproxy, доступ ко всем вышеперечисленным свойствам будет перехвачен, так что зависимости будут собираться при получении (то есть при запускеeffect), который запускает обновление при установке.
ref
Для простых типов данных, таких какnumber, мы не можем сделать это так:
let data = reactive(2)
// 😭oops
data = 5
Это не соответствует правилам адаптивного перехвата, нет возможности перехватитьdataСамо изменение можно только перехватитьdataАтрибуты тела меняются, поэтому есть ref.
const data = ref(2)
// 💕ok
data.value= 5
computed
Для вычисляемого свойства после обновления зависимого значения его значение также будет обновлено автоматически. На самом деле внутри вычисляемого тоже есть эффект.
Имеет расширенные функции, такие как наблюдение за другими вычисляемыми данными в вычисляемом и эффекты наблюдения за вычисленными изменениями.
выполнить
С точки зрения этих основных API-интерфейсов, пока эффект может быть подключен к системе React, другие API-интерфейсы в порядке, поскольку они только собирают зависимости эффекта и уведомляют эффект о запуске обновления.
Эффект принимает функцию, и эффект также поддерживает передачуscheduleПараметры для определения того, какая функция должна быть запущена при обновлении зависимости, если мы поместим этоscheduleЗаменить его обновлением соответствующего компонента? Чтобы знать, что в мире хуков очень просто принудительно обновить текущий компонент:
useForceUpdate
export const useForceUpdate = () => {
const [, forceUpdate] = useReducer(s => s + 1, 0);
return forceUpdate;
};
Это очень классический пользовательский хук, который заставляет компонент постоянно отображать состояние +1.
а такжеrxvОсновной API:useStoreтакже принимает функциюselector, это позволит пользователю выбрать данные, к которым необходимо получить доступ в компоненте.
Тогда идея очевидна:
- Пучок
selectorОбертка выполняется в эффекте для сбора зависимостей. - Когда указанная зависимость обновляется, вызывается функция
当前正在使用useStoreэтого компонентаforceUpdateФункция принудительного рендеринга.
Таким образом реализуются изменения данных и автоматически обновляются компоненты?
Простой взгляд на основную реализацию
useStore и провайдер
import React, { useContext } from 'react';
import { useForceUpdate, useEffection } from './share';
type Selector<T, S> = (store: T) => S;
const StoreContext = React.createContext<any>(null);
const useStoreContext = () => {
const contextValue = useContext(StoreContext);
if (!contextValue) {
throw new Error(
'could not find store context value; please ensure the component is wrapped in a <Provider>',
);
}
return contextValue;
};
/**
* 在组件中读取全局状态
* 需要通过传入的函数收集依赖
*/
export const useStore = <T, S>(selector: Selector<T, S>): S => {
const forceUpdate = useForceUpdate();
const store = useStoreContext();
const effection = useEffection(() => selector(store), {
scheduler: forceUpdate,
lazy: true,
});
const value = effection();
return value;
};
export const Provider = StoreContext.Provider;
Эта опция передается Vue3effectапи,
schedulerУкажите, какую операцию следует выполнить после обновления данных ответа, здесь мы используемforceUpdateдля повторного рендеринга компонента.
lazyУказывает на отложенное выполнение, и мы вручную вызываем его позжеeffectionвыполнить
{
scheduler: forceUpdate,
lazy: true,
}
посмотри сноваuseEffectionа такжеuseForceUpdate
import { useEffect, useReducer, useRef } from 'react';
import { effect, stop, ReactiveEffect } from '@vue/reactivity';
export const useEffection = (...effectArgs: Parameters<typeof effect>) => {
// 用一个ref存储effection
// effect函数只需要初始化执行一遍
const effectionRef = useRef<ReactiveEffect>();
if (!effectionRef.current) {
effectionRef.current = effect(...effectArgs);
}
// 卸载组件后取消effect
const stopEffect = () => {
stop(effectionRef.current!);
};
useEffect(() => stopEffect, []);
return effectionRef.current
};
export const useForceUpdate = () => {
const [, forceUpdate] = useReducer(s => s + 1, 0);
return forceUpdate;
};
Также очень просто, просто пропустите входящую функцию дляeffect, и остановиться, когда компонент будет уничтоженeffectВот и все.
обработать
- Сначала зарегистрируйте функцию принудительного обновления в текущем компоненте через useForceUpdate.
- Прочитайте хранилище, переданное пользователем от провайдера через useContext.
- Затем используйте эффект Vue, чтобы помочь нам выполнить селектор (хранилище), и укажите планировщик как forceUpdate, тем самым завершив сбор зависимостей.
- Затем после обновления значения в хранилище срабатывает планировщик или forceUpdate, и наш компонент React автоматически обновляется.
Всего несколько строк кода, и его можно использовать в React.@vue/reactivityвсе способности в .
преимущество:
- Непосредственно вводите @vue/reacivity, полностью используйте возможности реактивности Vue3, имеете различные возможности, такие как вычисление и эффект, а также предоставляете возможности реагирования для Set и Map. Продолжение станет более полным и мощным с обновлением этой библиотеки.
- Выполните тестовые примеры в репозитории vue-next.
- Полная поддержка типов TypeScript.
- Полностью повторно используйте @vue/reacivity для достижения сверхглобальных возможностей управления состоянием.
- Точные обновления на уровне компонентов в управлении состоянием.
- Vue3 всегда нужно изучать, учиться заранее, чтобы предотвратить безработицу!
недостаток:
- Из-за необходимости точного сбора зависимостей
useStore,такselectorФункции должны иметь доступ именно к тем данным, которые вам нужны. Даже если вам нужно инициировать обновление значения внутри массива, вы не можете просто вернуть сам массив в useStore.
Например:
function Logger() {
const logs = useStore((store: Store) => {
return store.state.logs.map((log, idx) => (
<p className="log" key={idx}>
{log}
</p>
));
});
return (
<Card hoverable>
<h1>控制台</h1>
<div className="logs">{logs}</div>
</Card>
);
}
Этот код находится непосредственно вuseStoreВесь jsx возвращается в , потому чтоmapВ процессе возвращения посетить каждый элемент массива для сбора зависимостей, единственный способ достичь цели отзывчивости.
Адрес источника
GitHub.com/forget 1673495/день…
Если вам нравится эта библиотека, добро пожаловать, чтобы поставить свою звезду✨, ваша поддержка - моя самая большая мотивация~