【Vue】快乐学习第三篇 组件及组件通信
写在前面
Vue快乐学习第三篇,本节主要学习 Vue 组件化和父子组件通信,通过学习,你应该了解组件化相关概念,学习和使用 Vue 全局组件和局部组件注册,以及父子组件通信等知识,干就完了 🎨
什么是组件化 ❓
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
组件化目的 📇
为了解耦:把复杂系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。
组件使用3步骤 🕒
♦️ 创建组件构造器
♦️ 注册组件
♦️ 使用组件
调用 Vue.extend() 插件的是一个组件构造器,通常在创建组件构造器时,传入 template 代表自定义组件的模板,这个模板就是要显示的 HTML 代码,事实上,这种写法实际上基本不使用,但这是基础
调用 Vue.component() 是将刚创建的组件构造器注册成为一个组件,并且给它起一个组件标签名,所以传递了两个参数,1.注册组件的标签名2.组件构造器
组件必须挂载在某个 Vue 实例下,否则是不会生效的
<body>
<div id="app">
<!-- 3.使用组件 -->
<hello></hello>
<hello></hello>
</div>
<script src="./vue.js"></script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `<div>
<h1>
Hello World
</h1>
</div>`
})
// 2.注册组件
Vue.component('hello',hello)
const vm = new Vue({
el: '#app'
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
全局组件和局部组件 🍓
全局注册
全局注册是通过 Vue.component 注册,它可以被所有 vue 实例使用
<body>
<div id="app">
<!-- 3.使用组件 -->
<hello></hello>
<hello></hello>
</div>
<div id="app1">
<hello></hello>
</div>
<script src="./vue.js"></script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `<div>
<h1>
Hello World
</h1>
</div>`
})
// 2.注册组件(全局注册,意味着可以在多个vue实例下使用)
Vue.component('hello',hello)
const vm = new Vue({
el: '#app'
})
const vm1 = new Vue({
el:'#app1'
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
局部注册
局部注册实在某个vue实例内部,通过配置项 components 配置,局部组件只能在注册它的 vue 实例下才可以使用
<body>
<div id="app">
<!-- 3.使用组件 -->
<hello></hello>
<hello></hello>
</div>
<div id="app1">
<hello></hello>
</div>
<script src="./vue.js"></script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `<div>
<h1>
Hello World
</h1>
</div>`
})
// 2.注册组件(全局注册,意味着可以在多个vue实例下使用)
const vm = new Vue({
el: '#app',
// 局部注册,只能在当前 vue 实例下使用
components:{
hello:hello
}
})
const vm1 = new Vue({
el:'#app1'
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
父子组件 🎨
组件和组件之间存在关系,最常见的就是父子组件的关系
<body>
<div id="app">
<c2></c2>
</div>
<script src="./vue.js"></script>
<script>
// 创建第一个组件
const c1 = Vue.extend({
template: `
<div>
<p>我是p1</p>
</div>
`
})
// 创建第二个组件
const c2 = Vue.extend({
template: `
<div>
<p>我是p2</p>
<c1></c1>
<c1></c1>
</div>
`,
components: {
c1: c1
}
})
new Vue({
el: '#app',
data: {
message: 'Hello World'
},
components: {
c2: c2
}
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
注意如果在vue实例下写标签 c1 是不正确的,因为我们既没有全局注册 c1 组件,也没有在 vue 实例中进行注册,而是在 c2 组件内部进行了注册,作用域在 c2 组件,所以可以在 c2 的模板中使用,这一点需要注意
注册组件语法糖 🍬
Vue为简化书写组件的过程,提供了注册的语法糖
省去了 Vue.extend() 这个步骤,而是直接使用一个对象来代替
全局注册组件语法糖,直接使用 Vue.component ,第一个配置组件名,第二个是template模板,底层还是extend的形式
<body>
<div id="app">
<hello></hello>
</div>
<script src="./vue.js"></script>
<script>
// 全局注册,语法糖,底层还是 extend
Vue.component('hello',{
template:`
<div>
<h1>Hello World</h1>
</div>
`
})
new Vue({
el: '#app'
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
局部注册组件语法糖,使用 components 配置项,组件名后跟模板,省去了 Vue.extend()
<body>
<div id="app">
<hello></hello>
</div>
<script src="./vue.js"></script>
<script>
new Vue({
el: '#app',
components:{
hello:{
template:`
<div>
<h1>Hello World</h1>
</div>
`
}
}
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
组件模板抽离的写法 ✍🏼
<body>
<div id="app">
<hello></hello>
<world></world>
</div>
<!-- 1. 模板在script标签中写,但是类型必须是 text/x-template 类型 -->
<script type="text/x-template" id="hello">
<div>
<h2>Hello Vue</h2>
</div>
</script>
<!-- 2. 使用template标签 -->
<template id="world">
<div>
<h2>Hello World</h2>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('hello',{
template:'#hello'
})
Vue.component('World',{
template:'#world'
})
new Vue({
el: '#app',
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
为什么组件data必须是函数 ❓
因为以函数形式返回,当多次调用组件,每个组件都会有自己的一份data,彼此互不影响
组件时一个单独模块的封装,这个模块有属于自居的 HTML 模板,也应该有自己的数据 data……
组件是不可以直接访问 Vue 实例的data的
Vue组件应该有自己保存数据的地方
组件对象也有一个 data 属性,也可以有 method 等属性
只是这个 data 属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据
<body>
<div id="app">
<hello></hello>
</div>
<template id="hello">
<div>
<h2>{{hello}}</h2>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('hello', {
template: '#hello',
data() {
return {
hello: 'Hello World,Hello Vue.js'
}
}
})
new Vue({
el: '#app',
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
这个计数器案例就很好的解释了,为什么vue组件但会data要使用函数的形式
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<template id="counter">
<div>
<button @click="sub">-</button>
{{count}}
<button @click="add">+</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('counter', {
template: '#counter',
data() {
return {
count: 0
}
},
methods: {
add() {
this.count++;
},
sub() {
this.count--;
}
},
})
new Vue({
el: '#app',
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
如果我们调用多次组件去操作的是一个对象,那就会引起连锁反应,像这样
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<template id="counter">
<div>
<button @click="sub">-</button>
{{obj.count}}
<button @click="add">+</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const obj = {
count: 0
}
Vue.component('counter', {
template: '#counter',
data() {
return {
obj
}
},
methods: {
add() {
this.obj.count++;
},
sub() {
this.obj.count--;
}
},
})
new Vue({
el: '#app',
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
父子组件通信 父传子props ✉️
实际开发,往往一些数据需要从上层传递到下层
Vue中通过 props 向子组件传递数据,通过事件向父组件发送消息
<body>
<div id="app">
<child :cmessage="message" :cmovies="movies"></child>
</div>
<template id="child">
<div>
<p>我是子组件</p>
<p>{{cmessage}}</p>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const child = {
template: '#child',
// 数组写法
// props: ['cmessage']
// 对象写法,还可以指定类型,
props: {
cmessage: {
// 类型
type: String,
// 默认值
default: '哈哈',
// 必须传值,否则报错
required: true
},
cmovies: {
type: Array,
default() {
return [];
}
}
}
}
new Vue({
el: '#app',
data: {
message: '你好啊,子组件',
movies: ['天气之子', '你的名字', '千与千寻']
},
components: {
child
}
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
父子组件通信 props驼峰标识 🛑
在未使用单文件组件,即以.vue为后缀的文件时,可能会遇到父组件传子组件时驼峰命名的问题,你可以使用-来解决
<body>
<div id="app">
<npm :c-npm="person"></npm>
</div>
<template id="npm">
<div>
<p>{{cNpm.width}}</p>
<p>{{cNpm.height}}</p>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const npm = {
template: '#npm',
props: {
cNpm: {
type: Object
}
}
}
new Vue({
el: '#app',
components: {
npm
},
data: {
person: {
height: 1.8,
width: 1.5
}
}
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
父子组件通信 子传父自定义事件 🥳
当子组件需要向父组件传递时,就要用到自定义事件了,之前学习的 v-on 不仅可以用于监听 DOM 事件,也可以监听组件间的自定义事件
在子组件使用 $emit()来触发事件,在父组件中使用 v-on 来监听子组件事件
<body>
<!-- 父组件模板 -->
<div id="app">
<!-- 监听自定义事件,接收到事件触发后调用aa函数 -->
<hello @cclick="aa"></hello>
</div>
<!-- 子组件模板 -->
<template id="zi">
<div>
<button v-for="item in shoppings" @click="btnClick(item.name)">{{item.name}}</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
// 子组件
const hello = {
template: '#zi',
data() {
return {
shoppings: [
{ id: 'a', name: '热门推荐' },
{ id: 'b', name: '家用电器' },
{ id: 'c', name: '数码办公' }
]
}
},
methods: {
btnClick(name) {
// 自定义事件 cclick
this.$emit('cclick', name)
}
},
}
// 父组件
new Vue({
el: '#app',
components: {
hello
},
methods: {
aa(name) {
console.log(name);
}
}
})
</script>
</body>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
文章来源: blog.csdn.net,作者:周棋洛ყ ᥱ ᥉,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/m0_53321320/article/details/124167929
- 点赞
- 收藏
- 关注作者
评论(0)