【7.0】Vue之组件

发布时间 2023-08-06 22:33:31作者: Chimengmeng

【一】组件介绍

【1】什么是组件

  • 组件就是:扩展 HTML 元素,封装可重用的代码,目的是复用
  • 例如:
    • 有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html
    • 组件把js,css,html放到一起,有逻辑,有样式,有html
  • 组件是在Web开发中常用的一种技术方式,它可以将页面上的不同部分进行封装,形成独立的、可复用的代码块。
    • 通过组件化的方式,我们可以将一些常见且通用的功能或UI元素进行抽象和封装,以便在不同的页面中进行复用,从而提高代码的可维护性和开发效率。
  • 一个组件通常由多个相关的HTML元素、CSS样式和JavaScript逻辑组成。
    • HTML元素定义了组件的结构和内容,CSS样式确定了组件的外观和布局,JavaScript逻辑则处理组件的交互行为和功能实现。

【2】使用组件的好处

  • 可复用性:

    • 组件可以在不同的页面和项目中重复使用,避免了重复编写相似的代码,节省了开发时间和工作量。
  • 维护性:

    • 当需要对某个功能或UI进行修改时,仅需修改组件本身的代码,即可使所有使用该组件的页面都获得相应的更新,减少了维护成本。
  • 模块化开发:

    • 通过组件化的思想,将一个复杂的页面拆分成多个小的组件,每个组件负责完成特定的功能,有助于提高代码的可读性和可维护性。
  • 拓展性:

    • 通过组件化的方式,我们可以方便地添加、删除或替换不同的组件,以适应需求的变化。

【3】小结

  • 在实际开发中,我们有时候会使用一些现有的组件库,如Bootstrap、Ant Design等,这些组件库提供了丰富的预定义组件,可以快速构建页面。同时,我们也可以根据项目的需求自定义开发组件,以满足特定的功能和样式要求。
  • 总之,组件化是一种重要的开发思想和技术手段,它可以提高代码的可复用性、可维护性和开发效率,对于大型项目和团队协作尤为重要。

【二】组件的分类

  • 全局组件:可以放在根中,可以在所有组件中使用
  • 局部组件:只能在当前组件中使用

【1】全局组件

  • 全局组件是指可以在整个应用中的任何组件中使用的组件,它们通常会被注册到根组件中,以便在其他组件中进行引用和使用。

  • 全局组件具有以下特点:

    • 全局可用:

      • 全局组件可以在应用的任何组件中使用,无需再次引入或注册。
      • 这样可以方便地在整个应用中共享同一组件,并且减少了重复的代码。
    • 跨层级访问:

      • 全局组件可以跨越组件层级进行访问。
      • 即使一个组件嵌套在另一个组件内部,只要全局组件被注册到了根组件中,就可以在任何子组件中使用。
    • 全局状态管理:

      • 由于全局组件可以在不同的组件中使用,因此它们也可以访问和管理全局的状态。
      • 这对于需要在多个组件间共享数据或状态的场景非常有用。
    • 布局和样式统一:

      • 通过使用全局组件,可以保持应用的布局和样式的统一性。
      • 例如,可以定义一个全局的页眉组件,确保在每个页面中都显示相同的页眉。

【2】局部组件

  • 与全局组件相对应的是局部组件,它只能在当前组件内部使用。

  • 局部组件具有以下特点:

    • 组件封装性:

      • 局部组件通常是为了实现当前组件特定的功能而创建的,与其他组件无关。
      • 这样可以增强组件的封装性,减少不必要的耦合。
    • 作用域限制:

      • 局部组件只能在创建它们的组件内部使用,无法跨越组件边界。
      • 这限制了局部组件的可见性和可复用性。
    • 私有状态管理:

      • 由于局部组件仅在单个组件内使用,因此通常只需要维护自身的私有状态,而无需考虑与其他组件之间的数据交互。

【三】案例演示

【1】全局组件

<body>
<div id="app">
    <h1>使用全局组件</h1>
    <button>显示全局组件</button>
    <h2>全局组件在根组件中使用</h2>
    <child></child>
    <h2>全局组件在根组件中第二次使用</h2>
    <child></child>
</div>

</body>

<script>
    // (1)定义一个全局组件:vue2中所有组件只能写在一个标签内管理
    Vue.component('child', {
        // template 类似于上面的其它代码只能包在一个app标签内书写
        template: `
            <div>
                <button @click="clickFunc">后退</button>
                <button>{{title}}</button>
                <button>前进</button>
            </div>
            `,
        data() {
            return {
                title: 'Hello'
            }
        },
        methods: {
            clickFunc() {
                alert('别点我!');
            }
        },

    })
    
    var vm = new Vue({
            el: '#app',
            data: {
                username: ''
            },
            methods: {},
        }
    )
</script>

【2】局部组件

<body>
<div id="app">
    <h1>使用局部组件</h1>
<first_child></first_child>

</div>


</body>

<script>

    // 根组件
    var vm = new Vue({
            el: '#app',
            data: {
                username: ''

            },
            methods: {},
            // (2)定义局部组件:局部组件定义在某个组件内,可以定义多个
            components: {
                'first_child': {
                    template:
                    `
                    <div>
                    <h1>局部组件 first_child </h1>
                    <img :src="url" alt="">
                    </div>
                    `,
                    data() {
                        return {
                            url:'https://img1.baidu.com/it/u=2425296538,2255658679&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691168400&t=34a93a0a94c0b259cca96d58e67816f2',
                        }
                    },
                    methods: {}
                }
            }
        }
    )
</script>

【3】局部/全局组件混合使用

<body>
<div id="app">
    <h1>使用全局组件</h1>
    <child></child>

    <h1>使用局部组件</h1>
    <first_child></first_child>

</div>


</body>

<script>
    // (1)定义一个全局组件:vue2中所有组件只能写在一个标签内管理
    Vue.component('child', {
        // template 类似于上面的其它代码只能包在一个app标签内书写
        template: `
            <div>
                <button @click="clickFunc">后退</button>
                <button>{{title}}</button>
                <button>前进</button>
            </div>
            `,
        data() {
            return {
                title: 'Hello'
            }
        },
        methods: {
            clickFunc() {
                alert('别点我!');
            }
        },

    })

    // (2)定义局部组件:局部组件定义在某个组件内,可以定义多个
    var first_child = {
        template:
            `
                    <div>
                    <h1>局部组件 first_child </h1>
                    <img :src="url" alt="">

                    <h1>局部组件内部使用全局组件</h1>
                    <child></child>
                    </div>
                    `,
        data() {
            return {
                url: 'https://img1.baidu.com/it/u=2425296538,2255658679&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691168400&t=34a93a0a94c0b259cca96d58e67816f2',
            }
        },
        methods: {}
    }

    // 根组件
    var vm = new Vue({
            el: '#app',
            data: {
                username: ''

            },
            methods: {},

            components: {first_child,}
        }
    )
</script>

【四】总结

  • 全局组件是使用Vue.component定义的,可以在全局任意组件中使用

  • 局部组件是定义在某个组件内的:components,只能用在当前组件中

  • 组件可以嵌套定义和使用

  • 扩展:elementui,提供给咱们很多全局组件
  • 全局组件是使用Vue.component定义的,可以在任何组件中使用

    • 而局部组件是定义在某个组件内部的,只能在当前组件中使用。
  • 组件的嵌套是指在一个组件内部使用另一个组件。

    • 通过这种方式,我们可以将页面拆分成更小的可复用的组件,提高代码的重用性和可维护性。
  • "elementui",这是一个第三方库,提供了许多全局组件。

    • Element UI是一个基于Vue.js的组件库,提供了丰富的UI组件,如按钮、表格、弹窗等,可以帮助开发者快速构建美观、功能完善的前端界面。
    • 使用Element UI不仅可以减少开发工作量,而且它的组件都经过优化和测试,在性能和用户体验方面表现较好。
    • 通过引入Element UI的全局组件,开发者可以更加高效地开发出具有一致风格的界面。

【五】组件间通信

组件嵌套,

  • 父组件被数据传递给子组件
    • 自定义属性
    • 在子组件中自定义属性,使用属性指令绑定父组件的变量
    • 在子组件中,使用props接受 ['属性名','属性名2']
    • 在子组件中,使用属性名即可
  • 子组件把数据传递给父组件
    • 自定义事件
    • 父组件中自定义事件:<lqz @myevent="handelEvent"></lqz>
    • 子组件中只要执行 this.$emit('myevent'),就会触发自定义事件对应的函数
  • 父组件向子组件传递数据:

    • 自定义属性:
      • 在父组件中可以通过在子组件标签上添加属性的形式,将数据传递给子组件。
      • 例如:<ChildComponent :message="data"></ChildComponent>,这里的:message是自定义的属性,通过v-bind指令可以将父组件中的data变量的值传递给子组件中的message属性。
    • 使用属性指令绑定父组件的变量:
      • 在子组件中可以通过使用属性指令将父组件中的变量绑定到子组件的自定义属性上。
      • 例如:<p>{{ message }}</p>,这里的message就是子组件中自定义的属性,它会显示父组件传递过来的值。
  • 子组件向父组件传递数据:

    • 自定义事件:
      • 在子组件中可以通过使用$emit方法来触发一个自定义事件,并将需要传递给父组件的数据作为参数传入。
      • 例如:在子组件中执行this.$emit('myevent', data),这里的myevent就是自定义的事件名,data是需要传递给父组件的数据。
    • 父组件中监听自定义事件:
      • 在父组件中可以通过在子组件标签上使用@或者v-on指令来监听子组件触发的自定义事件,并执行相应的函数。
      • 例如:<ChildComponent @myevent="handleEvent"></ChildComponent>,这里的@myevent是监听子组件的myevent事件,当子组件触发该事件时,会调用父组件中的handleEvent函数。

