(三)VUE组件及插槽概览

组件
组件是Vue.js最强大的功能之一,单文件组件、组件库、Vuex等等概念都是建立在组件的基础上的。

一、组件注册的简单知识

1、如何注册子组件

Vue.component('my-component-name', { /* ... */ })

组件名就是my-component-name

2、组件命名

驼峰命名 kebab-case

Vue.component('my-component-name', { /* ... */ })

首字母大写命名 PascalCase

Vue.component('MyComponentName', { /* ... */ })

但是这里我们注意,在js里是不分大小写的,所以直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

3、全局注册和局部注册

全局注册:
全局组件注册后,任何vue实例都可以用

<div id="example">
  <!-- 2、 组件使用 组件名称 是以HTML标签的形式使用  -->  
  <my-component></my-component>
</div>
<script>
    //   注册组件 
    // 1、 my-component 就是组件中自定义的标签名
    Vue.component('my-component', {
      template: '<div>A custom component!</div>'
    })

    // 创建根实例
    new Vue({
      el: '#example'
    })

</script>

局部注册:
只能在当前注册的vue实例中使用

  <div id="app">
      <my-component></my-component>
  </div>


<script>
    // 定义组件的模板
    var Child = {
      template: '<div>A custom component!</div>'
    }
    new Vue({
      //局部注册组件  
      components: {
        // <my-component> 将只在父模板可用  一定要在实例上注册了才能在html文件中使用
        'my-component': Child
      }
    })
 </script>

另:组件注意事项

  • 组件参数的data值必须是函数,同时这个函数要求返回一个对象(
    简单解释一下,如果data是一个对象,如果有多个组件的情况,data的属性是共享的,所以data必须是一个函数) ,像这样
   data(){
      return{
          firstName:'cocoa',
          lastName:'jia'
        }
      }

二、组件传值

组件直接传分三块,父传子,子传父,兄弟之间互传。

1、首先定义根组件 app.vue

<div id="app"></div>
var vm = new Vue({
        el: "#app",
      });

2、父组件以属性的形式绑定值到子组件身上,子组件通过props接收:

 <div id="app">
    <div>{{pmsg}}</div>
     <!--1、menu-item  在 APP中嵌套着 故 menu-item   为  子组件      -->
     <!-- 给子组件传入一个静态的值 -->
    <menu-item title='来自父组件的值'></menu-item>
    <!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle  来自父组件data 中的数据 . 
          传的值可以是数字、对象、数组等等
    -->
    <menu-item :title='ptitle' content='hello'></menu-item>
  </div>

  <script type="text/javascript">
    Vue.component('menu-item', {
      // 3、 子组件用属性props接收父组件传递过来的数据  
      props: ['title', 'content'],
      data: function() {
        return {
          msg: '子组件本身的数据'
        }
      },
      template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        ptitle: '动态绑定属性'
      }
    });

3、子传父
子传父emit在vue官网上说得比较复杂,首先子组件通过调用内建的 $emit 方法并传入事件名称来触发一个事件,使用事件抛出一个值(就是参数),在组件上使用 v-model绑定等,总结起来其核心步骤就两步

  1. 子组件里 @click='$emit("enlarge-text", 5)去触发事件
  2. 父组件通过@enlarge-text="handle($event) 去监听
 <div id="app">
      <div :style='{fontSize:fontSize + "px"}'>{{pmsg}}</div>
      // 2、enlarge-text绑定了handle事件处理函数
      <child-component @enlarge-text="handle($event)"></child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>
      Vue.component("child-component", {
        props: [],
        data: function() {
          return {};
        },
        //1 触发click事件,实际上是用$emit去触发事件enlarge-text
        template: `
            <button @click='$emit("enlarge-text", 5)'>扩大字号</button>
        `,
      });
      new Vue({
        el: "#app",
        data: {
          pmsg: "父组件的原生内容",
          fontSize: 10,
        },
        methods: {
          // 3 调用handle函数
          handle: function(val) {
            this.fontSize += val;
          },
        },
      });
    </script>

4、兄弟传值

我真正觉得麻烦的是兄弟组件之间的传值,需要通过事件中心传递(但是其实后面学了vuex,就感觉就简化了很多)

兄弟传值第一种方式,是利用上面学的父传子 子传父,先把要传的值放到父组件里,再由子组件通过props去取。

第二种方式是建立一个vue事件中心,在全局建立一个空的vue对象,然后将事件绑定到该空对象上,最后通过该空对象来触发$on监听的事件

