Como ouvir as alterações 'adereços'

257

Nos documentos do VueJs 2.0, não consigo encontrar nenhum gancho que ouça as propsalterações.

Os VueJs têm ganchos semelhantes onPropsUpdated()ou semelhantes?

Atualizar

Como o @wostex sugeriu, tentei watchminha propriedade, mas nada mudou. Então percebi que tenho um caso especial:

<template>
    <child :my-prop="myProp"></child>
</template>

<script>
   export default {
      props: ['myProp']
   }
</script>

Estou passando myPropque o componente pai recebe para o childcomponente. Então o watch: {myProp: ...}não está funcionando.

Amio.io
fonte
1
Não sei se entendi seu caso corretamente. Você pode explicar qual é seu objetivo exato? "Quando algo acontece, eu quero que algo aconteça aqui (onde?)". Você está tentando alterar manualmente o valor do suporte pai? Se sim, este é um caso totalmente diferente.
Egor Stambakio
@ wostex, aqui está o CodePen . A única coisa ruim é que na caneta que está funcionando ...
Amio.io
1
Entendo, o problema está em outro lugar. Mostre-me seu código, eu darei uma olhada.
Egor Stambakio
1
Olá. Aqui: ChannelDetail.vue, você não importa o componente FacebookPageDetail em nenhum lugar, mas o declara: gist.github.com/zatziky/… Acho que deveria ser o FacebookChannelDetail.vue importado para o ChannelDetail.vue como FacebookPageDetail, exceto o que parece ser bom
Egor Stambakio

Respostas:

322

Você pode watchadereços para executar algum código após as alterações de adereços:

