上一篇中谈到了子组件是不能引用父组件或Vue实例挂载的数据。这里Vue实例可看作根组件(root component)。
但是在开发中,往往一些数据确实需要从上层传递到下层:
如一个页面中,我们从服务器请求到了很多数据。但这些并非全部应用于一个组件来展示,其中一小部分需要包含的子组件来渲染,(这时我的评价是不如交给子组件去请求需要的数据,各组件请求自己的那份数据不是OK了吗),按照设计者的思路,此时并不会让子组件独自发送一个网络请求的,而是直接让其父组件(大组件)将数据传递给子组件(小组件)。嗯,回过头来想想雀食,一旦下面的子组件过多,所造成的冗余请求对服务器来说,简直是个灾难!
一般来说,组件可以有以下几种关系:
如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。
如何进行父子组件间的数据通信呢?Vue官方提到:
真实开发中,Vue实例与子组件的通信和父组件与子组件的通信过程是一样的。
(注:下面代码示例中,将Vue实例看作父组件,其包含子组件以简化代码)
在组件中,使用选项props来声明需要从父级接收到的数据。
方式一:字符串数组,数组中的字符就是传递时的属性名称(attribute)。
方式二:对象,里面可以设置传递时的类型,也可以设置默认值等。
- <div id="app">
-
- <cpn :cMovies="movies" :cMessage="message">cpn>
-
- div>
-
- <template id="cpn">
-
- <div>
- <ul>
- <li v-for="(item, index) in cmovies" :key="index">{{item}}li>
- ul>
- <h2>{{cmessage}}h2>
-
- div>
- template>
- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
-
- <script>
-
- // 父传子:props
- const cpn = {
- template: "#cpn",
- props: ['cmovies', 'cmessage'],// 数组写法,元素虽然是字符串,但可当做变量
- data() {
- return {
- }
- },
- methods: {
- },
- };
- const app = new Vue({
- el: "#app",
- data: {
- message: "你好",
- movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
- },
- components: {
- cpn
- }
- })
- script>
使用props数组方式接收父组件data数据时,一定要记住v-bind(或:),否则无法解析,会当成字符串输出。
使用props对象方式的话,需要进行类型验证。
数据类型 可以是下列原生构造函数中的一个:
String
Number
Boolean
Array
Object
Date
Function
Symbol
示例:
- Vue.component('my-component', {
- 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'].includes(value)
- }
- }
- }
- })
prop 对象和数组的默认值为什么要为工厂函数的形式返回?原因在这里
额外的,type
还可以是一个自定义的构造函数,并且通过 instanceof
来进行检查确认。例如,给定下列现成的构造函数:
|
你可以使用:
|
来验证 author
prop 的值是否是通过 new Person
创建的。
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
|
|
如果你使用字符串模板,那么这个限制就不存在了。
示例:
如果props定义的值只是首字母大写(如Cuser),则绑定数据时使用Cuser或C-user均可。
通过自定义事件($emit)可以实现子组件传递数据或事件到父组件中。
主要流程:
这部分直接看示例代码吧,用白话讲有点绕。
-
- <div id="app">
-
- <cpn @itemclick="cpnClcik">cpn>
-
- div>
-
-
- <template id="cpn">
-
- <div>
- <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}button>
- div>
- template>
-
- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
-
- <script>
- // 父传子:props
- const cpn = {
- template: "#cpn",
- data() {
- return {
- categoties: [{
- id: 'aaa',
- name: '热门推荐'
- },
- {
- id: 'bbb',
- name: '手机数码'
- },
- {
- id: 'ccc',
- name: '家用家电'
- },
- {
- id: 'ddd',
- name: '电脑办公'
- },
- ]
- }
- },
- methods: {
- btnClick(item) {
- this.$emit('itemclick', item)
- }
- },
- };
- const app = new Vue({
- el: "#app",
- data() {
- return {
-
- }
- },
- methods: {
- cpnClcik(item) {
- console.log('cpnClick'+item.name);
- }
- },
- components: {
- cpn
- },
- })
- script>
参考: