VUE:UI仅在我保存更改VS代码时才呈现数据

发布于 2025-02-13 21:10:00 字数 3610 浏览 4 评论 0原文

我是Vue的新手,我正在为一个非常奇怪的问题苦苦挣扎: 在我的组件中,在creates()函数中我获取数据并将其保存到我的项目数组中。我以V-For为段落显示项目的内容。

现在来了很奇怪的事情:当我启动应用程序时,什么也没有显示,尽管我可以在Vue Dev-Tool中看到正确的数据存储在项目数组中。当我刷新时,也会发生同样的事情。只有当我对代码进行更改并保存时,我才能看到UI中显示的正确数据。

代码: 每周lylanking.vue:

<script>
import { computed } from "vue"
import RankingItem from "./RankingItem.vue";
import {getUsersWithPoints} from "../../../data/data";

    export default {
    name: "WeeklyRanking",
    data() {
        return {
            items: [],
        };
    },
    created (){
        getUsersWithPoints().then((users) => {
            this.items = users;
            console.log(users);
        });
    },
    computed: {
        sortedRankings() {
            return [...this.items].sort((a, b) => b.points - a.points);
        },
        maxPoints() {
            return 95;
        }
    },
    components: { RankingItem },
}  
</script>

<template>
    <div class="weekly-ranking-container">
            <h1>Wochen - Ranking</h1>
            <div class="ranking-chart">
                <p v-for="r in items"> {{r.username}} </p>
                {{items}}
            </div>
            <button>
                <router-link to="/taskDone">+ Aufgabe erledigt</router-link>
            </button>
    </div>
</template>

<style>
    .weekly-ranking-container{
        padding: 24px;
        max-width: 400px;
        min-width: 300px;
    }
    .ranking-chart{
        display: flex;
        flex-direction: column;
        align-items: flex-start;
    }
</style>

console.log显示以下内容:

VUE Inspector显示数据如下:

我到目前为止尝试过的东西

  • 直接在数据()部分中直接使用假数据 - 作品。 UI显示了我在这里嘲笑的初始数据。

  • 从数据源中获取虚假数据 - 也可以工作。在人工延迟后显示正确的数据。 在这种情况下,有趣的是,控制台日志中的数组看起来不同:

我无法理解这两个日志之间的差异,尤其是因为Vue Inspector显示它完全相同。同样,保存代码的零件对我来说也不重要。

下面我显示数据调用代码。我将Firebase与模块化API一起使用。 感谢您的帮助!

数据调用代码:

