• Vue(四)--组件化


    一、组件化认识

    1.1、什么事组件化

    image

    1.2、组件化思想

    image

    二、组件注册使用

    2.1、注册步骤

    image

    <div id="app">
        <!--3.使用组件-->
        <my-cpn></my-cpn>
        <div>
            <my-cpn></my-cpn>
        </div>
    
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
        //1.创建组件构造对象
        const cpnC = Vue.extend({
            template: `
            <div>
                <h2>标题</h2>
                <p>内容1</p>
                <p>内容2</p>
            </div>`
        })
    
        //2.注册组件
        Vue.component('my-cpn',cpnC)
    
        const app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vuejs'
            }
        })
    </script>

    2.2、注册解析

    1)Vue.extend():

    • 调用Vue.extend()创建的是一个组件构造器。
    • 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
    • 该模板就是在使用到组件的地方,要显示的HTML代码。
    • 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

    2)Vue.component():

    • 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
    • 所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

    3.组件必须挂载在某个Vue实例下,否则它不会生效

    image

    三、全局组件和局部组件

    3.1、全局组件

    当我们通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。

    image

    3.2、局部组件

    我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

    image

    四、父子组件

    <div id="app">
        <parent-cpn></parent-cpn>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        //创建一个子组件
        const childCpn = Vue.extend({
            template: `
                <div>子组件内容</div>`
        });
    
        //创建一个父组件
        const parentCpn = Vue.extend({
            template: `
                <div>
                    <h2>父组件标题</h2>
                    <child-cpn></child-cpn>
                </div>`,
            components: {
                'child-cpn': childCpn
            }
        })
    
        //root组件
        const app = new Vue({
            el: '#app',
            components: {
                'parent-cpn': parentCpn
            }
        })
    </script>

    五、注册组件语法糖

    主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替

    <div id="app">
        <cpn1></cpn1>
        <cpn2></cpn2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
        // 2.注册全局组件
        Vue.component('cpn1', {
            template: `
          <div>
            <h2>我是标题1</h2>
            <p>我是内容, 哈哈哈哈</p>
          </div>
        `
        })
    
        // 2.注册局部组件的语法糖
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                'cpn2': {
                    template: `
                      <div>
                        <h2>我是标题2</h2>
                        <p>我是内容, 呵呵呵</p>
                      </div>`
                }
            }
        })
    </script>

    六、模板的分离

    如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。

    Vue提供了两种方案来定义HTML模块内容:

    • 使用<script>标签
    • 使用<template>标签

    6.1、使用<script>标签

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app">
        <cpn></cpn>
    </div>
    
    <!--1.script标签, 注意:类型必须是text/x-template-->
    <script type="text/x-template" id="cpn">
    <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈</p>
    </div>
    </script>
    
    <script src="../js/vue.js"></script>
    <script>
    
        // 1.注册一个全局组件
        Vue.component('cpn', {
            template: '#cpn'
        })
    
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            }
        })
    </script>
    </body>
    </html>

    6.2、使用<template>标签

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app">
        <cpn></cpn>
    </div>
    
    <!--2.template标签-->
    <template id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>我是内容,呵呵呵</p>
        </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    
        // 1.注册一个全局组件
        Vue.component('cpn', {
            template: '#cpn'
        })
    
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            }
        })
    </script>
    </body>
    </html>

    七、组件数据存放

    7.1、组件可以访问Vue实例数据吗?

    image

    7.2、组件数据的存放

    组件对象也有一个data属性,只是这个data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据

    <div id="app">
        <my-cpn></my-cpn>
    </div>
    
    <template id="cpn">
        <div>消息: {{message}}</div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
        // 1.注册一个全局组件
        Vue.component('my-cpn', {
            template: '#cpn',
            data() {
                return {
                    message: 'Hello'
                }
            }
        })
    
        const app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vuejs'
            }
        })
    </script>

    为什么data在组件中必须是一个函数呢?

    • 首先,如果不是一个函数,Vue直接就会报错。
    • 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <!--组件实例对象-->
    <div id="app">
      <cpn></cpn>
      <cpn></cpn>
      <cpn></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <h2>当前计数: {{counter}}</h2>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
      // 1.注册组件
      const obj = {
        counter: 0
      }
      Vue.component('cpn', {
        template: '#cpn',
        // data() {
        //   return {
        //     counter: 0
        //   }
        // },
        data() {
          return obj
        },
        methods: {
          increment() {
            this.counter++
          },
          decrement() {
            this.counter--
          }
        }
      })
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        }
      })
    </script>
    
    </body>
    </html>
    View Code

    八、父子之间通信

    image

    8.1、父传子-props基本用法

    在子组件中,使用选项props来声明需要从父级接收到的数据。

    props的值有两种方式:

    • 方式一:字符串数组,数组中的字符串就是传递时的名称。
    • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

    我们先来看一个最简单的props传递:

    image

    <div id="app">
        <cpn :cmessage="message" :cmovies="movies"></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <ul>
                <li v-for="item in cmovies">{{item}}</li>
            </ul>
            <h2>{{cmessage}}</h2>
        </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
        // 父传子: props
        const cpn = {
            template: '#cpn',
            props: ['cmovies', 'cmessage']
        }
    
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊',
                movies: ['海王', '海贼王', '海尔兄弟']
            },
            components: {
                cpn
            }
        })
    </script>

    8.2、父传子-props数据验证

    image

    <div id="app">
      <cpn :cmessage="message" :cmovies="movies"></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <ul>
          <li v-for="item in cmovies">{{item}}</li>
        </ul>
        <h2>{{cmessage}}</h2>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      // 父传子: props
      const cpn = {
        template: '#cpn',
        props: {
          // 提供一些默认值, 以及必传值
          cmessage: {
            type: String,
            default: 'aaaaaaaa',
            required: true
          },
          // 类型是对象或者数组时, 默认值必须是一个函数
          cmovies: {
            type: Array,
            default() {
              return []
            }
          }
        }
      }
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊',
          movies: ['海王', '海贼王', '海尔兄弟']
        },
        components: {
          cpn
        }
      })
    </script>

    8.3、父传子-pros中的驼峰标识

    <div id="app">
        <cpn :c-info="info" :child-my-message="message" v-bind:class></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <h2>{{cInfo}}</h2>
            <h2>{{childMyMessage}}</h2>
        </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
        const cpn = {
            template: '#cpn',
            props: {
                cInfo: {
                    type: Object,
                    default() {
                        return {}
                    }
                },
                childMyMessage: {
                    type: String,
                    default: ''
                }
            }
        }
    
        const app = new Vue({
            el: '#app',
            data: {
                info: {
                    name: 'why',
                    age: 18,
                    height: 1.88
                },
                message: 'aaaaaa'
            },
            components: {
                cpn
            }
        })
    </script>
    

    8.4、子传父-自定义事件

    当子组件需要向父组件传递数据时,就要用到自定义事件了。v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件

    自定义事件的流程:

    1. 在子组件中,通过$emit()来触发事件。
    2. 在父组件中,通过v-on来监听子组件事件。

    image

    <div id="app">
        <child-cpn @increment="changeTotal" @decrement="changeTotal"></child-cpn>
        <h2>点击次数: {{total}}</h2>
    </div>
    
    <template id="childCpn">
        <div>
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                total: 0
            },
            methods: {
                changeTotal(counter) {
                    this.total = counter
                }
            },
            components: {
                'child-cpn': {
                    template: '#childCpn',
                    data () {
                        return {
                            counter: 0
                        }
                    },
                    methods: {
                        increment() {
                            this.counter++;
                            this.$emit('increment',this.counter)
                        },
                        decrement() {
                            this.counter--;
                            this.$emit('decrement',this.counter)
                        }
                    }
                }
            }
        })
    </script>

    8.5、父子组件的访问方式: $children

    父组件访问子组件:使用$children或$refs reference(引用)

    子组件访问父组件:使用$parent

    this.$children是一个数组类型,它包含所有子组件对象

    image

    $children的缺陷:

    • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
    • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
    • 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

    8.6、父子组件的访问方式: $refs

    $refs和ref指令通常是一起使用的。

    首先,我们通过ref给某一个子组件绑定一个特定的ID。

    其次,通过this.$refs.ID就可以访问到该组件了。

    image

    8.7、父子组件的访问方式: $parent

    image

    九、插槽slot

    组件的插槽:

    • 组件的插槽也是为了让我们封装的组件更加具有扩展性。
    • 让使用者可以决定组件内部的一些内容到底展示什么

    例子:移动网站中的导航栏。

    • 移动开发中,几乎每个页面都有导航栏。
    • 导航栏我们必然会封装成一个插件,比如nav-bar组件。
    • 一旦有了这个组件,我们就可以在多个页面中复用了。

    image

    9.1、插槽的基本使用

    在子组件中,使用特殊的元素<slot>就可以为子组件开启一个插槽。该插槽插入什么内容取决于父组件如何使用

    image

    9.2、具名插槽slot

    image

    9.3、编译作用域

    image

    9.4、作用域插槽:使用

    image

    image

    <div id="app">
        <!--默认方式-->
        <cpn></cpn>
        <cpn>
            <!--获取子组件中的pLanguage-->
            <template slot-scope="slot">
                <span>{{slot.data.join(' - ')}}</span>
            </template>
        </cpn>
        <cpn>
            <!--获取子组件中的pLanguage-->
            <template slot-scope="slot">
                <span>{{slot.data.join(' * ')}}</span>
            </template>
        </cpn>
    </div>
    
    <template id="cpn">
        <div>
            <slot :data="pLanguage">
                <ul>
                    <li v-for="item in pLanguage">{{item}}</li>
                </ul>
            </slot>
        </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vuejs'
            },
            components: {
                cpn: {
                    template: '#cpn',
                    data() {
                        return {
                            pLanguage: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
                        }
                    }
                }
            }
        })
    </script>
  • 相关阅读:
    AcWing 125. 耍杂技的牛
    AcWing 148. 合并果子
    AcWing 907. 区间覆盖
    AcWing 908. 最大不相交区间数量
    AcWing 906. 区间分组
    AcWing 905. 区间选点
    AcWing 285. 没有上司的舞会
    AcWing 1049. 大盗阿福
    AcWing 901. 滑雪
    AcWing 91. 最短Hamilton路径
  • 原文地址:https://www.cnblogs.com/hujinzhong/p/13602241.html
Copyright © 2020-2023  润新知