看起来比较麻烦哈,其实核心就是在method里hub.$emit(),
在mounted()里接收hub.$on()

 var hub = new Vue();

      //tom component
      Vue.component("test-tom", {
        data: function() {
          return { num: 0 };
        },
        template: `
              <div>
                  <div>Tom:{{num}}</div>
                  <div>
                      <button @click="handle">点击</button>
                  </div>
              </div>
          `,
        methods: {
          // 传递
          handle: function() {
            hub.$emit("JERRY-EVENT", 2);
          },
        },
          // 接收
        mounted: function() {
          hub.$on("TOM-EVENT", (val) => {
            this.num += val;
          });
        },
      });

      //jerry component
      Vue.component("test-jerry", {
        data: function() {
          return { num: 1 };
        },
        template: `
              <div>
                  <div>jerry:{{num}}</div>
                  <div>
                      <button @click="handle">点击</button>
                  </div>
              </div>
          `,
        methods: {
          //传递
          handle: function() {
            hub.$emit("TOM-EVENT", 2);
          },
          
        },
        //接收
        mounted: function() {
          hub.$on("JERRY-EVENT", (val) => {
            this.num += val;
          });
        },
      });

      var vm = new Vue({
        el: "#app",
        data: {},
        methods: {
          handle: function() {
          //hub.$off 销毁
            // hub.$off("tom-event");
            // hub.$off("jerry-event");
          },
        },
      });

三 、组件插槽 slot

插槽简单解释就是,父组件传内容(区别传值,传值就是上面的props$emit)给子组件。插槽需要借助的就是关键词slot,简单看分三种,匿名插槽,具名插槽,和作用域插槽。

1、匿名插槽

<slot>默认内容</slot>确定子组件插入的位置,在父组件里定义要传送的内容,如下:

  <div id="app">
      <child-component>父组件内容</child-component>
      <child-component></child-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      Vue.component("child-component", {
        template: `
            <div>
            //当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”,如果父组件没有内容,则会显示slot里面的默认内容
                <slot>默认内容</slot>
            </div>
            `,
      });
      new Vue({
        el: "#app",
      });

2、具名组件
子组件的模版里用 <slot name='name'>绑定元素,来指定当前插槽的名字,父组件里用<template slot="name">来匹配,如果模版里name的值和父组件里slot的值相等,就渲染父组件的内容。插槽的顺序完全取决于模版,如果name没匹配上,则渲染匿名插槽。

 <div id="app">
      <child-component>
        // 通过slot属性指定,比较slot的值和name里的值
        <template slot="context">父组件内容</template>
      </child-component>
      <!-- <child-component></child-component> -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      Vue.component("child-component", {
        data: function() {
          return {};
        },
        template: `
            <div>
               // 在slot里通过属性'name'绑定元素 
               <slot name='context'>默认内容</slot>
            </div>
            `,
      });

      new Vue({
        el: "#app",
        data: {},
      });
    </script>

3、作用域插槽

好了,本文最后一个知识点来了,学完我们就结束了组件和插槽的基础知识。之所以叫做”作用域“插槽,是因为模板虽然是在父级作用域中渲染的,却能拿到子组件的数据。

自 2.6.0 起有所更新。使用 slot-scope attribute 的语法已废弃。vue官网上不是很好理解,我找到一篇解释很详细的文章[Vue核心技术-22,作用域插槽],对文章我加了一些自己的理解,老样子,先来个简单的栗子:

 <div id="app">
      <child-component>
        <template scope="props">
          <p>作用域插槽传值 : {{ props.msg }}</p>
        </template>
      </child-component>
    </div>

    <script type="text/javascript">
      Vue.component("child-component", {
        template:
          '<div class="container">' +
          '<slot msg="作用域插槽message!"></slot>' +
          "</div>",
      });

      var vm = new Vue({
        el: "#app",
      });
    </script>

在模版中,有一个scope="props"写法(props是任意取的名字),通过{{ props.lastname }} 可以取到子组件中传递的latename的值。

再看一个复杂的栗子, 过程可以根据代码的备注来理解。

<div id="app">
   // 2、通过模版,将books传递进来
    <my-list :books1="books">
        <template slot="book" scope="props">
        // 5、遍历、展示
            <li>书名:{{ props.bookName}}--售价:{{ props.bookPrice }}</li>
        </template>
    </my-list>
</div>

<script type="text/javascript">
    Vue.component('my-list',{
    //3、 在子组件里,用props接收
        props:['books1'],
        template:
        '<ul>' +
            '<slot name="book" ' +
            //4、结合v-for,使用:book-name和:book-price绑定并输出数组对象的属性值 ,通过slot暴露给外部插槽
                'v-for="book in books" ' +
                ':book-name="book.name" ' +
                ':book-price="book.price">' +
            '</slot>' +
        '</ul>'
    });

    var vm = new Vue({
        el: '#app',
    //  1、在根组件中,data里有个books的数组
        data:{
            books: [
                {name: 'Vue.js', price:50},
                {name: 'Javascript', price:30},
                {name: 'Css', price:40},
                {name: 'Html', price:60}
            ]
        }
    })
</script>

总结一下本文的所有内容

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值