vue组件选项props

8 篇文章 1 订阅

    组件接受的选项大部分与vue实例一样,而选项props是组建中非常重要的一个选项。在vue中,父子组件的关系可以总结为:

    props down,events up.

    父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。本文详细介绍vue组件选项props。

父子组件

在介绍props之前,先介绍父子组件的写法。

在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。

【正确写法】

<div id="example">
  <parent></parent>
</div>
<script>
var childNode = {
  template: '<div>childNode</div>',
}
var parentNode = {
  template: `
  <div class="parent">
    <child></child>
    <child></child>
  </div>
  `,
  components: {
    'child': childNode
  } 
};
// 创建根实例
new Vue({
  el: '#example',
    components: {
    'parent': parentNode
  }  
})
</script>

 

静态props

组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的props选项。

使用props传递数据包括静态和动态两种形式,下面先介绍静态props。

子组件要显式的使用props选项,来声明他期待获得的数据。

var childNode = {
    template: '<div>{{message}}</div>',
    props: ['message']
}

静态props通过为子组件在父组件中的占位符添加特性的方式来达到传值的目的。

<div id="example">
    <parent></parent>
</div>
<script>
var childNode = {
  template: '<div>{{message}}</div>',
  props:['message']
}

var parentNode = {
  template: `
  <div class="parent">
    <child message="aaa"></child>
    <child message="bbb"></child>
  </div>`,
  components: {
    'child': childNode
  }
};

// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

 

命名约定

对于props声明的属性来说,在父级HTML模板中,属性名需要使用中划线的写法

var parentNode = {
  template: `
  <div class="parent">
    <child my-message="aaa"></child>
    <child my-message="bbb"></child>
  </div>`,
  components: {
    'child': childNode
  }
};

子级props属性声明时,使用小驼峰或者中划线写法都可以;而子级模板使用父级传过来的变量时,需要使用对应的小驼峰写法。

var childNode = {
  template: '<div>{{myMessage}}</div>',
  props:['myMessage']
}


var childNode = {
  template: '<div>{{myMessage}}</div>',
  props:['my-message']
}

 

动态props

在模板中,要动态的绑定父组件的数据到子模板的props,与绑定到任何普通的html特性类似,就是使用v-bind。

每当父组件的数据变化时,该变化也会传导给子组件。

var childNode = {
  template: '<div>{{myMessage}}</div>',
  props:['myMessage']
}
var parentNode = {
  template: `
  <div class="parent">
    <child :my-message="data1"></child>
    <child :my-message="data2"></child>
  </div>`,
  components: {
    'child': childNode
  },
  data(){
    return {
      'data1':'aaa',
      'data2':'bbb'
    }
  }
};

 

传递数字

初学者常犯的一个错误是使用字面量语法传递数字

<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>
<div id="example">
  <my-parent></my-parent>
</div>
<script>
var childNode = {
  template: '<div>{{myMessage}}的类型是{{type}}</div>',
  props:['myMessage'],
  computed:{
    type(){
      return typeof this.myMessage
    }
  }
}
var parentNode = {
  template: `
  <div class="parent">
    <my-child my-message="1"></my-child>
  </div>`,
  components: {
    'myChild': childNode
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'MyParent': parentNode
  }
})
</script>

 

因为它是一个字面props,它的值是字符串“1”而不是number。如果想传递一个实际的number,需要使用v-bind,从而让它的值被当做JS表达式计算。

<!-- 传递实际的 number -->
<comp v-bind:some-prop="1"></comp>
var parentNode = {
  template: `
  <div class="parent">
    <my-child :my-message="1"></my-child>
  </div>`,
  components: {
    'myChild': childNode
  }
};

或者可以使用动态props,在data属性中设置对应的数字1

var parentNode = {
  template: `
  <div class="parent">
    <my-child :my-message="data"></my-child>
  </div>`,
  components: {
    'myChild': childNode
  },
  data(){
    return {
      'data': 1
    }
  }
};

 

props验证

可以为组件的props指定验证规格。如果传入的数据不符合规格,vue会发出警告,当组件给其他人使用时,这个很有用。

要指定验证规格,需要用对象的形式,而不能用字符串数组。

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    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 value > 10
      }
    }
  }

type可以是下面原生构造器

String
Number
Boolean
Function
Object
Array
Symbol

type也可以是一个自定义构造器函数,使用instanceof检测。

当props验证失败,vue会在抛出警告(如果使用的是开发版本)。props会在组件实例穿件之前进行校验,所以在default或者validator函数里,诸如data computed或methods等实例属性还是无法使用。

下面是一个简单例子,如果传入子组件的message不是数字,则抛出警告

<div id="example">
  <parent></parent>
</div>
<script>
var childNode = {
  template: '<div>{{message}}</div>',
  props:{
    'message':Number
  }
}
var parentNode = {
  template: `
  <div class="parent">
    <child :message="msg"></child>
  </div>`,
  components: {
    'child': childNode
  },
  data(){
    return{
      msg: '123'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

传入数字123时,则无警告提示。传入字符串'123'时,结果如下所示

 

将上面代码中,子组件的内容修改如下,可自定义验证函数,当函数返回为false时,则输出警告提示

var childNode = {
    template:'<div>{{message}}</div>',
    props:{
        'message':{
            validator: function(value){
                return value > 10
            }
        }
    }
}

在父组件中传入msg值为1,由于小于10,则输出警告提示

var parentNode = {
  template: `
  <div class="parent">
    <child :message="msg"></child>
  </div>`,
  components: {
    'child': childNode
  },
  data(){
    return{
      msg:1
    }
  }
};

 

 

单向数据流

props是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解

另外,每次父组件更新时,子组件的所有props都会更新为最新值。这意味着不应该在子组件内部改变props。如果这么做了,vue会在控制台给出警告。

下面是一个典型的例子

<div id="example">
  <parent></parent>
</div>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="childMsg">
    </div>
    <p>{{childMsg}}</p>
  </div>
  `,
  props:['childMsg']
}
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

父组件数据变化时,子组件数据会相应变化;而子组件数据变化时,父组件数据不变,并在控制台显示警告

修改子组件数据时,打开浏览器控制台会出现下图所示警告提示

 

修改props数据

修改props中的数据,通常来说有以下两种原因

1、props作为初始值传入后,子组件想把它当做局部数据来用

2、props作为初始值传入后,由子组件处理成其他数据输出

【注意】js中对象和数组是引用类型,指向同一个内存空间,如果props是一个对象或数组,在组子组件内部改变它会影响父组件的状态

对于这两种情况,正常的对应方式是:

1、定义一个局部变量,并用props的值初始化它

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

但是,定义的局部变量counter只能接受initialCounter的初始值,当父组件要传递的值发生变化时,counter无法接收到最新值

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  data(){
    return{
      temp:this.childMsg
    }
  },
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

2、定义一个计算属性,处理props的值并返回

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

但是,由于是计算属性,则只能显示值,而不能设置值。

<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  computed:{
      temp(){
        return this.childMsg
      }
  },
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

3、更加妥帖的方案是,使用变量存储props的初始值,并使用watch来观察props的值变化。发生变化时,更新变量的值

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  data(){
    return{
      temp:this.childMsg
    }
  },
  watch:{
    childMsg(){
      this.temp = this.childMsg
    }
  }
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值