首页 > 编程笔记 > JavaScript笔记
阅读:226
Vue父组件给子组件传值(超级详细)
在 Vue.js 中,可以在定义子组件中定义多个 prop 属性,用来接收父组件传过来的数据。也就是说,父组件可以通过子组件的 prop 属性,给子组件传递值。
定义一个 ViewCount 组件,显示通过 propCount 传入的值。在 vm 根实例组件中,注册并且使用 ViewCount 组件,显示单击按钮累计的单击次数,代码如下:
另外,每次父级组件发生变更时,子组件中所有的 prop 都将被刷新为最新的值。这意味着开发人员不应该在一个子组件的内部改变 prop。如果这样做了,Vue.js 则会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个prop的情形:
1) 使用 prop attribute 将一个初始值传递给子组件的本地属性,子组件直接操作本地属性。
2) 在子组件中定义计算属性,基于 prop attribute 传入的值进行计算处理。
代码如下:
为了定制 prop 的验证方式,开发人员可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组,代码如下:
实例的属性是在对象创建之前进行验证的,所以实例的属性(如 data 和 computed)在 default 和 validator 函数中不可用。
prop 支持的类型包括:String、Number、Boolean、Array、Object、Date、Function、Symbol,同时支持自定义的构造函数,能使用 instanceof 进行确认,代码如下:
显式定义的 prop 适用于向一个子组件传入信息,这也是 Vue.js 中推荐的做法,即向子组件传值的方式,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。
如下例子中的 son-component 组件的 notprop 属性,没有在 son-component 的 props 属性中定义,但是会被直接渲染到子组件的根元素(div元素)中,代码如下:
如上面的代码中,在 son-component 组件中定义了 class="subClass"name="subName",同时在组件使用的时候定义了:class="clsValue"和:name="nameValue",最后渲染的结果是 class="parentClass subClass"和name="parentName"。class 属性的值被合并了,而 name 属性的值只有一个:外面的值被替换了组件里面的 name 值。
因为继承的 attribute 只能作用到根元素上,如果需要将 attribute 继承到子组件的非根元素上,则可以使用 v-bind="$attrs" 将 attribute 绑定到子元素的非根元素上,代码如下:
定义一个 ViewCount 组件,显示通过 propCount 传入的值。在 vm 根实例组件中,注册并且使用 ViewCount 组件,显示单击按钮累计的单击次数,代码如下:
<div id="app">
<button v-on:click="clickMe">单击我</button>
<!-- 使用ViewCount组件显示单击次数 -->
<view-count :prop-count="count"></view-count>
</div>
<template id="viewCountTemplate">
<div>{{ propCount }}</div>
</template>
<script type="text/javascript">
const ViewCount = {
props: ['propCount'],
template: '#viewCountTemplate'
}
const vm = new Vue({
el: '#app',
components: {
'view-count': ViewCount
},
data: {
count: 0
},
methods: {
clickMe() {
this.count++;
}
}
});
</script>
传值的语法如下:
<子组件名称:prop 属性名称="表达式"></子组件名称>或者:
<子组件名称 v-bind:prop 属性名称="表达式"></子组件名称>
prop的大小写
HTML 中的 attribute 名对大小写不敏感,所以浏览器会把所有大写字符解释为小写字符。这意味着当开发人员使用 DOM 中的模板时,用 camelCase(驼峰命名法)命名的 prop 名需要使用其等价的 kebab-case(短横线分隔命名法)命名,代码如下:
Vue.component('sub-component', {
// 在 JavaScript 中使用的是 camelCase
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
});
// 在 HTML 中使用的是 kebab-case
<sub-component :post-title="hello"></sub-component>
prop的数据类型
prop 除了支持数字和 string 类型外,还支持其他类型,代码如下:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise
}
这不仅为组件提供了使用参考文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。使用方式和说明的样例代码如下。1) 传入一个数字
<!-- 即便‘42’是静态的,仍然需要v-bind来告诉Vue.js --> <!-- 这是一个JavaScript表达式而不是一个字符串. --> <sub-component v-bind:likes="42"></sub-component> <!-- 用一个变量进行动态赋值. --> <sub-component v-bind:likes="post.likes"></sub-component>
2) 传入一个布尔值
<!-- 包含该prop没有值的情况在内,都意味着true --> <sub-component is-published></sub-component> <!-- 即便'false'是静态的,仍然需要'v-bind'来告诉Vue.js --> <!-- 这是一个JavaScript表达式而不是一个字符串. --> <sub-component v-bind:is-published="false"></sub-component> <!-- 用一个变量进行动态赋值. --> <sub-component v-bind:is-published="post.isPublished"></sub-component>
3) 传入一个数组
<!-- 即便数组是静态的,我们仍然需要'v-bind'来告诉Vue.js --> <!-- 这是一个JavaScript表达式而不是一个字符串. --> <sub-component v-bind:comment-ids="[234, 266, 273]"></sub-component> <!-- 用一个变量进行动态赋值. --> <sub-component v-bind:comment-ids="post.commentIds"></sub-component>
4) 传入一个对象
<!-- 即便对象是静态的,仍然需要'v-bind'来告诉Vue.js -->
<!-- 这是一个JavaScript表达式而不是一个字符串. -->
<sub-component v-bind:author="{ name: 'Veronica', company: 'Meridian Dynamics' }"></sub-component>
<!-- 用一个变量进行动态赋值. -->
<sub-component v-bind:author="post.author"></sub-component>
5) 传入一个对象的所有property
如果开发人员要将一个对象的所有 property 都作为 prop 传入,则可以使用不带参数的 v-bind(取代 v-bind:prop-name)。将 user 对象的所有属性传递到组件中,代码如下:
<template id="subUserId">
<div>
<p>name: {{ userAttr.name }}</p>
<p>age: {{ userAttr.age }}</p>
</div>
</template>
<div id="app">
<!-- 传入用户对象 -->
<user-component :user="user"></user-component>
</div>
<script>
Vue.component('user-component', {
props: ['userAttr'],
template: '#subUserId'
});
const vm = new Vue({
el: "#app",
data: {
user: {
name: '张三',
age: 12
}
}
});
</script>
prop单向数据流
所有的 prop 都使其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。另外,每次父级组件发生变更时,子组件中所有的 prop 都将被刷新为最新的值。这意味着开发人员不应该在一个子组件的内部改变 prop。如果这样做了,Vue.js 则会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个prop的情形:
1) 使用 prop attribute 将一个初始值传递给子组件的本地属性,子组件直接操作本地属性。
2) 在子组件中定义计算属性,基于 prop attribute 传入的值进行计算处理。
代码如下:
<!-- 定义模板 -->
<template id="templateIn">
<div>
<!-- 初始值方式 -->
<p>
<span>传入的 counter: {{ counter }}</span><br>
sub Counter: <input v-model="subCounter" /><br>
<span>子组件更新 counter: {{ subCounter }}</span>
</p>
<!-- 计算属性方式 -->
<p>
<span>传入的 size: {{ size }}</span><br>
subSize: <input v-model="computeSize" /><br>
<span>子组件更新 size: {{ computeSize }}</span>
</p>
</div>
</template>
<div id="app">
counter: <input v-model="counter" /><br>
size: <input v-model="size" />
<sub-test :counter="counter" :size="size"></sub-test>
</div>
<script>
const subVue = {
props: ['counter', 'size'], // 定义prop属性接收父组件的值
data: function() {
return {
subCounter: this.counter // 给本地属性赋值
};
},
computed: {
computeSize: {
get: function() {
return 'result -> ' + this.size;
},
set: function(newValue) {
// 不能响应到父组件,会抛出异常
this.size = newValue.slice('result -> '.length);
}
}
},
template: '#templateIn'
};
const vm = new Vue({
el: "#app",
data: {
counter: 12,
size: 10
},
components: {
'sub-test': subVue
}
});
</script>
prop属性验证
开发人员还可以为组件的 prop 指定验证要求。如果有一个需求没有被满足,则 Vue.js 会在浏览器控制台中警告。这在开发一个会被别人用到的组件时尤其有帮助。为了定制 prop 的验证方式,开发人员可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组,代码如下:
<!-- 定义模板 -->
<template id="templateIn">
<div>
<span>{{ attr1 }}</span><br>
<span>{{ attr2 }}</span><br>
<span>{{ attr3 }}</span><br>
<span>{{ attr4 }}</span><br>
<span>{{ attr5.message }}</span><br>
<span>{{ attr6 }}</span><br>
</div>
</template>
<div id="app">
<son-component
:attr1="a1"
:attr2="a2"
:attr3="a3"
:attr4="a4"
:attr5="a5"
:attr6="a6">
</son-component>
</div>
<script>
const SonComponent = {
props: {
attr1: Number,
attr2: [String, Number],
attr3: {
type: String,
required: true
},
attr4: {
type: Number,
default: 10
},
attr5: {
type: Object,
default: function() {
return { message: 'hello' };
}
},
attr6: {
type: String,
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ["success", "warning", "danger"].indexOf(value) !== -1;
}
}
},
template: '#templateIn'
};
const vm = new Vue({
el: "#app",
components: {
SonComponent
},
data: {
a1: 1,
a2: 'hello',
a3: 'world',
a4: 11,
a5: { message: "hai" },
a6: 'success'
}
});
</script>
当 prop 验证失败时,Vue.js 将会发出一个控制台警告。实例的属性是在对象创建之前进行验证的,所以实例的属性(如 data 和 computed)在 default 和 validator 函数中不可用。
prop 支持的类型包括:String、Number、Boolean、Array、Object、Date、Function、Symbol,同时支持自定义的构造函数,能使用 instanceof 进行确认,代码如下:
<!-- 定义模板 -->
<template id="template1">
<div>
{{ personAttr.firstName }} {{ personAttr.lastName }}
</div>
</template>
<div id="app">
<son-component :person-attr="person"></son-component>
</div>
<script>
function Person(first, last) {
this.firstName = first;
this.lastName = last;
}
const SonComponent = {
template: '#template1',
props: {
personAttr: {
type: Person,
validator: function(value) {
return value instanceof Person;
}
}
}
};
const vm = new Vue({
el: "#app",
components: {
SonComponent
},
data: {
person: new Person("san", "zhang")
}
});
</script>
非prop的attribute
组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。显式定义的 prop 适用于向一个子组件传入信息,这也是 Vue.js 中推荐的做法,即向子组件传值的方式,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。
如下例子中的 son-component 组件的 notprop 属性,没有在 son-component 的 props 属性中定义,但是会被直接渲染到子组件的根元素(div元素)中,代码如下:
<!-- 定义模板 -->
<template id="template1">
<div class="subClass" name="subName">子组件</div>
</template>
<div id="app">
<son-component
:notprop="notPropValue"
:class="clsValue"
:name="nameValue">
</son-component>
</div>
<script>
const SonComponent = {
template: '#template1'
};
const vm = new Vue({
el: "#app",
data: {
notPropValue: "hello",
clsValue: "parentClass",
nameValue: "parentName"
},
components: {
SonComponent
}
});
vm.$data.notPropValue = "hai";
</script>
渲染代码如下:<div notprop="hai" class="parentclass subclass" name="parentName">子组件</div>div 中的 notprop="hai" 是从 son-component:notprop="notPropValue"/son-component 传递过去的。
1) 替换/合并已有的attribute
如果在子组件中也定义了非 prop 的 attribute,同时在使用组件的时候也定义了该 attribute,这时候最后的值,存在替换/合并问题。class 和 style 的值会被合并,其他属性值会被替换。如上面的代码中,在 son-component 组件中定义了 class="subClass"name="subName",同时在组件使用的时候定义了:class="clsValue"和:name="nameValue",最后渲染的结果是 class="parentClass subClass"和name="parentName"。class 属性的值被合并了,而 name 属性的值只有一个:外面的值被替换了组件里面的 name 值。
2) 禁用Attribute继承
如果开发人员不希望组件的根元素继承 attribute,则可以在组件的选项中设置 inheritAttrs:false因为继承的 attribute 只能作用到根元素上,如果需要将 attribute 继承到子组件的非根元素上,则可以使用 v-bind="$attrs" 将 attribute 绑定到子元素的非根元素上,代码如下:
<div id="app">
<son-component test="Value" required placeholder="请输入姓名"></son-component>
</div>
<script>
Vue.component('SonComponent', {
inheritAttrs: false,
template: '<div><input type="text" v-bind="$attrs"/></div>'
});
const vm = new Vue({
el: '#app'
});
</script>
注意,class 和 style 属性不在作用范围。
ICP备案:
公安联网备案: