Day10目录
前言
组件是Vue.js最核心的功能,在前端应用中可以采用模块化的开发,实现可重用、可扩展。
组件是带有名字的可复用的Vue实例,因此在根Vue实例中的各个选项在组件中也一样可以使用,唯一的例外是el选项,这是只用于根实例的特有选项。
一、注册组件的基本步骤
- 组件的使用分成三个步骤:
1.创建组件构造器 调用Vue.extend()方法创建组件构造器
2.注册组件 调用Vue.component()方法注册组件
3.使用组件 在Vue实例的作用范围内使用组件
例:
<div id="hey">
<my-cpn></my-cpn>
</div>
<script src="../vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>
`
})
//2.注册组件
Vue.component('my-cpn',cpnC)
const app = new Vue({
el: '#hey',
data: {
message: '你好啊!'
},
})
</script>
二、全局组件和局部组件
- 全局组件使用Vue.component()方法来注册,该方法接受两个参数,第一个参数是组件的ID(即名字),第二个参数是一个函数对象(使用Vue.extend()方法创建的组件构造器),也可以是一个选项对象
Vue.component ( id , [definition] )
案例:略,同上
- 局部组件是在Vue实例的选项对象中使用component选项来注册。
案例:
<div id="hey">
<my-cpn></my-cpn>
</div>
<script src="../../vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>
`
})
const app = new Vue({
el: '#hey',
data: {
message: '你好啊!'
},
components:{
'my-cpn': cpnC
}
})
</script>
三、父组件与子组件
<div id="hey">
<cpn2></cpn2>
<!-- 不能使用<cpn1></cpn1>-->
</div>
<script src="../../vue.js"></script>
<script>
//1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>
`
})
//2创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template:`
<div>
<h2>我是他</h2>
<p>不客气</p>
<cpn1></cpn1>
</div>
`,
components:{
cpn1:cpnC1
}
})
//根组件
const app = new Vue({
el: '#hey',
data: {
message: '你好啊!'
},
components:{
cpn2: cpnC2
}
})
</script>
四、注册组件语法糖
- Vue为了简化注册组件的过程,提供了注册的语法糖
- 主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>
`
})
const app = new Vue({
el: '#hey',
data: {
message: '你好啊!'
},
components:{
'my-cpn': cpnC
}
})
//语法糖形式
//1.全局组件
Vue.component('cpn1',{
template:`
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>`
})
//2.局部组件
const app= new Vue({
el: '#hey',
data: {
message: '你好啊!'
},
components:{
'my-cpn': {
template:`
<div>
<h2>我是他</h2>
<p>不客气</p>
</div>`
}
}
})
五、组件模板抽离方法
- 将template模板中的html分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。
- Vue提供了两种方案来定义html模块内容:
使用
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!--组件模板抽离的第一种写法-->
<script type="text/x-template" id="cpn1">
<div>
<h2>我是你</h2>
<p>谢谢</p>
</div>
</script>
<!--第二种写法:template标签-->
<template id="cpn2">
<div>
<h2>我是他</h2>
<p>不客气</p>
</div>
</template>
<script src="../../vue.js"></script>
<script>
// 注册组件:第一种模板抽离写法
Vue.component('cpn1',{
template: '#cpn1'
})
// 注册组件:第二种模板抽离写法
Vue.component('cpn2',{
template: '#cpn2'
})
const app = new Vue({
el: '#app',
data: {
msg: '欢迎使用Vue.js'
}
})
</script>
六、组件data必须是函数
- 这是因为组件是可复用的Vue实例,如果还是允许使用先前根实例的数据定义方式,那么所有复用的组件实例都将共享同一份数据,显然这很容易导致混乱。
- 采用函数定义方式,那么每个组件实例都将拥有自己的一份返回对象的独立拷贝,每复用一次组件,data函数就执行一次,从而返回一个新的数据对象。
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数:{{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../../vue.js"></script>
<script>
// 注册组件:第一种模板抽离写法
Vue.component('cpn',{
template: '#cpn',
data(){
return {
counter:0
}
},
methods:{
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})
const app = new Vue({
el: '#app',
data: {
msg: '欢迎使用Vue.js'
}
})
</script>
七、使用prop向子组件传递数据
-
给组件元素设置属性,组件内部如何接收呢?
首先需要在组件内部注册一些自定义的属性,成为prop,这些prop是放在组建的props选项中定义的;
之后在使用组件时,就可以把这些prop的名字作为元素的属性名来使用,通过属性向组件传递数据,这些数据将作为组件实例的属性被使用。
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{cmovies}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="../../vue.js"></script>
<script>
// 注册组件:第一种模板抽离写法
Vue.component('cpn',{
template: '#cpn',
props:['cmovies','cmessage'],
data(){
return {
}
}
})
const app = new Vue({
el: '#app',
data: {
message:'你好啊',
movies:['海王','你好,李焕英','唐人街探案']
}
})
</script>
八、prop验证
Vue.component('my-component', {
props: {
// 基础类型检测 (`null`和‘undefined’会通过任何类型验证)
age: Number,
// 多种类型
tell: [String, Number],
// 必传且是字符串
username: {
type: String,
required: true
},
// 数字,有默认值
sizeOfPage: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
greeting: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
info: {
validator: function (value) {
return value > 10
}
}
}
})
当prop验证失败了,如果使用的是开发版本会抛出一条警告。
type可以是下面的原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
type也可以是一个自定义构造器,使用instanceof检测。
九、驼峰标识
- HTML中的属性名是不区分大小的,浏览器在加载HTML页面的时候,会统一转换为小写字母,采用camelCase(驼峰命名法)的prop名要使用其等价的kebab-case(短横线命名)名字来使用。如果在字符串模板中或单文件组件内使用,则没有这个使用。
十、监听子组件事件(子传父)
- 在Vue.js中,通过自定义事件来实现子组件的某些功能和父组件通信。
- 子组件使用$emit()方法触发事件,负组件使用v-on指令监听子组件的自定义事件。
- $emit()方法的语法形式如下:
vm.$emit(eventName,[…args])
eventName是事件名,args是附加参数,这些参数会传给监听器的回调函数。如果子组件需要向父组件传递数据,就可以通过第二个参数来传。
<!--父组件模板-->
<div id="app">
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories"
@click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../../vue.js"></script>
<script>
//1.子组件
const cpn={
template:'#cpn',
data(){
return{
categories:[
{id:'aaa',name: '热门推荐'},
{id:'bbb',name: '手机数码'},
{id:'ccc',name: '家用家电'},
{id:'ddd',name: '电脑办公'},
]
}
},
methods:{
btnClick(item){
this.$emit('item-click',item)
}
}
}
//2.父组件
const app = new Vue({
el: '#app',
data: {
message:'你好啊',
},
components:{
cpn
},
methods:{
cpnClick(item){
console.log('cpnClick',item);
}
}
})
</script>
参考:
https://www.bilibili.com/video/BV15741177Eh?p=59
https://book.douban.com/subject/35019549/