Cómo escuchar los cambios de 'accesorios'

257

En los documentos de VueJs 2.0 no puedo encontrar ningún gancho que escuche los propscambios.

¿VueJs tiene ganchos similares onPropsUpdated()o similares?

Actualizar

Como sugirió @wostex, intenté con watchmi propiedad pero nada cambió. Entonces me di cuenta de que tengo un caso especial:

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

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

Estoy pasando myPropque el componente principal recibe al childcomponente. Entonces el watch: {myProp: ...}no funciona.

Amio.io
fuente
1
No estoy seguro de entender su caso correctamente. ¿Puedes explicar cuál es tu objetivo exacto? "Cuando algo sucede, quiero que algo suceda aquí (¿dónde?)". ¿Estás tratando de cambiar manualmente el valor del accesorio principal? Si es así, este es un caso totalmente diferente.
Egor Stambakio
@wostex, aquí está el CodePen . Lo malo es que en el bolígrafo está funcionando ...
Amio.io
1
Ya veo, el problema está en otro lugar. Muéstrame tu código, lo echaré un vistazo.
Egor Stambakio
1
Hola. Aquí: ChannelDetail.vue no importa el componente FacebookPageDetail en ninguna parte, pero declare: gist.github.com/zatziky/… Supongo que debería ser FacebookChannelDetail.vue importado en ChannelDetail.vue como FacebookPageDetail Aparte de que se ve bien
Egor Stambakio

Respuestas:

322

Puede watchusar accesorios para ejecutar algún código con los cambios de accesorios:

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
fuente
1
Gracias por wostex, ya lo he intentado watch. Debido a su ejemplo, me di cuenta de que mi caso es un poco diferente. He actualizado mi pregunta.
Amio.io
1
mira como se ve como componentWillReceiveProps (), gracias por ejemplo: D
yussan
@yussan sí, pero se aplica a un solo accesorio del que debes estar al tanto.
Egor Stambakio
1
Sé que watch no se recomienda porque generalmente es mejor usar una propiedad calculada, pero ¿hay algún problema de rendimiento al usar watch?
SethWhite
1
@SethWhite Por lo que entiendo de cómo se maneja la reactividad en Vue usando el patrón de observador, los observadores no son necesariamente menos eficientes que los datos calculados u otros datos reactivos. En los casos en que el valor de una propiedad depende de muchos valores, un accesorio computarizado ejecutará una sola función frente a una devolución de llamada de vigilancia separada para cada uno de los valores de los que depende el resultado. Creo que en los casos de uso más comunes, una propiedad calculada tendría el resultado deseado.
plong0
83

¿Has probado esto?

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
fuente
77
Esto funcionó para mí, donde la respuesta no lo hizo, tal vez es una actualización. Gracias BearInBox :)
Grant
2
inmediato: verdadero lo arregló para mí
base
2
Configurar el reloj de esta manera es lo que también me arregló, ¡gracias!
adamk22
2
A mí también me funciona. Esta debería ser la respuesta aceptada
titou10
1
funcionó para mí también, mientras que la respuesta aceptada no. +1
Roberto
25

Él,

En mi caso, necesitaba una solución en la que cada vez que cambiara cualquier accesorio, necesitaba analizar mis datos nuevamente. Estaba cansado de hacer un vigilante separado para todos mis accesorios, así que usé esto:

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

El punto clave que se debe sacar de este ejemplo es usarlo deep: truepara que no solo vea $ props sino también valores anidados como, por ejemplo,props.myProp

Puede obtener más información sobre estas opciones de reloj ampliado aquí: https://vuejs.org/v2/api/#vm-watch

JoeSchr
fuente
Me alegro de pagarlo! ¡Buena suerte!
JoeSchr
7

Debe comprender la jerarquía de componentes que tiene y cómo está pasando los accesorios, definitivamente su caso es especial y los desarrolladores no suelen encontrarlo.

Componente principal -myProp-> Componente secundario -myProp-> Componente nieto

Si myProp se cambia en el componente principal, también se reflejará en el componente secundario.

Y si myProp se cambia en el componente hijo, también se reflejará en el componente nieto.

Entonces, si myProp se cambia en el componente principal , se reflejará en el componente nieto. (Hasta aquí todo bien).

