如何聆听“道具”的变化


256

VueJs 2.0文档中,我找不到任何可以监听props更改的钩子。

VueJ是否具有类似onPropsUpdated()或类似的钩子?

更新资料

正如@wostex所建议的,我尝试进入watch我的财产,但没有任何变化。然后我意识到我有一个特殊情况:

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

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

myProp将父组件接收到的child组件传递给了我。然后,watch: {myProp: ...}它不起作用。



1
我不确定我是否正确理解您的情况。您能解释一下您的确切目标吗?“当某些事情发生时,我希望某些事情在这里发生(在哪里?)。” 您是否要手动更改父道具值?如果是,这是完全不同的情况。
Egor Stambakio

@wostex,这是CodePen。坏事是笔在工作……
Amio.io

1
我知道,问题出在别的地方。给我看你的代码,我来看一下。
Egor Stambakio

1
你好。在这里:ChannelDetail.vue您不会在任何地方导入FacebookPageDetail组件,而是声明它:gist.github.com/zatziky/…我想应该是将FacebookChannelDetail.vue作为FacebookPageDetail导入到ChannelDetail.vue中,除此之外,它看起来还不错
Egor Stambakio

Answers:


320

您可以watch通过props更改props来执行一些代码:

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>


1
Thx for wostex,我已经尝试过了watch。根据您的示例,我意识到我的情况有所不同。我已经更新了我的问题。
Amio.io

1
观看它看起来像componentWillReceiveProps(),例如:D
yussan

@yussan是的,但是它适用于您需要跟踪的单个道具。
Egor Stambakio

1
我知道不建议使用watch,因为通常最好使用计算属性,但是使用watch是否存在性能问题?
赛斯怀特

1
@SethWhite从我对使用观察者模式在Vue中如何处理反应性的理解中,观察者的效率不一定比计算或其他反应性数据低。如果属性的值取决于许多值,则计算的prop将针对结果所依赖的每个值运行一个函数,而不是单独的watch回调。我认为在最常见的用例中,计算属性将具有所需的结果。
plong0

83

你尝试过这个吗?

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


7
这对我有用,答案没有-也许这是更新。谢谢BearInBox :)
Grant

2
立即:为我修复了该问题
basbase

2
像这样设置手表对我来说也很重要,谢谢!
adamk22 '19

2
也为我工作。这应该是公认的答案
titou10 '19

1
也是为我工作,而被接受的答案却没有。+1
罗伯托

25

他,

就我而言,我需要一个解决方案,只要任何道具都会发生变化,就需要再次解析我的数据。我已经厌倦了为所有道具做单独的观察者,所以我使用了这个:

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

该示例的重点是使用,deep: true因此它不仅监视$ props,而且还嵌套了诸如eg的值props.myProp

您可以在此处了解有关此扩展监视选项的更多信息:https : //vuejs.org/v2/api/#vm-watch


很高兴向您付款!祝好运!
JoeSchr

7

您需要了解您所拥有的组件层次结构以及如何传递道具,当然您的情况很特殊,开发人员通常不会遇到这种情况。

父组件-myProp->子组件-myProp->孙子组件

如果myProp在父组件中进行了更改,它也将反映在子组件中。

如果myProp在子组件中更改,它也将反映在子组件中。

因此,如果myProp在父组件中更改,那么它将反映在孙组件中。(到目前为止,一切都很好)。

因此,在层次结构中,您无需执行任何操作,道具本质上就是被动的。

现在谈论上级

如果在grandChild组件中更改了myProp,它将不会反映在子组件中。您必须在child中使用.sync修饰符,并从grandChild组件发出事件。

如果myProp在子组件中更改,则不会反映在父组件中。您必须在父级中使用.sync修饰符并从子级组件发出事件。

如果在grandChild组件中更改了myProp,则它不会反映在父组件中(很明显)。您必须使用.sync修饰符child并从孙子组件中发出事件,然后在子组件中观察prop并在父组件使用.sync修饰符侦听更改时发出事件。

让我们看一些代码以避免混淆

亲子

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

小孩

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

孙子

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

但是在此之后,您将不会注意到vue的尖叫警告

“避免直接更改道具,因为每当父组件重新渲染时,该值就会被覆盖。”

同样,正如我之前提到的,大多数开发人员都不会遇到此问题,因为这是一种反模式。这就是为什么您收到此警告。

但是为了解决您的问题(根据您的设计)。我相信您必须解决以上问题(老实说,hack)。我仍然建议您应该重新考虑您的设计,使它不易出现错误。

希望对您有所帮助。


6

不知道您是否已解决它(以及我是否理解正确),但这是我的想法:

如果父级收到myProp,并且您希望它传递给子级并在子级中观看,则父级必须具有myProp的副本(非引用)。

试试这个:

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

并在html中:

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

实际上,在这种情况下处理复杂的集合时(必须传递几次),您必须非常小心


好主意。我现在不能只是对其进行验证。当我再次使用vue时,将对其进行检查。
Amio.io

1
谢谢@ m.cichacz,犯了同样的错误,并由于您的建议将副本传给孩子,因此立即修复
undefinederror

4

对于两种方式的绑定,您必须使用.sync修饰符

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

更多细节...

并且您必须使用子组件中的watch属性来侦听和更新任何更改

props: ['myprop'],
  watch: { 
    myprop: function(newVal, oldVal) { // watch it
      console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    }
  }

没错,这是监视道具在子组件中变化的新方法。
Amio.io

2

我使用类似的计算属性:

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

在这种情况下,资源是一个属性:

props: [ 'resources' ]

1

对我来说,这是一种礼貌的解决方案,可以更改一个具体的道具并以此创建逻辑

接收更改后,我将使用props和变量computed属性创建逻辑

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

因此,我们可以在html渲染上使用_objectDetail

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

或以某种方法:

literallySomeMethod: function() {
   if (this._objectDetail) {
   ....
   }
}

0

您可以使用监视模式来检测更改:

在原子级别上做所有事情。因此,首先要通过监视内部的内容来检查watch方法本身是否被调用。一旦确定要调用手表,请使用您的业务逻辑将其粉碎。

watch: { 
  myProp: function() {
   console.log('Prop changed')
  }
}

0

如果需要在接收更改后创建逻辑,则使用props和变量computed属性

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

        if (someValidation)
        ...
    }
}

-1

如果myProp是对象,则通常不会更改它。因此,观看将永远不会被触发。之所以不能更改myProp的原因是,在大多数情况下,您只需设置myProp的某些键即可。myProp本身仍然是其中之一。尝试观看myProp的道具,例如“ myProp.a”,它应该可以工作。


-1

监视功能应放在子组件中。不是父母。


-1

@JoeSchr有一个很好的答案。如果您不希望'deep:true',这是另一种方法。

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.