✍目录总览:(组件化概念、组件注册、数据存放、组件数据共享、组件插槽、使用步骤)
一、组件化概念
1. 组件化开发思想
组件化思想的特点:标准、分治、复用、组合
2. 组件定义
**组件化**开发:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
3. Vue中的组件化开发
**vue** 是一个**支持组件化开发的前端框架**。
**vue** 中规定:**组件的后缀名是 .vue**。之前接触到的 App.vue 文件本质上就是一个 vue 的组件,即**单组件**(单文件组件)。
4. Vue组件的三个组成部分
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
二、组件注册
1. 全局组件🔥
//全局组件使用
<div id="app">
<组件名称></组件名称>
</div>
//全局组件注册
Vue.component(组件名称, {
data: 组件数据,
template: 组件模板内容
})
- 1.1 全局组件组件可以在app实例内部任意地方使用:我们可以在 app 实例下使用,也可以在 home 实例下使用,也可以在 message 实例下使用。
<body>
<div id="app">
<div id="home">
<span>首页</span>
<button-counter></button-counter>
</div>
<div id="message">
<span>消息</span>
<button-counter></button-counter>
</div>
<!--调用全局注册组件-->
<button-counter></button-counter>
</div>
<script src="../js/vue.js"></script>
<script>
// 2.定义一个组件(全局组件)
app.component('button-counter',{
data() {
return {
count: 0
}
},
template: `
<button @click="count++">你点击了{{count}}次</button>`
})
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
}
});
// 3. 挂载vue实例
app.mount('#app');
</script>
</body>
- 1.2 可以设置多个全局组件,代码如下:
<body>
<div id="app">
<div id="home">
<span>首页</span>
<button-counter></button-counter>
</div>
<div id="message">
<span>消息</span>
<button-counter></button-counter>
<!--调用第二个注册组件-->
<lk-box></lk-box>
</div>
<!--调用全局注册组件-->
<button-counter></button-counter>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
}
});
// 2.定义一个组件(全局组件)
app.component('button-counter',{
data() {
return {
count: 0
}
},
template: `
<button @click="count++">你点击了{{count}}次</button>
`
})
// 定义第二个全局组件
app.component('lk-box',{
template: `
<div style="width: 200px;height: 200px;background-color:pink;">
盒子组件
</div>
`
})
// 3. 挂载vue实例
app.mount('#app');
</script>
</body>
- 1.3 全局组件之间可以相互使用,使用方式如下:我们在定义第二个全局组件,若向使用第一个全局组件,只需要将第一个全局组件的名称标签写入模板template中即可。
// 定义第二个全局组件
app.component('lk-box',{
template: `
<div style="width: 200px;height: 200px;background-color:pink;">
盒子组件
<button-counter></button-counter>
</div>
`
})
2. 局部组件🔥
- 局部组件只能在注册他的父组件中使用
//局部组件使用
<div id="app">
<ComponentA></ComponentA>
<ComponentB></ComponentB>
<ComponentC></ComponentC>
</div>
//局部组件注册
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app'
components: {
'component-a': ComponentA,
'component-b': ComponentB,
'component-c': ComponentC,
}
})
实例:
//局部组件使用
<body>
<div id="app">
<lk-count></lk-count>
<cc-count></cc-count>
</div>
<script src="../js/vue.js"></script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">你点击了{{count}}次</button>`
}
// 注册第二个局部组件
const Box = {
template: `
<div style="width: 200px;height: 200px;background-color:pink;">
盒子组件
</div> `
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
'cc-count': Box
}
});
// 挂载vue实例
app.mount('#app');
</script>
</body>
3. 注意事项
1. data必须是一个函数
2. 组件模板内容必须是单个跟元素
3. 组件模板内容可以是模板字符串(需要浏览器提供ES6语法支持)
4. 组件命名方式
- 短横线方式:
Vue.component('my-component', { /* ... */ })
- 驼峰方式:
Vue.component('MyComponent', { /* ... */ })
4.总结
全局组件:在整个Vue实例中都可以被调用,若想要全局组件之间相互使用,只需将想使用全局组件的名称写入 template 中
局部组件:只能在当前组件中被使用,若想在其他组件中使用,必须使用 components 将其挂载在想使用的组件中,然后再如全局组件那样向模板template写入名称标签
我们之后其实对局部组件用的更多一些。
三、组件数据存放
Data属性的值是一个函数🔥
- 为什么data在组件中必须是一个函数呢?
- 当然,如果不是一个函数,Vue直接就会报错。
- 组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方
- 而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的
- 基于这一理念,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响
- 组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据
- 而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。
四、组件之间数据的交互共享
4.1 父组件向子组件传值🔥
1. 组件内部通过props接收传递过来的值
Vue.component(‘menu-item', {
props: ['title'],
template: '<div>{{ title }}</div>'
})
2. 父组件通过属性将值传递给子组件
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
3. props属性名规则
- 在props中使用驼峰形式,模板中需要使用短横线的形式
- 字符串形式的模板中没有这个限制
Vue.component(‘menu-item', {
// 在 JavaScript 中是驼峰式的
props: [‘menuTitle'],
template: '<div>{{ menuTitle }}</div>'
})
<!– 在html中是短横线方式的 -->
<menu-item menu-title=“nihao"></menu-item>
4. props属性值类型
- 字符串 String
- 数值 Number
- 布尔值 Boolean
- 数组 Array
- 对象 Object
4.2 子组件向父组件传值🔥
1. 子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text") '>扩大字体</button>
2. 父组件监听子组件的事件
<menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>
3. 子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text", 0.1) '>扩大字体</button>
4. 父组件监听子组件的事件
<menu-item v-on:enlarge-text='fontSize += $event'></menu-item>
4.3 非父子组件间传值🔥
1. 单独的事件中心管理组件间的通信
var eventHub = new Vue()
2. 监听事件与销毁事件
eventHub.$on('add-todo', addTodo)
eventHub.$off('add-todo')
3. 触发事件
eventHub.$emit(‘add-todo', id)
五、组件插槽
5.1 组件插槽的作用
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。
父组件向子组件传递内容
5.2 组件插槽基本用法
//插槽的使用
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
//插槽的定义
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>`
});
5.3 具名插槽用法
//具名插槽的使用
<div id="app">
//第一种:当具名插槽内只有1个标签
<base-layout>
<p slot='header'>标题信息</p>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot='footer'>底部信息信息</p>
</base-layout>
//第二种:当具名插槽内需要填充多个标签
<base-layout>
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
//具名插槽的定义
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>`
});
5.4 作用域插槽
//作用域插槽的使用
<fruit-list v-bind:list= "list">
<template slot-scope="slotProps">
<strong v-if="slotProps.item.current">
{{ slotProps.item.text }}
</strong>
</template>
</fruit-list>
//作用域插槽的定义
<ul>
<li v-for= "item in list" v-bind:key= "item.id" >
<slot v-bind:item="item">
{{item.name}}
</slot>
</li>
</ul>