Por lo tanto, en la jerarquía, no tiene que hacer nada, los accesorios serán inherentemente reactivos.

Ahora hablando de subir en jerarquía

Si myProp se cambia en el componente grandChild , no se reflejará en el componente secundario. Debe usar el modificador .sync en el elemento secundario y emitir un evento desde el componente grandChild.

Si myProp se cambia en el componente secundario , no se reflejará en el componente principal. Debe usar el modificador .sync en el elemento primario y emitir un evento desde el componente secundario.

Si myProp se cambia en el componente grandChild , no se reflejará en el componente principal (obviamente). Tiene que usar el modificador .sync hijo y emitir un evento desde el componente nieto, luego mirar el accesorio en el componente hijo y emitir un evento sobre el cambio que está escuchando el componente padre utilizando el modificador .sync.

Veamos un código para evitar confusiones.

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>

Pero después de esto, no ayudarás a notar las advertencias de vue diciendo

'Evite mutar un accesorio directamente ya que el valor se sobrescribirá cada vez que el componente principal se vuelva a representar'.

Nuevamente, como mencioné anteriormente, la mayoría de los desarrolladores no encuentran este problema, porque es un anti patrón. Es por eso que recibes esta advertencia.

Pero para resolver su problema (de acuerdo con su diseño). Creo que tienes que hacer el trabajo anterior (piratear para ser honesto). Todavía te recomiendo que vuelvas a pensar en tu diseño y que sea menos propenso a errores.

Espero que ayude.

El hombre observador
fuente
6

No estoy seguro de si lo ha resuelto (y si lo entiendo correctamente), pero esta es mi idea:

Si el padre recibe myProp, y desea que se lo pase al niño y lo vea en niño, entonces el padre debe tener una copia de myProp (no de referencia).

Prueba esto:

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

y en html:

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

de hecho, debes tener mucho cuidado al trabajar en colecciones complejas en tales situaciones (pasar pocas veces)

m.cichacz
fuente
Buena idea. No puedo simplemente validarlo en este momento. Cuando vuelva a trabajar con vue, lo comprobaré.
Amio.io
1
gracias @ m.cichacz, cometió el mismo error y lo arregló rápidamente gracias a su sugerencia de pasarle una copia al niño
undefinederror
4

para el enlace bidireccional debe usar el modificador .sync

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

más detalles...

y debe usar la propiedad watch en el componente secundario para escuchar y actualizar cualquier cambio

props: ['myprop'],
  watch: { 
    myprop: function(newVal, oldVal) { // watch it
      console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    }
  }
Md Mahamudul Hasan
fuente
Es cierto que esta es la nueva forma de observar los cambios de accesorios en los componentes secundarios.
Amio.io
2

Trabajo con una propiedad calculada como:

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

Los recursos son en este caso una propiedad:

props: [ 'resources' ]
Wouter Schoofs
fuente
1

Para mí, esta es una solución educada para obtener un cambio específico de accesorios y crear lógica con ellos.

Me gustaría utilizar propsy variables de computedpropiedades para crear la lógica después de recibir los cambios

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

Entonces, podríamos usar _objectDetail en html render

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

o de alguna manera:

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

Puede usar el modo reloj para detectar cambios:

Haz todo a nivel atómico. Por lo tanto, primero verifique si se llama o no al método de reloj consolando algo dentro. Una vez que se haya establecido que se está llamando ese reloj, destrúyalo con su lógica comercial.

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

Uso propsy computedpropiedades de variables si necesito crear lógica después de recibir los cambios

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

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

Si myProp es un objeto, no se puede cambiar de la forma habitual. por lo tanto, el reloj nunca se activará. la razón por la cual myProp no se cambia es que solo configura algunas teclas de myProp en la mayoría de los casos. el myProp en sí sigue siendo el indicado. intente ver accesorios de myProp, como "myProp.a", debería funcionar.

adocking
fuente
-1

La función de reloj debe colocarse en el componente secundario. No padre

Cong Nguyen
fuente
-1

@JoeSchr tiene una buena respuesta. Aquí hay otra forma de hacer esto si no quieres 'profundo: verdadero'.

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