Vue 2 - Mutasyona uğrayan sahne vue-warn


181

Https://laracasts.com/series/learning-vue-step-by-step serisine başladım . Vue, Laravel ve AJAX dersinde bu hatayla durdum :

vue.js: 2574 [Vue warn]: Üst bileşen yeniden oluşturulduğunda değerin üzerine yazılacağından, bir pervaneyi doğrudan değiştirmekten kaçının. Bunun yerine, prop değerini temel alan bir veri veya hesaplanan özellik kullanın. Değişime uğrayan prop: "list" (bileşen içinde bulunur)

Main.js içinde bu kodu var

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})

Ben liste pervane üzerine yazdığınızda, sorun yaratılmış () olduğunu biliyorum, ama ben Vue bir acemi, bu yüzden tamamen nasıl düzeltmek için bilmiyorum. Herkes nasıl düzeltmek için bir fikir var (ve lütfen nedenini açıklamak)?


2
Sanırım, bu sadece bir uyarı mesajı ve bir hata değil.
David R

Yanıtlar:


264

Bu, bir pervaneyi lokal olarak mutasyona uğratmanın Vue 2'de bir anti-desen olarak kabul edilmesi ile ilgilidir.

Bir pervaneyi yerel olarak mutasyona uğratmak istiyorsanız, şimdi yapmanız gereken data, propsdeğeri başlangıç ​​değeri olarak kullanan ve sonra kopyayı değiştiren bir alan bildirmektir :

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

Bununla ilgili daha fazla bilgiyi Vue.js resmi rehberinde okuyabilirsiniz.


Not 1: unutmayınız notu size gereken değil şunlara ait aynı adı kullanmak propvedata , yani:

data: function () { return { list: JSON.parse(this.list) } // WRONG!!

Not 2: yana ben bazı karışıklıklar var hissediyorum ilgili propsve reaktivite , ben bir göz atmanızı öneririm bu parçacığı


3
Bu harika bir cevap, ama aynı zamanda olay bağlamayı içerecek şekilde güncellenmesi gerektiğini düşünüyorum. Alt öğeden tetiklenen bir olay, üst öğeyi güncelleyebilir; bu, güncellenmiş destekleri alt öğeye geçirir. Bu şekilde, gerçeğin kaynağı tüm bileşenlerde hala korunur. Ayrıca, birden fazla bileşen arasında paylaşılan durumu yönetmek için Vuex'ten bahsetmeye değer olabilir.
Wes Harper

@WesHarper, ebeveynin güncelleme olayını otomatik olarak almak için .sync değiştiricisini kısayol olarak kullanmak da mümkündür .
danb4r

48

Vue deseni propsaşağı veevents yukarı. Kulağa basit geliyor, ancak özel bir bileşen yazarken unutması kolay.

Vue 2.2.0'dan itibaren v-modelini ( hesaplanan özelliklere sahip ) kullanabilirsiniz. Bu kombinasyonun bileşenler arasında basit, temiz ve tutarlı bir arayüz oluşturduğunu gördüm:

  • propsBileşeninize iletilen herhangi bir reaktif kalır (yani, klonlanmamıştır ve watchdeğişiklikler algılandığında yerel bir kopyayı güncellemek için bir işlev gerektirmez ).
  • Değişiklikler otomatik olarak üst öğeye gönderilir.
  • Çok sayıda bileşenle kullanılabilir.

Hesaplanan özellik ayarlayıcı ve alıcıyı ayrı ayrı tanımlamanıza izin verir. Bu, Taskbileşenin aşağıdaki gibi yeniden yazılmasına izin verir :

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  

Model özelliği tanımlar propile ilişkilidir v-model, ve olay değişiklikleri yayılacaktır. Daha sonra bu bileşeni üst öğeden aşağıdaki gibi çağırabilirsiniz:

<Task v-model="parentList"></Task>

listLocalBilgisayarlı mülkiyet bileşeni içinde basit bir alıcı ve ayarlayıcı arayüz sağlar (özel değişken olmak gibi düşünün). İçinde #task-templaterender yapabilirsiniz listLocalve reaktif kalacaktır (yani, eğer parentListdeğişiklikler yapılırsa Taskbileşeni güncelleyecektir ). Ayrıca değişebilirlistLocal ayarlayıcıyı (örn. this.listLocal = newList) ve üst öğeye değişiklik gönderir.

Bu modelin en güzel yanı, listLocalbir alt öğeye geçebilmenizdir .Task (kullanım v-model) değişikliklerin en üst düzey bileşene yayılmasıdır.

Örneğin EditTask, görev verilerinde bir tür değişiklik yapmak için ayrı bir bileşenimiz olduğunu varsayalım. Aynı v-modelve hesaplanmış özellikler desenini listLocalkullanarak bileşene geçebiliriz (kullanarak v-model):

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>

Eğer EditTaskyayar uygun şekilde arayacak bir değişiklik set()üzerinde listLocalüst seviyeye olayı yaymak böylece ve. Benzer şekilde, EditTaskbileşen aynı zamanda diğer alt bileşenleri (form öğeleri gibi) kullanarak da çağırabilir v-model.


1
Senkronizasyon dışında benzer bir şey yapıyorum. Sorun, değişiklik olayımı yaydıktan sonra başka bir yöntem çalıştırmam gerekiyor, ancak yöntem çalıştığında ve alıcıya ulaştığında, change olayı henüz dinleyici tarafından alınmadı / çocuğa geri yayılmadığından eski değeri alıyorum. Bu, üst güncelleştirme geri yayılana kadar yerel verilerimin doğru olması için pervaneyi değiştirmek istediğim bir durumdur. Bu konuda herhangi bir düşünceniz var mı?
koga73

Alıcıyı pasörden çağırmanın iyi bir uygulama olmadığını düşünüyorum. Düşünebileceğiniz şey, hesaplanan mülkünüze bir saat koymak ve mantığınızı oraya eklemektir.
chris

sahne aşağı ve olaylar yukarı benimle tıkladı. Bu VueJ'lerin belgelerinde nerede açıklanır?
nebulousGirl


Bence bu, ana bileşendeki değişiklikleri yaymanın daha acısız bir yolu.
Muhammed

36

Vue sadece sizi uyarır: bileşendeki pervaneyi değiştirirsiniz, ancak üst bileşen yeniden oluşturulduğunda, "list" yazılır ve tüm değişikliklerinizi kaybedersiniz. Bu yüzden bunu yapmak tehlikelidir.

Bunun yerine hesaplanan özelliği kullanın:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});

