如何听'道具'变化

VueJs 2.0文档中,我找不到任何会监听props变化的钩子。

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

更新

正如@wostex所建议的,我尝试watch我的属性,但没有任何改变。然后我意识到我遇到了一个特殊的情况:

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


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

我将父组件接收到的myProp传递给child组件。那么watch: {myProp: ...}不工作。

460918 次浏览

你可以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>

不确定你是否已经解决了这个问题(如果我理解正确的话),但这是我的想法:

如果父节点接收myProp,并且你希望它传递给子节点并在子节点中观看它,那么父节点必须有myProp的副本(不是引用)。

< p >试试这个:

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>

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

你试过这个吗?

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

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

如果myProp是一个对象,通常不允许更改。所以,手表永远不会被触发。为什么myProp不被改变的原因是你在大多数情况下只设置myProp的一些键。myProp本身仍然是一个。 试着看myProp的道具,比如“myProp”。

你需要理解,你拥有的组件层次结构和你如何传递道具,你的情况肯定是特殊的,通常不会被开发人员遇到。

父组件- myprop ->子组件- myprop ->孙子 组件< / p >

如果myProp在父组件中被更改,它也会在子组件中会被反映出来

如果在子组件中更改myProp,则在孙辈组件中也更改会被反映出来

因此,如果myProp在父组件中被更改,那么它将在孙辈组件中会被反映出来。(到目前为止还不错)。

因此,在层次结构中,你不需要做任何事情,道具将具有固有的反应性。

现在说到等级的上升

如果在grandChild组件中改变myProp,则在child组件中不会被反射。你必须在child中使用.sync修饰符,并从grandChild组件中触发事件。

如果在子组件中更改myProp,则在父组件中不会被反射。你必须在父组件中使用.sync修饰符,并从子组件中触发事件。

如果在grandChild组件中改变myProp,则在父组件中不会被反射(显然)。你必须使用.sync修饰符子组件并从孙子组件发出事件,然后观察子组件中的道具并在更改时发出事件,该事件由使用.sync修饰符的父组件监听。

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

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>

但在这之后,你会情不自禁地注意到vue说的尖叫警告

避免直接更改prop,因为该值将被覆盖 每当父组件重新呈现。'

.

正如我之前提到的,大多数开发者不会遇到这个问题,因为这是一个反模式。这就是为什么你会得到这个警告。

但是为了解决你的问题(根据你的设计)。我相信你已经做了上面的工作(诚实地说,黑客)。我仍然建议你应该重新考虑你的设计,让它更不容易出现bug。

我希望这能有所帮助。

在我的情况下,我需要一个解决方案,任何时候任何道具都会改变,我需要再次解析我的数据。我厌倦了为我所有的道具制作分离的观察者,所以我用了这个:

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

从这个例子中得到的关键点是使用deep: true,因此它不仅监视$props,而且还监视它的嵌套值,例如props.myProp

你可以在这里了解更多关于这个扩展手表选项:https://v2.vuejs.org/v2/api/#vm-watch

手表功能应该放在子组件中。没有父母。

我使用计算属性,如:

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

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

props: [ 'resources' ]

你可以使用观察模式来检测变化:

在原子级别上做所有事情。首先检查watch方法本身是否被调用通过安慰内部的东西。一旦确定要调用手表,就用您的业务逻辑将其粉碎。

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

@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);
});
},

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

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


if (someValidation)
...
}
}


对我来说,这是一个礼貌的解决方案,让一个特定的道具发生变化,并用它创建逻辑

我将使用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) {
....
}
}

如果你的道具myProp有嵌套项,这些嵌套项将不会是反应性的,所以你需要使用类似lodash deepClone的东西:

<child :myProp.sync="_.deepClone(myProp)"></child>

就这样,不需要监视或其他任何东西。

道具和v型模型处理。如何将值从父对象传递给子对象,再从子对象传递给父对象。

另外,在Vue中改变道具也是一种反模式,所以你不应该改变子元素或组件中的道具值。使用$emit更改值,Vue将始终按预期工作。

