这样的元素只能出现在某些其它元素内部。
在自定义组件中使用这些受限制的元素时会导致一些问题,例如:
以上中,是会被认为是无效内容,因为table中只能包裹td,th,tr等元素,变通方案是使用特殊的is属性:
(三)data必须是函数
我们先来看一个案例:
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '{{ counter }} ',
data: function () {
return data
}
})
new Vue({
el: '#example-2'
})
你会发现,三个按钮是联动的!!! 因为上面中,dada是一个函数,我们返回给每个组件的实例引用了同一个data对象,由于这三个组件共享了同一个data,因此增加一个counter会影响所有的组件
为了解决这个问题,我们需要将data设置为一个方法函数
data: function () {
return {
counter: 0
}
}
接下来,每个按钮都会有了自己的状态了
(四)构成组件 在Vue中,父子组件的关系可以总结为props down,events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息
先来看看prop 组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的props选项。 还是举例说明: 子组件要显示的用props选项声明它期待获得的数据:
Vue.component('my-component', {
//声明props
props: ['message'],
//就像data一样,prop可以用在模板内
//同样也可以在vm实例中像"this.message" 这样使用
template: '{{ message }}
'
})
在使用时可以这样传入一个普通字符串
特别注意:
HTML 特性是不区分大小写的。所以,当使用的不是字符串模版,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '{{ myMessage }} '
})
动态prop
在模板中,要动态地绑定父组件的数据到子模板的props,与绑定到任何普通的HTML特性相类似,就是用 v-bind。每当父组件的数据变化时,该变化也会传导给子组件:
var Child = {
props: ['myMessage'],
template: '{{ myMessage }}
'
}
new Vue({
el: "#app",
data: {
a:1
},
components: {
'my-component': Child
}
});
字面量语法VS动态语法
初学者常犯的一个错误就是使用字面量语法传递数值:
因为它是一个字面 prop ,它的值是字符串 "1" 而不是number。如果想传递一个实际的number,需要使用 v-bind ,从而让它的值被当作 JavaScript 表达式计算:
单项数据流
prop是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态--这会让应用的数据流难以理解
另外,每次父组件更新时,子组件的所有prop都会更新为最新值,这意味着你不应该在子组件内改变prop
对于为什么会有修改prop中数据的冲动,VUE官方给出的解释: 1.prop 作为初始值传入后,子组件想把它当作局部数据来用; 2.prop 作为初始值传入,由子组件处理成其它数据输出。
对于这两种原因,正确的应对方式时: 1.定义一个局部变量,并用prop的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
2.定义一个计算属性,处理 prop 的值并返回。
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
prop验证
我们可以为组件的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
type也可以是一个自定义构造器函数,使用instanceof检测。
(四)自定义事件 父组件是使用props传递数据给子组件,但是如果子组件要把数据传递回去,应该怎么做呢?那就是接下来要说的是自定义事件
使用v-on绑定自定义事件
来看一个例子 使用 $on(eventName) 监听事件 使用 $emit(eventName) 触发事件
Vue.component('button-counter', {
template: '{{ counter }} ',
data: function(){
return {
counter: 0
}
},
methods: {
increment: function() {
this.counter += 1
this.$emit('increment')
}
}
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function() {
this.total += 1
}
}
})
在本例中,子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件,至于父组件是否关心则与它无关。留意到这一点很重要。
使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定
非父子组件通信
有时候两个组件也需要通信(非父子关系)在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线:
var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
// ...
})
(五)使用Slot分发内容 为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板,这个过程被称作内容分发 (1)编译作用域 父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。 如果要绑定作用域内的指令到一个组件的根节点,你应当在组件自己的模板上做:
Vue.component('child-component', {
//有效,因为是在正确的作用域内
template: 'Child
',
data: function() {
return {
someChildProperty: true
}
}
})
(2)单个Slot
--------未完待续