深入瞭解vue自定義組件之Prop

Prop就是在組件上自定義的特性
官方文檔

基本使用方式

子組件:PropDemo.vue

<template>
    <div>
        <p>{{myMsg}}</p>
    </div>
</template>

<script>
    export default {
        name: "PropDemo",
        props: {
            myMsg: {
                type: String,
                // 默認值,沒有傳入msg時使用
                default: 'hi prop'
            }
        }
    }
</script>

父組件

<template>
    <div>
        <prop-demo :my-msg="message" />
    </div>
</template>

<script>
    import PropDemo from '@/components/PropDemo'

    export default {
        name: "home",
        components: {
            PropDemo
        },
        data: () => ({
            message: 'hi vue.js'
        })
    }
</script>

標題Prop類型

props: {
    myMsg: {
        type: String,//字符串
        // 默認值,沒有傳入msg時使用
        default: 'hi prop'
    },
    count: Number,//數字
    isVueAwesome: Boolean,//布爾
    ids: Array,//數組
    author: Object,//對象
    callback: Function//函數
}

除了上述的類型外,還可以通過構造函數自定義一個類型

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}
props: {
    author: Person
}

單向數據流

所有的prop都是由父組件綁定到子組件,父組件更新prop,子組件會馬上刷新數據;
子組件不應該修改prop,否則會改變父組件中的數據,導致數據流變得混亂,難以理解。
如果子組件中需要修改prop,vue推薦了兩種方式

  1. 在子組件的data中定義一個屬性,將需要修改的prop用作其初始值

    props: ['initialCounter'],
    data: function () {
      return {
        counter: this.initialCounter
      }
    }
    
  2. 使用prop作爲初始值定義一個計算屬性

    props: ['size'],
    computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }
    

單向數據流反面例子下面的代碼是不推薦的

子組件

<template>
    <div>
        <p>{{author.name}} is {{author.feature}}</p>
    </div>
</template>

<script>
    export default {
        name: "PropDemo",
        props: {
            author: {
                type: Object,//對象
                default: function () {
                    return {
                        name: 'vue',
                        feature: 'awesome'
                    }
                }
            }
        },
        mounted() {
       		 //子組件中更新prop數據,會導致父組件中的數據發生無法預料的結果
            this.author.name = 'vuejs'
        }
    }
</script>

父組件

<template>
    <div>
        //由於子組件修改的prop,這裏並不會按照預期出現vue
        <p>{{author.name}}</p>
        <prop-demo  :author="author" />
    </div>
</template>

<script>
    import PropDemo from '@/components/PropDemo'

    export default {
        name: "home",
        components: {
            PropDemo
        },
        data: () => ({
            author: {
                name: 'vue',
                feature: 'Powerful'
            }
        })
    }
</script>

標題Prop 驗證

建議每一個prop都添加預期的條件
比如已經知道一個prop是一個布爾類型的屬性,就應該通過下面方式添加類型

props: {
	isVueAwesome: Boolean
}

或者

props: {
	isVueAwesome: {
		type: Boolean	
	}
}

更多的需求驗證方式

props: {
    // 基礎的類型檢查 (`null` 和 `undefined` 會通過任何類型驗證)
    propA: Number,
    // 多個可能的類型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認值的數字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認值的對象
    propE: {
      type: Object,
      // 對象或數組默認值必須從一個工廠函數獲取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函數
    propF: {
      validator: function (value) {
        // 這個值必須匹配下列字符串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }

當 prop 驗證失敗的時候,(開發環境構建版本的) Vue 將會產生一個控制檯的警告。

非prop的特性

非prop特性指的就是使用子組件的時候傳入一個屬性,但是子組件內部並沒有定義這個屬性

替換和合並

下面這段代碼中prop-demo標籤中的class和style是最常見的屬性,但是prop-demo組件中並沒有定義這兩個prop,所以他們就屬於非prop的特性

<template>
    <div>
        <prop-demo class="color-red" style="font-size: 20px"  />
    </div>
</template>

雖然沒有定義class和style屬性,但是vue並不會拋棄他們,而是將他們加到這個組件的根元素上,與根元素的樣式合併使用
但是並不是所有的非prop屬性都會合並
假如prop-demo的模板根元素是input,定義了屬性type爲number

<template>
    <input type="number" />
</template>

而父組件中也傳入了一個type爲date,這是傳入的type會替換掉子組件中的type,所以input的最終類型爲date

<template>
    <div>
        <prop-demo type="date" />
    </div>
</template>

禁用特性繼承

可以通過下面方式禁用根元素繼承特性(class和style除外

<script>
    export default {
        inheritAttrs: false,//禁用根元素繼承特性
        name: "PropDemo"
    }
</script>

禁用根元素繼承特性更多的是和$attrs配合使用
$attrs可以選擇子控件中的那個元素來繼承父組件傳入的特性(class和style除外
父組件

<template>
    <div>
        <prop-demo placeholder='Enter your age'/>
    </div>
</template>

子元素

<template>
    <div>
        <input type='number' v-bind='$attrs' />
    </div>
</template>

子元素的根元素爲div,顯然它不需要placeholder這個屬性,而input需要
只需要在input標籤中添加v-bind="$attrs"即可
在使用子組件的時候可以想html原生組件一樣,不需要關係根元素是什麼元素

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章