文章目录
- 一. 回顾
- 二. 组件化开发
- 2.1 什么是组件化?
- 2.2 使用组件的3个步骤
- 2.3 使用组件的简单例子
- 2.4 全局组件和局部组件
- 2.5 父组件和子组件
- 2.6 组件的语法糖形式
- 2.7 组件模板的分离写法
- 2.7.1 使用<script>标签的写法
- 2.7.2 通过<template>标签的写法
- 2.7.3 例子
- 2.8 组件中的数据存放问题
- 2.8.1 为什么组件的data必须是函数?
- 2.9 父组件向子组件通信
- 2.9.1 储备知识
- 2.9.2 例子
- 2.9.3 props中的驼峰标识
- 2.10 子组件传给父组件
- 2.10.1 情景
- 2.10.2 子传父的步骤
- 2.10.3 例子
一. 回顾
前面学习了Day18——v-model双向绑定,今天学习一下组件化开发
二. 组件化开发
2.1 什么是组件化?
一个完整的页面可以分成多个组件,每一个组件实现特定的功能,而每个组件可以再进行细分,如下:
2.2 使用组件的3个步骤
- 创建组件构造器(使用
Vue.extend()
)
const cpn = Vue.extend({
template: `
xxxxxxx
`
});
extend()方法要求传入一个参数,这个参数是一个对象,所以用{}
。这个对象通常要求定义一个template属性,template属性的内容就是我们要在DOM渲染出来的东西。
- 注册组件(使用
Vue.component()
)
Vue.component('xxx', 组件构造器)
component()方法将刚才的组件构造器注册为一个组件,并且给他起一个组件的标签名称。
- 使用组件。组件必须挂在某个vue实例下,否则它不会生效。也就是要在vue管理的区域内使用组件。
2.3 使用组件的简单例子
<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>
</div>`,
});
//2.注册组件
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
}
})
</script>
</body>
2.4 全局组件和局部组件
在vue实例外注册的组件是全局组件,在vue实例内注册的是局部组件。全局组件在所有vue实例管理的区域都能使用,而局部组件只能在 组件注册的vue实例 管理的区域使用。
开发中常用局部组件,而且一般只有一个实例,没有多个实例。
例子:
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//1. 创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈啊哈</p>
</div>
`
})
//2. 注册组件(全局组件:可以在多个Vue实例下面使用)
// Vue.component('cpn', cpnC);
//怎么注册局部组件呢?答:在vue实例里面注册就是局部组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
},
components: {
cpn: cpnC,//在vue实例里面注册的是局部组件
}
})
const app2 = new Vue({
el: '#app2'
})
</script>
</body>
2.5 父组件和子组件
在组件构造器里面注册的组件是子组件。 如下:
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容,呵呵呵呵</p>
<cpn1></cpn1>
</div>
`,
components:{
cpn1: cpnC1, //组件构造器1在组件构造器2里面注册,cpn1是子组件
}
})
例子:
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
//创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈哈</p>
</div>
`
})
//创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容,呵呵呵呵</p>
<cpn1></cpn1>
</div>
`,
components:{
cpn1: cpnC1, //组件构造器1在组件构造器2里面注册,cpn1是子组件
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
},
components: {
cpn2: cpnC2, //组件构造器2在Vue实例里面注册
}
})
</script>
</body>
2.6 组件的语法糖形式
实际是将组件构造器传入的对象,作为注册组件的第二个参数。如下:{}
中的就是组件构造器本来的内容
Vue.component('cpn1', {
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈哈</p>
</div>
`
})
2.7 组件模板的分离写法
2.7.1 使用<script>标签的写法
通过<script>
标签,type属性必须是text/x-template
,id属性是必须要有的,如下:
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</script>
定义好模板后,通过id选择器在注册组件里面建立联系
2.7.2 通过<template>标签的写法
id属性是必须要有的,如下:
<template id="cpn2">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</template>
定义好模板后,通过id选择器在注册组件里面建立联系
2.7.3 例子
<body>
<div id="app">
<cpn></cpn>
<cpn2></cpn2>
</div>
<!-- 1. 通过script标签,type必须是text/x-template-->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</script>
<!-- 2. 通过template标签-->
<template id="cpn2">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1. 注册一个全局组件
Vue.component('cpn', {
template: '#cpn',
})
Vue.component('cpn2', {
template: '#cpn2',
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
}
})
</script>
</body>
2.8 组件中的数据存放问题
组件是一个单独功能模块的封装,因此它应该要有属于自己的数据。组件不能直接访问vue实例里面的data数据,那么组件的数据应该有自己保存的地方。那就是保存在Vue.component中的data中,data必须是一个函数,而且返回的必须是一个对象
总结:组件的data存放在Vue.component()的data中,而且data必须是一个函数,返回的必须是一个对象。
2.8.1 为什么组件的data必须是函数?
因为组件可以复用,如果data不是一个函数而是一个对象,那么一个组件在多处地方都有使用,在任意一处地方修改了某些值,那么其他地方组件的值也被修改了。而我们进行组件复用,希望的就是每个组件都有自己的数据,其他地方与我相同的组件不能修改我的数据。
图例解释:
总结:组件的data是一个函数,那么每次使用组件的数据,返回的都是属于自己的数据,其他与我相同的组件也不能修改我的数据
2.9 父组件向子组件通信
2.9.1 储备知识
目前使用的是局部组件,它在vue实例里组件,所以vue实例是父组件。父组件向子组件通信,关键是在子组件里面使用props属性。props可以是数组(即props: [ ]
),也可以是对象(即props: { }
)。如果是对象,可以做类型限制。
总结:父组件向子组件通信,只需在子组件里面使用props属性,定义变量或者对象 接收父组件传来的值
2.9.2 例子
<body>
<div id="app">
<!--<cpn :cmovies="movies" :cmessage="message"></cpn>-->
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<p>{{cmessage}}</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
//props是数组类型
// props: ['cmovies', 'cmessage'],
//props是对象类型
props: {
//可以做类型限制
// cmovies: Array,
// cmessage: String,
cmovies: {
type: Array,
default(){ //如果变量的类型是一个对象或者数组时,默认值必须是一个函数
return []
}
},
cmessage: {
type: String,
default: 'aaaaaaaa', //如果没有传cmessage,而又要在dom层显示cmessage,那么默认值就是aaaaaaaa
required: true, //表示用此组件的时候,必须要把cmessage变量传进来
}
}
}
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
movies: ['海王', '海贼王', '海尔兄弟'],
},
components: {
cpn
}
})
</script>
</body>
2.9.3 props中的驼峰标识
props不支持驼峰标识,在props中定义了驼峰标识格式的变量(比如cInfo),绑定的时候需要使用c-info
总结:在没有用脚手架写vue代码的情况下,尽量不要用驼峰标识书写props属性相关的代码。可以用‘-’、‘_'隔开。
例子:
<body>
<div id="app">
<!-- props不支持驼峰标识,在props中定义了驼峰标识格式的变量(比如cInfo),绑定的时候需要使用c-info-->
<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template id="cpn">
<div>
<h2>{{cInfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return [];
}
},
childMyMessage: {
type: String,
default: '',
}
}
};
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 12,
height: 1.88,
},
message: 'aaaaa',
},
components: {
cpn,
}
})
</script>
</body>
2.10 子组件传给父组件
2.10.1 情景
有些时候在子组件发生的事件,要传给父组件,父组件进而根据传来的信息做出对应的处理(比如请求服务器某些数据,然后返回给子组件)
2.10.2 子传父的步骤
- 子组件监听发生在自己身上的事件
- 子组件定义方法,发射(使用this.$emit(‘事件名(自定义,不要使用驼峰标识)’);) 发生的事件 给父组件
- 父组件监听子组件传来的事件(注意不要使用驼峰标识),定义处理事件的方法
2.10.3 例子
<body>
<!--父组件模板-->
<div id="app">
<!--注意,这里监听的事件不能使用驼峰命名,不支持-->
<!--处理事件的方法默认会将子组件传来的参数接收,不需要在父组件这里显式定义参数接收-->
<cpn @item-click="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>
/**
* 子组件向父组件传递信息:
* 1.子组件监听发生在自己身上的事件
* 2.子组件定义方法,发射(使用this.$emit('事件名(自定义,不要使用驼峰标识)');) 发生的事件 给父组件
* 3.父组件监听子组件传来的事件(注意不要使用驼峰标识),定义处理事件的方法
*
* @type {{template: string, data(): {categories: [{name: string, id: string}, {name: string, id: string}, {name: string, id: string}, {name: string, id: string}]}, methods: {btnClick(*): void}}}
*/
//1.子组件
const cpn = {
template: '#cpn',
data(){
return {
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'},
{id: 'ddd', name: '电脑办公'},
],
}
},
methods: {
btnClick(item){
this.$emit('item-click', item);//this.$emit是发送事件, 第二个参数传递给父组件使用
}
}
}
//2.父组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
},
components: {
cpn,
},
methods: {
cpnClick(item){
console.log(item);
}
}
})
</script>
</body>