一.vue组件化思想
如果我们将一个的页面的所有处理逻辑都放在一起,那么处理起来就变得非常复杂,而且也不利于后续的管理和扩展,如果我们把一个页面拆分成一个一个小的功能模块,每个模块完成对应的功能,组件之间相互独立,那么后期对这个页面的维护和管理也就变得简单了。
二.使用组件的步骤
1.创建组件构造器
调用Vue.extend()方法创建组件构造器
2.注册组件
调用Vue.component()方法注册组件
3.使用组件
在Vue实例的作用范围内使用组件
三.组件化的使用
1.组件化的基本使用
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: ` <div>
<h2>标题标题标题</h2>
<p>内容内容内容内容</p>
<p>内容内容内容内容啊啊啊啊</p>
<p>内容内容内容内容嘿嘿嘿嘿</p>
</div>`
})
//2.注册组件
Vue.component('my-cpn',cpnC)//my-cpn是组件的标签名
new Vue({ el: '#app' })
const app = new Vue({
el: '#app',//用于挂在要管理的元素
})
</script>
</body>
运行结果:
步骤解析:
2.全局组件和局部组件
上面的案例是使用的全局组件,全局组件意味着可以在多个vue实例下使用。
那么怎么才算是局部组件?
在实例里面注册组件。在开发中一般用的局部组件。而且一般创建一个实例,不会创建多个实例。
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: ` <div>
<h2>标题标题标题</h2>
<p>内容内容内容内容</p>
<p>内容内容内容内容hhhhh</p>
<p>内容内容内容内容嘿嘿嘿嘿</p>
</div>`
})
//2.注册组件
const app = new Vue({
el: '#app',//用于挂在要管理的元素
components:{
'my-cpn':cpnC
}
})
</script>
</body>
运行结果:
3.父组件和子组件
3.1父组件和子组件的区分
组件和组件之间是有层级关系的,一般的层级关系就是父子关系。
那怎么体现父子关系呢?
子组件是注册到父组件的components里面的。
3.2注册组件的语法糖
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
//1.创建组件构造器对象
// const cpnC = Vue.extend()
//2.注册组件
Vue.component('my-cpn',{
template: ` <div>
<h2>标题标题标题</h2>
<p>内容内容内容内容</p>
<p>内容内容内容内容啊啊啊啊</p>
<p>内容内容内容内容嘿嘿嘿嘿</p>
</div>`
})//my-cpn是组件的标签名
const app = new Vue({
el: '#app',//用于挂在要管理的元素
})
</script>
</body>
这个语法糖直接把创建组件构造器对象这一步省略了,把构造器里面的模板的值赋给了注册组件。上面这个例子是全局组件的方法。
局部组件的话,直接在实例里面的component里面写模板内容就行了。
<body>
<div id="app">
<!-- 3.使用组件 -->
<cpn2></cpn2>
</div>
<script src="./js/vue.js"></script>
<script>
new Vue({ el: '#app' })
const app = new Vue({
el: '#app',//用于挂在要管理的元素
components:{
'cpn2':{
template:
`<div>
<h2>我是标题1</h2>
<p>我是内容</p>
</div>`
}
}
})
</script>
</body>
运行结果:
这样写的话,子组件在父组件可以里面嵌套,template这一块会导致代码看上去很乱,那么我们需要把模板分离出来。
模板分离方法一:在注册组件前面用script标签把内容写出来,标签中type="text/x-template" ,定义id名,在components里面的template引用id名即可。(一般不用这个方法)
<body>
<div id="app">
<!-- 3.使用组件 -->
<cpn2></cpn2>
</div>
<script src="./js/vue.js"></script>
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容。。</p>
</div>
</script>
<script>
const app = new Vue({
el: '#app',//用于挂在要管理的元素
components:{
'cpn2':{
template:
`#cpn`
}
}
})
</script>
</body>
运行结果:
模板分离方法二: 还是在注册界面前面用<template></template>标签,在标签内把模板内容写进去,并且需要id绑定。一般模板分离用这个方法。
<body>
<div id="app">
<!-- 3.使用组件 -->
<cpn2></cpn2>
</div>
<script src="./js/vue.js"></script>
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容。。</p>
</div>
</template>
<script>
const app = new Vue({
el: '#app',//用于挂在要管理的元素
components:{
'cpn2':{
template:
`#cpn`
}
}
})
</script>
</body>
运行结果是一样的。
3.3父子组件之间的通信
子组件是不能访问父组件或者vue的实例的,但是在开发中,往往一些数据需要从上层传递到下层,比如一个页面里面的一些内容不需要父组件展示,而是需要父组件下面的子组件展示。那么这个时候我们不会让子组件发送请求给父组件,而是让父组件将数据传递给子组件。
3.3.1如何进行父子组件的通信?
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
1.通过props向子组件传递数据
注意:
- 模板template必须有个根元素(代码中的div)。
- props的属性名是驼峰命名法的话,在显示的时候,在组件标签v-bind的时候需要按照驼峰命名法对应的属性名字来写。(尽量取名就用小写)
<body>
<div id="app">
<cpn v-bind:cnames="names" :cmessage="message"></cpn>
</div>
<template id='cpn'>
<div>
<ul>
<li v-for="item in cnames">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
// 父传子,props
const cpn ={
template:`#cpn`,
props:['cnames','cmessage']
}
const app = new Vue({
el: '#app',//用于挂在要管理的元素
data: {
message: 'Hello Vue!',
names:['京东','小米','美团']
},
components:{
'cpn':cpn
}
})
</script>
</body>
运行结果:
代码思路:首先在实例(父组件)里面准备数据(代码中为message和names),在实例中注册子组件cpn,在子组件的构造器添加props以及需要用到的变量名,在模板(template)里面使用这些变量,最后绑定在vue实例里面,用组件标签显示。
当然,props可以是对象,在对象里面对传进来的参数做一个类型限制。
const cpn ={
template:`#cpn`,
props:{
//1.类型限制
cnames:Array,
cmessage:String
}
}
或者类型限制的同时定义一个默认值。
const cpn ={
template:`#cpn`,
props:{
cmessage:{
type:String,
default:'aaaa',//默认值
required:true//这句表示这个属性传值的时候必须传这个值
}
}}
cnames:{
type:Array,
default:[]//在vue2.5.17版本下这个写法没有问题
}
如果类型是对象或者数组的时候,那么默认值必须是一个函数,函数里面要有返回值。
cnames:{
type:Array,
default(){
return []
}
}
2.通过事件向父组件发送消息
这个过程比父传子更常见,因为子组件一般会发生一些事件,比如用户点击事件之类的,子组件需要告诉父组件发生了这个事件,并且告诉父组件用户点击了什么,父组件根据子组件传过来的数据再去请求新的数据。
举个例子:在子组件定义几个按钮,点击按钮,控制台返回点击的信息。
<body>
<!-- 父组件模板 -->
<div id="app">
<cpn ></cpn>
</div>
<!-- 子组件模板 -->
<template id='cpn'>
<div>
<button v-for="item in categories" @click="btnclick(item)">{{item.name}}</button>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
//子组件
const cpn ={
template:`#cpn`,
props:{
cnames:{
type:Array
} ,
cmessage:{
type:String
}
},
data(){
return{
categories:[
{id:'aaa',name:'电脑手机'},
{id:'bbb',name:'热门推荐'},
{id:'ccc',name:'家用家电'},
{id:'ddd',name:'办公用品'},
]
}
},
methods:{
btnclick(item){
console.log(item);
}
}
}
// 父组件
const app = new Vue({
el: '#app',//用于挂在要管理的元素
data: {
message: 'Hello Vue!',
names:['京东','小米','美团']
},
components:{
'cpn':cpn
}
})
</script>
自定义发射事件
代码思路:在子组件里面的method通过$emit()定义一个触发事件,发射事件名称为itemclick,参数为item。在父组件定义一个事件名为cpnclick的事件来监听(v-on)子组件模板的发射事件。
<body>
<!-- 父组件模板 -->
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<!-- 子组件模板 -->
<template id='cpn'>
<div>
<button v-for="item in categories" @click="btnclick(item)">{{item.name}}</button>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
//子组件
const cpn ={
template:`#cpn`,
props:{
cnames:{
type:Array
} ,
cmessage:{
type:String
}
},
data(){
return{
categories:[
{id:'aaa',name:'电脑手机'},
{id:'bbb',name:'热门推荐'},
{id:'ccc',name:'家用家电'},
{id:'ddd',name:'办公用品'},
]
}
},
methods:{
btnclick(item){
this.$emit('itemclick',item)//发射一个事件,第一个参数为事件名称,第二个为事件参数
}
}
}
// 父组件
const app = new Vue({
el: '#app',//用于挂在要管理的元素
data: {
message: 'Hello Vue!',
names:['京东','小米','美团']
},
components:{
'cpn':cpn
},
methods:{
cpnclick(item){
console.log('cpnclick',item)
}
}
})
</script>
</body>
打开网页随意点击一个按钮,在控制台可以看到点击的信息
3.3.2父子组件的访问方式
上面的父子通信方式都是通过子组件发射事件给父组件的,其实我们可以直接让父组件通过对象来访问子组件,或者子组件通过对象访问父组件。
1.父组件访问子组件:使用$clidren或$refs
父组件访问子组件一般用$refs,这个有点像class,$refs是对象类型,默认是一个空的对象,当给组件加 ref="aaa(id名)"时,在父组件里面调用 this.$refs.aaa.xxx就可以访问指定的子组件以及子组件里面的属性了。
举个栗子
<body>
<div id="app">
<div>
<cpn ref="aaa">
</cpn>
<button @click="btnclick">按钮</button>
</div>
</div>
<template id="cpn">
<div>
我是子组件
</div>
</template>
<script src="./js/vue.js"></script>
<script>
var app = new Vue({
el: '#app',//用于挂在要管理的元素
data: {
message: 'Hello Vue!'//定义数据
},
methods:{
btnclick(){
console.log(this.$refs.aaa.name)
}
},
components:{
cpn:{
template:`#cpn`,
data(){
return{
name:'我是子组件的名字'
}
},
methods:{
showmessage(){
console.log('showmessage');
}
}
}
}
})
</script>
运行结果:点击按钮后
2.子组件访问父组件:$parent
几乎不用这个,这样用的话子组件的复用性就不强了。
补充:用$root可以访问根组件。