04:组件化开发
本文介绍了Vue中组件化开发的基本概念和注册组件的步骤,包括全局组件和局部组件的区别,以及如何使用组件构造器对象来创建和注册组件。通过示例演示了如何在Vue应用中使用组件实现页面模块化开发。

Vue自学笔记4:组件化开发
本文章为观看B站codewhy老师教学视频自学vuejs过程中记录的笔记,对视频进行了清晰的整理。
(帮大家节省自己整理笔记的时间啦,剩下的时间一定要去上机实操,多多练习!!)
ps:最全最新Vue、Vuejs教程,从入门到精通_哔哩哔哩_bilibili(附上视频链接!)
三、组件化开发
1. 认识组件化
1.1 什么是组件化?
1.2 Vue组件化思想
2. 注册组件
2.1 注册组件的基本步骤
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.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: {
}
})
</script>
</body>
2.2 注册组件步骤解析
第三步的解析
3. 组件其他补充
3.1 全局组件和局部组件(局部组件最常用)
全局组件:
<body>
<div id="app">
<!-- 3.使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script>
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
<p>我是内容</p>
</div>`
})
// 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用)
Vue.component('cpn', cpnC)
const app= new Vue({
el: "#app",
data: {
}
})
</script>
</body>
局部组件(用的最多):
<body>
<div id="app">
<!-- 3.使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script>
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
<p>我是内容</p>
</div>`
})
const app = new Vue({
el: "#app",
data: {
},
//2.注册组件
components: {
//cpn是使用组件时的标签名
cpn: cpnC
}
})
</script>
</body>
3.2 父组件和子组件
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script>
// 1.创建第一个组件
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是组件1</h2>
<p>我是内容</p>
<p>我是内容</p>
</div>`
})
// 2.创建第二个组件,作为第一个组件的父组件
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是组件2</h2>
<p>我是内容</p>
<p>我是内容</p>
<cpn1></cpn1>
</div>`,//注册子组件
components:{
cpn1:cpnC1
}
//组件2是组件1的父组件
})
const app = new Vue({
el: "#app",
data: {
},//3.注册组件
components: {
cpn2: cpnC2,
}
})
</script>
</body>
ps:
其实Vue的实例对象app也是一个组件,被称为根组件(root)
3.3 注册组件语法糖
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script>
//注册全局组件(语法糖)
Vue.component('cpn1', {
template: `
<div>
<h2>
hello,world
</h2>
<p>i'm content1111</p>
</div>
`
})
const app = new Vue({
el: "#app",
data: {
},
//注册局部组件(语法糖)
components: {
cpn2: {
template: `
<div>
<h2>
hello,world
</h2>
<p>i'm content222</p>
</div>
`
}
}
})
</script>
</body>
3.4 模板的分离写法
Vue提供了两种方案来定义HTML模块内容:使用<script>
标签 和 使用<template>
标签
<body>
<div id="app1">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!-- 方法一:script标签,注意:类型必须是text-template -->
<!-- <script type="text/x-template" id="cpn1">
<div>
<h2>i'm title</h2>
<p>i'm content</p>
</div>
</script> -->
<!-- 方法二:template标签 -->
<template id="cpn-tem">
<div>
<h2>i'm title</h2>
<p>i'm content</p>
</div>
</template>
<script>
//注册全局组件
Vue.component('cpn1', {
template: '#cpn-tem'
})
const app = new Vue({
el: '#app1',
data: {
},//注册局部组件
components: {
cpn2: {
template:'#cpn-tem'
}
}
})
</script>
</body>
4. 组件数据存放
4.1 组件可以访问Vue实例数据吗?
4.2 组件数据的存放
<body>
<div id="app1">
<cpn></cpn> //3.调用组件
</div>
<template id="cpn1"> //1.组件模板
<div>
<h2>{{title}}</h2>
<p>i'm content</p>
</div>
</template>
<script>
//组件中的数据存放在这
Vue.component('cpn', { //2.注册组件
template: '#cpn1',
data(){
return {
title:'我是组件的数据'
}
}
})
const app = new Vue({
el: '#app1',
data: {
}
})
</script>
</body>
为什么是一个函数呢?
(详见视频教程https://www.bilibili.com/video/BV15741177Eh?p=58&vd_source=507e428c4f7818b4cbe408245176625e)
如果#app1中多次调用cpn组件产生多个组件实例化对象,因为使用是函数,所以返回每个对象的数据之间是不互通的,是相互独立的.
5. 父子组件的通信
5.1 父级向子级传递
1) props基本用法
prop是property的缩写,意思是:属性
<body>
<!-- 父组件模板 -->
<div id="app">
<!-- 把:cmessage="message"删掉,就会显示默认值 -->
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<p>{{cmovies}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script>
//子组件
const cpn = {
template: '#cpn',
//方式一:
// props: ['cmovies', 'cmessage'],
//方式二:
props: {
//2.1.类型限制
// cmovies: Array,
// cmessag: String
//2.2.提供一些默认值
cmovies: {
type: Array,
default(){
return ['a','b','c']
}//类型时数组,对象时,默认值必须是一个函数
},
cmessage: {
type: String,
default: 'default message',
require: true
//require表示,在使用cmessage时,必须传值(传值指在使用cpn组件标签里面的:cmessage=""),否则报错
}
},
data() {
return {}
},
methods: {}
}
//父组件
const app = new Vue({
el: "#app",
data: {
message: 'hello,world',
movies: ['lalaland', 'star-war', 'her']
},
components: {
cpn
}
})
</script>
</body>
解读:
5.2 子级向父级传递
通过自定义事件
来完成
<body>
<!-- 父组件模板 -->
<div id="app">
<!-- 监听事件item_click -->
<!-- cpn_click未填写参数,默认传送发射事件的第二个参数,既item对象 -->
<cpn @item_click="cpn_click"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn_tem">
<div>
<button v-for="item in categories" @click="btn_click(item)">{{item.name}}</button>
</div>
</template>
<script>
//子组件
const cpn = {
template: '#cpn_tem',
data() {
return {
categories: [
{ id: 'aa', name: '热门推荐' },
{ id: 'bb', name: '手机数码' },
{ id: 'cc', name: '家用家电' },
{ id: 'dd', name: '电脑办公' }
]
}
},
methods:{
btn_click(item){
// 发射事件:自定义事件item_click
this.$emit('item_click',item)
}
}
}
//父组件
const app = new Vue({
el: "#app",
data: {
},
components: {
cpn
},
methods:{
cpn_click(item){
console.log('父组件接收到了子组件传来的数据:'+item.name);
}
}
})
</script>
</body>
解读:
5.3 组件通信-父子组件通信案例
视频链接:https://www.bilibili.com/video/BV15741177Eh?p=64&spm_id_from=pageDriver&vd_source=507e428c4f7818b4cbe408245176625e
目的: input框中输入的数据,与子组件props(既cnum),data(既dnum)中的变量都实现捆绑,并且与父组件传来的变量(既num)也实现捆绑
<body>
<div id="app">
<cpn :cnum1="num1" :cnum2="num2" @num1change="appnum1change" @num2change="appnum2change"></cpn>
<h2>num1:{{num1}}</h2>
<h2>num2:{{num2}}</h2>
</div>
<template id="cpn_tem">
<div>
<h2>cnum1:{{cnum1}}</h2>
<h2>dnum1:{{dnum1}}</h2>
<input type="number" :value="dnum1" @input="dnum1Input">
<h2>cnum2:{{cnum2}}</h2>
<h2>dnum2:{{dnum2}}</h2>
<input type="number" :value="dnum2" @input="dnum2Input">
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 2
},
methods: {
appnum1change(value) {
this.num1 = value
},
appnum2change(value) {
this.num2 = value
}
},
components: {
cpn: {
template: '#cpn_tem',
props: {
cnum1: Number,
cnum2: Number
},
data() {
return {
dnum1: this.cnum1,
dnum2: this.cnum2
}
},
methods: {
dnum1Input(event) {
this.dnum1 = event.target.value
this.$emit('num1change', this.dnum1)
},
dnum2Input(event) {
this.dnum2 = event.target.value
this.$emit('num2change', this.dnum2)
}
}
}
}
})
</script>
</body>
6.父子组件的访问
6.1 父组件访问子组件
1) $children
不常用,因为一些缺陷(
当遇到该父组件使用多次同名子组件时
)
2) $refs(常用)
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick"></button>
</div>
<template id="cpn_tem">
<div>
我是子组件
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnClick(){
// 1. $children
// console.log(this.$children);
// console.log(this.$children[0]);
// console.log(this.$children[0].name);
// 2.$refs
console.log(this.$ref.aaa);
}
},
components: {
cpn: {
template: '#cpn_tem',
props: {
},
data() {
return {
name:'我是子组件的name'
}
},
methods: {
}
}
}
})
</script>
</body>
6.2子组件访问父组件
1)$parent
访问父组件
不常用,因为耦合度太高
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn_tem">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn_tem">
<div>
<h2>我是ccpn组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {
},
components: {
cpn: {
template: '#cpn_tem',
data() {
return {
name:'我是cpn的name'
}
},
components: {
ccpn: {
template: '#ccpn_tem',
methods: {
btnClick() {
// 1.访问父组件$parent
console.log(this.$parent.name);
//子组件ccpn可以通过$parent访问到其父组件cpn的data
}
}
}
}
}
}
})
</script>
</body>
3.$root
访问根组件
也不常用,因为根组件是vue,而真实开发时vue中很简洁,没什么好访问的
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn_tem">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn_tem">
<div>
<h2>我是ccpn组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
message:'我是app的message'
},
methods: {
},
components: {
cpn: {
template: '#cpn_tem',
data() {
return {
name:'我是cpn的name'
}
},
components: {
ccpn: {
template: '#ccpn_tem',
methods: {
btnClick() {
// 2.访问根组件$root
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>
</body>
7. 插槽slot
7.1 前缀知识:编译作用域
7.2 slot的作用
7.2 slot基本使用
<body>
<div id="app">
<cpn><button>按钮</button></cpn>
<cpn><span>我是插槽里的内容</span></cpn>
<cpn><i>我也是插槽里的内容</i></cpn>
<cpn><input type="text" value="请输入内容"></cpn>
</div>
<template id="cpn_tem">
<div>
<h2>我是组件</h2>
<h1>我下面是插槽</h1>
<slot></slot>
<p>------------------</p>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
cpn: {
template: '#cpn_tem',
}
}
})
</script>
</body>
7.3 (使用v-slot)具名插槽slot
<body>
<div id = "app">
<cpn><button slot="center">修改中间默认值</button></cpn>
<cpn><button>修改无名默认值</button></cpn>
</div>
<template id="cpn_tem">
<div>
<slot name="left"><span>左边默认内容</span></slot>
<slot name="center"><span>中间默认内容</span></slot>
<slot name="right"><span>右边默认内容</span></slot>
<slot><span>无名默认内容</span></slot>
</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{},
method:{},
components:{
cpn:{
template:'#cpn_tem'
}
}
})
</script>
</body>
7.4 编译作用域
7.5 (已弃用)作用域插槽
1) 准备
2) 使用
8. (已取代slot了)v-slot
8.1 v-slot实现具名插槽
<body>
<div id="app">
<!-- <cpn slot="name1"></cpn> -->
<cpn v-slot:name1>v-slot修改默认内容</cpn>
</div>
<template id="cpn_tem">
<div>
<slot name="name1"><span>插槽默认内容</span></slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {},
method: {},
components: {
cpn: {
template: '#cpn_tem'
}
}
})
</script>
</body>
8.2 v-slot实现作用域插槽
实现功能:父组件获得子组件数据,将其放在插槽中
<body>
<!-- 2.6版本后才能使用 -->
<div id="app">
<!-- 未指定具名插槽时,则为无名插槽提供数据 -->
<!-- <cpn v-slot="message">{{message.msg}}</cpn> -->
<!-- 若子组件有多个具名插槽,想为名叫aaa的具名插槽提供数据 -->
<cpn v-slot:aaa="message">{{message.msg}}</cpn>
</div>
<template id="cpn_tem">
<div>
<slot :msg="son_data">第一个插槽,默认内容</slot>
<br>
<slot name="aaa" :msg="son_data">第二个插槽,默认内容</slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {},
method: {},
components: {
cpn: {
template: '#cpn_tem',
data() {
return {
son_data: '我是son_data'
}
}
}
}
})
</script>
</body>