vue组件(二)--prop传递数据
组件组合
组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。然而,通过一个良好定义的接口来尽可能将父子组件解耦也是很重要的。这保证了每个组件的代码可以在相对隔离的环境中书写和理解,从而提高了其可维护性和复用性。
在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。
Prop
prop 是父组件用来传递数据的一个自定义属性。
父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 "prop"
使用Prop传递数据
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中。
子组件要显式地用 props
选项声明它预期的数据:
传递静态prop
<div id="app">
<child message="hello!"></child>
</div>
<script>
// 注册
Vue.component('child', {
// 声明 props
props: ['message'],
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({ el: '#app'})
</script>
为了便于理解,你可以将这个Vue实例看作child的父组件。
动态prop
类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:
<div id="app">
<div>
<input v-model="parentMsg">
<br>
<child v-bind:message="parentMsg"></child>
</div>
</div>
<script>
// 注册
Vue.component('child', {
// 声明 props
props: ['message'],
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
el: '#app',
data: {
parentMsg: '父组件内容'
}
})
</script>
# 传入一个对象的所有属性
如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind:prop-name
)。例如,对于一个给定的对象 post
:
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> |
属性值都绑定在这个模板上了。
#
<div id="app">
<ol>
<todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
</ol>
</div>
<script>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{todo}}</li>'
})
new Vue({
el: '#app',
data: {
sites:{
name:'张三',
age:24
}
}
})
</script>
#
<div id="app">
<ol>
<todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
</ol>
</div>
<script src="../js/lib/vue.min.js"></script>
<script>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{todo.name}}</li>'
})
new Vue({
el: '#app',
data: {
sites:[
{name:"pp"},
{name:"yy"},
{name:"haha"}
]
}
})
</script>
字面量语法vs动态语法
<comp some-prop="1"></comp>
定义prop时,要是不使用v-bind是字面量prop,传递了一个字符串而不是数值1,如果想传递一个真正的 JavaScript 数值,则需要使用 v-bind
,从而让它的值被当作 JavaScript表达式计算。
<div id="app">
<!-- 这里传递是字符串 -->
<child my-message="123+456"></child>
<!-- 这里用了动态语法,传递的值会通过js的表达式计算,传递的是数字 -->
<child :my-message="123+456"></child>
</div>
<script>
Vue.component("child", {
props: ["myMessage"],
template: "<div>计算结果为: {{myMessage}}</div>",
});
new Vue({
el: "#app",
});
</script>
Prop的大小写(camelCase vs kebab-case)
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) |
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post> |
重申一次,如果你使用字符串模板,那么这个限制就不存在了。
单向数据流
父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图改变一个 prop 的情形:
-
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
-
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
代码在线工具箱:http://www.matools.com/