1.组件化
概念
Web页面中的组件其实就是页面组成的一部分,它是一个具有独立的逻辑和功能或界面。页面只不过是这些组件的容器,组件自由组合形成功能完善的界面,当不需要某个组件,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行,组件化开发,是目前项目开发的一个流行趋势。
组件化的优点
1.避免多人开发造成的冲突,利于协同开发;
2.提高了项目开发效率;
3.提升了整个项目的可维护性;
4.方便重复使用;
2.组件
vue组件概念
组件是一个html css js img 等的一个聚合体,在Vue中使用了一个叫做单文件组件的技术来实现组件,它是一个 xxx.vue 文件,这个文件在浏览器不能运行,必须经过编译【 gulp webpack 】才能运行
Vue组件的实现
Vue是通过Vue.extend() 来实现组件的,但是Vue的组件在使用时需要注册。
Vue组件命名
对于一个组件来说,组件是以标签化呈现的,那么始终需要给对应的组件进行命名,方便后面的操作使用。
你给予组件的名字可能依赖于你打算拿它来做什么。当直接在 DOM 中使用一个组件 (而不是在字符串模板或单文件组件) 的时候,推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突,当然除此之外也可以采用其他的命名方式。
- 大驼峰 HeadTitle
<head-title></head-title>
利用大驼峰命名时,要注意写成标签时,中间用 ‘-’ 连接
- 小写横杠 head-title
<head-title></head-title>
- 一个单词 Hello
<hello></hello> 或者 <Hello></Hello> 【 推荐 】
当用一个单词作为组件名时,建议标签名首字母大写(大小写都能解析),这样在html代码中,很容易辨认。
Vue组件注册
☆ 全局注册
全局注册的组件,它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
格式一:
//组件的配置
const Hello=Vue.extend({
template:'<h3>Hello Vue.js<h3>'
})
//组件注册 Vue.component('组件名',组件的配置)
Vue.component('Hello',Hello)
格式二:
//建立模板标签
<template id="Hello">
<h3>Hello Vue.js</h3>
</template>
//组件的配置
Vue.component('Hello',{
template:'#Hello'//模板标签与组件绑定
})
//组件注册 Vue.component('组件名',组件的配置)
Vue.component('Hello',Hello)
格式一完整代码案例:
<div id="app">
<Hello></Hello>//此处使用组件
</div>
格式二完整代码示例:
<div id="app">
<Hello></Hello>
</div >
<template id="Hello">
<h3>Hello Vue.js</h3>
</template>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const Hello=Vue.extend({
template:'#Hello'
})
Vue.component('Hello',Hello)
new Vue({
el:'#app',
})
</script>
全局注册的简写
就是将上面代码的组件配置直接写入组件注册中,如下:
格式一简写:
Vue.component('Hello',{
template:'<h3>Hello Vue.js</h3>'
})
格式二简写:
<template id="Hello">
<h3>Hello Vue.js</h3>
</template>
<script>
Vue.component('Hello',{
template:'#Hello'
})
</script>
【切记】全局注册必须写在创建的 Vue 根实例 (new Vue) 的前面
☆ 局部注册
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件,意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加,为了解决这个问题,就需要局部注册组件。
局部注册使用components选项来完成,局部注册只在当前注册的实例范围内有效。
格式一:
components:{//局部注册组件的选项
'组件的名称':{
template:'<h3>Hello Vue.js</h3>'//组件的选项
}
}
格式二:
//相当于建立了一个模板标签
<template id="Hello">
<h3>Hello World!</h3>
</template>
new Vue({
el:'#app',
components:{
'Hello':{
template:'#Hello'//利用id名将组件与模板标签绑定
}
}
})
使用时,直接将组件名称以标签的形式写在局部注册的实例范围内,如下:
<div id="app">
<Hello></Hello>
</div>
格式一完整代码案例:
<div id="app">
<Hello></Hello>//此组件只能作用于当前注册的实例范围
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
new Vue({
el:'#app',
components:{
'Hello':{
template:'<h3>Hello Vue.js</h3>'
}
}
})
</script>
格式二完整代码案例:
<body>
<div id="app">
<Hello></Hello>
</div>
<template id="Hello">
<h3>Hello World!</h3>
</template>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
new Vue({
el:'#app',
components:{
'Hello':{
template:'#Hello'
}
}
})
</script>
Vue组件的使用规则
解析 DOM 模板时的应该注意,有些 HTML 元素,诸如
- 、
- 、 和 ,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如
- 、
- 和 ,只能出现在其它某些特定的元素内部。
具有直接父子级关系的标签,当我们自定义组件时,会被作为无效的内容提升到外部,并导致最终渲染结果出错。我们可以通过is属性来解决这个问题。
常见的直接父子级关系的标签有:ul li 、ol li、table tr td、dl dd dt
例:
<div id="app">
<table border="1">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<Hello></Hello>
</table>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component('Hello', {
template: `
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
`
})
new Vue({
el: '#app',
})
</script>
此时无法渲染出我们想要的结果,浏览器运行结果如下:
而且代码被放置在table外
对于此种问题,可以用is属性来解决,代码修改如下:
<div id="app">
<table border="1">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr is="Hello"></tr>//改用is属性
</table>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component('Hello', {
template: `
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
`
})
new Vue({
el: '#app',
})
</script>
这样修改后的代码就可以实现需要的效果。
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里,点击对应的按钮界面内容进行切换。要想实现这样的效果,可以通过 Vue 的 元素加一个特殊的 is 特性来实现。
<!-- 组件会在 `currentTabComponent` 改变时改变 ,也就是以此实现不同组件间的动态切换-->
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括:已注册组件的名字,或一个组件的选项对象
当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。例如还是一个多标签界面,点击对应的按钮界面内容进行切换。我们会发现,如果我们每次点击按钮切换时,只显示当前组件,但是其他组件却消失了,这是因为你每次点击按钮切换的时候,Vue 都创建了一个新的 currentTabComponent 实例。
重新创建动态组件的行为通常是非常有用的,但是在这样的案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 元素将其动态组件包裹起来。
案例:点击按钮实现两个组件的切换
<body>
<div id="app">
<button @click="clickChange">点击切换</button>
<keep-alive>
<component :is="type"></component>
</keep-alive>
</div>
<template id="EN">
<h3>Hello World!</h3>
</template>
<template id="CH">
<h3>你好 世界!</h3>
</template>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
type: 'english'
},
methods: {
clickChange() {
this.type = (this.type === 'english') && 'chinese' || 'english'
}
},
components: {
//组件一
'english': {
template: '#EN',
},
//组件二
'chinese': {
template: '#CH'
}
}
})
</script>
vue组件嵌套
子组件以标签的形式要在父组件的模板中使用,但是要切记template标签下面的子元素是惟一的。
● 局部注册嵌套
<body>
<div id="app">
<Father>
</Father>
</div>
<template id="father">
<!-- 唯一根元素 -->
<div>
<h3>父组件</h3>
<Son></Son>//子组件以标签的形式在父组件的模板中使用
</div>
</template>
<template id="son">
<h3>子组件</h3>
</template>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
new Vue({
el: '#app',
components: {
'Father': {
template: '#father',
components: {
'Son': {
template: '#son'
}
}
}
}
})
</script>
● 全局注册嵌套
<body>
<div id="app">
<Father></Father>
</div>
<template id="father">
<div>
<h3>父组件</h3>
<Son></Son>//子组件以标签的形式在父组件的模板中使用
</div>
</template>
<template id="son">
<h3>子组件</h3>
</template>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
Vue.component('Father', {
template: '#father'
})
Vue.component('Son', {
template: '#son'
})
new Vue({
el: '#app',
})
</script>
组件中数据的定义
组件中数据的定义是一个函数,并且数据使用return返回出来,数据定义如下所示,而且组件的数据只能在组件的模板中使用,其他地方使用会报错,并且此处的msg是全局变量,可以直接使用。
data(){
return{
msg:'Hello World!'//组件的数据,使用范围只能在组件的模板中
}
}
完整案例代码如下:
<body>
<div id="app">
<Data1></Data1>
</div>
<template id="data">
<h3>{{msg}}</h3>
</template>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
Vue.component('Data1',{
template:'#data',
data(){
return{
msg:'Hello World!'//组件的数据,使用范围只能在组件的模板中
}
}
})
new Vue({
el:'#app',
})
</script>
组件通信
组件通信可分为四种:父子组件通信、子父组件通信、非父子组件通信、多组件状态共享。
● 父子组件通信----父组件的数据传递给子组件
通信过程:
- 父组件中定义一个数据
Vue.component('Father', {
template: '#father',
data() {
return {
msg: 'Hello,son'
}
}
})
- 在父组件的模板中,用 v-bind 将父组件的数据绑定在子组件身上,子组件是以标签的形式写在父组件模板中,此处绑定用的son属性可以自己随意定义。
<template id="father">
<div>
<h3>父组件</h3>
<Son :son='msg'></Son>//子组件以标签的形式写在父组件模板中
</div>
</template>
- 在子组件的选项中,通过props选项来接收这个属性
Vue.component('Son',{
template:'#son',
props:['son']
})
- 这个定义的属性可以在子组件的模板中以全局变量的形式使用
<template id="son">
<div>
<h3>子组件</h3>
<p>{{son}}</p>
</div>
</template>
● 子父组件通信----子组件的数据传送给父组件
通信过程:
- 先在子组件中定义一个数据
Vue.component('Son', {
template: '#son',
data() {//定义一个数据
return {
msg1: 'Hello,father'
}
}
})
- 在父组件中也定义一个数据,这个数据用来接收子组件传递过来的数据
Vue.component('Father', {
template: '#father',
data() {
return {
msg2: 'Hello,son'//定义一个数据
}
}
})
-
在父组件中定义一个事件处理程序,用于改变父组件定义的数据,这个事件处理程序是有参数的,这个参数就是子组件传递过来的数据
Vue.component('Father', { template: '#father', data() { return { msg2: 'Hello,son' } }, //定义一个事件处理程序,用于改变父组件中定义的数据, //这个事件处理程序是有参数的,这个参数就是子组件传来的数据 methods: { msgchange(data) {//data是子组件传递的数据 this.msg2 += data; } } })
-
将这个事件处理程序通过事件绑定的形式绑定在子组件身上,子组件以标签的形式写在父组件模板中,子组件绑定的是一个自定义事件,事件类型随意定义
<template id="father">
<div>
<h3>父组件</h3>
<p>父组件:{{msg2}}</p>
<Son @aa="msgchange"></Son>//子组件事件绑定
</div>
</template>
- 在子组件中定义一个事件处理程序,这事件处理程序中通过 this.$emit来触发自定义事件,并传递一个参数给父组件
Vue.component('Son', {
template: '#son',
data() {
return {
msg1: 'Hello,father'
}
},
methods:{
say(){
this.$emit('aa',this.msg1)
}
}
})
● 非父子组件通信----一个父组件下有多个子组件,子组件利用父组件为中转站,子组件一先将数据发送给父组件,父组件接收到之后,在转发给子组件二
ref:
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
通信过程:
1.首先在子组件一中定义一个数据,该数据由子组件一定义
Vue.component('Son1', {
template: '#son1',
data() {//son1定义的数据,即需要传递的数据
return {
flag: false
}
},
2.在父组件模板中子组件一加上ref标记,这样子组件一的数据可以在父元素中的this.$refs的对象中获取子组件一。
<template id="father">
<div>
<h3>父组件</h3>
<Son1 ref="son1"></Son1>//加上ref标记
<Son2 :fn='emitsay'></Son2>
</div>
</template>
- 在父组件中定义一个方法,获取到了子组件一中的数据,最终子组件一中的数据需要传递给子组件二,所以需要将该方法,绑定在子组件二上,即此处实现的是父组件与子组件二之间的通信。
Vue.component('Father', {
template: '#father',
methods: {
emitsay() {//此处父组件可以获取到子组件一,也可以获取到子组件一中的
this.$refs.son1.say()
}
}
})
将上述定义的方法绑定给子组件二
<Son2 :fn='emitsay'></Son2>
4.子组件二接收该方法,用props接收
Vue.component('Son2', {
template: '#son2',
props: ['fn']//
})
5.此处的fn为随意定义的变量,为全局的变量,绑定给子组件二
<template id="son2">
<div>
<h3>子组件二</h3>
<button @click="fn">打招呼</button>
</div>
</template>