vue中的组件,父子组件通信,插槽,事件总线知识详解

310 阅读4分钟

这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

组件化

组件的定义:实现应用中局部代码和资源的集合。

使用组件的注意事项:

  1. 组件名:组件命名需要使用kebal-case方式,不能使用CamelCase命名。但是在脚手架中两者都可以使用。
  2. 组件标签:必须使用双标签,不能使用单标签,使用单标签后,后面的组件不能渲染。但是在脚手架中两者都支持。
  3. const shool = Vue.extend(options) 可简写为:const school = options
  4. 没有el配置选项。

关于VueComponent

  const school = Vue.extend({
         name: "schools",
         template: `<p>{{name}}</p>`,
 ​
         data() {
           return {
             name: "liuzhuang",
             age: this.getdate(),
           };
         },
         methods: {
           getdate() {
             console.log(this);
           },
         },
       });
 ​
  1. 当我们使用Vue.extend()创建组件时,实质上返回的是一个VueComponent构造函数。

  2. 当我们使用标签使用组件时,Vue会帮解析创建实例对象,即执行 new VueComponent(options);

  3. 当我们创建组件,每次调用Vue.extend时,返回的都是一个全新的VueCompont构造函数。

  4. this指向:

    (1)在组件配置中:data函数,methods中的函数,watch中的函数,computed中的函数,它们的this都是指向【VueComponent实例对象】。

    (2)在Vue实例对象配置中:data函数,methods中的函数,watch中的函数,computed中的函数,它们的this都是指向【Vue实例对象】。

内置关系

Vuecomponent.prototype.--prop-- =Vue.prototype

image.png

组件化的三个步骤:

1、创建组件构造器

Vue.extend({ template: 模板 })

2、注册组件

Vue.component({ 标签名,构造器名字})

3、使用组件

全局组件:

在页面任何地方都可以使用

 const cpnC = Vue.extend({
                 template: `
             <div>
             <h1>你好啊</h1>
             <p>第一个vue组件化实例</p>
             </div>`
             })
  注册组件
  Vue.component('my-cpn', cpnC);

局部组件:

只能在

中使用组件

 <div id="app">
         <cpn></cpn>
         <cpn></cpn>
         <cpn></cpn>
 </div>
 ​
 ​
 <script>
         //创建组件构造器对象
         const cpnC = Vue.extend({
                 template: `
             <div>
             <h1>你好啊</h1>
             <p>第一个vue组件化实例</p>
             </div>`
             })
         const app = new Vue({
             el: '#app',
             data: {
                 message: 546
             },
             //局部注册
             components: {
             //标签名:组件构造器
                 cpn: cpnC
             }
         })
     </script>

父子组件

原理:通过父组件来注册和使用子组件。

 <div id="app">
     <cpn2></cpn2>
 </div>
 <script src="./vue.js"></script>
 <script>
     //组件构造器1
     const cpnC1 = Vue.extend({ //子组件
         template: `
             <div>
             <p>我是one</p>
             </div>
             `,
     })
 //组件构造器1
 const cpnC2 = Vue.extend({ //父组件
     //使用cpn1组件
     template: `
             <div>
             <p>我是two</p>
             <cpn1></cpn1> 
             </div>
             `,
     components: { //用cpn2来注册cpn1
         //此时cpn2是cpn1的父组件
         cpn1: cpnC1
     }
 })
 //根组件
 const app = new Vue({
     el: '#app',
     data: {
         message: 'one'
     },
     components: {
         cpn2: cpnC2
     }
 })
 </script>
     

创建组件语法糖

全局组件语法糖:

 Vue.component('cpn1', {
             template: `
             <div>
             <p>我是one</p>
             </div>
             `,
         })

局部组件语法糖:

 components: {
                 'cpn1': {
                     template: `
                     <div>
                     <p>我是one</p>
                     </div>
                     `
                 }
             }

template模板的分离写法

 <!-- 分离写法2 -->
     <template id="cpn">
         <div>
             <p>我是tow</p>
         </div>
     </template>

组件中的data属性

data属性必须是一个函数,目的是为了让每一个组件都有自己的对象,防止连锁反应。如果data对象时,那么每一个组件都指向同一个data对象,一个组件改变data中的值,其他的复用组件也会改变,这就是连锁反应。