new Vue({
  el: '#app',
  data: {
    text: 'Hello'
  },
  components: {
    'child' : {
      template: `<p>{{ myprop }}</p>`,
      props: ['myprop'],
      watch: { 
      	myprop: function(newVal, oldVal) { // watch it
          console.log('Prop changed: ', newVal, ' | was: ', oldVal)
        }
      }
    }
  }
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <child :myprop="text"></child>
  <button @click="text = 'Another text'">Change text</button>
</div>

Egor Stambakio
fonte
1
Thx para wostex, eu já tentei com watch. Devido ao seu exemplo, percebi que meu caso é um pouco diferente. Eu atualizei minha pergunta.
Amio.io
1
observe que ele se parece com componentWillReceiveProps (), obrigado por exemplo: D
yussan
@yussan sim, mas é aplicado a um único suporte que você precisa acompanhar.
Egor Stambakio
1
Sei que o watch é desencorajado porque geralmente é melhor usar uma propriedade computada, mas há algum problema de desempenho com o uso do watch?
SethWhite
1
@SethWhite Pelo que entendi de como a reatividade é tratada no Vue usando o padrão de observador, os observadores não são necessariamente menos eficientes do que os dados computados ou outros dados reativos. Nos casos em que o valor de uma propriedade depende de muitos valores, um suporte computado executará uma única função versus um retorno de chamada de observação separado para cada um dos valores dos quais o resultado depende. Eu acho que nos casos de uso mais comuns, uma propriedade computada teria o resultado desejado.
plong0
83

Você já tentou isso?

watch: {
  myProp: {
    // the callback will be called immediately after the start of the observation
    immediate: true, 
    handler (val, oldVal) {
      // do your stuff
    }
  }
}

https://vuejs.org/v2/api/#watch

BearInBox
fonte
7
Isso funcionou para mim, onde a resposta não - talvez seja uma atualização. Graças BearInBox :)
Grant
2
imediato: true fixa-lo para mim
basbase
2
Ajustar o relógio como este é o que o corrigiu para mim também, obrigado!
precisa saber é
2
Funciona para mim também. Esta deve ser a resposta aceita
titou10
1
funcionou para mim também, enquanto a resposta aceita não. 1
Roberto
25

Ele,

no meu caso, precisava de uma solução em que, a qualquer momento, qualquer objeto fosse alterado, precisava analisar meus dados novamente. Eu estava cansado de criar um observador separado para todos os meus adereços, então usei o seguinte:

  watch: {
    $props: {
      handler() {
        this.parseData();
      },
      deep: true,
      immediate: true,
    },

O ponto principal a ser retirado deste exemplo é usar deep: true lo, para que ele observe não apenas $ props, mas também valores aninhados, como por exemploprops.myProp

Você pode saber mais sobre essas opções de exibição estendida aqui: https://vuejs.org/v2/api/#vm-watch

JoeSchr
fonte
Fico feliz em pagar adiante! Boa sorte!
31919 JoeSchr
7

Você precisa entender, a hierarquia de componentes que você está tendo e como está passando os adereços. Definitivamente, seu caso é especial e geralmente não é encontrado pelos desenvolvedores.

Componente pai -myProp-> Componente filho -myProp-> Componente neto

Se myProp for alterado no componente pai, ele será refletido no componente filho.

E se myProp for alterado no componente filho, será refletido no componente neto.

Portanto, se myProp for alterado no componente pai, ele será refletido no componente neto. (Por enquanto, tudo bem).

Portanto, na hierarquia em que você não precisa fazer nada, os objetos serão inerentemente reativos.

Agora falando sobre subir na hierarquia

Se myProp for alterado no componente grandChild, não será refletido no componente filho. Você precisa usar o modificador .sync no filho e emitir um evento do componente grandChild.

Se myProp for alterado no componente filho, ele não será refletido no componente pai. Você precisa usar o modificador .sync no pai e emitir evento do componente filho.

Se myProp for alterado no componente grandChild, ele não será refletido no componente pai (obviamente). Você deve usar o modificador .sync filho e emitir um evento do componente neto; depois, assistir o componente prop no filho e emitir um evento de alteração que está sendo escutado pelo componente pai usando o modificador .sync.

Vamos ver algum código para evitar confusão

Parent.vue

<template>
    <div>
    <child :myProp.sync="myProp"></child>
    <input v-model="myProp"/>
    <p>{{myProp}}</p>
</div>
</template>

<script>

    import child from './Child.vue'

    export default{
        data(){
            return{
                myProp:"hello"
            }
        },
        components:{
            child
        }
    }
</script>

<style scoped>
</style>

Child.vue

<template>
<div>   <grand-child :myProp.sync="myProp"></grand-child>
    <p>{{myProp}}</p>
</div>

</template>

<script>
    import grandChild from './Grandchild.vue'

    export default{
        components:{
            grandChild
        },
        props:['myProp'],
        watch:{
            'myProp'(){
                this.$emit('update:myProp',this.myProp)

            }
        }
    }
</script>

<style>

</style>

Grandchild.vue

<template>
    <div><p>{{myProp}}</p>
    <input v-model="myProp" @input="changed"/>
    </div>
</template>

<script>
    export default{
        props:['myProp'],
        methods:{
            changed(event){
                this.$emit('update:myProp',this.myProp)
            }
        }
    }
</script>

<style>

</style>

Mas depois disso você não deixará de notar os gritos avisos do vue dizendo

'Evite alterar um suporte diretamente, pois o valor será substituído sempre que o componente pai for renderizado novamente.'

Novamente, como mencionei anteriormente, a maioria dos desenvolvedores não encontra esse problema, porque é um anti-padrão. É por isso que você recebe esse aviso.

Mas, para resolver seu problema (de acordo com o seu design). Eu acredito que você precisa fazer o trabalho acima (hackear para ser honesto). Eu ainda recomendo que você repense o seu design e faça com que seja menos propenso a erros.

Espero que ajude.

O Homem Observador
fonte
6

Não tenho certeza se você o resolveu (e se eu entendi corretamente), mas aqui está a minha ideia:

Se o pai receber o myProp e você desejar que ele passe para o filho e o assista no filho, o pai deverá ter uma cópia do myProp (não referência).

Tente o seguinte:

new Vue({
  el: '#app',
  data: {
    text: 'Hello'
  },
  components: {
    'parent': {
      props: ['myProp'],
      computed: {
        myInnerProp() { return myProp.clone(); } //eg. myProp.slice() for array
      }
    },
    'child': {
      props: ['myProp'],
      watch: {
        myProp(val, oldval) { now val will differ from oldval }
      }
    }
  }
}

e em html:

<child :my-prop="myInnerProp"></child>

na verdade, você precisa ter muito cuidado ao trabalhar em coleções complexas nessas situações (passando algumas vezes)

m.cichacz
fonte
Boa ideia. Não posso apenas validar isso no momento. Quando eu trabalhar com o vue novamente, vou verificar.
Amio.io
1
Obrigado @ m.cichacz, cometeu o mesmo erro e fixas prontamente graças a sua sugestão para passar uma cópia para baixo para a criança
undefinederror
4

para ligação bidirecional, é necessário usar o modificador .sync

<child :myprop.sync="text"></child>

mais detalhes...

e você deve usar a propriedade watch no componente filho para ouvir e atualizar as alterações

props: ['myprop'],
  watch: { 
    myprop: function(newVal, oldVal) { // watch it
      console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    }
  }
Md Mahamudul Hasan
fonte
É verdade que essa é a nova maneira de observar as alterações nos adereços nos componentes filhos.
Amio.io # 25/18
2

Eu trabalho com uma propriedade computada como:

    items:{
        get(){
            return this.resources;
        },
        set(v){
            this.$emit("update:resources", v)
        }
    },

Recursos é, neste caso, uma propriedade:

props: [ 'resources' ]
Wouter Schoofs
fonte
1

Para mim, essa é uma solução educada para obter alterações específicas de um suporte e criar lógica com ele

Eu usaria propse computedproperties de variáveis para criar lógica depois de receber as alterações

export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
  objectDetail: { // <--- we could access to this value with this.objectDetail
    type: Object,
    required: true
  }
},
computed: {
  _objectDetail: {
    let value = false
    // ...
    // if || do || while -- whatever logic
    // insert validation logic with this.objectDetail (prop value)
    value = true
    // ...
    return value 
  }
}

Então, poderíamos usar _objectDetail na renderização html

<span>
  {{ _objectDetail }}
</span>

ou em algum método:

literallySomeMethod: function() {
   if (this._objectDetail) {
   ....
   }
}
Manuel Alanis
fonte
0

Você pode usar o modo de exibição para detectar alterações:

Faça tudo no nível atômico. Portanto, verifique primeiro se o próprio método watch está sendo chamado ou não, consolando algo dentro. Depois de estabelecer que o relógio está sendo chamado, esmague-o com sua lógica de negócios.

watch: { 
  myProp: function() {
   console.log('Prop changed')
  }
}
Rahul Sharma
fonte
0

Uso propriedades propse variáveis computedse precisar criar lógica depois para receber as alterações

export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
    objectDetail: {
      type: Object,
      required: true
    }
},
computed: {
    _objectDetail: {
        let value = false
        ...

        if (someValidation)
        ...
    }
}
Manuel Alanis
fonte
-1

se myProp for um objeto, ele não poderá ser alterado normalmente. portanto, o relógio nunca será acionado. a razão pela qual o myProp não foi alterado é que você acabou de definir algumas chaves do myProp na maioria dos casos. o myProp em si ainda é o único. tente assistir a adereços do myProp, como "myProp.a", ele deve funcionar.

adocking
fonte
-1

A função de relógio deve colocar no componente Filho. Não é pai.

Cong Nguyen
fonte
-1

@JoeSchr tem uma boa resposta. aqui está outra maneira de fazer isso se você não quiser 'deep: true'.

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
Alvin Smith
fonte