Лиан Пэнфэй, главный инженер группы облачных сервисов WeDoctor, понимает мир технологий с «предубеждениями».
Сцены
Сначала поймите, что такое хук.Принимая во внимание введение React, его определение таково:
Это позволяет вам «подключаться» к состоянию React и функциям жизненного цикла в функциональных компонентах без написания класса.
Для нового API Vue для написания компонентов Vue: Composition API RFC эффект аналогичен, поэтому мы также можем называть его Vue Hooks, например React.
- API вдохновлен React Hooks.
- Но есть несколько интересных отличий, которые позволяют обойти некоторые проблемы React.
Значение эпохи Крюка
Фреймворк служит бизнесу, и проблема, которую трудно избежать в бизнесе, заключается в следующем: повторное использование логики, одна и та же функция, один и тот же компонент в разных случаях, иногда нам приходится писать 2+ раза, чтобы избежать связывания, затем основные фреймворки придумали некоторые методы, такие как minix, render props, компоненты высокого порядка и т. д., для достижения логического повторного использования, но есть некоторые дополнительные проблемы.
- Существуют неявные зависимости между minix и компонентами, которые могут конфликтовать. Имеет тенденцию добавлять больше состояний, снижая предсказуемость приложения.
- Компоненты более высокого порядка оборачивают вложенные компоненты в несколько слоев, что увеличивает сложность и стоимость понимания, а также является черным ящиком для внешнего слоя.
- Реквизиты рендеринга громоздки в использовании, не просты в обслуживании, размер кода слишком велик, и их также легко вкладывать слишком глубоко.
- ...
Появление хука стало эпохальным, благодаря извлечению функций реализована внутренняя инкапсуляция сложной логики:
- повторное использование логического кода
- Уменьшенный размер кода
- не беспокойтесь об этом
React Hooks
React Hooks позволяют «подключать» функции React, такие как состояние компонента и обработка побочных эффектов. Хуки можно использовать только в функциональных компонентах, и они позволяют нам переносить состояние, обработку побочных эффектов и многое другое в компоненты без необходимости создавать классы.
Стратегия внедрения основной команды React не противоречит классовым компонентам, поэтому вы можете обновить свою версию React, начать экспериментировать с хуками в новых компонентах и оставить существующие компоненты без изменений.
пример:
import React, { useState, useEffect } from "React";
const NoteForm = ({ onNoteSent }) => {
const [currentNote, setCurrentNote] = useState("");
useEffect(() => {
console.log(`Current note: ${currentNote}`);
});
return (
<form
onSubmit={e => {
onNoteSent(currentNote);
setCurrentNote("");
e.preventDefault();
}}
>
<label>
<span>Note: </span>
<input
value={currentNote}
onChange={e => {
const val = e.target.value && e.target.value.toUpperCase()[0];
const validNotes = ["A", "B", "C", "D", "E", "F", "G"];
setCurrentNote(validNotes.includes(val) ? val : "");
}}
/>
</label>
<button type="submit">Send</button>
</form>
);
};
- useState и useEffect — некоторые примеры хуков React, которые позволяют добавлять состояние и запускать побочные эффекты в функциональные компоненты.
- Хуков гораздо больше, даже один можно настроить. Хуки открывают новые возможности для повторного использования кода и расширяемости.
Vue Composition API
Vue Composition API построен на новой настройке параметров компонента.setup()Предоставляет состояние, вычисленные значения, наблюдатели и хуки жизненного цикла для компонентов Vue.
API не приводит к исчезновению исходного API (теперь называемого «API на основе параметров»). Позволяет разработчикам комбинировать старые и новые API
Доступно в Vue 2.x через
@vue/composition-apiПлагины пробуют новые API
пример:
<template>
<form @submit="handleSubmit">
<label>
<span>Note:</span>
<input v-model="currentNote" @input="handleNoteInput">
</label>
<button type="submit">Send</button>
</form>
</template>
<script>
import { ref, watch } from "vue";
export default {
props: ["divRef"],
setup(props, context) {
const currentNote = ref("");
const handleNoteInput = e => {
const val = e.target.value && e.target.value.toUpperCase()[0];
const validNotes = ["A", "B", "C", "D", "E", "F", "G"];
currentNote.value = validNotes.includes(val) ? val : "";
};
const handleSubmit = e => {
context.emit("note-sent", currentNote.value);
currentNote.value = "";
e.preventDefault();
};
return {
currentNote,
handleNoteInput,
handleSubmit,
};
}
};
</script>
разница
принцип
Нижний слой React Hook основан на реализации связанного списка.Условие вызова состоит в том, что все хуки будут выполняться последовательно каждый раз при рендеринге компонента, поэтому следующий код сообщит об ошибке
function App(){
const [name, setName] = useState('demo');
if(condition){
const [val, setVal] = useState('');
}
}
Поскольку нижний слой представляет собой связанный список, следующий из каждого хука указывает на следующий хук, и если это приведет к неправильному порядку, что приведет к ошибке, поэтому React не позволяет использовать хук таким образом.
Vue Hook будет зарегистрирован и вызван только один раз.Vue может избежать этих неприятных проблем, потому что его ответ на данные основан на прокси, и он напрямую наблюдает за данными через прокси. В этом сценарии, если в любом месте, где данные изменяются, соответствующая функция или шаблон будут пересчитаны, что позволит избежать проблем с производительностью, с которыми может столкнуться React.
Когда данные React изменяются, это вызывает повторный рендеринг, а повторный рендеринг снова перерегистрирует хуки, поэтому начать работу с React сложнее.
Конечно, у React есть свои решения для них, такие как useCallback, useMemo и другие хуки, которые представлены на этих официальных сайтах.
Выполнение кода
В Vue «крюк» — это метод жизненного цикла.
-
Vue Composition APIизsetup()После хука beforeCreate, перед вызовом созданного хука - React Hooks будет запускаться каждый раз при рендеринге компонента, в то время как Vue setup() будет запускаться только один раз при создании компонента.
Поскольку хуки React запускаются несколько раз, метод рендеринга должен подчиняться определенным правилам, таким как:
Не вызывайте хуки внутри циклов, условий или вложенных функций.
// React 文档中的示例代码:
function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = `${name} ${surname}`;
});
// ...
}
Если вы хотите запустить соответствующий побочный эффект, даже если имя пусто, вы можете просто переместить условный оператор внутри обратного вызова useEffect:
useEffect(function persistForm() {
if (name !== '') {
localStorage.setItem('formData', name);
}
});
Для приведенной выше реализации Vue написан следующим образом:
export default {
setup() {
// 1. Use the name state variable
const name = ref("Mary");
// 2. Use a watcher for persisting the form
if(name.value !== '') {
watch(function persistForm() => {
localStorage.setItem('formData', name.value);
});
}
// 3. Use the surname state variable
const surname = ref("Poppins");
// 4. Use a watcher for updating the title
watch(function updateTitle() {
document.title = `${name.value} ${surname.value}`;
});
}
}
setup() в Vue будет запускаться только один раз, вы можете использовать различные функции в Composition API (реактивные, ref, вычисляемые, часы, хуки жизненного цикла и т. д.) как часть цикла или условного оператора.
Но оператор if, как и React Hooks, запускается только один раз, поэтому он не может реагировать на изменение имени, если мы не обернем его внутри обратного вызова watch.
watch(function persistForm() => {
if(name.value !== '') {
localStorage.setItem('formData', name.value);
}
});
Объявление состояния
React
useStateэто основной способ для React Hooks объявить состояние
- Вы можете передать начальное значение в качестве параметра вызова
- Если начальное значение сложно вычислить, его также можно выразить в виде функции, которая будет выполняться только при первом рендеринге.
useState() возвращает массив, первый элемент — это состояние, а второй элемент — функция установки
const [name, setName] = useState("Mary");
const [age, setAge] = useState(25);
console.log(`${name} is ${age} years old.`);
useReducerявляется полезной альтернативой, и ее обычная форма принимает функцию редуктора в стиле Redux и начальное состояние:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({type: 'increment'}); // state 就会变为 {count: 1}
useReducer также имеет форму ленивой инициализации, передавая функцию инициализации в качестве третьего параметра.
Vue
Vue использует две основные функции для объявления состояния: ref и reactive.
ref()Возвращает реактивный объект, к внутренним значениям которого можно получить доступ через его свойство value. Вы можете использовать его для примитивных типов, но также и для объектов
const name = ref("Mary");
const age = ref(25);
watch(() => {
console.log(`${name.value} is ${age.value} years old.`);
});
reactive()Принимает на вход только объект и возвращает ему реактивный прокси
const state = reactive({
name: "Mary",
age: 25,
});
watch(() => {
console.log(`${state.name} is ${state.age} years old.`);
});
Уведомление:
- При использовании ref вам нужно использовать свойство value для доступа к содержащемуся значению (если только в шаблоне Vue не позволяет вам его опустить)
- При использовании реактивного помните, что если вы используете деструктурирование объекта, вы потеряете его реактивность. Поэтому вам нужно определить ссылку на объект и получить доступ к свойству состояния через него.
Подведем итог обработки с использованием этих двух функций:
- Просто используйте ref и reactive, как объявление примитивных и объектных переменных в обычном JavaScript.
- Всякий раз, когда используется реактивный, не забывайте использовать toRefs() при возврате реактивных объектов из функций композиции. Это снижает накладные расходы при использовании слишком большого количества ссылок.
// toRefs() 则将反应式对象转换为普通对象,该对象上的所有属性都自动转换为 ref
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
return toRefs(state)
}
const {foo, bar} = useFeatureX();
Как отслеживать зависимости
Хук useEffect в React позволяет запускать определенные побочные эффекты (такие как запрос данных или использование веб-API, таких как хранилище) после каждого рендеринга и выполнять некоторую очистку перед выполнением следующего обратного вызова или когда компонент размонтирован.
По умолчанию все функции, зарегистрированные с помощью useEffect, будут запускаться после каждого рендеринга, но можно определить состояние и свойства реальных зависимостей, чтобы React выполнял рендеринг, когда соответствующие зависимости не изменились (например, вызванные другими частями в состоянии ). ), чтобы пропустить выполнение хука useEffect.
// 传递一个依赖项的数组作为 useEffect Hook 的第二个参数,只有当 name 改变时才会更新 localStorage
function Form() {
const [name, setName] = useState('Mary');
const [surname, setSurname] = useState('Poppins');
useEffect(function persistForm() {
localStorage.setItem('formData', name);
}, [name]);
// ...
}
По-видимому, забыв исчерпывающе объявить все зависимости в массиве зависимостей при использовании React Hooks, может легко случиться, в результате чего обратный вызов useEffect в конечном итоге «полагается и ссылается на устаревшие данные из последнего рендеринга вместо последних данных» и, таким образом, не может быть обновлен
решение:
-
eslint-plugin-React-HooksСодержит правило lint для отсутствующих зависимостей -
useCallback 和 useMemoПараметр массива зависимостей также используется для принятия решения о том, должен ли он возвращать кэшированную (запоминаемую) версию обратного вызова или значение, соответственно, такое же, как при последнем выполнении.
В случае Vue Composition API побочные эффекты могут быть выполнены с помощью watch() в ответ на изменения состояния или свойства. Зависимости отслеживаются автоматически, а зарегистрированные функции вызываются реактивно при изменении зависимостей.
export default {
setup() {
const name = ref("Mary");
const lastName = ref("Poppins");
watch(function persistForm() => {
localStorage.setItem('formData', name.value);
});
}
}
Access Component Life Cycle (Доступ к жизненному циклу компонента)
Хуки представляют собой полный сдвиг ментальной парадигмы при работе с жизненным циклом компонентов React, побочными эффектами и управлением состоянием. В документации React также говорится:
Если вы знакомы с методами жизненного цикла класса React, вы можете рассматривать useEffect Hook как набор компонентов componentDidMount, componentDidUpdate и componentWillUnmount.
useEffect(() => {
console.log("This will only run after initial render.");
return () => { console.log("This will only run when component will unmount."); };
}, []);
Подчеркивается, что при использовании React Hooks более принято не думать с позиции методов жизненного цикла, а учитывать, от какого состояния зависят побочные эффекты.
Vue Component APIпройти через onMounted、onUpdated 和 onBeforeUnmount:
setup() {
onMounted(() => {
console.log(`This will only run after initial render.`);
});
onBeforeUnmount(() => {
console.log(`This will only run when component will unmount.`);
});
}
Таким образом, изменение ментальной модели в случае Vue больше связано с прекращением управления кодом с помощью опций компонентов (данные, вычисляемые, часы, методы, хуки жизненного цикла и т. д.) и обращением к использованию различных функций для обработки соответствующих функций.
Пользовательский код
Одна из причин, по которой команда React сосредоточилась на хуках, заключается в том, что пользовательские хуки — это лучший способ для разработчиков писать повторно используемый код вместо компонентов более высокого порядка или реквизитов рендеринга, принятых в предыдущем сообществе.
Пользовательские хуки — это обычные функции JavaScript, внутри которых используются хуки React. Одно из соглашений, которым он следует, заключается в том, что его имя должно начинаться с использования, чтобы указать, что оно используется как хук.
// custom Hook - 用于当 value 改变时向控制台打印日志
export function useDebugState(label, initialValue) {
const [value, setValue] = useState(initialValue);
useEffect(() => {
console.log(`${label}: `, value);
}, [label, value]);
return [value, setValue];
}
// 调用
const [name, setName] = useDebugState("Name", "Mary");
В Vue функции композиции (функции композиции) и хуки согласованы в целях извлечения логики и повторного использования. Реализуйте аналогичную функцию композиции useDebugState во Vue.
export function useDebugState(label, initialValue) {
const state = ref(initialValue);
watch(() => {
console.log(`${label}: `, state.value);
});
return state;
}
// elsewhere:
const name = useDebugState("Name", "Mary");
Примечание. По соглашению составные функции также имеют префикс использования, как и React Hooks, чтобы указать их эффект, и, по-видимому, функция используется в setup().
Refs
И useRef в React, и ref в Vue позволяют ссылаться на дочерний компонент или элемент DOM для присоединения.
Реагировать:
const MyComponent = () => {
const divRef = useRef(null);
useEffect(() => {
console.log("div: ", divRef.current)
}, [divRef]);
return (
<div ref={divRef}>
<p>My div</p>
</div>
)
}
Vue:
export default {
setup() {
const divRef = ref(null);
onMounted(() => {
console.log("div: ", divRef.value);
});
return () => (
<div ref={divRef}>
<p>My div</p>
</div>
)
}
}
Дополнительные функции
React Hooks запускаются при каждом рендеринге, в Vue нет эквивалента вычисляемой функции. Таким образом, вы можете объявить переменную, значение которой зависит от состояния или свойства и будет указывать на последнее значение после каждого рендеринга:
const [name, setName] = useState("Mary");
const [age, setAge] = useState(25);
const description = `${name} is ${age} years old`;
В Vue setup() запускается только один раз. Следовательно, необходимо определить вычисляемые свойства, которые должны отслеживать некоторые изменения состояния и соответствующим образом обновляться:
const name = ref("Mary");
const age = ref(25);
const description = computed(() => `${name.value} is ${age.value} years old`);
Вычисление значения дорого. Вы не хотите вычислять его каждый раз при рендеринге компонента. React включает в себяuseMemo Hook:
function fibNaive(n) {
if (n <= 1) return n;
return fibNaive(n - 1) + fibNaive(n - 2);
}
const Fibonacci = () => {
const [nth, setNth] = useState(1);
const nthFibonacci = useMemo(() => fibNaive(nth), [nth]);
return (
<section>
<label>
Number:
<input
type="number"
value={nth}
onChange={e => setNth(e.target.value)}
/>
</label>
<p>nth Fibonacci number: {nthFibonacci}</p>
</section>
);
};
React рекомендует использовать useMemo для оптимизации производительности, а не кэшированное значение перед любыми изменениями зависимостей.
React advice you to use useMemo as a performance optimization and not as a guarantee that the value will remain memoized
Vue вычисляет автоматическое отслеживание зависимостей, поэтому ему не нужен массив зависимостей.
Контекст и предоставление/внедрение
Хук useContext в React можно использовать как новый способ чтения текущего значения определенного контекста. Возвращаемое значение обычно определяется ближайшим слоем<MyContext.Provider>Атрибут value дерева предков определяется
// context object
const ThemeContext = React.createContext('light');
// provider
<ThemeContext.Provider value="dark">
// consumer
const theme = useContext(ThemeContext);
Аналогичный API в Vue называетсяprovide/inject. Существовал как опция компонента в Vue 2.x, добавлена пара в Composition API для использования в setup()provide 和 injectфункция:
// key to provide
const ThemeSymbol = Symbol();
// provider
provide(ThemeSymbol, ref("dark"));
// consumer
const value = inject(ThemeSymbol);
Если вы хотите продолжать реагировать, он должен явно предоставить ref / recessive, как значение
Выставление значений для контекста рендеринга
В случае Реакта
- Весь код хуков определен в компоненте
- и вы вернете элемент React для рендеринга в той же функции
Таким образом, у вас есть полный доступ к любому значению в области видимости, как и в любом коде JavaScript:
const Fibonacci = () => {
const [nth, setNth] = useState(1);
const nthFibonacci = useMemo(() => fibNaive(nth), [nth]);
return (
<section>
<label>
Number:
<input
type="number"
value={nth}
onChange={e => setNth(e.target.value)}
/>
</label>
<p>nth Fibonacci number: {nthFibonacci}</p>
</section>
);
};
В случае с Вью
- Во-первых, определение шаблона или параметров шаблона рендеринга
- Во-вторых, используя одно файловый компонент, верните объект из настройки (), который содержит все значения, которые вы хотите выводить на шаблон
Операторы возврата также имеют тенденцию становиться многословными из-за вероятности наличия слишком большого количества значений для раскрытия.
<template>
<section>
<label>
Number:
<input
type="number"
v-model="nth"
/>
</label>
<p>nth Fibonacci number: {{nthFibonacci}}</p>
</section>
</template>
<script>
export default {
setup() {
const nth = ref(1);
const nthFibonacci = computed(() => fibNaive(nth.value));
return { nth, nthFibonacci };
}
};
</script>
}
Один из способов добиться такой же чистой производительности в React — вернуть функцию рендеринга из самой setup(). Тем не менее, шаблоны — более распространенная практика в Vue, поэтому предоставление объекта, содержащего значение, — это то, с чем вы неизбежно столкнетесь при использовании Vue Composition API.
Заключение
И у React, и у Vue есть свои «сюрпризы», и между ними нет никакой разницы.С тех пор как React Hooks был представлен в 2018 году, сообщество использовало его для создания множества отличных работ, а масштабируемость пользовательских хуков также породила множество Open Source. взносы.
Vue был вдохновлен React Hooks, чтобы адаптировать его к своей собственной структуре, что и стало этимИстории успеха о том, как различные технологии принимают изменения и делятся вдохновением и решениями
Ссылаться на
- https://composition-api.vuejs.org/#summary
- https://Reactjs.org/docs/Hooks-intro.html
- https://dev.to/voluntadpear/comparing-React-Hooks-with-vue-composition-api-4b32