25
2 yönlü bağlama pervanesi ne olacak?
Josh

7
2 yönlü veri bağlama istiyorsanız, özel olayları kullanmalısınız, daha fazla bilgi için: vuejs.org/v2/guide/components.html#sync-Modifier Hala pervaneyi doğrudan değiştiremezsiniz, bir olay ve işleve ihtiyacınız vardır. sizin için bir pervane değişikliği yapar.
Marco

22

Lodash kullanıyorsanız, geri dönmeden önce pervaneyi klonlayabilirsiniz. Bu model, hem üst hem de alt öğedeki pervaneyi değiştirirseniz yardımcı olur.

Diyelim ki bileşen ızgarasında destek listemiz var .

Üst Bileşende

<grid :list.sync="list"></grid>

Alt Bileşende

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}

19

Sahne aşağı, olaylar yukarı. Bu Vue'nun Deseni. Mesele şu ki, bir ebeveynden geçen sahne değiştirmeye çalışırsanız. Çalışmaz ve üst bileşen tarafından tekrar tekrar üzerine yazılır. Alt bileşen yalnızca üst bileşene sth bildirmek için bir olay gönderebilir. Bu kısıtlamayı sevmiyorsanız, VUEX kullanabilirsiniz (aslında bu desen karmaşık bileşenler yapısında emilir, VUEX kullanmalısınız!)


2
Etkinlik otobüsü kullanma seçeneği de var
Steven Pribilinskiy

etkinlik otobüsü vue 3'te yerel olarak desteklenmiyor. github.com/vuejs/rfcs/pull/118
AlexMA

15

Alt bileşendeki desteklerin değerini değiştirmemelisiniz. Gerçekten değiştirmeniz gerekiyorsa kullanabilirsiniz .sync. Aynen böyle

<your-component :list.sync="list"></your-component>

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.$emit('update:list', JSON.parse(this.list))
    }
});
new Vue({
    el: '.container'
})

7

VueJs 2.0'a göre, bileşenin içindeki bir pervaneyi değiştirmemelisiniz. Sadece ebeveynleri tarafından mutasyona uğrarlar. Bu nedenle, farklı adlara sahip verilerdeki değişkenleri tanımlamalı ve gerçek sahne izleyerek onları güncel tutmalısınız. Liste pervane bir üst öğe tarafından değiştirilirse, üst öğeyi ayrıştırabilir ve mutableList öğesine atayabilirsiniz. İşte tam bir çözüm.

Vue.component('task', {
    template: ´<ul>
                  <li v-for="item in mutableList">
                      {{item.name}}
                  </li>
              </ul>´,
    props: ['list'],
    data: function () {
        return {
            mutableList = JSON.parse(this.list);
        }
    },
    watch:{
        list: function(){
            this.mutableList = JSON.parse(this.list);
        }
    }
});

Şablonunuzu oluşturmak için mutableList kullanır, böylece bileşeninizi pervane listesinde güvende tutarsınız.


izlemek pahalı değil mi?
Kick Buttowski

6

doğrudan bileşenlerde sahne değiştirmeyin. değiştirmeniz gerekiyorsa böyle yeni bir özellik ayarlayın:

data () {
    return () {
        listClone: this.list
    }
}

Ve listClone değerini değiştirin.


6

Cevap basit, bazı yerel bileşen değişkenlerine değer atayarak doğrudan prop mutasyonunu kırmalısınız (data özelliği, alıcılarla, ayarlayıcılarla veya izleyicilerle hesaplanabilir).

İşte izleyiciyi kullanarak basit bir çözüm.

<template>
  <input
    v-model="input"
    @input="updateInput" 
    @change="updateInput"
  />

</template>

<script>
  export default {
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      input: '',
    };
  },
  watch: {
    value: {
      handler(after) {
        this.input = after;
      },
      immediate: true,
    },
  },
  methods: {
    updateInput() {
      this.$emit('input', this.input);
    },
  },
};
</script>

Herhangi bir veri girişi bileşenleri oluşturmak için kullandığım ve gayet iyi çalışıyor. Üst öğeden gönderilen tüm yeni veriler (v-modeli (ed)) değer izleyici tarafından izlenir ve giriş değişkenine atanır ve giriş alındıktan sonra, bu eylemi yakalayabilir ve verinin girdi olduğunu gösteren üst öğeye girdi gönderebiliriz form öğesinden.


5

Ben de bu sorunla karşılaştım. Uyarı i kullandıktan sonra gitti $onve $emit. Kullanım gibi bir şeydir $onve $emitalt bileşenden üst bileşene veri gönderilmesi önerilir.


4

Destekleri değiştirmek istiyorsanız - nesneyi kullanın.

<component :model="global.price"></component>

bileşen:

props: ['model'],
methods: {
  changeValue: function() {
    this.model.value = "new value";
  }
}

Bir not olarak, nesneleri değiştirirken dikkatli olmalısınız. Javascript nesneleri başvuru ile iletilir, ancak bir uyarı vardır: Bir değere eşit bir değişken ayarladığınızda başvuru bozulur .
ira

3

Bunun gibi hesaplanmış bir yöntem eklemeniz gerekir

component.vue

props: ['list'],
computed: {
    listJson: function(){
        return JSON.parse(this.list);
    }
}

3

https://vuejs.org/v2/guide/components.html uyarınca tek yönlü Veri Akışı , bileşen tek Yönlü Veri Akışı'nı izler, Tüm sahne öğeleri alt öğe özelliği ve üst öğe arasında tek yönlü bir bağ oluşturur biri, üst mülk güncellendiğinde, alt öğeye aşağı doğru akacak, ancak başka bir şekilde değil, bu, alt bileşenlerin ebeveynin yanlışlıkla değiştirmesini önler ve bu da uygulamanızın veri akışının anlaşılmasını zorlaştırabilir.

Ayrıca, ana bileşen her güncellendiğinde, alt bileşenlerdeki tüm sahne donanımı en son değerle yenilenir. Bu, bir alt bileşenin içindeki bir pervaneyi değiştirmeye çalışmamanız gerektiği anlamına gelir. Bunu yaparsanız .vue sizi konsolda uyaracaktır.

Genellikle bir pervaneyi değiştirmenin cazip olduğu iki durum vardır: Pervane bir başlangıç ​​değerinde geçmek için kullanılır; alt bileşen daha sonra bunu bir yerel veri özelliği olarak kullanmak ister. Prop, dönüştürülmesi gereken ham bir değer olarak aktarılır. Bu kullanım örneklerine uygun yanıt: Prop'ın başlangıç ​​değerini başlangıç ​​değeri olarak kullanan bir yerel veri özelliği tanımlayın:

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

