一.组件化思想介绍
组件化开发,本质就是将相同功能的模块开发成 一个个可以复用的小组件,然后通过引入各个组件实现一个完整页面的开发,组件化开发就像树结构一样,每一个组件都是树形结构上的一个分支节点
1.1 Vue实例创建组件
在Vue中创建组件构造器有几种实现方式,但是大体思想包含3步,如下:
1.创建组件构造器
2.注册组件
3.在Vue实例对象中使用组件
组件第一种创建使用方法,基于extend方式:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
</style>
</head>
<body>
<div id="shop" v-cloak>
<!-- vue实例对象中使用全局组件 -->
<my_content></my_content>
</div>
<template id="content">
<div>
{{childMovies}}
</div>
</template>
<script>
// 1. 创建组件构造器
const content = Vue.extend({
template:`
<div>
<h2>这是一个标题</h2>
<p>这是一段内容</p>
</div>
`,
})
// 2. 注册组件
Vue.component('my_content', content)
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
</body>
</html>
注意点:通过调用Vue中extend方法首先创建组件构造器,这种实现方法比较底层
实现方式二,直接通过Vue.component()语法糖方式实现
Vue.component('my_content', {
template:`
<div>
<h2>这是一个标题1</h2>
<p>这是一段内容</p>
</div>
`,
})
语法糖实现直接将extend中实现模板的内容放到了component中,其实语法糖底层实现也是调用了extend,但是这样实现更简洁
1.2 全局组件和局部组件
全局组件:全局组件的创建和注册都在与vue实例同一层级的结构中实现
例如:
全局组件
<script>
// 创建组件构造器, 注册组件,这样是一个全局组件,因为组件创建和注册和new Vue实例化同级
Vue.component('my_content', {
template:`
<div>
<h2>这是一个标题1</h2>
<p>这是一段内容</p>
</div>
`,
})
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
上面Vue.component()创建组件和注册组件都是通过Vue对象实现,而且和Vue对象实例化同级
局部组件
<script>
// 创建组件构造器, 注册组件,这样是一个局部组件,因为组件创建和注册放到了vue实例对象中
var cpm = {
template:`
<div>
<h2>这是一个标题111</h2>
<p>这是一段内容</p>
</div>
`,
}
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
components:{
'my_content': cpm
}
})
</script>
局部组件:因为组件创建和注册放到了vue实例对象中,通过components创建注册
1.3 父子组件
子组件在父组件进行注册和创建,引用也在父组件中,例如:
父子组件
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
</style>
</head>
<body>
<div id="shop" v-cloak>
<!-- vue实例对象中使用全局组件 -->
<component1></component1>
<!-- <component2></component2> 子组件中创建的组件只能在父组件中使用,不能越级 -->
</div>
<template id="content">
<div>
{{childMovies}}
</div>
</template>
<script>
// 创建组件构造器, 注册组件,这样是一个局部组件,因为组件创建和注册放到了vue实例对象中
Vue.component('component1', {
template:`
<div>
<h2>这是一个标题111</h2>
<p>这是一段内容</p>
<component2></component2>
</div>
`,
components:{
'component2':{
template:`
<div>
<h2>这是一个标题222</h2>
<p>这是一段内容222</p>
</div>
`,
}
}
})
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
</body>
</html>
component2子组件的实现是在其父组件component1中实现的,而且其使用而在component1的template中,而不能放到vue实例中去使用(其实new Vue实例化可以看成一个根组件root,组件化的思想也是基于vue实例化)
1.4 template模板抽离
上面我们把html模板template的内容都放到了组件里面,然后通过template去渲染模板,这样我们组件中会存放很多html模板,这样代码会很臃肿,所以我们要进行解耦,将template抽离
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
</style>
</head>
<body>
<div id="shop" v-cloak>
<!-- vue实例对象中使用全局组件 -->
<component1></component1>
<!-- <component2></component2> 子组件中创建的组件只能在父组件中使用,不能越级 -->
</div>
<template id="content">
<div>
<div>
<h2>这是一个标题111</h2>
<p>这是一段内容</p>
</div>
</div>
</template>
<script>
// 创建组件构造器, 注册组件,这样是一个局部组件,因为组件创建和注册放到了vue实例对象中
Vue.component('component1', {
template:'#content',
})
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
</body>
</html>
注意点:我们在vue实例对象的模板下面添加了template标签,然后通过id设置模板的唯一名称,在Vue.componen他()创建和注册组件时直接通过id选择器定位template后引入并渲染,这样vue组件中不会存放大量的html内容,代码解耦性好
父子组件中模板抽离
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
</style>
</head>
<body>
<div id="shop" v-cloak>
<!-- vue实例对象中使用全局组件 -->
<component1></component1>
<!-- <component2></component2> 子组件中创建的组件只能在父组件中使用,不能越级 -->
</div>
<template id="content1">
<div>
<div>
<h2>这是一个标题111</h2>
<p>这是一段内容</p>
<component2></component2>
</div>
</div>
</template>
<template id="content2">
<div>
<div>
<h2>这是一个标题222</h2>
<p>这是一段内容222</p>
</div>
</div>
</template>
<script>
// 创建组件构造器, 注册组件,这样是一个局部组件,因为组件创建和注册放到了vue实例对象中
Vue.component('component1', {
template:'#content1',
components: {
'component2': {template: '#content2'}
}
})
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
</body>
</html>
将父组件component1中的子组件component2的模板都抽离出来,然后通过template进行引用,但是要注意的是component2组件的使用只能在component1中,即子组件只能在父组件中进行使用
1.5 子组件中data存放数据
vue中子组件不能直接访问实例中的数据,需要通过绑定访问,子组件中数据存放在data中,和vue实例结构很相似,但是子组件中data是一个能返回对象的函数,函数有自己的作用予,可以起到数据作用域独立,不会造成数据引用混乱
因为函数在调用是,内存中会有一个栈空间来存在函数对象并设置变量,以及分配内存空间进行存放,这样所以创建的变量都是拥有独立的作用域的
<div id="shop" v-cloak>
<component1></component11>
</div>
<template id="content">
<div>
<h2>{{ title }}</h2>
<p>我是内容,哈哈哈哈222</p>
<p>我是内容,呵呵呵呵2222</p>
</div>
</template>
<script>
Vue.component('component1',{
template: '#content',
data(){
return {'title': '我是标题22222'}
},
methods:{
}
})
var vm = new Vue({
el:'#shop',
data:{
msg: "kdf",
},
methods:{
},
})
</script>
我们看到当(此时可以把vue实例看组root根组件)子组件的template使用到title变量时通过插值表达式进行数据赋值,title时存放在data函数中,并返回一个包含title属性的对象
二. 父子组件通信
父子组件通信分为两种情况
1.父组件通过props向子组件传递数据
2.子组件通过$emit的事件向父组件传递数据,自定义事件
2.1 父组件通过props向子组件传递数据
通过v-bind将父组件中的数据绑定到子组件中
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
</style>
</head>
<body>
<div id="shop" v-cloak>
<!-- vue实例对象中使用全局组件 -->
<component1></component1>
<!-- <component2></component2> 子组件中创建的组件只能在父组件中使用,不能越级 -->
</div>
<template id="content1">
<div>
<div>
<h2>{{ title + '在父组件中'}}</h2>
<p>这是一段内容</p>
<component2 :mytitle='title'></component2>
</div>
</div>
</template>
<template id="content2">
<div>
<h2>{{ mytitle + '子组件中' }}</h2>
<p>这是一段内容222</p>
</div>
</template>
<script>
// 创建组件构造器, 注册组件,这样是一个局部组件,因为组件创建和注册放到了vue实例对象中
Vue.component('component1', {
template:'#content1',
components: {
'component2': {
template: '#content2',
props:{
mytitle:{
type: String,
default(){
return "父组件中没有传递title"
},
required: false
}
}
}
},
data(){
return {'title': '标题222'}
},
})
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
},
})
</script>
</body>
</html>
在子组件中设置props属性并进行简单校验,然后在组件中绑定父组件的中传递的title值,然后在子组件红进行插值引用
2.2 子组件通过$emit自定义事件向父组件传递数据
子组件中向父组件中传递数据时通过自定义事件实现,事件自定义流程如下:
在子组件中触发子组件methods中方法,然后在methods中使用this.$emit(‘eventname’, para)将事件发射到父组件中,然后在父组件的模板template中使用v-on监听子组件传递的事件,然后触父组件中methods进行相应的请求
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewpot" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuezujianhua</title>
<script src='./vue.js'></script>
<style>
ul>li {
list-style: none;
}
</style>
</head>
<body>
<!-- 父组件模板 -->
<div id="shop" v-cloak>
<son_component @childbtn='fatherbtn'>
</son_component>
</div>
<!-- 子组件模板 -->
<template id="content">
<div>
<ul v-for="item in goodList">
<li><a href="#" @click='childclick(item)'>{{item.name}}</a></li>
</ul>
</div>
</template>
<script>
const son_component = {
template: '#content',
data: function(){
goodLists = [
{id: '1', name:'热门推荐'},
{id: '2', name:'手机数码'},
{id: '3', name:'家用家电'},
{id: '4', name:'电脑办公'},
]
return {'goodList': goodLists}
},
methods:{
childclick(item){
this.$emit('childbtn', item); //向父组件中发射事件并传递参数
}
},
}
var vm = new Vue({
el:'#shop',
data:{
movies: ['海王', '海贼王'],
},
methods:{
fatherbtn(item){
console.log("father", item)
}
},
components:{
son_component
}
})
</script>
</body>
</html>
注意点:子组件通过自定义事件this.$emit(‘eventname’,para)向父组件中发射事件