/* COMPONENT - CHILD */
Vue.component('props-change-component', {
props: ['value', 'atext', 'anumber'],
mounted() {
var _this = this
    

this.$emit("update:anumber", 6)
    

setTimeout(function () {
// Update the parent binded variable to 'atext'
_this.$emit("update:atext", "4s delay update from child!!")
}, 4000)
    

setTimeout(function () {
// Update the parent binded v-model value
_this.$emit("input", "6s delay update v-model value from child!!")
}, 6000)
},
template: '<div> \
v-model value: \{\{ value }} <br> \
atext: \{\{ atext }} <br> \
anumber: \{\{ anumber }} <br> \
</div>'
})


/* MAIN - PARENT */
const app = new Vue({
el: '#app',
data() {
return {
myvalue: 7,
mynumber: 99,
mytext: "My own text",
}
},
mounted() {
var _this = this
    

// Update our variable directly
setTimeout(function () {
_this.mytext = "2s delay update mytext from parent!!"
}, 2000)
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">


<props-change-component
v-model='myvalue'
:atext.sync='mytext'
:anumber.sync='mynumber'>
    

</props-change-component>
  

</div>

对一些用例的有趣观察。

如果您通过道具从存储中监视数据项,并且在同一存储中多次更改数据项,则不会监视该数据项。

但是,如果您将数据项更改分离为相同突变的多个调用,它将被监视。

  • 这段代码不会触发监控器:

    // Somewhere in the code:
    this.$store.commit('changeWatchedDataItem');
    
    
    // In the 'changeWatchedDataItem' mutation:
    state.dataItem = false;
    state.dataItem = true;
    
  • 这段代码将在每个突变时触发观察者:

    // Somewhere in the code:
    this.$store.commit('changeWatchedDataItem', true);
    this.$store.commit('changeWatchedDataItem', false);
    
    
    // In the 'changeWatchedDataItem' mutation:
    changeWatchedDataItem(state, newValue) {
    state.dataItem = newValue;
    }
    

我下面的答案是适用的,如果有人使用Vue 2合成API。 所以setup函数将是

setup: (props: any) => {
watch(() => (props.myProp), (updatedProps: any) => {
// you will get the latest props into updatedProp
})
}

但是,您需要从组合API导入手表函数。

我认为在大多数情况下,Vue会在道具变化时更新组件的DOM。

如果这是你的情况,那么你可以使用beforeUpdate()updated()钩子(文档)来观看道具。

如果你只对newVal感兴趣而不需要oldVal,你可以这样做

new Vue({
el: '#app',
data: {
text: ''
},
components: {
'child': {
template: `<p>\{\{ myprop }}</p>`,
props: ['myprop'],
beforeUpdate() {
console.log(this.myprop)
},
updated() {
console.log(this.myprop)
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">
<child :myprop="text"></child>
<input v-model="text" placeholder="Type here to view prop changes" style="width:20em">
</div>

如果你添加道具,道具将会改变

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


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

默认情况下,组件中的道具是反应性的,您可以在组件中的道具上设置监视,这将帮助您根据需要修改功能。下面是一个简单的代码片段来展示它是如何工作的

setup(props) {
watch(
() => props.propName,
(oldValue, newValue) => {
//Here you can add you functionality
// as described in the name you will get old and new value of watched property
},
{ deep: true },
{ immediate: true } //if you need to run callback as soon as prop changes
)
}
希望这能帮助你得到你想要的结果。

在我的情况下,我没有得到任何信息,也无法检索信息。确保在watchs的主体中使用try and catch

我的情况

setup(props, { emit, attrs, slots, expose }) {
...
....
.....
}
.....
watch: {
isModalActive: function () {
console.log('action:: ', this.props.isModalActive) // here cause error undefined and no error information on my inspect element dunno why
}
},

所以当我试图console为其他

  watch: {
isModalActive: function () {
console.log('action:: ') // this work and printed
console.log('action:: ', this.props.isModalActive)
}
},