【Vue】快乐学习第三篇 组件及组件通信



写在前面

Vue快乐学习第三篇,本节主要学习 Vue 组件化和父子组件通信,通过学习,你应该了解组件化相关概念,学习和使用 Vue 全局组件和局部组件注册,以及父子组件通信等知识,干就完了

在这里插入图片描述

文章目录

  • 什么是组件化 ❓
  • 组件化目的
  • 组件使用3步骤
  • 全局组件和局部组件
  • 父子组件
  • 注册组件语法糖
  • 组件模板抽离的写法 ✍
  • 为什么组件data必须是函数 ❓
  • 父子组件通信 父传子props ✉️
  • 父子组件通信 props驼峰标识
  • 父子组件通信 子传父自定义事件

什么是组件化 ❓

组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
【Vue】快乐学习第三篇 组件及组件通信_第1张图片

组件化目的

为了解耦:把复杂系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。

组件使用3步骤

♦️ 创建组件构造器
♦️ 注册组件
♦️ 使用组件

调用 Vue.extend() 插件的是一个组件构造器,通常在创建组件构造器时,传入 template 代表自定义组件的模板,这个模板就是要显示的 HTML 代码,事实上,这种写法实际上基本不使用,但这是基础

调用 Vue.component() 是将刚创建的组件构造器注册成为一个组件,并且给它起一个组件标签名,所以传递了两个参数,1.注册组件的标签名2.组件构造器

组件必须挂载在某个 Vue 实例下,否则是不会生效的
【Vue】快乐学习第三篇 组件及组件通信_第2张图片

<body>
    <div id="app">
        
        <hello>hello>
        <hello>hello>
    div>

    <script src="./vue.js">script>

    <script>
        // 1.插件组件构造器
        const hello = Vue.extend({
            template:  `

Hello World

`
}) // 2.注册组件 Vue.component('hello',hello) const vm = new Vue({ el: '#app' })
script> body>

全局组件和局部组件

全局注册

全局注册是通过 Vue.component 注册,它可以被所有 vue 实例使用
【Vue】快乐学习第三篇 组件及组件通信_第3张图片

<body>
    <div id="app">
        
        <hello>hello>
        <hello>hello>
    div>
    <div id="app1">
        <hello>hello>
    div>

    <script src="./vue.js">script>

    <script>
        // 1.插件组件构造器
        const hello = Vue.extend({
            template:  `

Hello World

`
}) // 2.注册组件(全局注册,意味着可以在多个vue实例下使用) Vue.component('hello',hello) const vm = new Vue({ el: '#app' }) const vm1 = new Vue({ el:'#app1' })
script> body>

局部注册

局部注册实在某个vue实例内部,通过配置项 components 配置,局部组件只能在注册它的 vue 实例下才可以使用
【Vue】快乐学习第三篇 组件及组件通信_第4张图片

<body>
    <div id="app">
        
        <hello>hello>
        <hello>hello>
    div>
    <div id="app1">
        <hello>hello>
    div>

    <script src="./vue.js">script>

    <script>
        // 1.插件组件构造器
        const hello = Vue.extend({
            template:  `

Hello World

`
}) // 2.注册组件(全局注册,意味着可以在多个vue实例下使用) const vm = new Vue({ el: '#app', // 局部注册,只能在当前 vue 实例下使用 components:{ hello:hello } }) const vm1 = new Vue({ el:'#app1' })
script> body>

父子组件

组件和组件之间存在关系,最常见的就是父子组件的关系

<body>
<div id="app">
    <c2>c2>
div>

<script src="./vue.js">script>
<script>
    // 创建第一个组件
    const c1 = Vue.extend({
        template: `
        

我是p1

`
}) // 创建第二个组件 const c2 = Vue.extend({ template: `

我是p2

`
, components: { c1: c1 } }) new Vue({ el: '#app', data: { message: 'Hello World' }, components: { c2: c2 } })
script> body>

【Vue】快乐学习第三篇 组件及组件通信_第5张图片
注意如果在vue实例下写标签 c1 是不正确的,因为我们既没有全局注册 c1 组件,也没有在 vue 实例中进行注册,而是在 c2 组件内部进行了注册,作用域在 c2 组件,所以可以在 c2 的模板中使用,这一点需要注意


注册组件语法糖

Vue为简化书写组件的过程,提供了注册的语法糖
省去了 Vue.extend() 这个步骤,而是直接使用一个对象来代替

全局注册组件语法糖,直接使用 Vue.component ,第一个配置组件名,第二个是template模板,底层还是extend的形式

<body>
<div id="app">
    <hello>hello>
div>

<script src="./vue.js">script>
<script>
    // 全局注册,语法糖,底层还是 extend
    Vue.component('hello',{
        template:`
        

Hello World

`
}) new Vue({ el: '#app' })
script> body>

在这里插入图片描述

局部注册组件语法糖,使用 components 配置项,组件名后跟模板,省去了 Vue.extend()

<body>
<div id="app">
    <hello>hello>
div>

<script src="./vue.js">script>
<script>
    new Vue({
        el: '#app',
        components:{
            hello:{
                template:`
                    

Hello World