【六】组件间通信之父传子

<body>
<div id="app">
    <h1>组件通信之父传子</h1>
    <first_child :url="url" :myshow="true"></first_child>


</div>


</body>

<script>
    var first_child = {
        template:
            `
                    <div>
                    <h1>局部组件 first_child </h1>
                    <img :src="url" alt="">
                    </div>
                    `,
        data() {
            return {}
        },
        methods: {},
        props: ['url','myshow']
    }
    // 根组件
    var vm = new Vue({
            el: '#app',
            data: {
                username: '',
                url: 'https://img1.baidu.com/it/u=2425296538,2255658679&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691168400&t=34a93a0a94c0b259cca96d58e67816f2',
            },
            methods: {},
            // (2)定义局部组件:局部组件定义在某个组件内,可以定义多个
            components: {
                first_child,
            }
        }
    )
</script>

【七】组件间通信之子传父

</body>

<script>
    var firstChild  = {
        template:
            `
                    <div>
                    <h1>局部组件 first_child </h1>
                    <img :src="url" alt="">
                    <input type="text" v-model="username">
                     <button @click="handleSend">传递到父组件</button>
                    </div>
                    `,
        data() {
            return {
                username: ''
            }
        },
        methods: {
            handleSend() {
                // 传递给父组件
                this.$emit('myevent', this.username)
            }
        },

    }

    // 根组件
    var vm = new Vue({
            el: '#app',
            data: {
                username: ''
            },
            methods: {
                handelEvent(username) {
                    console.log('父组件自定义事件的event执行了')
                    console.log(username)
                    this.username = username
                },

                // (2)定义局部组件:局部组件定义在某个组件内,可以定义多个
                components: {
                    firstChild ,
                }
            }
        }
    )
</script>

【八】动态组件

【1】component标签

  • <component :is="who"></component>

  • component标签的is属性等于组件名字,这里就会显示这个组件

<body>
<div id="app">
    <h1>动态组件</h1>
    <button @click="who='home'">首页</button>
    <button @click="who='good'">商品</button>
    <button @click="who='order'">订单</button>
    <hr>
    <h2>component标签的is属性等于组件名字,这里就会显示这个组件</h2>
    <component :is="who"></component>
</div>

</body>

<script>
    // 定义局部组件
    var good = {
        template:
            `
        <div>
        <h1>商品页面</h1>
        </div>
        `
    }
    var home = {
        template:
            `
        <div>
        <h1>首页</h1>
        </div>
        `
    }
    var order = {
        template:
            `
        <div>
        <h1>订单页面</h1>
        </div>
        `
    }


    var vm = new Vue({
        el: "#app",
        data: {
            who: 'home',
        },
        components: {
            good,
            home,
            order,
        }
    })
</script>

【2】keep-alive

  • keep-alive:可以将数据缓存起来,不被销毁。
  • Vue中的keep-alive是一个抽象组件,它可以将其包裹的内容缓存起来,避免在组件切换时被销毁和重新创建。
    • 通过使用keep-alive,可以提高组件的性能和用户体验。
  • 当keep-alive包裹的组件被切换时,它会将组件的状态、DOM节点以及相关的上下文进行保存,以便下次再次渲染时可以直接使用之前的数据,从而避免重新创建和初始化组件。
    • 这样做可以大幅减少组件的初始化时间和重新渲染的开销,提升页面的响应速度和性能。
  • 在使用keep-alive时,需要注意以下几点:
    • keep-alive只能包裹一个组件,并作为该组件的直接父级。
    • keep-alive需要通过设置include或exclude属性,指定哪些组件需要被缓存或排除于缓存之外。
    • 可以通过设置max属性,限制keep-alive同时缓存的组件实例数量。
    • 缓存的组件会触发activated和deactivated两个生命周期钩子函数,可以在这些钩子函数中执行相应的操作。
<body>
<div id="app">
    <h1>动态组件</h1>
    <button @click="who='home'">首页</button>
    <button @click="who='good'">商品</button>
    <button @click="who='order'">订单</button>
    <hr>
    <h2>keep-alive:可以将数据缓存起来,不被销毁。</h2>
     <hr>
    <keep-alive>
        <component :is="who"></component>
    </keep-alive>
</div>

</body>

