วิธีฟังการเปลี่ยนแปลง 'อุปกรณ์ประกอบฉาก'


257

ในเอกสาร VueJs 2.0ฉันไม่พบ hooks ที่จะฟังpropsการเปลี่ยนแปลง

VueJs มีตะขอเช่นนั้นonPropsUpdated()หรือคล้ายกัน?

ปรับปรุง

ตามที่ @wostex แนะนำฉันได้ลองใช้watchทรัพย์สินของฉัน แต่ไม่มีอะไรเปลี่ยนแปลง จากนั้นฉันก็รู้ว่าฉันมีคดีพิเศษ:

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

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

ฉันกำลังส่งผ่านmyPropองค์ประกอบหลักที่ได้รับไปยังchildองค์ประกอบ จากนั้นwatch: {myProp: ...}ไม่ทำงาน



1
ฉันไม่แน่ใจว่าฉันเข้าใจกรณีของคุณอย่างถูกต้อง คุณช่วยอธิบายเป้าหมายที่แน่นอนของคุณได้อย่างไร "เมื่อมีบางสิ่งเกิดขึ้นฉันต้องการให้มีสิ่งใดเกิดขึ้นที่นี่ (ที่ไหน?)" คุณกำลังพยายามเปลี่ยนค่า prop หลักเองหรือไม่? ถ้าใช่นี่เป็นกรณีที่แตกต่างกันโดยสิ้นเชิง
Egor Stambakio

@wostex นี่คือCodePen สิ่งที่แย่คือปากกากำลังทำงานอยู่ ...
Amio.io

1
ฉันเห็นปัญหาอยู่ที่อื่น แสดงรหัสของคุณฉันจะดู
Egor Stambakio

1
สวัสดี. ที่นี่: ChannelDetail.vue คุณไม่ได้นำเข้าส่วนประกอบ FacebookPageDetail ทุกที่ แต่ประกาศว่า: gist.github.com/zatziky/ฉันคิดว่ามันน่าจะเป็น FacebookChannelDetail.vue ที่นำเข้าสู่ ChannelDetail.vue เป็น FacebookPageDetail นอกเหนือจากที่ดูดี
Egor Stambakio

คำตอบ:


321

คุณสามารถwatchประกอบฉากเพื่อรันโค้ดบางอย่างได้เมื่อมีการเปลี่ยนแปลงอุปกรณ์

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
ขอบคุณสำหรับ wostex watchผมได้พยายามแล้วกับ จากตัวอย่างของคุณฉันรู้ว่าคดีของฉันแตกต่างกันเล็กน้อย ฉันได้อัปเดตคำถามของฉันแล้ว
Amio.io

1
ดูมันดูเหมือนว่า componentWillReceiveProps () ขอบคุณเช่น: D
yussan

@ yussan ใช่ แต่มันใช้กับเสาเดี่ยวที่คุณต้องติดตาม
Egor Stambakio

1
ฉันรู้ว่านาฬิกาหมดกำลังใจเพราะปกติแล้วจะใช้ทรัพย์สินที่คำนวณได้ดีกว่า แต่มีปัญหาเรื่องประสิทธิภาพในการใช้นาฬิกาหรือไม่
SethWhite

1
@SethWhite จากสิ่งที่ฉันเข้าใจว่าการจัดการปฏิกิริยาใน Vueโดยใช้รูปแบบการสังเกตการณ์นักดูไม่จำเป็นต้องมีประสิทธิภาพน้อยกว่าการคำนวณหรือข้อมูลปฏิกิริยาอื่น ๆ ในกรณีที่มูลค่าของทรัพย์สินขึ้นอยู่กับหลาย ๆ ค่า prop ที่คำนวณแล้วจะเรียกใช้ฟังก์ชั่นเดียวกับการเรียกกลับที่แยกต่างหากสำหรับแต่ละค่าที่ผลลัพธ์ขึ้นอยู่กับการเรียกกลับ ฉันคิดว่าในกรณีการใช้งานทั่วไปส่วนใหญ่คุณสมบัติที่คำนวณแล้วจะมีผลลัพธ์ที่ต้องการ
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 :)
แกรนท์

2
ทันที: จริงแก้ไขให้ฉัน
basbase

2
การตั้งค่านาฬิกาแบบนี้เป็นสิ่งที่แก้ไขให้ฉันด้วยขอบคุณ!
adamk22

2
ทำงานให้ฉันด้วย นี่ควรเป็นคำตอบที่ได้รับการยอมรับ
titou10

1
ทำงานให้ฉันด้วยในขณะที่คำตอบที่ยอมรับไม่ได้ +1
Roberto

25

เขา,

ในกรณีของฉันฉันต้องการวิธีแก้ปัญหาเมื่อใดก็ตามที่อุปกรณ์ประกอบฉากใด ๆ เปลี่ยนไปฉันต้องแยกวิเคราะห์ข้อมูลของฉันอีกครั้ง ฉันเบื่อที่จะทำหน้าที่เฝ้าดูแยกสำหรับอุปกรณ์ประกอบฉากทั้งหมดของฉันดังนั้นฉันจึงใช้สิ่งนี้:

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

