一、什么是组件?
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
二、创建全局组件:
(一)Vue2.0的创建方式
方式一:
1、使用Vue.extend创建组件
var com1 = Vue.extend({
// 通过 template 属性,指定了组件要展示的HTML结构
template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
})
2、使用 Vue.component('组件的名称', 创建出来的组件模板对象) 注册组件
Vue.component('myCom1', com1)
注意:如果使用Vue.Component 注册全局组件的时候,组件的名称使用了驼峰命名,则在引用组件的时候需要把大写的驼峰改为小写的字母,同时,两个单词之前,使用 “–” 链接。如果不使用则直接拿名称来使用即可。
示例:

方式二:
直接使用Vue.component的方式创建
- Vue.component('mycom2', {
-
- template: '<div>
- <h3>这是直接使用 Vue.component 创建出来的组件</h3>
- <span>123</span>
- </div>'
-
- })
示例:

方式三
1、被控制的 #app 外面,使用 template 元素,定义组件的HTML模板结构
- <template id="tmpl">
-
- <div>
-
- <h1>这是通过 template 元素,在外部定义的组件结构</h1>
-
- <h4>好用,不错!</h4>
-
- </div>
-
- </template>
2、使用id注册组件
- Vue.component('mycom3', {
-
- template: '#tmpl'
-
- })
示例:

(二)Vue3.0中的创建方式:在main.js文件中注册
app.Component(“标签名”,组件名)
三、创建局部组件
局部组件的创建和全局组件的创建方法一样。唯一区别的是,局部组件是在Vue实例中定义的

1、组件可以拥有自己的数据
2、组件中的data 和实例中的data 有点不一样,实例中的data 可以为一个对象。但是组件中的data必须是一个方法
3、组件中的data除了是一个方法,还必须返回一个对象。
4、组件中的data 的使用方式和 实例中的data 使用方式一样
5、组件中的Methods 的定义和使用与实例中一样

五、组件间的通信方式

【组件间的关系】:A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)
(一)父子间通信(props/$emit):父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
1、父组件向子组件传值
(1)子组件users.vue
- <template>
- <div class="hello">
- <ul>
- <li v-for="(user,index) in users" v-bind:key="index">{{ user }}</li>
- </ul>
- </div>
- </template>
-
- <script>
- export default {
- name: "users",
- props: {
- users: { //父组件中子标签自定义的名字
- type: Array,
- require: true
- }
- }
- }
- </script>
-
- <style scoped>
- li{
- list-style-position: inside;
- }
- </style>
(2)父组件App.vue
- <template>
- <div id="app">
- <img alt="Vue logo" src="./assets/logo.png">
- <Users v-bind:users="users"> </Users>
- </div>
- </template>
-
- <script>
- import Users from "@/components/users";
- export default {
- name: 'App',
- data(){
- return {
- users: ['西安邮电','西安石油','西北政法','西安工业','西安财经']
- }
- },
- components: {
- Users,
- }
- }
- </script>
【总结】父组件通过props向下传递数据给子组件。
【注】组件中的数据共有三种形式:data、props、computed
2、子组件向父组件传值(通过事件形式)
(1)子组件
- <template>
- <header>
- <h1 @click="changeTitle">{{ title }}</h1>
- </header>
- </template>
- <script>
- export default {
- name: "Son",
- data(){
- return {
- title: 'Vue.js Demo'
- }
- },
- methods: {
- changeTitle(){
- this.$emit('titleChanged','西安邮电大学');
- }
- }
- }
- </script>
-
- <style scoped>
- h1{
- background-color: greenyellow;
- }
- </style>
(2)父组件
- <template>
- <div id="app">
- <Son v-on:titleChanged="updateTitle"></Son>
- <h2>{{ title }}</h2>
- </div>
- </template>
- <script>
- import Son from "@/components/Son";
- export default {
- name: "Father",
- data(){
- return {
- title: '传递的是一个值'
- }
- },
- methods: {
- updateTitle(e){
- this.title = e
- }
- },
- components:{
- Son,
- }
- }
- </script>
【总结】子组件通过events(事件)给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
(二)兄弟间通信:使用第三方库mitt创建一个中央事件总线(事件中心),用它来触发(emit)事件和监听(on)事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
示例:
(1)安装mitt库:
npm install mitt
(2)创建事件中心(event.js)
- import mitt from "mitt";
- const emitter = mitt();
- export default emitter;
(3)创建兄弟组件
Brother1.vue
- <template>
- <div class="Brother1">
- <h2>兄弟---关羽</h2>
- <p>{{ data1 }} {{ data2 }}</p>
- <button @click="sendData">将美酒送给三弟</button>
- </div>
- </template>
-
- <script>
- import emitter from "@/event";
- export default {
- name: "Brother1",
- data(){
- return {
- data1: "美酒",
- data2: ''
- }
- },
- methods:{
- //接收数据的方法
- receive: function () {
- emitter.on('two-to-one',(data)=>{
- console.log(data);
- this.data2 = data;
- })
- },
- sendData:function () {
- emitter.emit('one-to-two',this.data1);
- }
- },
- mounted() {
- //执行该方法
- this.receive();
- }
- }
- </script>
-
- <style scoped>
- .Brother1{
- color: blueviolet;
- font-size: 20px;
- border: 1px solid #bbb;
- margin-bottom: 10px;
- }
- </style>
Brother2.vue
- <template>
- <div class="Brother12">
- <h2>兄弟---张飞</h2>
- <h3>{{ data1 }} {{ data2 }}</h3>
- <button @click="sendData">将宝剑送给二哥</button>
- </div>
- </template>
-
- <script>
- import emitter from '@/event.js';
- export default {
- name: "Brother12",
- data(){
- return {
- data1: "宝剑",
- data2: ''
- }
- },
- methods:{
- //发送数据的方法
- sendData: function () {
- emitter.emit('two-to-one',this.data1)
- },
- receive: function (){
- emitter.on('one-to-two',(data)=>{
- this.data2= data;
- })
- }
- },
- mounted() {
- this.receive();
- }
- }
- </script>
-
- <style scoped>
- .Brother12{
- color: yellowgreen;
- font-size: 20px;
- border: 1px solid #ccc;
- }
- </style>
App.vue
- <template>
- <img alt="Vue logo" src="./assets/logo.png">
- <Brother1></Brother1>
- <Brother2></Brother2>
- </template>
-
- <script>
- import Brother1 from "@/components/Brother1";
- import Brother2 from "@/components/Brother2";
-
- export default {
- name: 'App',
- components: {
- Brother1,
- Brother2
- }
- }
- </script>
运行结果