<script>
    // 定义局部组件
    var good = {
        template:
            `
        <div>
        <h1>商品页面</h1>
         <p>搜索商品:<input type="text" v-model="name">
         <button @click="handleSearch">搜索</button>
        </div>
        `,
        data() {
            return {
                name: ''
            }
        },
        methods: {
            handleSearch() {
                alert(this.name)
            }
        },
    }
    var home = {
        template:
            `
        <div>
        <h1>首页</h1>
        </div>
        `
    }
    var order = {
        template:
            `
        <div>
        <h1>订单页面</h1>
        </div>
        `
    }


    var vm = new Vue({
        el: "#app",
        data: {
            who: 'home',
        },
        components: {
            good,
            home,
            order,
        }
    })
</script>

【九】插槽

  • 一般情况下,编写完1个组件之后,组件的内容都是写死的,需要加数据 只能去组件中修改,扩展性很差

  • 然后就出现了插槽这个概念,只需在组件中添加<slot></slot>,就可以在body的组件标签中添加内容

  • 使用步骤:

    • 在组件的html的任意位置,放个标签
      • <slot></slot>
    • 后期在父组件使用该组件时
      • <first_child>放内容</first_child>
    • 放的内容,就会被渲染到slot标签中

【1】插槽(Slot)的概念

  • 插槽(Slot)允许父组件在子组件中插入内容。

  • 下面对插槽的详细内容进行解释:

    • 传统组件的限制:

      • 在传统的组件开发中,组件的内容是写死的,无法通过外部传入数据来动态修改组件的内部内容。

      • 这会导致组件的扩展性受限。

    • 插槽的引入:

      • 为了提高组件的扩展性,Vue 引入了插槽的概念。
      • 通过在子组件中添加 <slot></slot> 标签,定义一个能够接受外部内容的插槽。
  • 具体使用步骤如下:

  • 在子组件的模板中添加 <slot></slot> 标签。

<template>
  <div>
    <h2>子组件的标题</h2>
    <p>子组件的内容</p>
    <slot></slot>
  </div>
</template>
  • 在父组件中使用子组件,并在子组件标签内添加要插入的内容。
<template>
  <div>
    <first_child>
      <p>要插入的内容</p>
    </first_child>
  </div>
</template>
  • 渲染结果:在父组件中使用 <first_child> 标签包裹着的内容会被插入到子组件的插槽位置(<slot></slot>)。
<div>
  <h2>子组件的标题</h2>
  <p>子组件的内容</p>
  <p>要插入的内容</p>
</div>
  • 通过使用插槽,父组件可以向子组件插入任意内容,并且可以实现动态的内容展示。

    • 这样就大大提高了组件的扩展性和灵活性。
  • 除了普通插槽外,还有具名插槽和作用域插槽的概念,它们进一步增加了插槽的灵活性和可复用性。

    • 通过使用具名插槽和作用域插槽,我们可以更精确地控制插槽的内容和作用域。
    • 然而,对于初学者来说,了解普通插槽的基本用法已经足够应对大多数情况了。

【2】示例详解

  • 普通插槽的使用
<body>
<div id="app">
    <first_child>
        <div>新插入的div</div>
    </first_child>
    <hr>
    <first_child>
        <img src="https://img2.baidu.com/it/u=1161485666,473333105&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691254800&t=183e7cdd1c1af8af023c9f23bfb0817d" alt="">
    </first_child>
</div>

</body>

<script>

    var first_child = {
        template:
            `
            <div>
            <h1>这是一个内部组件</h1>
            <slot></slot>
            <h2>这是一个内部组件的h2</h2>
            </div>
            `,

    }

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

【3】具名插槽

  • 使用步骤

    • 组件中可以留多个插槽,命名

      <div>
          <h1>我是一个组件</h1>
          <slot name="middle"></slot>
          <h2>我是组件内部的h2</h2>
          <slot name="footer"></slot>
      </div>
      
    • 在父组件中使用时,指定某个标签渲染到某个插槽上

      <first_child>
          <div slot="footer">
              我是div
          </div>
          <img src="https://scpic.chinaz.net/files/default/imgs/2023-05-12/23659b1edfc0a984.jpg" alt="" slot="middle">
      
      </first_child>
      
<body>
<div id="app">
    <first_child>
        <div slot="first">新插入的div</div>
        <img src="https://img2.baidu.com/it/u=1161485666,473333105&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691254800&t=183e7cdd1c1af8af023c9f23bfb0817d"
             alt="" slot="second">
    </first_child>
    <hr>


</div>

</body>

<script>

    var first_child = {
        template:
            `
            <div>
            <h1>这是一个内部组件</h1>
            <slot name="first"></slot>
            <h2>这是一个内部组件的h2</h2>
            <slot name="second"></slot>
            </div>
            `,

    }

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