จุดสำคัญที่จะนำออกไปจากตัวอย่างนี้คือการใช้deep: trueดังนั้นมันจึงไม่เพียง แต่ดู $ props แต่ยังเป็นค่าที่ซ้อนกันเช่นprops.myProp

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับตัวเลือกนาฬิกาแบบขยายได้ที่นี่: https://vuejs.org/v2/api/#vm-watch


ดีใจที่ได้จ่ายไปข้างหน้า! โชคดี!
JoeSchr

7

คุณต้องเข้าใจลำดับชั้นขององค์ประกอบที่คุณมีและวิธีการส่งอุปกรณ์ประกอบฉากกรณีของคุณเป็นสิ่งพิเศษและไม่ได้พบกับ devs

พาเรนต์คอมโพเนนต์ -myProp-> คอมโพเนนต์ลูก -myProp-> คอมโพเนนต์ Grandchild

ถ้า myProp เปลี่ยนไปในองค์ประกอบพาเรนต์ระบบจะแสดงผลในองค์ประกอบย่อยด้วย

และหาก myProp เปลี่ยนไปในองค์ประกอบลูก ๆ มันก็จะแสดงให้เห็นในองค์ประกอบของหลานด้วย

ดังนั้นหาก myProp เปลี่ยนไปในองค์ประกอบหลักแล้วมันจะปรากฏในองค์ประกอบของหลาน (ดีมาก)

ดังนั้นลำดับชั้นที่คุณไม่ต้องทำอุปกรณ์ประกอบฉากใด ๆ จะมีปฏิกิริยาโดยเนื้อแท้

ตอนนี้กำลังพูดถึงการเพิ่มขึ้นของลำดับชั้น

หาก myProp เปลี่ยนไปในองค์ประกอบ grandChild จะไม่มีผลกับองค์ประกอบลูก คุณต้องใช้. sync โมดิฟายเออร์ในเหตุการณ์ child และ emit จากองค์ประกอบ grandChild

หาก myProp เปลี่ยนไปในองค์ประกอบลูกมันจะไม่ปรากฏในองค์ประกอบหลัก คุณต้องใช้. sync โมดิฟายเออร์ใน parent และปล่อยอีเวนต์จากองค์ประกอบลูก

หาก myProp เปลี่ยนไปในองค์ประกอบ grandChild จะไม่มีผลกับองค์ประกอบหลัก (ชัด ๆ ) คุณต้องใช้. sync โมดิฟายเออร์และปล่อยอีเวนต์จากองค์ประกอบของ grandchild จากนั้นเฝ้าดู prop ในคอมโพเนนต์ย่อยและปล่อยอีเวนต์เมื่อมีการเปลี่ยนแปลงซึ่งจะถูกฟังโดยองค์ประกอบหลักโดยใช้โมดิฟายเออร์

ลองดูโค้ดเพื่อหลีกเลี่ยงความสับสน

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 โดยตรงเนื่องจากค่าจะถูกเขียนทับเมื่อใดก็ตามที่องค์ประกอบหลักแสดงผลอีกครั้ง'

อีกครั้งที่ฉันกล่าวถึง devs ส่วนใหญ่ไม่พบปัญหานี้เพราะเป็นรูปแบบการต่อต้าน นั่นเป็นเหตุผลที่คุณได้รับคำเตือนนี้

แต่เพื่อที่จะแก้ปัญหาของคุณ (ตามการออกแบบของคุณ) ฉันเชื่อว่าคุณต้องทำงานข้างต้น (แฮ็คจะซื่อสัตย์) ฉันยังคงแนะนำให้คุณทบทวนการออกแบบของคุณใหม่

ฉันหวังว่ามันจะช่วย


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>

รายละเอียดเพิ่มเติม...

และคุณต้องใช้คุณสมบัติการเฝ้าดูในองค์ประกอบย่อยเพื่อรับฟังและอัพเดตการเปลี่ยนแปลงใด ๆ

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

ดังนั้นเราสามารถใช้ _objectDetail กับ html render ได้

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

หรือในบางวิธี:

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

0

คุณสามารถใช้โหมดนาฬิกาเพื่อตรวจจับการเปลี่ยนแปลง:

ทำทุกอย่างในระดับอะตอม ดังนั้นก่อนตรวจสอบว่าวิธีการดูตัวเองกำลังถูกเรียกหรือไม่โดยการปลอบใจบางสิ่งภายใน เมื่อมีการสร้างขึ้นว่านาฬิกาถูกเรียกใช้ให้ใช้ตรรกะทางธุรกิจของคุณ

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 มีคำตอบที่ดี นี่เป็นอีกวิธีหนึ่งในการทำเช่นนี้หากคุณไม่ต้องการ 'ลึก: จริง'

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.