Vue新手开发聊天应用遇属性只读及直接修改Prop问题求助
Hey there! Let's work through each of the errors you're hitting while building your chat app—great start with the left-side friend list and routed chat boxes, by the way. Here's how to fix each issue:
1. $attrs is readonly & $listeners is readonly
These errors almost always happen when Vue is loaded multiple times in your project, which creates conflicting instances. Here's how to resolve it:
- Check your
package.jsonto make sure you only have one version of Vue installed. If you see duplicate entries (likevueand a nestedvueunder another dependency), runnpm dedupeoryarn dedupeto clean it up. - If you're using CDNs, ensure you only include Vue once in your HTML—don't re-include it in component files or other scripts.
- For Vue CLI projects, verify your
vue.config.jsdoesn't haveexternalsconfigured in a way that loads Vue again separately. - Double-check that your Vue Router version matches your Vue version: Vue 2 needs Vue Router 3, Vue 3 needs Vue Router 4. Mismatched versions can also trigger this conflict.
2. Avoid mutating a prop directly
Vue enforces one-way data flow: props are passed from parent to child, and children shouldn't modify them directly (this breaks predictability in your app). Here's how to fix this for your friend list/chat routing flow:
Option 1: Use a local data variable (if you don't need to sync back to parent)
If your child component just needs to work with a copy of the prop value, assign it to a local data property:
<template> <!-- Your chat list template --> </template> <script> export default { props: ['selectedFriendId'], data() { return { // Create a local copy to modify localSelectedId: this.selectedFriendId } } } </script>
Option 2: Emit custom events to update the parent (best for your chat app)
Since you need to route to a chat box when a friend is clicked, the child chatlist-component should tell the parent component to update the selected friend ID (which will then update the route). Here's how:
In chatlist-component (child):
When a friend is clicked, emit an event with the friend's ID:
<template> <div v-for="friend in friendList" @click="selectFriend(friend.id)"> {{ friend.name }} </div> </template> <script> export default { props: ['friendList'], methods: { selectFriend(friendId) { // Emit an event to the parent this.$emit('friend-selected', friendId) } } } </script>
In your parent component (the #chat container):
Listen for the event and update your route or selected ID state:
<template> <div class="chat-container clearfix" id="chat"> <div class="people-list" id="people-list"> <!-- Listen for the emitted event --> <chatlist-component :friend-list="friendList" @friend-selected="handleFriendSelect"></chatlist-component> </div> <div class="chat"> <router-view></router-view> </div> </div> </template> <script> export default { data() { return { friendList: [] } }, methods: { async loadFriendList() { // Your AJAX call to load friends const response = await fetch('/api/friends') this.friendList = await response.json() }, handleFriendSelect(friendId) { // Update the route to the chat box for this friend this.$router.push(`/chat/${friendId}`) } }, mounted() { this.loadFriendList() } } </script>
Option 3: Use v-model syntax sugar (for two-way binding)
If you prefer a more concise approach, you can use Vue's v-model with a prop:
- In the child component, accept a prop named
modelValue(Vue 3) orvalue(Vue 2), and emitupdate:modelValue(Vue 3) orinput(Vue 2) when the value changes. - Then in the parent, use
v-modelto bind the selected ID.
Quick Recap
- Fix duplicate Vue instances to resolve the
$attrs/$listenerserrors. - Use custom events to pass selected friend IDs from the child list to the parent, keeping your data flow one-way and avoiding direct prop mutations.
内容的提问来源于stack exchange,提问作者Tanvir

