组件
组件(Component)是 Vue.js 最核心的功能,也是整个框架设计最精彩的地方。
组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。
使用组件
组件注册
- 全局注册 全局注册之后,任何Vue实例都可以使用到这个组件,比如loading,msg
Vue.component('global-component',{
template:'<div>{{msg}}</div>',
data:function(){
return{
msg:'我是全局注册的组件'
}
}
})
- 局部注册,在Vue实例中,使用components选项可以局部注册组件,注册后的组件只在该实例作用域下有效
<template>
<div>
<global-component></global-component>
<local-component></local-component>
</div>
</template>
<script>
var localComponent = {
template:'<div>我是局部注册2的组件</div>'
}
export default {
components:{
localComponent
}
}
</script>
组件间的数据传递
组件之间不仅可以复用,更重要的是组件之间的通信,在Vue.js中,父组件要正向的向子组件传递数据或者参数,子组件接收后根据参数不同来渲染不同的内容进行操作,这个正向的过程就是通过props来实现的。
props
- 字符串数组
- 在子组件的
props选项中构造一个数组,接收一个来自父组件的数据msg,父组件只需要把数据填入子组件的对应的msg属性中 - 使用
v-bind可以动态绑定props的值,当父组件的数据变化时,也会传递给子组件 props是单向绑定的,父组件属性发生变化的时,传到给子组件,但是不会反过来。这是为了防止子组件无意间修改了父组件的状态,因此这是,单向的数据流- 通常我们有两种改变props的情况
- 作为初始值传入,子组件只是将其作为本地数据的初始值使用
- 作为需要被转变的值传入
- 在子组件的
<template>
<div>
<input type="text" name="" v-model="msg" id="">
<local-component :msg='msg'></local-component>
</div>
</template>
<script>
var localComponent = {
template:`<div>
<p>{{message}}</p>
<p>{{messageM}}</p>
</div>`,
props:['msg'],
data(){
return {
message:this.msg
}
},
computed:{
messageM(){
return '我是第二种:' + this.msg
}
}
}
export default {
data(){
return{
msg:'我是第一种'
}
},
components:{
localComponent
}
}
</script>
- 对象
- 当props数据需要验证的时候,就需要使用对象写法
- 对象可以设置
type、required、default、validator - 当数据验证不通过时,控制台会发出警告
<template>
<div>
<obj-component :msg='"sda"' :num=9 :arr="[1,2,3]"></obj-component>
</div>
</template>
<script>
var objComponent = {
template:`<div>
<p>{{msg}}</p>
<p>{{num}}</p>
<p>{{arr}}</p>
</div>`,
props:{
msg:{
type:String,
default:'msg',
required:true
},
arr:{
type: Array,
default:function(){
return []
}
},
num:{
type:Number,
validator:function(value){
return value < 10
}
}
},
data(){
return {}
}
}
export default {
data(){
return{
msg:''
}
},
components:{
objComponent
}
}
</script>
组件通信
子组件需要向父组件传递数据时,需要用到自定义事件
- 父组件用
v-on绑定自定义事件到子组件上 - 子组件需要通过
$emit()来触发事件- 通过自定义事件来传递数据
<template> <div> <event-component @setTotal="setTotal"></event-component> <p>value = {{value}}</p> </div> </template> <script> var eventComponent = { template:`<div> <button @click='add'>+</button> <button @click='reduce'>-</button> </div>`, data(){ return { count:0 } }, methods:{ add(){ this.count++; this.$emit('setTotal',this.count); }, reduce(){ this.count--; this.$emit('setTotal',this.count); } } } export default { data(){ return{ value:0 } }, components:{ eventComponent }, methods:{ setTotal(value){ this.value = value; } } } </script>- 使用
v-modal其实使用v-modal是@input的语法糖,甚至可以在子组件中进行数据的双向绑定,只需要满足两个要求- 接收一个
value属性 或者 直接在子组件v-model绑定 - 触发
input事件
- 接收一个
<template> <div> <!-- <event-component @input="setTotal"></event-component> --> <event-component v-model="value"></event-component> <p>value = {{value}}</p> </div> </template> <script> var eventComponent = { template:`<div> <input v-model='count' @input='updateCount' /> <button @click='add'>+</button> <button @click='reduce'>-</button> </div>`, data(){ return { count:0 } }, methods:{ add(){ this.count++; this.$emit('input',this.count); }, reduce(){ this.count--; this.$emit('input',this.count); }, updateCount(event){ this.$emit('input',this.count); } } } export default { data(){ return{ value:0 } }, components:{ eventComponent }, methods:{ setTotal(value){ this.value = value; } } } </script>
slot
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发。Vue.js 实现了一个内容分发 API ,参照了当前 Web组件规范草案,使用特殊的 <slot> 元素作为原始内容的插槽。
- 单个slot
<template>
<div>
<slot-component>
<p>parent to child slot</p>
</slot-component>
</div>
</template>
<script>
var slotComponent = {
template:`<div>
<p>我是子组件</p>
<slot>我是分发内容</slot>
</div>`,
data(){
return {
}
},
}
export default {
data(){
return{
}
},
components:{
slotComponent
},
}
</script>
渲染结果为
<div>
<p>我是子组件</p>
<p>parent to child slot</p>
</div>
- 具名slot 可以给slot指定一个name后分发多个内容,且具名的slot可以与单个slot共存,单个slot将作为一个默认的slot出现,即父组件没有使用slot属性的元素将出现在默认slot中
<template>
<div>
<slot-component>
<div slot='header'>
<p>我是头部2</p>
</div>
<div slot='footer'>
<p>我是底部</p>
</div>
<p>我是正文</p>
</slot-component>
</div>
</template>
<script></script>
渲染结果为
<div>
<div>
<p>我是头部</p>
</div>
<p>我是正文</p>
<div>
<p>我是底部</p>
</div>
<p>我是子组件</p>
</div>
- 可以通过
$slot访问某个具名的slot
组件的高级用法
-
递归组件
组件可以在模板内递归的调用自己,只要在组件中设置
name的选项,值得注意的是,需要设定一个条件值来限定其递归数量,否则会发生栈溢出的报错,一般用来开发一些未知层级关系的组件。<template> <div> <recursive-component :count="0"></recursive-component> </div> </template> <script> var recursiveComponent = { template:`<div> {{count}} <recursive-component :count="count + 1" v-if="count<3"></recursive-component> </div>`, name:'recursive-component', props:{ count:{ type: Number, default: 1 } } } export default { data(){ return{ } }, components:{ recursiveComponent }, } </script> -
动态组件
Vue.js提供了一个特殊的元素<component>来动态的挂载不同的组件,使用is特性来选择要挂载的组件。<template> <div> <!-- 动态组件 --> <component :is="targetComponent"></component> <button @click="handleChangeView('aComponent')">A</button> <button @click="handleChangeView('bComponent')">B</button> <button @click="handleChangeView('cComponent')">C</button> </div> </template> <script> var aComponent = { template:`<div> 我是A </div>`, } var bComponent = { template:`<div> 我是B </div>`, } var cComponent = { template:`<div> 我是C </div>`, } export default { data(){ return{ targetComponent:aComponent } }, components:{ aComponent, bComponent, cComponent }, methods:{ handleChangeView(component){ this.targetComponent = component } } } </script> -
异步组件
在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了让事情更简单, Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染