Prop değerinden hesaplanan hesaplanmış bir özelliği tanımlayın:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

2

Vue.js sahne, Vue'de bir Anti-Pattern olarak kabul edildiğinden mutasyona uğratılmamalıdır.

Uygulamanız gereken yaklaşım, bileşeninizde listenin orijinal prop özelliğine başvuran bir veri özelliği oluşturmaktır

props: ['list'],
data: () {
  return {
    parsedList: JSON.parse(this.list)
  }
}

Şimdi bileşene iletilen liste yapınıza başvurulur ve databileşeninizin özelliği aracılığıyla değiştirilir :-)

List özelliğinizi ayrıştırmaktan daha fazlasını yapmak istiyorsanız, Vue bileşeni ' computedözelliğinden yararlanın. Bu, aksesuarlarınıza daha derinlemesine mutasyonlar yapmanızı sağlar.

props: ['list'],
computed: {
  filteredJSONList: () => {
    let parsedList = JSON.parse(this.list)
    let filteredList = parsedList.filter(listItem => listItem.active)
    console.log(filteredList)
    return filteredList
  }
}

Yukarıdaki örnek, liste prop'ınızı ayrıştırır ve yalnızca etkin listelere filtreler , schnitts ve kıkırdamalar için oturumu kapatır ve döndürür.

not : hem data& computedözelliklerine şablonda aynı referans verilir;

<pre>{{parsedList}}</pre>

<pre>{{filteredJSONList}}</pre>

Bir computedmülkün (yöntem olarak) çağrılması gerektiğini düşünmek kolay olabilir ...


2
Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
      middleData() {
        return this.list
      }
    },
    watch: {
      list(newVal, oldVal) {
        console.log(newVal)
        this.newList = newVal
      }
    },
    data() {
      return {
        newList: {}
      }
    }
});
new Vue({
    el: '.container'
})

Belki bu ihtiyaçlarınızı karşılayacaktır.


2

En iyi cevaba ekleyerek,

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