async function getUsersWithPoints(){
    // With the commented part it works
    // const fakeUsers = [
    //     {username:"Marcelo", points:95, id:"PRirMre5IUeHP7BA08wh"},
    //     {username:"Sebse", points:80, id:"PRirMasdoiHP7BA08wh"},
    //     {username:"Simi", points:20, id:"yhaHRRxN7PFmfyHZqZS1"},
    // ];
    // await new Promise(r => setTimeout(r, 2000));
    // console.log("FAKE USERS:");
    // console.log(fakeUsers);
    // return fakeUsers;

    //with the below part it does not
    let users = [];
    const usersQuery = query(collection(db, `groups/${wgKey}/users`));
    const usersSnap = await getDocs(usersQuery);
    usersSnap.forEach(async(user)=>{
        const tasksQuery = query(collection(db, `${user.ref.path}/tasks`));
        const tasks = await getDocs(tasksQuery);
        let points = 0;
        tasks.forEach((task)=>{
            points+=task.data().points;
        });
        users.push({
            username: user.data().name,
            points: points,
            id: user.id
        });
    });
    return users;
}
```

I am new to Vue and I am struggling with an extremely weird issue:
In my component, in the creates() function I fetch data and save them into my items Array. I display the content of the items with v-for as a paragraph.

Now here comes the weird thing: When I start the App, nothing shows, although I can see in the Vue dev-tools that the proper data is stored in the items array. When I refresh, same happens. Only when I make changes in my code and save it, I see the proper data being displayed in the UI.

Code:
WeeklyRanking.vue:

<script>
import { computed } from "vue"
import RankingItem from "./RankingItem.vue";
import {getUsersWithPoints} from "../../../data/data";

    export default {
    name: "WeeklyRanking",
    data() {
        return {
            items: [],
        };
    },
    created (){
        getUsersWithPoints().then((users) => {
            this.items = users;
            console.log(users);
        });
    },
    computed: {
        sortedRankings() {
            return [...this.items].sort((a, b) => b.points - a.points);
        },
        maxPoints() {
            return 95;
        }
    },
    components: { RankingItem },
}  
</script>

<template>
    <div class="weekly-ranking-container">
            <h1>Wochen - Ranking</h1>
            <div class="ranking-chart">
                <p v-for="r in items"> {{r.username}} </p>
                {{items}}
            </div>
            <button>
                <router-link to="/taskDone">+ Aufgabe erledigt</router-link>
            </button>
    </div>
</template>

<style>
    .weekly-ranking-container{
        padding: 24px;
        max-width: 400px;
        min-width: 300px;
    }
    .ranking-chart{
        display: flex;
        flex-direction: column;
        align-items: flex-start;
    }
</style>

The console.log shows the following:
Console log result in created()

The Vue inspector show the data as follows:
Vue inspector Data

What I have tried so far

  • Use fake data directly in the data() part - works. The UI shows the initial data that I mock here.

  • get fake data from data source with fake delay - works as well. Shows the proper data after the artificial delay.
    The interesting thing in this case, is that the Array in the console log looks different:
    Console log when using fake data

I could not make sense of the difference between those two logs, especially because the Vue inspector displays it exactly the same. Also, the part with the code saving does not make sense to me.

Below I show the data call code. I use firebase with the modular api.
Thanks for your help!

Data call code:

async function getUsersWithPoints(){
    // With the commented part it works
    // const fakeUsers = [
    //     {username:"Marcelo", points:95, id:"PRirMre5IUeHP7BA08wh"},
    //     {username:"Sebse", points:80, id:"PRirMasdoiHP7BA08wh"},
    //     {username:"Simi", points:20, id:"yhaHRRxN7PFmfyHZqZS1"},
    // ];
    // await new Promise(r => setTimeout(r, 2000));
    // console.log("FAKE USERS:");
    // console.log(fakeUsers);
    // return fakeUsers;

    //with the below part it does not
    let users = [];
    const usersQuery = query(collection(db, `groups/${wgKey}/users`));
    const usersSnap = await getDocs(usersQuery);
    usersSnap.forEach(async(user)=>{
        const tasksQuery = query(collection(db, `${user.ref.path}/tasks`));
        const tasks = await getDocs(tasksQuery);
        let points = 0;
        tasks.forEach((task)=>{
            points+=task.data().points;
        });
        users.push({
            username: user.data().name,
            points: points,
            id: user.id
        });
    });
    return users;
}
```

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

木槿暧夏七纪年 2025-02-20 21:10:00

我记得几年前与VUE 2项目遇到了相同的问题。据我所知, valentin rapp 已经给了您正确的解释。文档还指出:

阵列突变器

vue能够检测何时称为反应阵列的突变方法

来源: https://vuejs.s.s.org/指南/essexents/list.html#array-change-detection

一种可能的解决方案是将产生的元素推向反应性阵列,因此触发是突变器。

    // ...
    created (){
        getUsersWithPoints().then((users) => {
            users.forEach(user => {
                this.items.push(user)
            })
            console.log('Users:', this.items)
        })
    },
    // ...

对象突变器

根据用户阵列的大小(&gt; 10k)的大小这种方法在某些设备上可能会滞后/非常缓慢。另一种方法是,通过更新对象道具来使用对象的反应性突变器。请注意,我尚未使用VUE 3选项API使用,因此这可能是错误的:

    // ...
    data() {
        return {
            user: {
                items: [],
                loaded: false, // can be used for some loading animation
            },
        }
    },
    created (){
        getUsersWithPoints().then((users) => {
            this.user.items = users
            this.user.loaded = true
            console.log('Users:', this.user.items)
        })
    },
    // ...