例如:

     //根组件模板
     <div id="app">
         <cpn1></cpn1>
         <cpn1></cpn1>
     </div>
     //cpn子组件模板
     <script type="text/x-template" id="cpn">
         <div>
             <p>{{one}}</p>
             <button @click=one-->-</button>
             <button @click=one++>+</button>
         </div>
     </script>
      const app = new Vue({
             el: '#app',
             data: {
                 message: 'one'
             },
             components: {
                 'cpn1': {
                     template: '#cpn',
                     data() {
                         return {
                             one: 0,
                         };
                     },
                 },
             }
         })

多次使用组件时,改变one的值,不会影响另一个的组件的值。

父子组件向子组件传递数据(props)

详见我的这篇文章 juejin.cn/post/705340…

子组件向父组件发射事件

详见我的这篇文章 juejin.cn/post/705443…

全局事件总线

当两个组件不是父子关系时,我们可以通过全局事件总线来进行通信。

定义全局事件总线:

 new Vue({
   render: (h) => h(App),
   beforeCreate() {
     Vue.prototype.$bus = this;
   },
 }).$mount("#app");

使用:和自定义事件的使用方式一样,

 //绑定事件
 this.$bus.$on("getname", (name) => {
       console.log(name);
     });
 ​
 //发射事件
 this.$bus.$emit('getname',this.name);
 ​
 //解绑事件
 this.$bus.$off('getname');

插槽

插槽的使用可以使我们的组件更加灵活,可以有使用组件的人控制在组件中插入哪些元素,提高了组件的复用性和灵活程度。

默认插槽的使用

注意点:

  1. 在使用组件时,我们在组件中插入的标签,都会插入到一个插槽当中。在子组件中多次使用插槽,插入的标签会多次渲染。
  2. 我们可以在插槽中编写后备内容,就是当我们在使用组件时,如果没有插入标签,则会渲染插槽中后备的内容。
 //父组件中使用cpn组件
 <div id="app">
         <cpn>
             <h3>465</h3>
             <h4>你好</h4>
         </cpn>
 </div>
 ​
 //子组件
 <template id="cpn">
     <div>
     <slot><span>hello</span></slot>
     </div>
 </template>

具名插槽

为slot添加name属性,然后在插入标签的时候,我们需要使用<template>包裹我插入的内容,并且使用v-slot属性知名要插入到那个插槽当中。

v-slot:可以简写为#

 //父组件
 <div id="app">
         
         <cpn>
               <template v-slot:header>
                 <button>按钮</button>
               </template>
         </cpn>
         <cpn>
                 //使用简写#
               <template #:center>
                 <button>按钮</button>
               </template>
         </cpn>
 </div>
 //子组件
 <template id="cpn">
     <div>
         <slot name="left"><span ></span></slot>
         <slot name='center'><span ></span></slot>
     </div>
 </template>

作用域插槽

在某个场景下,我们需要父组件来控制插入的标签的样式,或者修改显示的内容时,当时数据实在子组件中的,这个场景下我们可以使用作用域插槽。

我使用插槽时,接受到的值是一个对象,因此,我们可以对它进行结构。重命名,赋默认值等操作。

 //赋默认值
 { SlotName = 'wangwu' }
 //重命名,将SlotName改为sName
 { SlotName : sName }
 //子组件name值
 name:zhangsan
 ​
 //子组件
 <template>
   <div class="son">
     <h1>{{ title }}</h1>
     //绑定prop数据SlotName,发送给使用该组件的父组件
     //作用域插槽和具名插槽结合
     <slot name="content" :SlotName="name"></slot>
 ​
     <h4>间隔</h4>
     //默认插槽和作用域插槽结合,传输一个普通的字符串
     <slot SlotName="年后"></slot>
   </div>
 </template>
 ​
 //父组件
 <son title="美食">
         //作用域插槽和具名插槽结合
       <template #content="{ SlotName }">
         <a href="">百度</a>
         <span>{{ SlotName }}</span>
       </template>
         //默认插槽和作用域插槽结合
       <template #default="{ SlotName }">
         <a href="">淘宝</a>
         <span>{{ SlotName }}</span>
       </template>
 </son>
 ​
 ​