предисловие
Поскольку я недавно начал писать Vue, это на самом деле вызов для меня, упорного игрока в React. На самом деле, сейчас я больше склоняюсь к написанию нативного JS, потому что большинство хорошо сделанных фреймворковых библиотек на рынке почти неразделимы.Vitrual DOM
система.
И то, что мы знаем, это то, чтоVitrual DOM
Для обновления реального DOM производительность определенно лучше, чем производительность при работе непосредственно с родным DOM.Если бы я мог точно знать, какой DOM изменится, это было бы просто.document.getElementById(id).xx
как хорошо?
Vitrual DOM
Ценность никогда не в производительности. эммм... Сегодняшняя тема — детоксикация исходного кода Vue, цель — четко понять, что сделал Vue, каковы его преимущества и недостатки.
1. Начнем с алгоритма сравнения Vue.
идтиVitrual DOM
Нет способа убежатьdiff
алгоритм.diff
Алгоритмы есть, так чтоVue3
изdiff
Как выглядит алгоритм.
Сначала рассмотрим каштан.
<ul key="ul1">
<li>渣男<li>
<li>胖子<li>
<li>就知道吃<li>
<ul>
необходимо преобразовать в:
<ol key="ul1">
<li>渣男<li>
<li>胖子<li>
<li>就知道吃吗?<div>你个渣男!</div><li>
<ol>
Q: 就把
ul变成
ol ,key都没变,甚至其子结点都不变。请问 Vue 重新如何渲染?
A: Перерендерите все из них.
Итак, разумно ли это? Если существование разумно, почему оно должно быть спроектировано таким образом? Кто-то здесь хочет меня диссить, такие сцены слишком редки в реальной разработке. (Очень грустно, когда тебя ругают, поговорим об этом позже. Это действительно может решить такую проблему...😂)
стратегия исполнения diff
同一个虚拟节点,才进行精细化diff比较。
// 先看源码中的一个方法
function isSameVNodeType(n1, n2) {
// ...
return n1.type === n2.type && n1.key === n2.key
}
На самом деле вы можете понять это, посмотрев на название метода — это способ определить, являются ли два VNode одинаковыми. Глядя на возвращаемое функцией значение, вы более четко поймете, что два VNode должны быть одного типа узла и одного и того же ключа.
只进行同层比较,不会进行跨层比较
Затем вернитесь к заданному выше вопросу и продолжайте смотреть на каштан:
<ul key="ul1">
<li>渣男<li>
<li>胖子<li>
<li>就知道吃吗?<div>你个渣男!</div><li>
<ul>
Q: 如果 ul 不再变,只是其中一个 li 元素的内容发生了变化。那请问又是咋渲染的?
Ответ: еслиli
Отправляйте изменения, только вноситеli
Сравнение различий одного и того же слоя не будет выполняться.li
Подэлементыdiv
диф.我相信使用过Vue的人都知道答案。
patch Children - обновить дочерние узлы
на исходный код.
const patchChildren = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized = false) => {
const c1 = n1 && n1.children;
const prevShapeFlag = n1 ? n1.shapeFlag : 0;
const c2 = n2.children;
const { patchFlag, shapeFlag } = n2;
// fast path
if (patchFlag > 0) {
if (patchFlag & 128 /* KEYED_FRAGMENT */) {
// this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays
/*
*1 - patchKeyedChildren
*/
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
return;
}
else if (patchFlag & 256 /* UNKEYED_FRAGMENT */) {
// unkeyed
/*
* 2 - patchUnkeyedChildren
*/
patchUnkeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
return;
}
}
// children has 3 possibilities: text, array or no children.
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
// text children fast path
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
unmountChildren(c1, parentComponent, parentSuspense);
}
if (c2 !== c1) {
hostSetElementText(container, c2);
}
}
else {
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
// prev children was array
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
// two arrays, cannot assume anything, do full diff
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
else {
// no new children, just unmount old
unmountChildren(c1, parentComponent, parentSuspense, true);
}
}
else {
// prev children was text OR null
// new children is array OR null
if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(container, '');
}
// mount new if array
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
}
}
};
Посмотрите на этот исходный код, и вы узнаете:
- узел имеет
patchFlag
,shapeFlag
два свойства. -
patchChildren
Во входном параметре n1 — старая вершина, аprevShapeFlag = n1.shapeFlag
. - n2 — новый узел (после обновления старого узла)
-
patchFlag
Это флаг быстрого канала.Как только узел имеет этот флаг и значение > 0, обработка различий с ключом выполняется напрямую. - Для не ускоренного режима требуются три решения: текстовый узел, дочерний узел и отсутствие дочернего узла. Среди них рекурсивно обрабатывается узел массива.
В нем я отметил два места (исходников слишком много, показаны только ключевые части)
- 1 - patchKeyedChildren: обрабатывать узлы с ключами
const patchKeyedChildren = (c1/*旧的vnode*/, c2/*新的vnode*/, container, parentAnchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
let i = 0;/* 记录索引 */
const l2 = c2.length; /* 新vnode的数量 */
let e1 = c1.length - 1; // prev ending index : 老vnode 最后一个节点的索引
let e2 = l2 - 1; // next ending index : 新节点最后一个节点的索引
// 1. sync from start
while (i <= e1 && i <= e2) { // ### 1. 头头比较,发现不同就跳出
const n1 = c1[i];
const n2 = (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i]));
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
else {
break;
}
i++;
}
// 2. sync from end
while (i <= e1 && i <= e2) { // ### 2. 尾尾比较,发现不同就跳出
const n1 = c1[e1];
const n2 = (c2[e2] = optimized
? cloneIfMounted(c2[e2])
: normalizeVNode(c2[e2]));
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
else {
break;
}
e1--;
e2--;
}
// 3. common sequence + mount
// 老节点全部patch,还有新节点
if (i > e1) { // / 新节点大于老节点
if (i <= e2) { // // 并且新节点e2指针还没有走完,表示需要新增节点
const nextPos = e2 + 1;
const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
while (i <= e2) {
patch(null, (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i])), container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
i++;
}
}
}
// 4. common sequence + unmount
// 新节点全部patch,还有老节点
else if (i > e2) { // 新节点e2指针全部patch完
while (i <= e1) { // 新节点数小于老节点数,需要卸载节点
unmount(c1[i], parentComponent, parentSuspense, true);
i++;
}
}
// 5. unknown sequence : 剩余不确定元素
// [i ... e1 + 1]: a b [c d e] f g
// [i ... e2 + 1]: a b [e d c h] f g
// i = 2, e1 = 4, e2 = 5
else {
const s1 = i; // prev starting index
const s2 = i; // next starting index
// 5.1 build key:index map for newChildren
const keyToNewIndexMap = new Map();
for (i = s2; i <= e2; i++) {
const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i]));
if (nextChild.key != null) {
if (keyToNewIndexMap.has(nextChild.key)) {
warn(`Duplicate keys found during update:`, JSON.stringify(nextChild.key), `Make sure keys are unique.`);
}
keyToNewIndexMap.set(nextChild.key, i);
}
}
// 5.2 loop through old children left to be patched and try to patch
// matching nodes & remove nodes that are no longer present
// code ....
// 5.3 move and mount
// generate longest stable subsequence only when nodes have moved
// code ...
}
};
Уважаемый, сначала взгляните на примечания к цитатам с числовыми метками в исходном коде, которые все поставляются вместе с исходным кодом. Если не понял, посмотри еще раз китайские комментарии, вот что я добавил.
Ну а если вы увидите исходный код, то у вас голова закружится,那我来总结一下这个方法中的数字 5
.
5.1 ключ сборки, запись нового узла
Сначала посмотрите на переменные, объявленные в коде:
const s1 = i // 第一步遍历到的index
const s2 = i
const keyToNewIndexMap = new Map() // 把没有比较过的新的vnode节点,通过map保存
for (i = s2; i <= e2; i++) {
if (nextChild.key != null) {
keyToNewIndexMap.set(nextChild.key, i)
}
}
let j // 新指针j
let patched = 0
const toBePatched = e2 - s2 + 1 // 没有经过 path 的 新的节点的数量
let moved = false // 是否需要移动
let maxNewIndexSoFar = 0
const newIndexToOldIndexMap = new Array(toBePatched)
// 建立一个数组,每个子元素都是0 [ 0, 0, 0, 0, 0, 0 ]
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0;
существуетkeyToNewIndexMap
переменная, результат, который мы получаем: (при условии, что ключ узла e равен e).
keyToNewIndexMap = {"e" => 2, "d" => 3, "c" => 4, "h" => 5}
с новым указателемj
для записи индекса оставшихся новых узлов.
newIndexToOldIndexMap
Используется для хранения индекса нового узла и индекса старого узла.
5.2 Сопоставьте узлы и удалите несуществующие узлы
for (i = s1; i <= e1; i++) { /* 开始遍历老节点 */
const prevChild = c1[i] // c1是老节点
if (patched >= toBePatched) {
/* 已经patch数量大于等于剩余节点数量,卸载老的节点 */
unmount(prevChild, parentComponent, parentSuspense, true)
continue
}
let newIndex // 目标新节点的索引
/* 如果,老节点的key存在 ,通过key找到对应的新节点的index */
if (prevChild.key != null) {
newIndex = keyToNewIndexMap.get(prevChild.key)
} else {
/*
如果,老节点的key不存在,遍历剩下的所有新节点
按我们上面的节点来讲,就是遍历 [e d c h],代码中s2=2 e2=5,
*/
for (j = s2; j <= e2; j++) {
if (
newIndexToOldIndexMap[j - s2] === 0 &&
isSameVNodeType(prevChild, c2[j])
) {
/* 如果找到与当前老节点对应的新节点那么 ,将新节点的索引,赋值给newIndex */
newIndex = j
break
}
}
}
if (newIndex === undefined) {
/* 没有找到与老节点对应的新节点,删除当前节点 */
unmount(prevChild, parentComponent, parentSuspense, true)
} else {
/* 把老节点的索引,记录在存放新节点的数组中, */
newIndexToOldIndexMap[newIndex - s2] = i + 1
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex
} else {
/* 证明有节点已经移动了 */
moved = true
}
/* 找到新的节点进行patch */
patch(
prevChild,
c2[newIndex],
container,
null,
parentComponent,
parentSuspense,
isSVG,
optimized
)
patched++ // 记录已经在新节点中找到了了多少个老节点了
}
}
Таким образом, вы можете понять, что он в основном выполняет 2 шага:
Шаг 1:
Найдите индекс нового узла через ключ старого узла Здесь есть два случая:
- У старого узла нет ключа, пройдитесь по всем оставшимся новым узлам и попытайтесь найти индекс
- У старого узла есть ключ, в
keyToNewIndexMap
индекс найден в
Шаг 2:
- Если Индекс по-прежнему не найден на первом шаге, это означает, что нет старого узла, соответствующего новому узлу, и текущий старый узел удаляется.
- Если индекс найден, это означает, что в старом узле есть соответствующий узел, а новый индекс узла присваивается
newIndex
. Затем проиндексируйте старый узел и запишите его в массив нового узла.newIndexToOldIndexMap
, индекс +1 здесь потому, что начальное значение равно 0. Если индекс сохраняется напрямую и отличается от первого, сохраненный индекс будет равен 0, и он будет непосредственно рассматриваться как отсутствие совпадения старого узла.
Объяснение Решение:newIndex >= maxNewIndexSoFar
Поскольку обход старого массива — это обход спереди назад, то если сказано, что при обходе записывается положение узла в новом массиве узлов, если наоборот, то этоmaxNewIndexSoFar > newIndex
, это означает, что поменялся узел из нового и старого узлов.diff
Обязательно будет движение элементов, участвующих в процессе.
// 举个栗子
if 旧节点 = [a, b, c, f];
if 新节点 = [a, f, b, c];
so
循环遍历旧结点:
when Pointer -> b ,newIndex = 2 and maxNewIndexSoFar = 0
when Pointer -> c ,newIndex = 3 and maxNewIndexSoFar = 2
when Pointer -> f ,newIndex = 1 and maxNewIndexSoFar = 3
result -> moved = true
// 把流程串起来
旧节点: a b [c d e] f g , c key 存在,d、e 的 key === undefined
新节点: a b [e d c h] f g
得到待处理的节点: [e d c h]
按以上逻辑,先遍历 [c d e]。
when when Pointer -> c, newIndex = 4 s2 = 2 newIndexToOldIndexMap = [0,0,3,0].执行 patch
when when Pointer -> d, newIndex = undefined ,删除 d
when when Pointer -> e, newIndex = undefined ,删除 e
Какой ужасный факт, если ключ не существует, удалите старый узел напрямую. Итак, я пришел к выводу: при написании кода Vue мы должны обращать внимание на наличие ключа? Я сам почти поверил
提出一个很重要的概念: 最长递增子序列
Я буду писать китайские аннотации для всех. 😊
// 5.3 move and mount
// generate longest stable subsequence only when nodes have moved
// 移动老节点、创建新节点
const increasingNewIndexSequence = moved
? getSequence(newIndexToOldIndexMap)
: EMPTY_ARR;
// // 用于节点移动判断
j = increasingNewIndexSequence.length - 1;
// looping backwards so that we can use last patched node as anchor
// 向后循环,也就是倒序遍历。 因为插入节点时使用 insertBefore, 即向前插以便我们可以使用最后一个更新的节点作为锚点
for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i;
const nextChild = c2[nextIndex];
const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
if (newIndexToOldIndexMap[i] === 0) { // 如果仍然是默认值 0, 证明是一个全新的节点
// mount new
patch(null, nextChild, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
else if (moved) {
// move if:
// There is no stable subsequence (e.g. a reverse)
// OR current node is not among the stable sequence: 当前索引不是最长递增子序列里的值,需要移动
if (j < 0 || i !== increasingNewIndexSequence[j]) {
move(nextChild, container, anchor, 2 /* REORDER */);
}
else {
// 是最长递增子序列里的值,则指向下一个
j--;
}
}
}
- 2 - patchUnkeyedChildren: обрабатывать узлы без ключей
Что касается узла ключа, он обрабатывается ...
Пряный цыпленок, такой грубый, вы не можете смотреть прямо. Проверьте исходный код самостоятельно. (Это для сравнения длины старого и нового узлов, новая длина напрямую монтируется новая. Старая длина сначала размонтируется)
Обобщить:
- Если узел без изменений ключа, идите сразу в крематорий.
- Узел с ключевыми изменениями
- сравнить лоб в лоб
- Сравните хвост с хвостом
- Сравните голову и хвост
- Найдите самую длинную возрастающую подпоследовательность, перемещайтесь в любое время и создавайте новые узлы в любое время.
2. Разделение времени
Vue3 отказался от разделения времени, что заставило меня… эмммм, что еще сказать, ты не застрял.
О том, почему Vue3 не использует разделение времени (Time Slicing), Юйси вVuejs issue
Там есть подробные ответы.Оригинальный адрес ответа You Yuxi
Отлично. Дай-ка переведу (думал, а если не переведу, старые утюги сразу к исходному тексту перейдут и побьют?).
В веб-приложении обновите содержимое丢帧(janky)
обычно большим количествомCPU时间
+原始DOM更新
вызвано синхронной операцией.时间切片
это попытка сохранить отзывчивость приложения во время работы процессора, но влияет только на работу процессора. Но обновление обновлений DOM должно быть синхронным, чтобы обеспечить согласованность конечного состояния DOM.
Итак, представьте два сценария с пропущенными обновлениями кадров:
1. Время работы ЦП находится в пределах 16 мс, но операция обновления собственного DOM очень велика (например, монтировать большое количество нового содержимого DOM). Приложение по-прежнему кажется «жестким (пропущенные кадры)» с разделением времени или без него.
- Задача ЦП очень тяжелая и занимает более 16 мс. Теоретически в игру вступает квантование времени. Однако исследования HCI показывают, что если это не анимация, большинство людей не почувствуют разницы при обычном взаимодействии с пользователем, если только обновление не займет более 100 мс.
Тем не менее, разделение времени становится практически полезным только тогда, когда частые обновления требуют более 100 мс чистого времени процессора.
Тем не менее, разделение времени практически полезно только в том случае, если обновления выполняются часто для задач чистого ЦП более 100 мс.
Интересно то, что такой сценарий чаще происходит в React, потому что:
-
I. Виртуальные манипуляции с DOM в React (
reconciliation 调度算法
) по своей природе медленнее, потому что использует многоFiber架构
; -
2. Использование React JSX для рендеринга функций сложнее оптимизировать, чем рендеринг шаблонов, а шаблоны легче анализировать статически.
-
3. React Hooks оставляют большую часть оптимизации компонентов на уровне дерева (т. е. предотвращение ненужного повторного рендеринга дочерних компонентов) разработчику, который в большинстве случаев должен использовать его явно.
useMemo
. Кроме того, всякий раз, когда React получаетchildren
свойство, оно почти всегда перерисовывается, потому что каждый дочерний компонент представляет собой новое дерево vdom. Это означает, что приложение React, использующее хуки, будет перерисовывать в конфигурации по умолчанию. Хуже того, что-то вродеuseMomo
Этот тип оптимизации не может легко применяться автоматически, потому что:- Ему нужен правильный массив deps;
- Использование его вслепую и произвольно может заблокировать обновления, которые должны быть сделаны, подобно
PureComponent
.
К сожалению, большинство разработчиков ленивы и не оптимизируют свои приложения активно. Таким образом, большинство приложений React, использующих хуки, выполняют много ненужной работы процессора.
Напротив, Vue сравнивает вышеуказанные проблемы:
-
По своей сути проще, поэтому манипуляции с виртуальным DOM выполняются быстрее (
no时间切片-> no
fiber-> 更低开销
); -
Многие оптимизации AOT выполняются путем анализа шаблонов, что снижает основные накладные расходы на операции виртуального DOM. Контрольные показы,Для типичного блока кода DOM соотношение динамического и статического содержимого составляет примерно 1:4, а собственное выполнение Vue3 даже быстрее, чем Svelte, затрачивая менее 1/10 времени на ЦП React.
-
Интеллектуальная оптимизация компонентов на уровне дерева за счет адаптивной трассировки, компиляции слотов в функции (чтобы избежать повторного рендеринга дочерних элементов) и автоматического кэширования встроенных дескрипторов (чтобы избежать повторного рендеринга встроенных функций). Дочерние компоненты никогда не нужно повторно отображать, если в этом нет необходимости. Все это без какой-либо ручной оптимизации разработчиком.
Это означает, что для одного и того же обновления приложение React может вызывать повторную визуализацию нескольких компонентов, но в Vue большую часть времени это вызывает повторную визуализацию только одного компонента.
默认情况下
,Приложения Vue3 тратят меньше рабочего времени ЦП, чем приложения React, и вероятность того, что время работы ЦП превысит 100 мс, значительно снижается, если только в некоторых крайних случаях DOM не может стать более серьезным узким местом.
Теперь режим разделения времени или режим параллелизма представляет собой другую проблему: поскольку инфраструктура теперь планирует и координирует все обновления, это создает много дополнительных сложностей с точки зрения определения приоритетов, аннулирования, повторного создания экземпляров и т. д. Вся эта логическая обработка не может бытьtree-shaken
, что увеличит размер памяти ЦП, занимаемой средой выполнения. даже если включеныSuspense
и всеtree-shaken
, среда выполнения Vue 3 по-прежнему составляет лишь 1/4 размера текущего React + React DOM.
Обратите внимание: это не означает, что параллельный режим в целом — плохая идея. Он предоставляет интересные новые способы решения определенных видов проблем (особенно связанных с координацией асинхронных переходов между состояниями), но разделение времени (как подфункция параллелизма) специально решает проблемы, которые в React более заметны, чем в других фреймворках. также несет свои расходы. Для Vue 3 компромисс не стоит того.
Если вы также старый игрок в реакцию, вы не должны быть убеждены. Ответ Юйси, кажется, указывает на некоторые недостатки и недостатки реакции. Существует ритм уступки другим, чтобы возвысить себя.
Ю Юйси указал:
- Память процессора React + React DOM выше, чем у Vue, когда он работает, и эта доля достигает
4:1
- React Hooks не просты в использовании, даже если они используются
useMemo 、 memo
Также необходимо обеспечить правильность отп. - Операция React с виртуальным DOM на самом деле относится к
React
Алгоритм планирования относительно медленный. а такжеVue
Обширный анализ шаблоновAOT优化
, уменьшая основные накладные расходы на манипуляции с виртуальным DOM.所以Vue的操作虚拟 DOM 要比 React 快。
- Параллельный режим не является некротическим, но разделение времени не обязательно, по крайней мере, подход React с разделением времени не очень хорош.
Как человек из прошлого, я хорошо осведомлен о некоторых недостатках React. Посмотрим на пункты 1-4 под другим углом.
-
Честно говоря, кто бы ни работает быстрее. Если реагировать занимает 4 часа и Vue занимает 1 час, кто вы думаете, что быстрее? Но React работает 400 мс, Vue работает 100 мс, кого вы думаете, что быстрее? Другими словами, это действительно необходимо для этой проблемы? Как оптимизировать беспрепятственный производительность узкого места? Лучше реагировать лучше или верят?
-
React Hooks хороши в использовании, но их нелегко сделать правильно. Но если я использую его, эта проблема все еще существует?
-
Алгоритм планирования React медленный, Vue относительно быстрый, а затем оценка двух аспектов
- React может контролировать скорость, фактически написав код, например, каждый раз используя как можно меньше VDOM. Может ли оптимизация AOT Vue позволить разработчикам сделать это? Очевидно, что Vue не может.
- Реакт действительно медленный? Или Vue действительно лучше, чем React, в сценариях, где много манипулируют DOM?
-
Расскажите немного о так называемом
React 时间切片做法
. Реагирование даст задачу слова волокна в простое время браузера для завершения. Этот процесс может быть прерван в любое время. После прерывания задача может продолжать выполнять задачу из предыдущей позиции в следующий раз.- Применение «разрезки времени» в реакции далеко не быстрое или неприятное, а для восстанавливаемости. Например, когда пользователь выполняет ответственное взаимодействие или страница выполняет сложную анимацию, если React усиливает собственное потребление, но обеспечивает плавность взаимодействия и анимации, как вы думаете, оно того стоит?
Резюме
На самом деле сейчас на рынке идет много жарких дискуссий о React и Vue, которые все связаны с их собственными преимуществами и недостатками.
Например, многие люди в Интернете атакуют друг друга:
«Vue подходит только для небольших проектов, а не для крупных»
«В React есть бесчисленное количество обратных вызовов, бесчисленное количество выражений выбора, эта привязка… хаос!»
«С Vue легко начать работу, и есть много рабочих мест»
«Большие фабрики в основном используют React, а не Vue»
Тогда, если рассматривать это с точки зрения использования, эммм, сделайте коробку.
вопрос | Vue | React |
---|---|---|
эта путаница | Реализация исходного кода уже обработала это, никакой дополнительной обработки от вас не требуется | React Hooks больше не существует. |
начать | easy | normal |
использовать это хорошо | normal | hard |
Подходит для новичков | очень дружелюбный | недружелюбный |
Масштабируемость | в общем | мощный |
низкоуровневая реализация | Хардкор, все умею | Хардкор, но с большим содержанием |
hook | разрабатывать | разрабатывать |
3. Сравнение Vue3 и React17
Когда впервые была выпущена бета-версия Vue 3.0, было много шума. Впечатлен двумя жалобами.
- Структура кода спагетти Tucao
- свалены в кучу
setup
Я мог бы также использовать реакцию напрямую - Непонятна структура кода и непонятна семантика, эта операция равносильна выбрасыванию плюсов самого vue.
- Структура не ясна, я беспокоюсь, что объем кода будет непростым в обслуживании.
- свалены в кучу
- Копировать
-
Vue-Composition-Api
Основным источником вдохновения являетсяReact Hooks
Творчество (это также самое безжалостное место для жалоб)
-
Фактически, люди, которые действительно использовали и понимают хуки React, поймут это, когда увидят это.Vue Composition API (VCA)
а такжеhooks
Существенная разница.VCA
С точки зрения реализации, он просто более явно раскрывает отзывчивую систему самого Vue. Если вы действительно хотите сказать что-то вроде,VCA
а такжеMobX
Немного больше.
(Здесь я извиняюсь за Vue, который показывает, что я все еще очень впечатляю. В конце концов, это речь после изучения исходного кода Vue)
дать одинVue CLI UI file explorerПример официальных жалоб, этот компонент находится в gui Vue-CLI (то есть мы обычно вводим в командной строкеvue ui
Это сложный файловый браузер, компонент графической консоли, который вышел.Это написано боссом официальной команды Vue.Я считаю, что это более убедительный случай.
Поскольку я посмотрел на github, я больше не буду публиковать код, сейчас час ночи.
Тогда посмотрите на официальную картинку и все поймете.
Левая сторона изображения — оригинальный стиль, правая —hook
стиль.
Один из методов в стиле хука:
function useCreateFolder(openFolder) {
// originally data properties
const showNewFolder = ref(false);
const newFolderName = ref("");
// originally computed property
const newFolderValid = computed(() => isValidMultiName(newFolderName.value));
// originally a method
async function createFolder() {
if (!newFolderValid.value) return;
const result = await mutate({
mutation: FOLDER_CREATE,
variables: {
name: newFolderName.value,
},
});
openFolder(result.data.folderCreate.path);
newFolderName.value = "";
showNewFolder.value = false;
}
return {
showNewFolder,
newFolderName,
newFolderValid,
createFolder,
};
}
Давайте посмотрим на кусок кода в стиле Vue Hook:
export default {
setup() {
// ...
},
};
function useCreateFolder(openFolder){
// ...
}
function useCurrentFolderData(networkState) {
// ...
}
function useFolderNavigation({ networkState, currentFolderData }) {
// ...
}
function useFavoriteFolder(currentFolderData) {
// ...
}
function useHiddenFolders() {
// ...
}
function useCreateFolder(openFolder) {
// ...
}
давайте посмотрим на настоящееsetup
функция.
export default {
setup() {
// Network
const { networkState } = useNetworkState();
// Folder
const { folders, currentFolderData } = useCurrentFolderData(networkState);
const folderNavigation = useFolderNavigation({ networkState, currentFolderData });
const { favoriteFolders, toggleFavorite } = useFavoriteFolders(currentFolderData);
const { showHiddenFolders } = useHiddenFolders();
const createFolder = useCreateFolder(folderNavigation.openFolder);
// Current working directory
resetCwdOnLeave();
const { updateOnCwdChanged } = useCwdUtils();
// Utils
const { slicePath } = usePathUtils();
return {
networkState,
folders,
currentFolderData,
folderNavigation,
favoriteFolders,
toggleFavorite,
showHiddenFolders,
createFolder,
updateOnCwdChanged,
slicePath,
};
},
};
🐂🍺了,干净不?
Сравните принцип крючка.
Или каштан.
<template>
<div>
<span>{{count}}</span>
<button @click="add"> Add By 1 </button>
</div>
</template>
export default {
setup() {
const count = ref(0)
const add = () => count.value++
effect(function active(){
console.log('count changed!', count.value)
})
return { count, add }
}
}
Очень простой каштан.
- настройка выполняется только один раз,
- Если нужно в
count
Чтобы что-то сделать, когда есть изменение, нам просто нужно ввестиeffect
функция. - это
active
Функция будет сгенерирована только один раз, эта функция читаетcount.value
соберет его как зависимость, то в следующий разcount.value
После обновления может срабатывать естественноactive
Функция выполнена повторно.
Подводя итог: хук инициализируется один раз, а затем используется бесконечно.
Еще один взгляд на каштаны.
export default function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('渣男');
const add = () => setCount((prev) => prev + 1);
useEffect(()=>{
setName(`渣男渣了${count}次`)
},[count])
return (
<div>
<span>{count}</span>
<span>{name}</span>
<button onClick={add}> +1 </button>
</div>
);
}
Как видите, функционал тот же, но это компонент React. по ссылке<Counter />
Представленный таким образом, мы знаем, что JSX — это js, и Babel на самом деле скомпилирует его вReact.createElement(Counter)
Такая функция выполняется.
То есть при каждом рендеринге эта функция будет полностью выполняться один раз.
useState
вернуть count
а также setCount
Он будет сохранен в соответствующем компоненте компонентаFiber
Node, и порядок выполнения каждой функции React Hook должен быть одинаковым.
Хуки-функции в React Hooks могут вызываться несколько раз, что также является наиболее удобной для разработчиков идеей, которую я считаю React на данный момент. Я могу в полной мере использовать эти функции ловушек, чтобы максимизировать детализацию моей логики и добиться повторного использования, не влияя друг на друга.
Вышеупомянутые недостатки зависимости от deps. Хуки React Многие хуки зависят от переменных состояния. Проще говоря, зависимые переменные состояния изменились, и можно выполнять соответствующие операции. Звучит здорово, правда? Но одна плохая вещь в том, что застежка попала в ловушку ... Если вы используете ее правильно, это корова. Если вы не используете его хорошо, вы острая курица.
Так что, если вы убежденный поклонник стиля функционального программирования, React Hooks, безусловно, ваш фаворит.
Кроме того, мне вдруг пришла в голову фраза в Интернете: Vue 给你持久,React给你自由。
Поэтому, проводя техническое исследование, четко обдумайте свой сценарий. Другие действительно ничего.Код всегда пишут люди.Каким бы простым ни был Vue, его можно написать как си.Каким бы сложным ни был React, его можно написать хорошо.
Сейчас 1:26 утра, и технических статей слишком много, чтобы писать, потому что действительно есть о чем поговорить... Для детоксикации исходного кода React вы можете прочитать прошлые статьи. Что касается остального исходного кода Vue, то его действительно немного, в отличие от исходного кода Vue, который действительно намного меньше, и аннотации тоже богатые (понятнее, чем китайцы пишут на английском). Так что, если у вас есть возможность, добавьте его.