`
} } })
script> body>

在这里插入图片描述

组件模板抽离的写法 ✍

<body>

    <div id="app">
        <hello>hello>
        <world>world>
    div>

    
    <script type="text/x-template" id="hello">
        <div>
            <h2>Hello Vue</h2>
        </div>
    script>

    
    <template id="world">
        <div>
            <h2>Hello Worldh2>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        Vue.component('hello',{
            template:'#hello'
        })
        Vue.component('World',{
            template:'#world'
        })
        new Vue({
            el: '#app',
        })
    script>
body>

在这里插入图片描述


为什么组件data必须是函数 ❓

因为以函数形式返回,当多次调用组件,每个组件都会有自己的一份data,彼此互不影响

组件时一个单独模块的封装,这个模块有属于自居的 HTML 模板,也应该有自己的数据 data……
组件是不可以直接访问 Vue 实例的data的
Vue组件应该有自己保存数据的地方

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

<body>

    <div id="app">
        <hello>hello>
    div>

    <template id="hello">
        <div>
            <h2>{{hello}}h2>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        Vue.component('hello', {
            template: '#hello',
            data() {
                return {
                    hello: 'Hello World,Hello Vue.js'
                }
            }
        })

        new Vue({
            el: '#app',
        })
    script>
body>

在这里插入图片描述

这个计数器案例就很好的解释了,为什么vue组件但会data要使用函数的形式
【Vue】快乐学习第三篇 组件及组件通信_第6张图片

<body>

    <div id="app">
        <counter>counter>
        <counter>counter>
        <counter>counter>
    div>

    <template id="counter">
        <div>
            <button @click="sub">-button>
            {{count}}
            <button @click="add">+button>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        Vue.component('counter', {
            template: '#counter',
            data() {
                return {
                    count: 0
                }
            },
            methods: {
                add() {
                    this.count++;
                },
                sub() {
                    this.count--;
                }
            },
        })

        new Vue({
            el: '#app',
        })
    script>
body>

如果我们调用多次组件去操作的是一个对象,那就会引起连锁反应,像这样
【Vue】快乐学习第三篇 组件及组件通信_第7张图片

<body>

    <div id="app">
        <counter>counter>
        <counter>counter>
        <counter>counter>
    div>

    <template id="counter">
        <div>
            <button @click="sub">-button>
            {{obj.count}}
            <button @click="add">+button>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        const obj = {
            count: 0
        }
        Vue.component('counter', {
            template: '#counter',
            data() {
                return {
                    obj
                }
            },
            methods: {
                add() {
                    this.obj.count++;
                },
                sub() {
                    this.obj.count--;
                }
            },
        })

        new Vue({
            el: '#app',
        })
    script>
body>

父子组件通信 父传子props ✉️

实际开发,往往一些数据需要从上层传递到下层
Vue中通过 props 向子组件传递数据,通过事件向父组件发送消息
【Vue】快乐学习第三篇 组件及组件通信_第8张图片

<body>

    <div id="app">
        <child :cmessage="message" :cmovies="movies">child>
    div>

    <template id="child">
        <div>
            <p>我是子组件p>
            <p>{{cmessage}}p>
            <ul>
                <li v-for="item in cmovies">{{item}}li>
            ul>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        const child = {
            template: '#child',
            // 数组写法
            // props: ['cmessage']
            // 对象写法,还可以指定类型,
            props: {
                cmessage: {
                    // 类型
                    type: String,
                    // 默认值
                    default: '哈哈',
                    // 必须传值,否则报错
                    required: true
                },
                cmovies: {
                    type: Array,
                    default() {
                        return [];
                    }
                }
            }
        }

        new Vue({
            el: '#app',
            data: {
                message: '你好啊,子组件',
                movies: ['天气之子', '你的名字', '千与千寻']
            },
            components: {
                child
            }
        })

    script>
body>

【Vue】快乐学习第三篇 组件及组件通信_第9张图片


父子组件通信 props驼峰标识

在未使用单文件组件,即以.vue为后缀的文件时,可能会遇到父组件传子组件时驼峰命名的问题,你可以使用-来解决
在这里插入图片描述

<body>
    <div id="app">
        <npm :c-npm="person">npm>
    div>

    <template id="npm">
        <div>
            <p>{{cNpm.width}}p>
            <p>{{cNpm.height}}p>
        div>
    template>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        const npm = {
            template: '#npm',
            props: {
                cNpm: {
                    type: Object
                }
            }
        }

        new Vue({
            el: '#app',
            components: {
                npm
            },
            data: {
                person: {
                    height: 1.8,
                    width: 1.5
                }
            }
        })
    script>
body>

父子组件通信 子传父自定义事件

当子组件需要向父组件传递时,就要用到自定义事件了,之前学习的 v-on 不仅可以用于监听 DOM 事件,也可以监听组件间的自定义事件
在子组件使用 $emit()来触发事件,在父组件中使用 v-on 来监听子组件事件
【Vue】快乐学习第三篇 组件及组件通信_第10张图片

<body>
    
    <div id="app">
        
        <hello @cclick="aa">hello>
    div>
    
    <template id="zi">
        <div>
            <button v-for="item in shoppings" @click="btnClick(item.name)">{{item.name}}button>
        div>
    template>

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
    <script>
        // 子组件
        const hello = {
            template: '#zi',
            data() {
                return {
                    shoppings: [
                        { id: 'a', name: '热门推荐' },
                        { id: 'b', name: '家用电器' },
                        { id: 'c', name: '数码办公' }
                    ]
                }
            },
            methods: {
                btnClick(name) {
                    // 自定义事件 cclick
                    this.$emit('cclick', name)
                }
            },
        }
        // 父组件
        new Vue({
            el: '#app',
            components: {
                hello
            },
            methods: {
                aa(name) {
                    console.log(name);
                }
            }
        })
    script>
body>

在这里插入图片描述

你可能感兴趣的:(vue,前端框架,vue.js,vue)