Комментарии 18
Javascript нетипизированный язык(или слабо типизированный, если точнее), и одна и та же переменная способна принимать и строку, и число, и даже объект.
Это динамическая типизация.
<li :class="todo.completed ? 'completed' : ''"></li>
Ведь в соседнем листинге у вас:
<li :class="{ completed: todo.completed }"></li>
Поправьте пжлст.
JSON.parse(JSON.stringify(...))
это клон обьектов? А почем не
Object.assign
?
const source = { name: 'Vasya', age: 25, hobbies: { books: ['fantasy', 'novels'], music: ['rock', 'classic']}}
const target = { ...source }
target.hobbies.books.push("sci-fi")
console.log(source.hobbies.books)
Самое красивое решение будет использование сторонней библиотеки, например lodash:
const target = _.cloneDeep(source)
Ни разу не специалист в vue, но всё же: для чего вообще глубокое клонирование каждого элемента контейнера (массива или объекта), если вы меняете только один из них (например, todos[index]
)? Насколько я понимаю идею реактивных фреймворков, клонировать нужно только сам мутируемый элемент/свойство и его родительский контейнер, сохраняя неизменными ссылки на немутировавшие элементы, иначе у вас произойдёт полная инвалидация состояния, и всё выродится в тупой digest-цикл, разве не так?
Там написано, что "замена одного массива другим, в случае совпадения части элементов этих массивов, будет очень эффективной операцией", но что подразумеватся под "совпадением" элементов? В реактивных фреймворках это обычно ссылочная эквивалентность (oldArray[i] === newArray[i]
), потому что структурную эквивалентность и равенство по значению проверять дорого. В случае JSON.parse(JSON.stringify())
ссылочная эквивалентность полностью теряется.
Насколько я понимаю, эффективнее было бы делать примерно так:
const onUpdateTodo = (text: string, index: number) => {
// клонируем контейнер, сохраняя старые элементы
const todos = [ ...store.state.currentNote.todos ];
// клонируем строго один элемент, модифицируя свойство `text`
todos[index] = { ...todos[index], text };
store.commit("updateTodos", todos);
}
Я не вижу в чем ваш способ эффективнее, все еще используется клонирование, только с помощью spread оператора. В общем-то клонирование нужно только потому, что Vuex запрещает действия над своими состоянием вне мутаций в 'strict' режиме. Можно полностью избежать клонирования, если перенести изменение массива в мутацию. Мне пришло в голову такое решение:
const onUpdateTodo = (text: string, index: number) => {
store.commit("updateTodo", {
index: index,
todo: {
text: text,
completed: note.value.todos[index].completed
}
})
}
Ну и соответственно мутация в хранилище:
updateTodo(state, payload: { index: number, todo: ToDo }) {
state.currentNote.todos[payload.index] = payload.todo
},
Мутации в vuex могут принимать только один аргумент, поэтому нужна такая сложная структура для payload, чтобы передать индекс.
Я понимаю, что обилие `JSON.parse(JSON.stringify())` режет глаза. Попробую переписать код, и избавиться от этого, где возможно.
Насколько я понимаю, имеется в виду структурная эквивалентность
Что-то сомнительно.
поскольку методы массива, которые приводятся, возвращают новый массив.
Массив может быть новым, но при этом хранить ссылки на старые элементы, а не на их клоны.
В общем-то клонирование нужно только потому, что Vuex запрещает действия над своими состоянием вне мутаций в 'strict' режиме.
В моём примере нет действий над состоянием, исходное состояние остаётся нетронутым. Просто новое состояние максимально переиспользует неизменённые объекты из старого, чтобы не переаллоцировать объекты постоянно.
Я не вижу в чем ваш способ эффективнее
- Нет сериализации/десериализации.
- При клонировании массива через spread op копируются только ссылки на элементы массива, сами элементы не клонируются. Копировать ссылки намного дешевле. Из всех элементов массива клонируется только один-единственный изменённый элемент (причём в нём тоже копируются только ссылки везде, где данные не поменялись).
- Меньше аллокаций, меньше нагрузка на сборщик мусора
- Эффективнее поиск изменений: если 2 ссылки одинаковы, то структуру этих объектов и значения их полей можно не сравнивать, так как это один и тот же объект.
Vue 3 на Typescript