Vue.js — автоматическое обновление v-for не работает с назначением нового массива

Я следил за этим руководством по vue3: Учебное пособие по Vue.js: от новичка до разработчика интерфейса https://thewikihow.com/video_1GNsWa_EZdw&t=48s

На моей стороне тоже все работает, кроме «обновления» навигационного списка. Он говорит о Eventlistener для обновления страницы, начиная с 04:01:20.

Почему назначение нового массива для this.pages не работает для обновления v-for на моей стороне и что я могу попробовать/отладить?

this.$bus.$on('page-updated', () => {
    this.pages = [...this.$pages.getAllPages()];
});

Соответствующие части сценария:

Navbar.vue

<template>
    ...
    <navbar-link
        v-for = "(page, index) in publishedPages" 
        class = "nav-item" 
        :key = "index"
        :page = "page"
        :index = "index"                  
    ></navbar-link>
    ...
</template>

<script>
import NavbarLink from './NavbarLink.vue';

export default {
    components: {
        NavbarLink
    },
    inject: ['$pages', '$bus'],
    created() {
        this.pages = this.$pages.getAllPages();
        this.$bus.$on('page-updated', () => {
            this.pages = [...this.$pages.getAllPages()];
        });
    },
    data() {
        return {
            theme: 'dark',
            data: []
        }
    },
    computed: {
        publishedPages() {
            return this.pages.filter(p => p.published)
        }
    },
    ..
  } 
}
</script>

PadeEdit.vue

<script setup>
import { useRouter } from 'vue-router';
import { inject } from 'vue';
const router = useRouter();
const pages = inject('$pages');
const bus = inject('$bus');
const {index} = defineProps(['index']);
let page = pages.getSinglePage(index);
function submit() {
    pages.editPage(index, page);
    bus.$emit('page-updated', {
        index,
        page
    });
    ...
}
...
</script>

События.js

const events = new Map();
export default {
    $on(eventName, fn) {
        if (!events.has(eventName)) {
            events.set(eventName, [])
        }
        events.get(eventName).push(fn);
    },
    $emit(eventName, data) {
        if (events.has(eventName)) {
            events.get(eventName).forEach(fn => fn(data));
        }
    }
};

данные.js

const pagesKey = 'pages';

let pagesJson = localStorage.getItem(pagesKey);
let pagesStore = JSON.parse(pagesJson);

function save() {
    localStorage.setItem(pagesKey, JSON.stringify(pagesStore));
}

export default {
    addPage(page){
        pagesStore.push(page);
        save();
    },
    getAllPages() {
        return pagesStore;
    },
    getSinglePage(index) {
        return pagesStore[index];
    },
    editPage(index, page) {
        pagesStore[index] = page;
        save();
    }
}

Я выложил проект на github: https://github.com/qumundo/vue-cli-starter

Вы можете загрузить весь код в репозиторий или онлайн-песочницу? это множество частей, взаимодействующих друг с другом, и было бы проще всего отладить работающий пример. некоторые из моих вопросов включают, обновляется ли свойство published при редактировании? обновляет ли editPage pagesStore, как ожидалось, и обновляет ли функция save localStorage, как ожидалось? какое фактическое значение массива возвращает getAllPages? и т. д.

yoduh 11.07.2023 18:58

@yoduh Я выложил проект на github: github.com/qumundo/vue-cli-starter

Philipp M 12.07.2023 21:53

Привет, Филипп, можешь ли ты убедиться, что все файлы загружены в проект github? Единственным компонентом Vue сейчас является App.vue, и кроме .js нет файлов main.js. Как только весь проект будет загружен, я посмотрю на него. Спасибо

yoduh 12.07.2023 22:40

@yoduh Извините, я пропустил некоторые ... все файлы должны быть доступны сейчас. Спасибо, что заглянули!

Philipp M 12.07.2023 23:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Короткий ответ:

Ваш Navbar.vue data() должен быть изменен на:

data() {
    return {
        theme: 'dark',
        pages: [], // <--- was previously data: []
        counter: 0
    }
},

Длинный ответ:

После отладки полного компонента Navbar.vue проблема заключается в том, что вычисляемое свойство ссылается на неправильную ссылку this.pages

publishedPages() {
    return this.pages.filter(p => p.published);
}

Вышеупомянутое this.pages на самом деле исходит от крючка created():

created() {
    this.getThemeSetting();

    this.pages = this.$pages.getAllPages();          
    ...
}

Но this.pages здесь не реактивная переменная. При обновлении вычисляемое свойство не запускается повторно.

Это может вызвать вопрос: «Почему это работает изначально?».

Это потому, что вычисляемые свойства запускаются один раз при создании компонента, чтобы получить их начальные значения, и вы буквально определяете pages на this с помощью инструкции this.pages = ..., поэтому во время инициализации все работает нормально. Но поскольку this.pages в хуке created() не является реактивной переменной, this.pages не «отслеживается» Vue, поэтому вычисляемое свойство никогда не запускается снова при обновлении this.pages. Только переменные, определенные в data(), являются реактивными.

Тогда решение состоит в том, чтобы добавить pages к data()

data() {
    return {
        theme: 'dark',
        pages: [],  // <--- was originally "data: []"
        counter: 0
    }
},

Теперь любая ссылка на this.pages будет ссылкой на эту реактивную переменную данных. Остальной код теперь будет работать. Итак, когда это работает:

this.pages = [...this.$pages.getAllPages()];

Он обновит переменную data, а поскольку pages является реактивной зависимостью вычисляемого свойства publishedPages, он перезапустится. Наконец, v-for в шаблоне будет перерисован, потому что вычисленное значение свойства обновилось:

<navbar-link v-for = "(page, index) in publishedPages" />

P.S. это изменение с data[] на pages[] происходит в обучающем видео между 3:22:00 и 3:22:30

Сначала он устанавливает свойство данных как data: [], но через несколько секунд очень быстро меняет его на pages: []. Вероятно, это было очень трудно заметить во время кодирования.

Спасибо, что заглянули и заметили! Также отличный ответ и объяснение! С наилучшими пожеланиями

Philipp M 13.07.2023 09:19

Другие вопросы по теме