Vue warn $listeners and $attrs is readonly

Total
4
Shares

I am getting a lot of Vue warnings saying $listeners is readonly or $attrs is readonly and related to different Bootstrap items or to .

For example:

[Vue warn]: $attrs is readonly.

found in

---> <BDropdown>
       <Display>
         <App>
           <Root>

I am very sure it has something to do with loading the Vue instance twice somehow, but I don’t really know, how to do it any other way, so that the routing still works.

In my main.js the code is as follows:

import Vue from 'vue'
import App from './App'
import router from './router'
import firebase from 'firebase';
import './components/firebaseInit';
import store from './store';
import { i18n } from './plugins/i18n.js'
import BootstrapVue from 'bootstrap-vue'
import VueCarousel from 'vue-carousel';

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue);
Vue.use(VueCarousel);

let app;
firebase.auth().onAuthStateChanged(user => {
  if(!app) {
    app = new Vue({
      el: '#app',
      router,
      store,
      i18n,
      components: { App },
      template: '<App/>'
    })
  }
})

My router/index.js code looks as follows:

import Vue from 'vue'
import Router from 'vue-router'
import firebaseApp from '@/components/firebaseInit'

Vue.use(Router)

let router = new Router({
  routes: [
    {
      path: '/',
      name: 'display',
      component: Display
    },
  ...
  ]
})

// Nav Guards
router.beforeEach((to, from, next) => {
  // check for requiredAuth
  if(to.matched.some(record => record.meta.requiresAuth)) {
    // check if NOT logged in
    ...
    } else {
      // proceed to route
      next();
    }
  } else {
    next();
  }
})

export default router;

As the sample errors come from Display.vue, here is an extract of that code:

<template>
  <div>
    <b-row>
      <b-input-group prepend="Category">
        <b-dropdown v-bind:text="currentCategory">
          <b-dropdown-item @click="categroyChanged('All')">All</b-dropdown-item>
          <b-dropdown-item v-for="c in categories" v-bind:key="c" @click="categoryChanged(c)">{{c}}</b-dropdown-item>
        </b-dropdown>
      </b-input-group>
    </b-row>
    <div class="row" v-for="i in Math.ceil(products.length / 3)" v-bind:key="i">
      <div v-for="product in products.slice((i - 1) * 3, i * 3)" v-bind:key="product.id" class="col-md-4 col-6 my-1">
        <b-card 
          v-bind:img-src="product.thumbUrl"
          img-fluid
          img-alt="image"
          overlay>
          <div slot="footer">
            <small class="text-muted">{{product.name}}<br />{{product.price}} VND</small>   
          </div>
            <router-link v-bind:to="{name: 'view-product', params: {product_id: product.product_id}}" class="secondary-content">
              <i class="fa fa-eye"></i>
            </router-link>
            <router-link v-if="isEmployee" v-bind:to="{name: 'edit-product', params: {product_id: product.product_id}}" class="secondary-content">
              <i class="fa fa-pencil"></i>
            </router-link>
            <button @click='addToCart(product)' class='button is-info'><i class="fa fa-cart-arrow-down"></i></button>
        </b-card>
      </div>
    </div>
  </div>
</template>

<script>        
import firebaseApp from './firebaseInit'
import { mapActions } from 'vuex'

export default {
  name: 'display',
  data () {
    return {
      txtSearch: null,
      isLoggedIn: false,
      currentUser: false,
      isEmployee: false,
      products: []
    }
  },
  beforeMount () {
    var db = firebaseApp.firestore();
      db.collection('products').get().then(querySnapshot => {
        querySnapshot.forEach(doc => {
          const data = {
              'product_id': doc.id,
              'article_number': doc.data().article_number,
              'barcode': doc.data().barcode,
              'category': doc.data().category,
              'colour': doc.data().colour,
              'description': doc.data().description,
              'name': doc.data().name,
              'name_ger': doc.data().name_ger,
              'price': doc.data().price,
              'size': doc.data().size,
              'thumbUrl': doc.data().thumbUrl,
          }
          this.products.push(data)
        })
      }) 
    }
  },
  methods: {
    ...mapActions(['addToCart']),

    ... many methods ...

  }
}
</script>

How can I get rid of these errors?


Solution

There are two common reasons why this can happen:

Multiple Vue Locations

This can be due to contradictory locations of where you are importing Vue from, in different files, as others have said. So you might have both import Vue from 'vue' and perhaps import Vue from 'vue.runtime.esm' in your code, for example.

But this can result in multiple instances of Vue, which will cause these errors.

The solution in this case is to use import Vue from 'vue' everywhere in your code, and then alias it in your packaging system (webpack, Parcel, rollup etcetera). An example of this in webpack.config.js, or webpack.renderer.config.js if you’re using Electron, would be:

module.exports = {
  // ...
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
    }
  }
  // ...
}

See more examples in the Vue documents.

White Listing

This can also be because of a need for Vue to be whitelisted as not one of the externals in webpack, for example.

It is worth noting that changes in Bootstrap Vue from 2.0 to a later version, definitely by 2.15 (and possibly earlier), caused this same problem to occur.

module.exports = {
  // ...
  externals: [
    'fast-glob',
    'jquery',
    'bunyan',
    'yaml',
    'vue',              // Remove this
    'bootstrap-vue',    // Remove this
  // ...
}
Leave a Reply

Your email address will not be published. Required fields are marked *