【Vue学习笔记_10】组件化开发
【Vue学习笔记_10】组件化开发组件的基本使用全局组件和局部组件父组件和子组件注册组件的语法糖组件HTML模板的分离写法组件中的data属性组件通信-父传子(props)组件通信-子传父(自定义事件)修改props传入的数据(可跳过)组件访问-父访问子组件访问-子访问父配套可执行代码示例 => GitHub终于到了Vue最核心的组件化开发!组件化:我们将一个页面拆分成很多个组件,每个组件都
【Vue学习笔记_10】组件化开发
终于到了Vue最核心的组件化开发!
组件化:我们将一个页面拆分成很多个组件,每个组件都用于实现页面的一个功能块,而每一个组件又可以进行细分。这样会让代码更加方便组织和管理,而且扩展性也更强。
Vue组件化:提供了一种抽象,让我们可以开发出一个个独立可复用的Vue组件来构造应用。任何应用都会被抽象成一棵组件树。
组件的基本使用
组件的使用分成三个步骤:
- 调用Vue.extend()方法创建组件构造器
- 调用Vue.component()方法注册组件
- 在Vue实例的作用范围内使用组件
注:第一步和第二步在后面用起来.vue文件之后,都可以省略。
<div id="app">
<!-- 3.使用组件-->
<my-cpn></my-cpn>
</div>
<script>
//1.创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>标题</h2>
<p>内容内容内容</p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn',cpnC)
const app = new Vue({
el: '#app'
})
</script>
全局组件和局部组件
全局组件:如上述调用Vue.component()方法注册的组件,可以在任意Vue实例下使用
局部组件:通过components属性挂载在某个Vue实例中,只能在该Vue实例下使用
const app = new Vue({
el: '#app',
// 注册局部组件
components: {
'my-cpn': cpnC
}
})
父组件和子组件
<script>
//创建子组件构造器
const cpnC1 = Vue.extend({
template: `
<div>
<h2>标题1</h2>
<p>内容1内容1内容1</p>
</div>`
})
//创建父组件构造器
const cpnC2 = Vue.extend({
template: `
<div>
<h2>标题2</h2>
<p>内容2内容2内容2</p>
<cpn1></cpn1>
</div>`,
//在cpnC2中注册子组件cpnC1
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: '#app',
data: {
},
//在app中注册子组件cpnC2
components: {
cpn2: cpnC2
}
})
</script>
在上面这个例子中,cpnC2是cpnC1的父组件,app又是cpnC2的父组件。可以把Vue实例看成最顶层的root组件。
注册组件的语法糖
主要是省去了调用Vue.extend()的步骤,直接使用一个对象来代替
注册全局组件的语法糖
Vue.component('cpn1', {
template: `
<div>
<h2>标题</h2>
</div>`
})
注册局部组件的语法糖
const app = new Vue({
el: '#app',
components: {
'cpn2': {
template: `
<div>
<h2>标题</h2>
</div>`
}
}
})
组件HTML模板的分离写法
Vue提供了两种方法来定义HTML模板的内容:
- script标签
<script type="text/x-template" id="cpn">
<div>
<h2>标题</h2>
<p>内容内容内容</p>
</div>
</script>
- template标签(常用)
<template id="cpn">
<div>
<h2>标题</h2>
<p>内容内容内容</p>
</div>
</template>
注册组件时,用#id获取HTML模板:
Vue.component('cpn',{
template: '#cpn'
})
注:组件模板必须包含一个根元素,一般用 <div>
组件中的data属性
每个组件都有自己的数据data,也有自己的方法methods。
需要注意的是,组件的data属性必须是一个函数,这个函数返回一个对象,对象内部保存着数据。
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>内容内容内容</p>
</div>
</template>
<script>
Vue.component('cpn',{
template: '#cpn',
data() {
return {
title: 'abc'
}
}
})
</script>
为什么组件中的data必须是一个函数?
因为一个组件的各实例之间不应该共享同一个data,所以需要把data定义成函数,这样每次创建组件实例时,data都会返回一个新的对象,从而每个组件实例都有一个属于自己的data。
组件通信-父传子props属性
需求:整个页面的大组件从服务器请求到了很多数据,需要下面的小组件进行展示。这时并不会让子组件再次发送一个网络请求,而子组件是不能访问父组件或者Vue实例的数据的,因此需要让父组件将数据传递给子组件。
第一步:在定义子组件构造器时,使用props属性来声明需要从父组件接受的数据。props的值有两种方式:
- 字符串数组:数组元素就是传递时的数据名。
- 对象:对象属性可以限制传递时的数据类型(type,可以是自定义类型)、提供的默认值(default,当类型是对象或数组时,default必须是一个函数,并return设置的默认值)、是否要求必须传值(required)等。
第二步:在使用子组件时,通过v-bind将父组件的data绑定到子组件的属性上,然后绑定的属性就可以在子组件的模板中像data一样使用了。
<!--父组件模板-->
<div id="app">
<!--v-bind将父组件的data绑定到子组件的属性上-->
<cpn v-bind:cmovies="movies" :cmsg="msg"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
{{cmovies}}
{{cmsg}}
</div>
</template>
<script>
const cpn = {
template: '#cpn',
//子组件使用props属性来声明需要从父组件接受的数据
//props: ['cmovies', 'cmsg'],
props: {
//限制数据类型(可以是自定义类型)
cmovies: Array,
cmsg: {
type: String,
//提供默认值(当类型是对象或数组时,default必须是一个函数,return设置的默认值)
default(){
return 'Hello'
},
//要求必须传值
required: true
}
}
}
const app = new Vue({
el: '#app',
data: {
msg: 'Hi',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
</script>
注:props中如果是驼峰标识,则在组件中 v-bind绑定时需要转换成 “-”连接的方式
组件通信-子传父$emit自定义事件
需求:子组件中发生事件,需要传数据给父组件,父组件再根据接收到的值去请求新一轮的数据。
第一步:在子组件定义的methods中,通过$emit触发自定义事件,可以传入参数。
第二步:然后在父组件中使用子组件时,用v-on监听子组件的自定义事件,默认接收传入的参数。
<!--父组件模板-->
<div id="app">
<!--v-on监听子组件的自定义事件-->
<cpn v-on:item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script>
//子组件
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'}
]
}
},
methods: {
btnClick(item){
//在子组件中触发自定义事件
this.$emit('item-click',item)
}
}
}
//父组件
const app = new Vue({
el: '#app',
data: {
},
methods: {
cpnClick(item){
console.log('cpnClick', item)
}
},
components: {
cpn
}
})
</script>
注:在HTML模板的标签中,v-on监听无法识别驼峰标识,因此自定义事件需采用 “-”连接小写字母的方式。
修改props传入的数据 (可跳过)
props传入的数据只能用于展示,而不能在<input>标签中修改,否则会报错。如果有修改的需求,以下两种方法:
- 设置data属性,返回由传入的props数据初始化的对象,然后在<input>标签中绑定data属性,但仅仅这样不能让父组件中的data随着变化。再把<input>的v-model拆成v-bind和v-on,在@input绑定的方法中触发自定义事件并传入修改值,在父组件中监听事件,这样就可以在事件绑定的方法中修改父组件的data。如对number1的处理。
- 同样需要先设置data属性,返回由传入的props数据初始化的对象,然后在<input>标签中绑定data属性。再设置watch属性,监听data的改变,在处理函数中触发自定义事件并传入修改值,在父组件中监听事件,并在事件绑定的方法中修改父组件的data。如对number2的处理。
<!--父组件模板-->
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<input type="text" :value="dnumber1" @input="num1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script>
//父组件
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
methods: {
num1change(value) {
this.num1 = parseInt(value)
},
num2change(value) {
this.num2 = parseInt(value)
}
},
components: {
//子组件
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
this.dnumber1 = event.target.value
this.$emit('num1change', this.dnumber1)
}
},
watch: {
dnumber2(newValue, oldValue) {
this.dnumber2 = newValue
this.$emit('num2change', newValue)
}
}
}
}
})
</script>
组件访问-父访问子
this.$children:获取所有子组件,返回值为数组类型
this.$refs(常用):根据后缀获取指定的子组件,返回值为对象类型,如果不跟后缀默认返回一个空对象
<div id="app">
<!--在父组件模板为子组件设置ref后缀-->
<cpn ref="aaa"></cpn>
</div>
<script>
const app = new Vue({
...
//在父组件中根据ref后缀获取指定的子组件
console.log(this.$refs.aaa);
})
</script>
组件访问-子访问父
this.$parent:获取父组件,最顶层组件的父组件是Vue实例,返回值为对象类型
this.$root:获取根组件Vue实例,返回值为对象类型
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)