Vue 警告 $listeners 和 $attrs 是只读的

Posted

技术标签:

【中文标题】Vue 警告 $listeners 和 $attrs 是只读的【英文标题】:Vue warn $listeners and $attrs is readonly 【发布时间】:2019-04-11 20:43:26 【问题描述】:

我收到很多 Vue 警告,说 $listeners 是只读的,或者 $attrs 是只读的,并且与不同的 Bootstrap 项目或 .

例如:

[Vue warn]: $attrs is readonly.

found in

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

我很确定这与以某种方式加载 Vue 实例两次有关,但我真的不知道如何以其他方式执行此操作,以便路由仍然有效。

在我的 main.js 中代码如下:

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/>'
    )
  
)

我的 router/index.js 代码如下所示:

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;

由于示例错误来自 Display.vue,以下是该代码的摘录:

<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-
          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>

我怎样才能摆脱这些错误?

【问题讨论】:

你找到解决这个问题的方法了吗?我现在正在发生这种情况。 【参考方案1】:

发生这种情况的常见原因有两个:

多个 Vue 位置

正如其他人所说,这可能是由于您从不同文件中导入 Vue 的位置相互矛盾。因此,例如,您的代码中可能同时包含 import Vue from 'vue'import Vue from 'vue.runtime.esm'

但这可以result in multiple instances of Vue,会导致这些错误。

在这种情况下,解决方案是在代码中的任何地方使用import Vue from 'vue',然后在打包系统(webpack、Parcel、rollup 等)中为其设置别名。 webpack.config.jswebpack.renderer.config.js(如果您使用的是 Electron)中的一个示例是:

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

见more examples in the Vue documents。

白名单

这也可能是因为需要将 Vue 列入白名单,例如,不是 webpack 中的externals 之一。

值得注意的是,Bootstrap Vue 从 2.0 到更高版本(肯定是 2.15(可能更早))的更改导致了同样的问题发生。

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

【讨论】:

从外部移除它们很重要。【参考方案2】:

追了一个小时后,我意识到我拥有的一个组件imported 也在访问Vue。该文件的顶部是import Vue from 'vue/dist/vue.esm'。其他所有文件都只是在做import Vue from 'vue',这是我双重导入的来源。

不同的 javascript 打包程序有不同的解决重复项的方法。对于 WebPack,Resolve Configuration 在依赖项导入不同的 Vue 实例的情况下可能会有所帮助。

【讨论】:

这也可以是[Vue warn]: this.$store not defined in component的分辨率。【参考方案3】:

这是我的情况 (https://***.com/a/62262296/4202997) 但我会在这里重复一遍以节省您的时间:我从 CDN 导入 vue。我只是删除了脚本,问题就解决了。

【讨论】:

以上是关于Vue 警告 $listeners 和 $attrs 是只读的的主要内容,如果未能解决你的问题,请参考以下文章

vue $attrs、$listeners使用

Vue2.x $attrs和$listeners

Vue2.x $attrs和$listeners

vue$attrs和$listeners的使用

vue中的$attrs和$listener简介

Vue 学习笔记:$attrs 和 $listeners 的用法