组件化开发
组件化开发是vue的一大特色,也是一个很重要的编程思想。
那组件化是什么意思呢?
所谓组件化,就是把页面拆分成多个组件,每个组件依赖的 CSS、JS、模板、图片等资源放在一起开发和维护。 以函数作比喻,每个函数都有自己的功能,一个项目中可以多次调用相同的函数或者在别的类中可以导入然后使用此函数。那么在组件化中,每个组件就相当于一个函数(不知道这样比喻对不对,这是我的个人理解)。
那组件化有啥用?比如你在之前开发了一个项目,现在需要用到之前项目的一些东西,那么使用之前写好的组件进行复用就可以啦。
温馨提示:这章有点长建议多次食用。。。。。
1.组件的基本使用
使用组件的三步骤 (组件构造要放在构造vue对象前面)
1.创建组件构造器对象
const cpnC = Vue.extend({template:'模板内容'})
可以把 ’ ’ 换成 ` `(里面可换行)
2.注册组件
Vue.component('组件名',组件构造器对象)
使用此方法注册的是全局组件,任何一个vue对象都可以使用
若要创建局部组件,需在vue实例里面定义components属性的template里注册,后面会提到。
3.使用组件
<组件名></组件名> 或 <组件名/> 如果不用添加什么内容可以用单标签
tips:使用多个组件时或元素时需要使用div或者p标签包裹
组件的语法糖
Vue.component(‘组件名’,{template:‘模板内容’})
<div id="app">
<!-- 使用全局组件 -->
<cpn_quanju></cpn_quanju>
<!-- 如果不用添加什么内容可以用单标签 -->
<!-- 使用局部组件,因为在app注册的只能在app里面用 -->
<cpn_jubu/>
</div>
<div id="vm">
<h2>我是第2个div,我不可以使用在app注册的组件</h2>
<cpn_quanju></cpn_quanju>
</div>
<script src="..\vue.js"></script>
<script>
// 创建组件构造器对象
const cpnC_quanju = Vue.extend({
template:`
<div><h2>我是全局组件</h2></div>
`
})
// 注册全局组件
Vue.component('cpn_quanju',cpnC_quanju)
const cpnC_jubu = Vue.extend({
template:`
<div><h2>我是局部组件</h2></div>
`
})
const app = new Vue({
el: '#app',
components:{
// 注册局部组件
// 组件名:构造器对象
cpn_jubu:cpnC_jubu
}
})
new Vue({
el: '#vm'
})
</script>
</body>
看到这里应该有人会想,可不可以在组件之间添加自己想添加的内容呢,比如<cpn_quanju><h1>啊杭是帅B</h1></cpn_quanju>
,你试了你会发现,添加的内容好像没有显示在页面中,并不是因为啊杭不帅,而是因为这要用到后面的知识,在最后组件化高级开发那里会讲插槽,那里会解决你的困惑。
2.父子组件
组件之间也有爸爸跟儿子的关系?那谁是爸爸谁是儿子呢?
简单来说:谁含住了谁,谁就是爸爸 。并且儿子构造器需定义在爸爸构造器的前面
Talk is cheap,Show your the code. --Linus
<div id="app">
<cpn_fathor></cpn_fathor>
</div>
<script src="..\vue.js"></script>
<script>
const cpnC_son = Vue.extend({
template:`
<div><h2>我是儿子组件</h2></div>
`
})
const cpnC_father = Vue.extend({
template:`
<div>
<h2>我是爸爸组件</h2>
<cpnSon></cpnSon>
</div>
`,
components:{
cpnSon:cpnC_son
}
})
const app = new Vue({
el: '#app',
components:{
// 组件名:构造器对象
cpn_fathor:cpnC_father
}
})
</script>
</body>
3.组件模板抽离的写法
看了前面两节的代码是不是觉得组件的代码结构看上去有点复杂,一大坨代码在那,其实我们可以把template那部分的代码抽离出来。
方法有两种:
1.使用script标签 type一定要写text/x-template
2.使用template标签。常用
<body>
<div id="app">
<cpn></cpn>
</div>
<!-- 1.使用script标签 type一定要写text/x-template -->
<script type="text/x-template" id="isme1">
<div><h2>是我1</h2></div>
</script>
<!-- 2.使用template标签 常用 -->
<template id="isme2">
<div><h2>是我2</h2></div>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components:{
cpn:{
template:'#isme2'
}
}
})
</script>
</body>
4.组件的深入
接下来让我们一起深入组件吧。
组件是一个特殊的vue实例,但他不能引用vue实例的属性及方法,所以呢他有自己的data(但必须是一个函数),methods等。
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="isme2">
<div id="add">
<h2>当前计数:{{counter}}</h2>
<button @click="aaa">+</button>
<button @click="sub">-</button>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components:{
cpn:{
template:'#isme2',
data(){ // 必须是一个函数
return { // 返回一个对象
counter:0
}
},
methods:{
aaa(){this.counter++},
sub(){this.counter--}
}
}
}
})
</script>
</body>
为什么组件的data要是一个函数呢?其实是为了避免同个组件被调用多次时造成数据的同步修改。 上面这个例子调用了两次组件,如果data不是一个函数,那么这两个组件会调用同一个对象(因为函数每次执行的时候都地址都会改变),当一个组件修改counter时另一个组件的counter值也会随着改变。
tips:return返回的对象不能是一个先前创建好的对象,否则会变成每个cpn都去引用此对象而不是每个组件都去创建一个对象了
既然组件可以复用那就要考虑到数据会被共享的问题,vue在这方面做得很周到。
那组件深入就到这里啦。
5.父子之间的通信
作为父子,那么两父子之间有通信交流那是正常不过了。
父组件向子组件传值:
父组件传递参数::定义的属性名="要传递的值"
子组件接收参数的方式:
- props:[‘父组件中定义的属性名’]
例: props:['cmessage']
- props:{ 父组件中定义的属性名:指定类型}
例:props:{ cmessage:String}
- 父组件中定义的属性名{
type: 指定类型,
default: 指定默认值,
required: true|false 指定此属性是否必须传入
}
例cmessage:{ type:Array, default:[], required:true }
<body>
<div id="app">
<!-- 父亲传递数据 -->
<cpn :cmessage="message"></cpn>
<!-- 如果不动态绑定,只会当成字符串传过去 -->
<cpn cmessage="message"></cpn>
</div>
<template id="isme">
<div>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const cpn = {
template:'#isme',
props:['cmessage'] // 接收数据
}
// vue实例可看成父组件
const app = new Vue({
el: '#app',
data:{
message:['hang']
},
components:{
cpn
}
})
</script>
</body>
子组件向父组件传值:
子组件:
- methods:{this.$emit(‘自定义事件名称’, 数据)}
- 子组件标签上绑定@自定义事件名称=‘回调函数’
父组件:methods: { 回调函数() { //逻辑处理 } }
<body>
<div id="app">
<!-- 因为事件对象是自定义的所以就不会默认传event -->
<cpn @father_click="click"></cpn>
</div>
<template id="isme">
<div>
<button v-for="book in books" @click="Son_click(book)">
{{book}}
</button>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const cpn = {
template:'#isme',
data(){
return {
books:['C','JAVA','PYTHON']
}
},
methods:{
Son_click(book){
console.log('子组件----点击了',book)
this.$emit('father_click',book)
}
}
}
const app = new Vue({
el: '#app',
components:{
cpn
},
methods:{
click(item){
console.log('我是爸爸,我的儿子点击了'+item);
}
}
})
</script>
</body>
6.父子组件互相访问内容(了解)
以下介绍的方法尽量避免使用,因为父子关系一旦搞复杂了就难以debug了。就好比如父子俩老是侵入对方的隐私,这不太好,每个人有权利保留点秘密。
父组件访问子组件的东西
- this.$children[index] 不常用
- this.$refs.子组件标签的ref名字
<body>
<div id="app">
<div>
<cpn ref="son_ref"></cpn>
<button @click="btnclick">按钮</button>
</div>
</div>
<template id="isme">
<div>我是子组件</div>
</template>
<script src="..\vue.js"></script>
<script>
const cpn = {
template:'#isme',
data(){
return {
name:'儿子的名字'
}
},
methods:{
show(){
console.log('儿子是我')
}
}
}
const app = new Vue({
el: '#app',
methods:{
btnclick(){
// 1.$children
// console.log(this.$children);
// console.log(this.$children[0].show());
// console.log(this.$children[0].name);
// 2.$refs 需在组件上定义ref='' 不然默认空对象
console.log(this.$refs)
console.log(this.$refs.son_ref)
console.log(this.$refs.son_ref.show())
console.log(this.$refs.son_ref.name)
}
},
components:{
cpn
}
})
</script>
</body>
子组件访问父组件的东西
访问父组件:this.$parent.属性/方法
访问根:this.$root.属性/方法
<body>
<div id="app">
<div>
<cpn></cpn>
</div>
</div>
<template id="isme">
<div>
我是子组件
<button @click="btnclick">按钮</button>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const cpn = {
template:'#isme',
methods:{
btnclick(){
console.log(this.$parent.show())
console.log(this.$root.show())
}
}
}
const app = new Vue({
el: '#app',
methods:{
show(){
console.log('我是爸爸');
}
},
components:{
cpn
}
})
</script>
</body>
组件化高级开发-插槽
就快完了。。。。再坚持一会。。。
前面留有困惑的小伙伴,现在终于可以释然了!
首先了解一下什么是插槽。插槽就是一些方便我们需要添加新功能新东西的时候方便我们添加的东西。相比如usb插槽,充电插槽等,想用的时候在就能方便的时候。
1.插槽的基本使用
插槽的作用就是在引用组件的时候可以在组件标签内部添加自己想要的东西。
语法:<v-slot>不添加东西时,slot的默认值</v-slot>
<body>
<div id="app">
<cpn></cpn>
<cpn><button>我是扩展的按钮</button></cpn>
<cpn><p>我是扩展的p</p></cpn>
</div>
<template id="isme2">
<div id="add">
<h2>我是基础的</h2>
<slot></slot>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components:{
cpn:{
template:'#isme2',
}
}
})
</script>
</body>
2.具名插槽的使用
顾名思义,具名插槽就是有名字的插槽,当需要向组件标签添加多个功能的时候,可以指定每个功能添加到哪一个具体的插槽上。
语法:<v-slot name=''>默认值</v-slot>
使用方式(vue2.6的版本及以上):
<template v-slot:插槽的名字>要添加的内容</template>
语法糖:<template #插槽的名字>要添加的内容</template>
2.6以下的版本可自行百度
<div id="app">
<cpn>
<template><h2>没有名字的我修改的是默认值</h2></template>
<!-- <template v-slot:middle><h2>我来修改中间的啦</h2></template> -->
<template #middle>
<h2>我来修改中间的啦</h2>
<h2>我是来帮忙修改中间的</h2>
</template>
<template #left>
<h2>我来修改左边的啦</h2>
</template>
</cpn>
</div>
<template id="isme2">
<div id="add">
<slot>我是个默认值</slot>
<slot name="left">左边</slot>
<slot name="middle">中间</slot>
<slot name="right">右边</slot>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components:{
cpn:{
template:'#isme2',
}
}
})
</script>
</body>
3.编译作用域
插槽是有作用域的,即在插槽中能使用父容器里数据,但是不能使用子组件里的(数据)。
<div id="app">
<!-- 这里的isShow属于vue的 -->
<cpn v-show="isShow"></cpn>
</div>
<template id="me">
<h2>我是第一行,第二行不会显示</h2>
<!-- 这里的isShow属于组件的 -->
<h2 v-show="isShow">我是第二行,第一行才会显示</h2>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data:{
// 这里的isShow只会在vue实例的模板中才有效
isShow:true
},
components:{
cpn:{
template:'#isme',
data(){
return{
// 这里的isShow只会在id为me的组件模板中才有效
isShow:false
}
}
}
}
})
</script>
</body>
以上是定义slot时使用组件的数据。那么现在有一个需求,就是使用slot时需要用到组件的属性时呢?
- 需要在插槽定义的时候把数据作为实参传出去,跟父传子的方法差不多。
- 在使用插槽的时候创建一个插槽对象(我是这么理解的)
<div id="app">
<cpn></cpn>
<h2>以下部分是作用域插槽的展示</h2>
<cpn>
<!-- 相当于创建一个slot对象,然后用这个对象去访问传递过来的参数 -->
<template v-slot="myslot">
<i>我是在使用slot时调用组件的属性</i>
<i>{{myslot.cmessage}}</i>
</template>
</cpn>
</div>
<template id="isme">
<div>
<h2>我是组件部分</h2>
<!-- 相当于把cpn的message作为实参传递给slot对象的data形参
并对其进行动态绑定 -->
<slot :cmessage="message">
我是在定义时使用message(cpn里定义的数据)
<br>
{{message}}
</slot>
</div>
</template>
<script src="..\vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components:{
cpn:{
template:'#isme',
data(){
return {
message:'由组件传给slot的数据'
}
}
}
}
})
</script>
</body>
好啦,本章结束啦!!
希望对你们有帮助!
同步学习代码可以在我的guthub项目中查看