组成API

如前所述的 ,并可能导致

    // Vue 3 Component with Composition API — not a 1:1 approach!
    // ...
    setup() {
        const items = ref([])

        getUsersWithPoints().then((users) => {
            users.forEach(user => {
                this.items.push(user)
            })
        })

        return {
            items,
        }
    },
    // ...

VS代码保存错误

此“错误”更可能是上述问题的确认:突变器。然而,您给定的描述几乎没有给您一些详细信息。

我们不知道“存储在Vscode”之间的整个工具链如何将新代码带入浏览器”。您的代码如何被转移?如何观看文件?如何实现热重载?

但是总的来说,我认为这会像这样工作:

  1. 文件存储在您的编辑器中,
  2. 您的工具链观察器检测一个更改的文件hash for一个js文件
  3. 您的工具链transpiles transpiles transpiles transpiles
  4. transpiles您的热加载服务将重新加载并注入新的代码
  5. 中,为了在浏览器实例中同步您的当前数据和热封装组件中的默认数据将被正确替换为VUES突变器的使用。
  6. 您的组件将正确检测到更改。您的v-for get rerendered且数据将正确显示

但正如我所说,这是纯粹的猜测,纯粹取决于您的设置。

I can remember having an identical issue with an Vue 2 project some years ago. As far as I can see Valentin Rapp gave you the correct explanation already. The documentation also states:

Array Mutators

Vue is able to detect when an reactive array's mutation methods are called

Source: https://vuejs.org/guide/essentials/list.html#array-change-detection

One possible solution would be, to push your resulting elements to the reactive array, therefore triggering it's mutators.

    // ...
    created (){
        getUsersWithPoints().then((users) => {
            users.forEach(user => {
                this.items.push(user)
            })
            console.log('Users:', this.items)
        })
    },
    // ...

Object Mutators

Depending on the size of your users-array (> 10k) this approach could potentially lag/very slow on some devices. Another approach would be, to use the reactive mutators of an object by updating an objects prop. Please note, that I haven't used that with Vue 3 options API yet, so this could be faulty:

    // ...
    data() {
        return {
            user: {
                items: [],
                loaded: false, // can be used for some loading animation
            },
        }
    },
    created (){
        getUsersWithPoints().then((users) => {
            this.user.items = users
            this.user.loaded = true
            console.log('Users:', this.user.items)
        })
    },
    // ...

Composition API

As stated before and propably lead to the confusion of Valentin Rapp is the useage of the options API in Vue 3. While this should be fully supported by Vue, with version 3 the composition API was implemented that could, besides other things, fix this problem for you:

    // Vue 3 Component with Composition API — not a 1:1 approach!
    // ...
    setup() {
        const items = ref([])

        getUsersWithPoints().then((users) => {
            users.forEach(user => {
                this.items.push(user)
            })
        })

        return {
            items,
        }
    },
    // ...

VS Code Saving Bug

This "bug" is more likely a confirmation for the problem stated above: mutators. Yet your given description is way to little to give you some detailed information here.

We don't know what your whole toolchain between "storing in vscode" to "bringing the new code to the browser" looks like. How does your code get transpiled? How is the file beeing watched? How is the hot reloading implemented?

Yet in general I think this will work like so:

  1. File gets stored within your editor
  2. Your toolchains watcher detects a changed file hash for one js file
  3. Your toolchain transpiles your code
  4. Your hot reload service will reload and inject the new code
  5. In order to sync your current data within your browser instance and the default data within the hot-reloaded component will be replaced correctly with usage of vues mutators.
  6. Your component therefor will detect the changes correctly. Your v-for get's rerendered and the data will be displayed correctly

But as I said, this is pure speculation and is purly depending on your setup.

