(day9-2)
Prop的大小写、Prop类型、传递一个静态或动态Prop
单向数据流、Prop验证、非Prop的Attribute
一、Prop的大小写、Prop类型、传递一个静态或动态Prop
1、Prop的大小写(camelCase vs kebab-case)
camelCase(驼峰命名法)、kebab-case(短横线分隔命名)
在JavaScript中是驼峰命名,在HTML中将会是kebab-case。(因为浏览器会把所有的大写字符解释为小写字符)
2、Prop类型
# 通常看到的以字符串形式列出的prop:
props:['title','likes','isPublished','commentIds','author']
# 以对象的形式列出prop,这些property 的名称和值分别是 这些props各自的名称和类型:
props:{
title:String,
likes:Number,
isPublished:Boolean,
commentIds:Array,
author:Object,
callback:Function,
contactsPromise:Promise //or any other constructor
}
这样写可以为你组件的Prop提供文档,并且这种写法可以用在类型检查以及其他Prop验证,后面会讲到。
3、传递静态或动态Prop
# 静态赋值:直接给事先定义好的prop传入一个静态的值:
<blog-post title="My journey with Vue"><blog-post>
# 动态赋值:通过 v-bind 动态赋值:
<!--动态的赋予一个变量值-->
<blog-post v-bind:title="post.title"></blog-post>
<!--动态的的赋予一个复杂表达式的值-->
<blog-post v-bind:title="post.id + 'by' + post.author.name"></blog-post>
# 上述的传递给prop 的是字符串,实际上任何类型的值都可以传递给prop。
数字、布尔值、数组、对象、传入一个对象的所有property。
# 数字:
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue --> <!-- 这是一个 JavaScript 表达式而不是一个字符串。--> <blog-post v-bind:likes="42"></blog-post> <!-- 用一个变量进行动态赋值。--> <blog-post v-bind:likes="post.likes"></blog-post>
# 布尔值:(注意下面的第一个情况)
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。--> <blog-post is-published></blog-post> <!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue --> <!-- 这是一个 JavaScript 表达式而不是一个字符串。--> <blog-post v-bind:is-published="false"></blog-post> <!-- 用一个变量进行动态赋值。--> <blog-post v-bind:is-published="post.isPublished"></blog-post>
# 数组:
<!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue --> <!-- 这是一个 JavaScript 表达式而不是一个字符串。--> <blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post> <!-- 用一个变量进行动态赋值。--> <blog-post v-bind:comment-ids="post.commentIds"></blog-post>
# 对象:
<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue --> <!-- 这是一个 JavaScript 表达式而不是一个字符串。--> <blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics' }" ></blog-post> <!-- 用一个变量进行动态赋值。--> <blog-post v-bind:author="post.author"></blog-post>
# 一个对象的所有property:
post: { id: 1, title: 'My Journey with Vue' }
<blog-post v-bind="post"></blog-post> <!--上面的写法等价于--> <blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
二、单向数据流、 Prop验证、非Prop的Attribute
1、单向数据流
对于所有的prop,父子prop之间会形成一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。(这样可以防止子组件意外变更父组件的状态,从而导致你的数据流向难以理解)
(有点类似于复制变量值,子组件复制父组件的变量值,子组件的改变不会影响父组件的值,父组件的改变会赋值给子组件从而导致子组件的跟随父组件变化)
每次父级组件的发生变更,子组件中所有的prop都将会刷新为最新的值。
这意味着你不应该在一个子组件内部改变prop。(如果做了,Vue会在浏览器的控制台发出警告)
两种常见的试图变更一个prop 的情形:
# 这个prop用来传递一个初始值;这个子组件接下来希望将其作为一个本地的prop数据来使用。
//在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props:['initialCounter'],
data:function(){
return {
counter:this.initialCounter
}
}
# 这个prop以一种原始的值传入且需要进行转换。
//在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意:在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
2、Prop验证
# 验证
也就是在 props 中的值提供一个带有验证需求的对象,而不是字符串数组。如:
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'].indexOf(value) !== -1
}
}
}
})
基础检查、多可能的类型、必填(required:true)、默认值(default:String/Object)、自定义验证函数(validator:function(){})
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
注意:那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如
data
、computed
等) 在default
或validator
函数中是不可用的。
# 类型检查
type 可以是原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
额外的,type
还可以是一个自定义的构造函数,并且通过 instanceof
来进行检查确认。例如,
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
//使用下面,来验证 author prop 的值是否是通过 new Person 创建的。
Vue.component('blog-post', {
props: {
author: Person
}
})
3、非Prop的Attribute
一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute。
就是指一个Attibute,它传给了一个组件,但这个组件的props中没有定义这个Attirbute,那么这个Attribute就是 非Prop的Attribute。
因为显式定义的 prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。
# 替换/合并已有的Attribute
<!--<bootstrap-date-input> 的模板是这样的:-->
<input type="date" class="form-control">
<!--应用模板时-->
<!--为了给我们的日期选择器插件定制一个主题,我们可能需要像这样添加一个特别的类名-->
<bootstrap-date-input
data-date-picker="activated"
class="date-picker-theme-dark"
></bootstrap-date-input>
在这种情况下,我们定义了两个不同的 class
的值:
form-control
,这是在组件的模板内设置好的date-picker-theme-dark
,这是从组件的父级传入的
对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值。
所以如果传入type="text"
就会替换掉type="date"
并把它破坏!庆幸的是,
class
和style
attribute 会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:form-control date-picker-theme-dark
。
# 禁用Attribute继承
inheritAttrs: false ——不希望组件的根元素继承 attribute。
这尤其适合配合实例的 $attrs
property 使用。
有了 inheritAttrs: false
和 $attrs
,你就可以手动决定这些 attribute 会被赋予哪个元素。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
这个模式允许你在使用基础组件的时候更像是使用原始的 HTML 元素,而不会担心哪个元素是真正的根元素:
<base-input
label="Username:"
v-model="username"
required
placeholder="Enter your username"
></base-input>
注意
inheritAttrs: false
选项不会影响style
和class
的绑定。
- 类型:{ [key: string]: string }
- 只读
- 详细:
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class
和style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。