VUE 父組件與子組件交互


1. 概述

1.1 說明

在項目過程中,會有很多重復功能在多個頁面中處理,此時則需要把這些重復的功能進行單獨拎出,編寫公用組件(控件)進行引用。在VUE中,組件是可復用的VUE實例,此時組件中的data必須是一個函數,因為data是一個函數時,每次引用此組件時相當於重新對返回對象進行獨立的拷貝(實例化/new),如果data不是一個函數而是一個對象,那么多個引用同一組件時其中一個引用更改數據,其他引用中的數據都會更改。由於業務的不同,組件中的數據交互也會不同。

1.2 父組件向組件傳遞數據(Prop)

   Prop可以在組件上注冊一些自定義特性,當一個值傳遞給一個prop特性的時候,它就變成了那個組件(子組件)實例的一個屬性,可以像訪問data中的值一樣去訪問prop中的特性(屬性列表)。任何類型的值都可以傳給一個prop,prop使得其父子prop之間形成一個單向下行綁定,即數據是在父組件中更改后才綁定到子組件中,父組件每次發生更新,子組件中所有的prop都將會刷新為最新的值。

  prop的常見書寫方式如:

    • props: ['name', 'address', 'desc']
    • props: { name: String, address: String, desc: String, userInfo: Object }

  1.2.1 子組件示例代碼

  子組件props中需要包含父組件流向的數據信息,以便顯示在子組件中(可以直接頁面綁定{{}},也可數據接收處理)。

<template>
  <div class="child-wrap">
    <p>姓名:{{name}}</p>
    <p>性別:{{sex}}</p>
    <p>年齡:{{age}}</p>
  </div>
</template>
<script>
  export default {
    props:{
      name:{
        type:String,
        default:''
      },
      sex:{
        type:String,
        default:''
      },
      age:{
        type:Number,
        default:0
      }
    }
  }
</script>
<style scoped>
</style>

  1.2.2 父組件示例代碼

  父組件中對子組件傳遞的數據像使用屬性一樣的去傳遞。

<template>
  <div class="parent-wrap">
    <child-component :sex="pSex" :name="pName" :age="pAge"></child-component>
  </div>
</template>
<script>
  import child from './child/child'
  export default {
    data() {
      return {
        pSex:'男',
        pName:'張三',
        pAge:28
      }
    },
    components: {
      "child-component": child
    },
  }
</script>
<style scoped>
</style>

1.3 父組件調用子組件事件

  父組件直接訪問子組件時,需要使用ref為子組件指定一個引用ID,渲染完成后即為指定子組件的實例,即可操作其內方法。

  1.3.1 子組件示例代碼

  子組件methods中需要包含父組件所調用的子組件方法,可以傳遞參數值從父組件至子組件,以便來更改或操作子組件中的某些數據。

<template>
  <div class="child-wrap">
    <button @click="confirmClick">子組件按鈕</button>
  </div>
</template>

<script>
  export default {
    methods: {
      confirmClick(val) {
        console.log(val)
      }
    }
  }
</script>

<style scoped>

</style>

  1.3.2 父組件示例代碼

  在父組件中,子組件使用ref來注冊引用信息,引用信息將會注冊在父組件的$refs對象上。

  注意:ref被用來給元素或子組件注冊引用信息,如果是dom元素,引用只想的就是dom元素,如果是組件,引用就指向組件實例。

  ref注冊時間:ref本身是作為渲染結果被創建的,在初始渲染的時候不能夠進行訪問它。

<template>
  <div class="parent-wrap">
    <child-component ref="child"></child-component>
    <button @click="parentClick" class="btnClass">父組件按鈕</button>
  </div>
</template>

<script>
  import child from './child/child'
  export default {
    data() {
      return {}
    },
    components: {
      "child-component": child
    },
    methods: {
      parentClick() {
        this.$refs.child.confirmClick("父組件調用");
      }
    }
  }
</script>
<style scoped>