Bir dizi tarafından sahne ayarlamak, geliştirme / prototipleme içindir, üretimde prop türlerini ( https://vuejs.org/v2/guide/components-props.html ) ayarladığınızdan ve pervane ayarlanmamışsa varsayılan bir değer ayarladığınızdan emin olun ebeveyn tarafından doldurulur.

Vue.component('task', {
    template: '#task-template',
    props: {
      list: {
        type: String,
        default() {
          return '{}'
        }
      }
    },
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

Bu şekilde mutableList, tanımsızsa en azından JSON.parse hatası yerine boş bir nesne alırsınız .


0

Vue.js bunu bir anti-desen olarak görüyor. Örneğin,

this.propsVal = 'new Props Value'

Bu sorunu çözmek için, bir Vue örneğinin desteklerinden verilere veya hesaplanan özelliğe bir değer almanız gerekir:

props: ['propsVal'],
data: function() {
   return {
       propVal: this.propsVal
   };
},
methods: {
...
}

Bu kesinlikle işe yarayacak.


0

Yukarıdakilere ek olarak, aşağıdaki sorunu yaşayan diğerleri için:

"Props değeri gerekli değilse ve bu nedenle her zaman döndürülmezse, iletilen veriler undefined(boş yerine) döndürülür ". Hangi <select>varsayılan değeri karıştırabilir, ben değerin aşağıdaki gibi ayarlandığını beforeMount()(ve değilse ayarlayın) kontrol ederek çözdüm:

JS:

export default {
        name: 'user_register',
        data: () => ({
            oldDobMonthMutated: this.oldDobMonth,
        }),
        props: [
            'oldDobMonth',
            'dobMonths', //Used for the select loop
        ],
        beforeMount() {
           if (!this.oldDobMonth) {
              this.oldDobMonthMutated = '';
           } else {
              this.oldDobMonthMutated = this.oldDobMonth
           }
        }
}

Html:

<select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">

 <option selected="selected" disabled="disabled" hidden="hidden" value="">
 Select Month
 </option>

 <option v-for="dobMonth in dobMonths"
  :key="dobMonth.dob_month_slug"
  :value="dobMonth.dob_month_slug">
  {{ dobMonth.dob_month_name }}
 </option>

</select>

0

Bir sürü kod, izleyici ve hesaplanan özellikleri kullanmaktan kaçınmaya yardımcı olan bu cevabı vermek istiyorum. Bazı durumlarda bu iyi bir çözüm olabilir:

Sahne donanımı tek yönlü iletişim sağlamak için tasarlanmıştır.

Bir show/hidepervane ile kalıcı bir düğmeniz olduğunda bana en iyi çözüm bir etkinlik yayınlamaktır:

<button @click="$emit('close')">Close Modal</button>

Ardından kalıcı öğeye dinleyici ekleyin:

<modal :show="show" @close="show = false"></modal>

(Bu durumda pervane showmuhtemelen gereksizdir, çünkü v-if="show"doğrudan bir temel modda bir kolay kullanabilirsiniz )


0

Şahsen her zaman, sahne malzemelerini mutasyona uğratmanız, önce bunları hesaplanmış mülke geçirmeniz ve oradan geri dönmeniz gerekiyorsa, daha sonra biri kolayca mutasyona uğrayabilir, hatta bir diğerinden mutasyon geçiriyorsa, prop mutasyonunu takip edebilirsiniz. bileşen de ya da biz de izleyebilirsiniz.


0

Vue sahne özellikleri tek yönlü veri akışı olduğundan, bu, alt bileşenlerin yanlışlıkla ebeveynin durumunu değiştirmesini önler.

Resmi Vue belgesinden, bu sorunları çözmenin 2 yolunu bulacağız

  1. alt bileşen sahne aksesuarlarını yerel veri olarak kullanmak istiyorsa, yerel veri özelliği tanımlamak en iyisidir.

      props: ['list'],
      data: function() {
        return {
          localList: JSON.parse(this.list);
        }
      }
    
  2. Prop, dönüştürülmesi gereken ham bir değer olarak aktarılır. Bu durumda, pervane değerini kullanarak hesaplanan bir özelliği tanımlamak en iyisidir:

      props: ['list'],
      computed: {
        localList: function() {
           return JSON.parse(this.list);
        },
        //eg: if you want to filter this list
        validList: function() {
           return this.list.filter(product => product.isValid === true)
        }
        //...whatever to transform the list
      }
    
    

0

EVET !, vue2'deki mutasyona uğrayan öznitelikler anti-modeldir. AMA ... Sadece diğer kuralları kullanarak kuralları çiğneyin ve ilerleyin! İhtiyacınız olan şey, paret kapsamındaki bileşen özniteliğinize .sync değiştirici eklemektir. <your-awesome-components :custom-attribute-as-prob.sync="value" />

Simple Basit batman öldürmek 😁


0

TypeScript tercih ettiğiniz dil olduğunda. gelişimin

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop({default: 0}) fees: any;

// computed are declared with get before a function
get feesInLocale() {
    return this.fees;
}

ve yok

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop() fees: any = 0;
get feesInLocale() {
    return this.fees;
}

0

Ben verince Aşağıda snack bar bileşeni olan snackbar değişkenini doğrudan v-modeline çalıştırdığımda çalışacak, ancak konsolda bir hata vereceğim

Üst bileşen her yeniden oluşturulduğunda değerin üzerine yazılacağından, bir pervaneyi doğrudan değiştirmekten kaçının. Bunun yerine, prop değerini temel alan bir veri veya hesaplanan özellik kullanın.

<template>
        <v-snackbar v-model="snackbar">
        {{ text }}
      </v-snackbar>
</template>

<script>
    export default {
        name: "loader",

        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },

    }
</script>

Bu mutasyon hatasından kurtulmanın doğru yolu kullanmaktır gözlemciyi

<template>
        <v-snackbar v-model="snackbarData">
        {{ text }}
      </v-snackbar>
</template>

<script>
/* eslint-disable */ 
    export default {
        name: "loader",
         data: () => ({
          snackbarData:false,
        }),
        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },
        watch: { 
        snackbar: function(newVal, oldVal) { 
          this.snackbarData=!this.snackbarDatanewVal;
        }
      }
    }
</script>

Bu çerez çubuğunu yükleyeceğiniz ana bileşende sadece bu kodu yapabilirsiniz

 <loader :snackbar="snackbarFlag" :text="snackText"></loader>

Bu benim için çalıştı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.