Vue组件化思想
如果将一个页面中所有的处理逻辑全部放在一起,那么处理起来会非常复杂混乱,而且不利于后续的管理以及扩展。如果将页面拆分成一个个小的功能块,每个功能块完成属于自己的独立功能,那么整个页面的管理和维护就变得容易了。一个页面可以分为多个组件,每个组件又可以细分Vue的组件思想①它提供了一种抽象,让我们开发出一个个独立可复用的小组将来构造我们的应用。②任何的应用都会被抽象成一棵组件树。一、注册组件的基本步骤
·
如果将一个页面中所有的处理逻辑全部放在一起,那么处理起来会非常复杂混乱,而且不利于后续的管理以及扩展。
如果将页面拆分成一个个小的功能块,每个功能块完成属于自己的独立功能,那么整个页面的管理和维护就变得容易了。
一个页面可以分为多个组件,每个组件又可以细分
Vue的组件思想
①它提供了一种抽象,让我们开发出一个个独立可复用的小组将来构造我们的应用。
②任何的应用都会被抽象成一棵组件树。
一、注册组件的基本步骤
组件的使用分为三个步骤
-
创建组件构造器
-
注册组件(全局注册、局部注册)
-
使用组件
1.1 调用Vue.extend() API:创建组件构造器:const x = Vue.extend({ })
2.1 调用Vue.component() API:注册组件
3.1 在Vue实例范围内使用组件
1.1.1 创建组件模板:(旧的写法)
x 是构造器名称,在注册是会引用此名称。组件就相当于一个html页面,最外层要用一个div来包裹,然后再是内容。
const x = Vue.extend({
template:
`
<div>
<h2>我是组件标题</h2>
</div>
`
})
2.1.1 全局注册组件: 'my-cpn’是组件的标签名,x 是1.1.1的内容。也就是说,以后用到x 组件时,用<my-cpn></my-cpn>就可以代替了。
Vue.component('my-cpn',x)
3.1.1 在Vue实例中使用:
<div id="app">
<my-cpn></my-cpn>
</div>
二、创建组件构造器步骤解析
1、Vue.extend() 创建的是一个组件构造器,通常在创建组件的地方,传入template代表自定义组件的模板,该模板就是在使用到组件的地方,要显示的HTML代码。旧写法参考1.1.1内容。
2、Vue.component()要传入两个参数,一个是注册组件的标签名称,一个是组件构造器的名称。如1.1.1中的 x 。
3、可以多重嵌套使用自定义组件,但组件标签中不能有其他内容<my-cpn>用
四、父组件和子组件
先构造两个组件:
第一个:
const cpn1 = Vue.extend({
template :`<div><h2>组件1</h2></div>`
})
第二个:把组件1在组件2中注册:
const cpn2 = Vue.extend({
template :`
<div>
<h2>组件2</h2>
<cpn1></cpn1>
</div>
`,
components:{
cpn1:cpn1 // 可以简写为一个cpn1
}
})
组件2在实例中注册
const app = new Vue({
el: "#app",
components: {
cpn2: cpn2 // 可以简写为一个cpn1
},
})
使用:
<div id="app">
<cpn1></cpn1> // 未在实例中注册,不可使用
<cpn2></cpn2> // 已在实例中注册,可以使用
</div>
其实const app实例也算是一个组件,只不过是页面的最大组件:根组件ROOT,所以vue实例也有template。子组件必须先于父组件创建,否则识别不了子组件。
五、注册组件的语法糖
此语法糖是将创建组件的构造器嵌入注册步骤当中。
原先:遵循1.1->2.1->3.1的顺序
现在 1.1和2.1结合:(全局)
Vue.component('my-cpn',{
template: `<div><h2>组件标题</h2></div>`
})
(局部)
const app = new Vue({
el: "#app",
components: {
'my-cpn': {
template:`<div><h2>组件标题</h2></div>`
}
},
})
六、组件模板抽离的写法
由于在template中写html语句的便捷性不好,所以将html语句抽离出来写
6.1 写在<script>标签中:
<script type="text/x-template" id="cpn1">
<div>
<h2>组件1</h2>
</div>
</script>
script标签类型为:text/x-template ,并且要指定id,以便于使用
以全局注册为例:
Vue.components( 'my-cpn1' , { template: '#cpn1' })
在实例中使用:
<div id="app">
<my-cpn1></my-cpn1>
</div>
6.2 写在<template>标签中:
<template id="cpn2">
<div>
<h2>组件2</h2>
</div>
</template>
给template标签加上id,便于使用。
以局部注册为例:
const app = new Vue({
el: "#app",
components: {
'my-cpn2': {
template: '#cpn2'
}
},
})
在实例中使用:
<div id="app">
<my-cpn2></my-cpn2>
</div>
七、注意点
1、组件内部不能直接访问实例内部数据或方法等。
2、组件是一个单独功能模块的封装,有属于自己的html模板。
3、即使不是直接而是间接地让组件访问到实例中的数据,那么假设一个页面有成千上百个组件,数据全部存放到实例中,会让实例显得臃肿。
4、vue组件要有存放自己数据的地方。
八、组件的数据存放
在注册的时候
<template id="cpn3">
<div>
<h2>{{title}}</h2>
</div>
</template>
Vue.components( 'my-cpn3' ,
{
template: '#cpn3',
data() {
return {
title: '组件3'
}
}
})
组件存放自己数据的地方,在与template同级下的 data()函数中,所以需要返回值,这个值是对象类型,与实例中的data:{}不太一样。其实组件的原型是指向vue实例的,所以组件中也有实例的东西,例如生命周期函数等。
九、为什么组件中的data(){}必须是一个函数?
1、若是以对象的形式存放
①先定义个对象:
const obj = {name: "小明", age:18}
②存到data中:
data(){ return obj }
此时已分配内存空间来存这个对象,假设 内存地址:0x100
后续操作:
let obj1 = data()
let obj2 = data()
obj1.name = '小红'
console.log(obj1)
console.log(obj2)
结果: name: '小红' age :18
name: '小红' age :18
obj1、obj2指向的是同一片内存空间的内容,obj1.name = '小红‘,相当于修改这个内存地址中的内容,所以即使obj2没有对这个对象进行修改,但是获取到的内容已经改变了。
2、若是以函数形式存放
data(){
return{
name: "小明",
age:18
}
}
仅是定义和声明,在内存中暂无对象存储的空间,有调用到此函数,再开辟内存空间。
后续操作:
let obj1 = data()
let obj2 = data()
obj1.name = '小红'
console.log(obj1)
console.log(obj2)
结果: name: '小红' age :18
name: '小明' age :18
obj1调用一次data(),内存开辟空间存 name、age,假设内存地址为0x100
obj2调用一次data(),内存开辟空间存 name、age,假设内存地址为0x200
每调用一次data(),就会开辟新的空间来存,所以内存地址不一样,也就是说,obj1、obj2只是内容一样,但没有关系的两块内存空间。在后续操作中,obj1修改了name,只是修改了0x100中的数据,obj2还是保持和第一次获取到的内容一致,没有受影响,二者是独立使用data的。保证了组件的 独立性 和 可 复用性。
十、父子组件的通信
1、父组件通过props向子组件传递消息。
2、子组件通过事件向父组件发送消息。
props的基本用法:从父组件(根组件)传递数据给子组件
10.1 定义一个模板(子组件)
<template id="soncpn">
<div>
<p>{{sonmessage}}</p>
<h2>{{sonmovies}}</h2>
</div>
</template>
// 先留两个空位给message和movies
10.2 构造组件,构造器名称:cpn
const cpn = {
template: "#soncpn",
props: ['sonmessage','sonmovies']
}
// props用来接收父组件传来的数据
10.3 将vue实例视作父组件,将子组件在父组件中注册
const app = new Vue({
el: "#app",
data:{
message: "hello world!",
movies:['电影1','电影2']
}
components: {
cpn
},
})
10.4 传递数据
<div id="app">
<cpn :sonmessage="message" :sonmovies="movies"></cpn>
</div>
十一、props的第二种写法及类型验证
1、写法
第一种写法:参照10.2
props:['sonmessage','sonmovies']
第二种写法:
props:{
sonmessage: String,
sonmovies: Array
}
第二种写法扩展:
props:{
sonmessage: {
type: String,
default: '默认值'
}
sonmovies: {
type: Array,
default(){
return []
}
}
}
第二种写法是强制规定传入的数据类型,当传入类型定义是数组或是对象时,默认值必须使用工厂函数来指定。
当传入值为多种类型:
type: [String,Number...]
2、props名注意点:驼峰命名要转换成以-连接
props:['sonMessageInfo']
在实例中:
<cpn :son-message-info="xxx"></cpn>
在<template> 中没有关系
<template>
<div>
<h2>{{sonMessageInfo}}</h2>
</div>
</template>
十二、子组件向父组件传递数据(自定义事件)
props的双向绑定
1、子组件通过$emit发送事件和参数。
2、父组件通过 v-on / @ 来监听
如果要对传入的props进行双向绑定,不建议直接 v-model=“props” ,要用data中的一个新变量来代替。这里是举例input输入框的input事件,将动态绑定的value,也就是numberdata,通过$emit传递给父组件,再在父组件模板中监听此事件,并获取到传过来的值,在父组件methods中定义方法,将获取到的值赋给父组件data中数据。
<body>
<div id="app">
<h2>{{homeNum}}</h2>
<cpn :numberprops="homeNum" @numinput="num1change"></cpn>
</div>
<script src="./js/vue.js"></script>
<template id="cpn">
<div>
<h2>子组件:{{numberdata}}</h2>
<h2>props:{{numberprops}}</h2>
<input type="text" :value="numberdata" @input="numinput">
</div>
</template>
<script>
// 3、在页面中使用组件
const app = new Vue({
el: "#app",
data: {
homeNum: 2
},
methods: {
num1change(value) {
this.homeNum = parseInt(value)
// console.log(value)
}
},
components: {
cpn: {
template: "#cpn",
props: {
numberprops: Number
},
data() {
return {
numberdata: this.numberprops
}
},
methods: {
numinput() {
this.numberdata = event.target.value
// console.log(this.number)
this.$emit('numinput', this.numberdata)
}
}
}
},
})
</script>
</body>
总结为4步:
1、子模版添加默认事件。
2、子组件methods中使用$emit来发送自定义事件。
3、父模板中 @自定义事件名=“父组件处理事件名” 来监听。
4、父组件methods中处理: 父组件处理事件名(){ 处理逻辑 }
十三、父子组件的访问方式
通过对象直接访问
父组件访问子组件:$children 、 $refs
要想使用$refs,必须在组件标签中添加ref属性。
// 在父模板中
<div id="app">
<cpn ref="aaa"></cpn>
<cpn1 ref="bbb"></cpn1>
<cpn2></cpn2>
</div>
this.$refs 就可以获取到所有添加了ref属性的子组件
this.$refs.aaa 可以取到具体项
子组件访问父组件:$parent
父组件中可能包含多个子组件,所以this.$children获取到的是一个数组类型。
this.$parent 是单个对象,它只能获取到上一级组件
补充:获取根实例:this.$root
更多推荐
已为社区贡献2条内容
所有评论(0)