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>
posted @ 2020-09-02 16:03  运维人在路上  阅读(303)  评论(0编辑  收藏  举报