情丝乱 2025-02-20 21:10:00

因此,我尝试了多个方法来修复它:

  • 使用数组突变器切换到组成API
  • ,而不是
  • 使用异步替换数组 - 等待那时(不幸的是)

,不幸的是,这都没有解决问题。因此,我更深入地研究了getuserswithpoints的功能,以找到我认为是错误的东西:
将foreach与异步函数一起使用。
使用Promise.All修复了问题。这是代码:

async function getUsersWithPoints(){
    let usersExtended = new Array();
    const usersQuery = query(collection(db, `groups/${wgKey}/users`));
    const usersSnap = await getDocs(usersQuery);

    await Promise.all(usersSnap.docs.map(async(user) => {
        const tasksQuery = query(collection(db, `${user.ref.path}/tasks`));
        const tasks = await getDocs(tasksQuery);
        let points = 0;
        tasks.forEach((task)=>{
            points+=task.data().points;
        });
        usersExtended.push({
            username: user.data().name,
            points: points,
            id: user.id
        });
    }));


    return usersExtended;
}

使用数组突变器似乎是一件合乎逻辑的事情,尽管文档说:

使用非熔化方法时,我们应该用新数组替换旧数组
...
您可能会认为这会导致Vue丢弃现有的DOM并重新渲染整个列表 - 幸运的是,事实并非如此。 vue实现了一些智能启发式学以最大化DOM元素重复使用,因此用一个包含重叠对象的另一个数组替换数组是一个非常有效的操作。

来源: https://vuejs.s.s.org/guide/guide/essentials/essentials/list。 html#阵列变更 - 检测

据我了解,替换数组不会重新渲染整个 dom,但Vue使用“智能启发式”来使替换阵列非常有效。因此,现在我修复了我尝试替换和使用数组突变器的数据调用,并且都起作用。

So I tried multiple things to fix it:

  • Switching to Composition API
  • Using Array Mutators instead of replacing the Array
  • Using async - await instead of then(..)

Unfortunately, none of that fixed the problem. So I looked more deeply into the getUsersWithPoints function to find what I think was the mistake:
Using forEach with an async function.
Using Promise.all instead fixed the issue. Here is the code:

async function getUsersWithPoints(){
    let usersExtended = new Array();
    const usersQuery = query(collection(db, `groups/${wgKey}/users`));
    const usersSnap = await getDocs(usersQuery);

    await Promise.all(usersSnap.docs.map(async(user) => {
        const tasksQuery = query(collection(db, `${user.ref.path}/tasks`));
        const tasks = await getDocs(tasksQuery);
        let points = 0;
        tasks.forEach((task)=>{
            points+=task.data().points;
        });
        usersExtended.push({
            username: user.data().name,
            points: points,
            id: user.id
        });
    }));


    return usersExtended;
}

Using array mutators seemed like a logical thing to do, although the docs say:

When working with non-mutating methods, we should replace the old array with the new one
...
You might think this will cause Vue to throw away the existing DOM and re-render the entire list - luckily, that is not the case. Vue implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping objects is a very efficient operation.

source: https://vuejs.org/guide/essentials/list.html#array-change-detection

So as far as I understand, replacing the array will not re-render the entire DOM, but vue uses "smart heuristics" to render the replacing array very efficiently. So now that I fixed the data call I tried replacing and using Array Mutators and both worked.

伴梦长久 2025-02-20 21:10:00

在创建的生命周期钩中,您覆盖了反应性数据阵列。这不会触发此处所述的更新:

https:// vuejs。 org/guide/ensexents/list.html#array-change-detection

您需要将数据推到数组。修改和清除它使用剪接。

在VUE 3中,您将不再需要处理。

In your created lifecycle hook you overwrite the reactive data array. This will not trigger an update as described here:

https://vuejs.org/guide/essentials/list.html#array-change-detection

You will need to push the data to the array. To modify and clear it use splice.

In Vue 3 you will not have to deal with this anymore.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文