Props 声明

sreing[] 写法

<template>
    <div>
        <!-- 设置自定义属性 (传递数据) -->
        <Son
            name="superman"
            age="21"
        />
    </div>
</template>
<script>
    import Son from './components/Son.vue';
    export default {
        name: 'App',
        components: { Son },
    };
</script>
<template>
    <div>
        <h2>姓名: {{ name }}</h2>
        <h2>年龄: {{ age }}</h2>
    </div>
</template>
<script>
    export default {
        name: 'Son',
        // 编写 props 配置项 (接收数据)
        props: ['name', 'age'],
    };
</script>

对象式写法(推荐)

<template>
    <div>
        <!-- 设置自定义属性 (传递数据) -->
        <Son
            name="superman"
            :age="21"
        />
        <!-- 注意, 这里使用 v-bind 绑定 age, 所以传递的是数值 -->
    </div>
</template>
<script>
    export default {
        name: 'Son',
        // 配置 props 属性 (接收数据)
        props: {
            name: String, // 限制 name 的属性值为 string 类型
            age: Number, // 限制 age 的属性值为 number 类型
        },
    };
</script>

传递 prop 的细节

// 假设有一个 post 对象
const post = {
    id: 1,
    title: 'My Journey with Vue',
};
<!-- 以下两种写法等效: -->
<BlogPost v-bind="post" />
<BlogPost
    :id="post.id"
    :title="post.title"
/>

单向数据流

<template>
    <div>
        <h1>{{ showApp() }}</h1>
        <button @click="num++">修改 age</button>
        <hr />
        <Son :age="num" />
    </div>
</template>
<script>
    import Son from './components/Son.vue';
    export default {
        name: 'App',
        components: { Son },
        data() {
            return { num: 21 };
        },
        methods: {
            showApp() {
                console.log('showApp');
                return 'App';
            },
        },
    };
</script>
<template>
    <div>
        <h2>{{ showSon() }}</h2>
        <h2>年龄: {{ age }}</h2>
        <button @click="age++">修改 age</button>
    </div>
</template>
<script>
    export default {
        name: 'Son',
        props: ['age'],
        methods: {
            showSon() {
                console.log('showSon');
                return 'Son';
            },
        },
    };
</script>

在子组件中 修改父组件传递过来的数据:控制台抛出警告、子组件的数据可以被成功修改、重新渲染子组件
参与子组件数据显示的方法 showSon,在页面完成时被调用一次;子组件修改数据后,重新渲染子组件,被调用一次

此时我们再在父组件中修改原数据,则子组件的数据也会被修改,就是说,子组件之前修改的数据会被覆盖
参与页面数据显示的方法 showAppshowSon,在页面完成时依次被调用一次;父组件修改数据后,页面重新渲染,再依次被调用一次

<template>
    <div>
        <h2>{{ showSon() }}</h2>
        <h2>年龄: {{ sonAge }}</h2>
        <button @click="sonAge++">修改 sonAge</button>
    </div>
</template>
<script>
    export default {
        name: 'Son',
        data() {
            return { sonAge: this.age }; // 将接收到的数据设置为组件自己的数据
        },
        props: ['age'],
        methods: {
            showSon() {
                console.log('showSon');
                return 'Son';
            },
        },
    };
</script>

此时,子组件渲染的是 data 中的数据 sonAge
更新子组件 data 中的数据,子组件会被重新渲染、showSon 被调用
更新父组件的数据,父组件会被重新渲染、showApp 被调用;此时子组件不会被重新渲染

注意:自己的数据不要与接收的数据同名,否则会报错(页面会优先显示 props 的数据

引用类型数据

<template>
    <div>
        <h1>{{ showApp() }}</h1>
        <button @click="sonObj.num++">修改 age</button>
        <hr />
        <!-- 传入对象数据 -->
        <Son :ageObj="sonObj" />
    </div>
</template>
<script>
    import Son from './components/Son.vue';
    export default {
        name: 'App',
        components: { Son },
        data() {
            return { sonObj: { num: 21 } };
        },
        methods: {
            showApp() {
                console.log('showApp');
                return 'App';
            },
        },
    };
</script>
<template>
    <div>
        <h2>{{ showSon() }}</h2>
        <h2>年龄: {{ ageObj.num }}</h2>
        <!-- 直接修改对象数据 -->
        <button @click="ageObj.num++">修改 ageObj</button>
    </div>
</template>
<script>
    export default {
        name: 'Son',
        props: ['ageObj'], // 接收对象数据
        methods: {
            showSon() {
                console.log('showSon');
                return 'Son';
            },
        },
    };
</script>

可以发现,页面初始化时,showAppshowSon 依次被调用
不论在子组件还是在父组件中修改数据,都能成功修改数据、并重新渲染组件模版,showSon 被调用

子组件传递数据给父组件

父组件给子组件传递函数,子组件给函数传入参数,从而实现子组件给父组件传递数据

  1. 通过自定义属性,父组件给子组件传递函数
  2. 子组件通过 props 属性接收该函数
  3. 在调用该函数时,传入数据作为参数
  4. 父组件就能 [以参数的形式] 接收到子组件传递过来的数据
<template>
    <div>
        <p>父组件数据:{{ msg }}</p>
        <hr />
        <!-- 给子组件绑定属性,传递方法 -->
        <Son :parentFun="parentFun" />
    </div>
</template>
<script>
    import Son from './components/Son.vue';
    export default {
        name: 'App',
        components: { Son },
        data() {
            return { msg: 'father' };
        },
        methods: {
            parentFun(val) {
                console.log('val', val);
                this.msg += val;
            },
        },
    };
</script>
<template>
    <div class="son">
        <!-- 调用方法,传入数据 -->
        <button @click="parentFun(sonMsg)">点击将子组件的数据传递给父组件</button>
    </div>
</template>
<script>
    export default {
        name: 'Son',
        props: ['parentFun'], // 设置 props 属性,接收方法
        data() {
            return { sonMsg: 'son' };
        },
    };
</script>

Prop 校验

{
    // 基础类型检查
    propA: Number, // 给出 `null` 和 `undefined` 值则会跳过任何类型检查
    // 多种可能的类型
    propB: [String, Number],
    // 必传, 且为 String 类型
    propC: {
        type: String,
        required: true
    },
    // Number 类型的默认值
    propD: {
        type: Number,
        default: 100
    },
    // 对象类型的默认值
    propE: {
        type: Object,
        // [对象] / [数组] 的默认值, 必须从一个工厂函数返回
        // 该函数接收组件所接收到的原始 prop 作为参数
        default(rawProps) {
            return { message: 'hello' }
        }
    },
    // 自定义类型校验函数
    propF: {
        validator(value) {
            // The value must match one of these strings
            // return false 则抛出警告 (开发模式下)
            return ['success', 'warning', 'danger'].includes(value)
        }
    },
    // 函数类型的默认值
    propG: {
        type: Function,
        // 不像对象或数组的默认, 这不是一个工厂函数. 这会是一个用来作为默认值的函数
        default() {
            return 'Default function'
        }
    }
}

一些个细节

prop 的类型

// 假设有 Person 类
class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
defineProps({
    author: Person, // 使用 Person 类约束类型
    // Vue 会通过 instanceof Person 来校验 author prop 的值是否是 Person 类的一个实例
});

Boolean 类型转换

// 假设组件设置如下 props
defineProps({
    disabled: Boolean,
});
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />
defineProps({
    disabled: [Boolean, Number],
});

发表回复