</style>

1.4 子組件觸發父組件事件

   在子組件里用$emit向父組件觸發一個事件,父組件監聽這個事件。

  1.4.1 子組件示例代碼

  子組件methods中去注冊能夠調用父組件方法的入口。

<template>
  <div class="child-wrap">
    <button @click="confirmClick">子組件按鈕</button>
  </div>
</template>
<script>
  export default {
    methods: {
      confirmClick() {
        this.$emit('child-click','點擊了子組件按鈕')
      }
    }
  }
</script>
<style scoped>
</style>

  1.4.2 父組件示例代碼

  父組件的子組件中使用事件進行監聽所需調用的方法,其中@后跟隨的是子組件中注冊的自定義事件名,后邊所調用的方法為父組件中所定義的方法名。

<template>
  <div class="parent-wrap">
    <child-component @child-click="childClick"></child-component>
    <p>{{strChild}}</p>
  </div>
</template>
<script>
  import child from './child/child'
  export default {
    data() {
      return {
        strChild:''
      }
    },
    components: {
      "child-component": child
    },
    methods: {
      childClick(val) {
        this.$data.strChild=val;
      }
    }
  }
</script>
<style scoped>
</style>

  注意:也可直接props傳遞方法進行子組件調用父組件方法。

 

2. 數據交互

  在實際項目應用中,很多時候需要實時的獲取到子組件中的數據信息,即子組件數據更改父組件數據。此時就需要監控子組件數據是否更改(watch),而子組件則需要進行計算其數據集(computed)

2.1 代碼示例

  2.1.1 父組件示例代碼

  子組件每次調用時都需要是重新實例化的(每次調用子組件才會是單獨的組件),故可以使用v-if來進行處理

<template>
  <div>
    <Button
      type='primary'
      @click='addPerson()'>新增</Button>
    <Button
      type='primary'
      @click='editPerson()'>編輯</Button>

    <Modal
      v-model='childModalVisible'
      title="新增人員"
      width='800'
      @on-cancel='cancleModal'
      closable>
      <child-update v-if="childModalVisible" ref="childUpdate" :data='objChild' />
      <div slot='footer'>
        <Button @click='cancleModal'>取消</Button>
        <Button @click='submitModal' type='primary'>確定</Button>
      </div>
    </Modal>
    <Row style="margin-top: 20px; background: #f5f5f5;font-size: 40px;font-weight: 600;">
      <Col span="6">姓名:{{objChild.name}}</Col>
      <Col span="6">性別:{{objChild.sex}}</Col>
      <Col span="6">電話:{{objChild.phoneNo}}</Col>
      <Col span="6">地址:{{objChild.address}}</Col>
    </Row>
  </div>
</template>

<script>
  import ChildUpdate from '../components/updateChild'
  export default {
    name: "updateParent.vue",
    components: {
      ChildUpdate,
    },
    data(){
      return {
        childModalVisible:false,
        objChild:{}
      }
    },
    methods:{
      addPerson(){
        this.objChild={
          name: '',
          sex: 0,
          phoneNo: '',
          address:''
        };
        this.childModalVisible=true
      },
      editPerson(){
        this.objChild={
          name: '張三',
          sex: 0,
          phoneNo: '15136466666',
          address:'浙江省杭州市'
        }
        this.childModalVisible=true
      },
      cancleModal(){
        this.childModalVisible=false
      },
      submitModal(){
        if(this.$refs.childUpdate.validateForm('childPersonForm')){
          let bSubmit=true
          if (this.objChild.phoneNo) {
            const reg = /^[1][3,4,5,7,8][0-9]{9}$/
            if (!reg.test(this.objChild.phoneNo)) {
              bSubmit=false
            }
          }
          if(bSubmit){
            this.childModalVisible=false
          } else {
            this.$Message.error('請輸入正確的電話號碼!')
          }
        } else {
          this.$Message.error('請輸入正確的人員信息!')
        }
      }
    }
  }