(三)跨级通信(provide/inject)
provide / inject 类似于消息的订阅和发布。provide 提供或发送数据, inject 接收数据。
Vue3 的 provide / inject 只能在 setup 期间调用,使用之前,必须从 vue 显示导入 provide/inject 方法。
provide( name,value ):函数接收两个参数
name:定义提供 property 的 name 。
value :property 的值。
inject(name,default):函数有两个参数
name:接收 provide 提供的属性名。
default:设置默认值,可以不写,是可选参数。
附:setup钩子函数位于created 和beforeCreated之前,用于代替created 和beforeCreated
(1)在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
(2)由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
(3)在setup函数里不能访问到this。由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
(4)setup函数只能是同步的不能是异步的
(5)provide/inject 只能写在setup中
示例:
QianLong.vue
- <template>
- <div>
- <h2>孙子:乾隆</h2>
- <div>爷爷:{{ getYeYeData }}</div>
- </div>
- </template>
-
- <script>
- import { inject } from "vue";
- export default {
- name: "QianLong",
- setup(){
- let getYeYeData = inject('giveLastWords')
- return {
- getYeYeData
- }
- }
- }
- </script>
YongZheng.vue
- <template>
- <h2>儿子:雍正</h2>
- <div>父亲:{{ getFatherData}}</div>
- <hr/>
- <ql-com></ql-com>
- </template>
-
- <script>
- import { inject } from "vue";
- import QianLong from "@/components/QianLong";
- export default {
- name: "YongZheng",
- components: {
- "ql-com": QianLong
- },
- setup() {
- let getFatherData = inject('giveLastWords')
- return {
- getFatherData
- }
- }
- }
- </script>
KangXi.vue
- <template>
- <h2>康熙:{{ lastWords }}</h2>
- <yz-com></yz-com>
- </template>
-
- <script>
- import YongZheng from "@/components/YongZheng";
- import { provide } from "vue";
-
- export default {
- name: "KangXi",
- components: {
- "yz-com": YongZheng
- },
- setup(){
- let lastWords = "整顿吏治"
- provide('giveLastWords',lastWords)
- return {
- lastWords
- }
- }
-
- }
- </script>
App.vue
- <template>
- <img alt="Vue logo" src="./assets/logo.png">
- <KangXi></KangXi>
- </template>
-
- <script>
- import KangXi from "@/components/KangXi";
- export default {
- name: 'App',
- components: {
- KangXi
- }
- }
- </script>
运行结果:

组件间通信常见使用场景可以分为三类:
父向子传递数据是通过 props,子向父是通过 events($emit);
Bus(mitt);Vuex
Bus(mitt);Vuex;provide / inject