</script>

<style scoped>

</style>

  2.1.2 子組件示例代碼

  通過監控子組件數據更新去更新父組件數據信息,使用watch

  通過計算屬性去判斷父組件數據更新賦值於子組件數據,使用 computed

<template>
  <div>
    <Form
      ref='childPersonForm'
      :model='childPersonForm'
      :rules='formRules'
      :label-width='90'>
      <Row>
        <Col span='12'>
          <FormItem label='姓名' prop='name'>
            <Input
              v-model='childPersonForm.name'
              :maxlength='20'
              type='text'
              placeholder='請輸入'></Input>
          </FormItem>
        </Col>
        <Col span='12'>
          <FormItem label='性別' prop='sex'>
            <Select v-model='childPersonForm.sex' clearable>
              <Option
                v-for='item in personSexOption'
                :value='item.value'
                :label='item.name'
                :key='item.value'>{{item.name}}</Option>
            </Select>
          </FormItem>
        </Col>
      </Row>
      <Row>
        <Col span='12'>
          <FormItem label='電話' prop='phoneNo'>
            <Input
              v-model='childPersonForm.phoneNo'
              :maxlength='11'
              type='text'
              @on-blur='telNoValidate'
              placeholder='請輸入'></Input>
          </FormItem>
        </Col>
        <Col span='12'>
          <FormItem label='住址' prop='address'>
            <Input
              v-model='childPersonForm.address'
              :maxlength='20'
              type='text'
              placeholder='請輸入'></Input>
          </FormItem>
        </Col>
      </Row>
    </Form>
  </div>
</template>

<script>
  export default {
    props: {
      data: {
        type: Object,
        default() {
          return {
            name: '',
            sex: 0,
            phoneNo: '',
            address:''
          }
        },
      },
    },
    data() {
      // eslint-disable-next-line
      const nameRules = (rule, value, callback) => {
        const reg = /[0-9]/
        const regEn = /[`~!@#$%^&*()_+<>?:"{},\\.\/;'[\]]/im
        const regCn = /[·!#¥(——):;“”‘、,|《。》?、【】[\]]/im
        if (value === '') {
          callback(new Error('姓名不能為空'))
        } else if (regEn.test(value) || regCn.test(value)) {
          callback(new Error('姓名不能包含特殊字符'))
        } else if (reg.test(value)) {
          callback(new Error('姓名不能包含數字'))
        } else {
          callback()
        }
      }
      return {
        formRules: {
          name: [{ required: true, validator: nameRules, trigger: 'blur' }],
        },
      }
    },
    computed: {
      personSexOption() {
        return [{value:0,name:'男'},{value:1,name:'女'}]
      },
      childPersonForm() {
        console.log(this.data)
        return this.data
      },
    },
    watch: {
      childPersonForm: {
        handler(oldVal, newVal) {
          this.data.name = newVal.name
          this.data.sex = newVal.sex
          this.data.phoneNo = newVal.phoneNo
          this.data.address = newVal.address
        },
        deep: true,
      },
    },
    methods: {
      telNoValidate() {
        if (this.childPersonForm.phoneNo) {
          const reg = /^[1][3,4,5,7,8][0-9]{9}$/
          if (!reg.test(this.childPersonForm.phoneNo)) {
            this.$Message.error('請輸入正確的電話號碼!')
          }
        }
      },
      /**
       * 表單驗證
       */
      validateForm(name) {
        let bReturn = false
        this.$refs[name].validate(valid => {
          if (valid) {
            bReturn = true
          } else {
            bReturn = false
          }
        })
        return bReturn
      },
    },
  }
</script>

<style scoped>

</style>

3. 父子組件生命周期鈎子執行順序

  • 加載渲染過程
    • 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 子組件更新過程
    • 父beforeUpdate->子beforeUpdate->子updated->父updated
  • 父組件更新過程
    • 父beforeUpdate->父updated
  • 銷毀過程
    • 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM