vue.js学习笔记六

Posted -恰饭第一名-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue.js学习笔记六相关的知识,希望对你有一定的参考价值。

一、全局组件和局部组件

  • 当我们通过调用Vue.component()注册组件时,组件的注册是全局的
  • 这意味着该组件可以在任意Vue示例下使用
  • 如果我们注册的组件是挂载在某个实例中,那么就是一个局部组件
    <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
    </div>

    <div id="app2">
        <cpn></cpn>
    </div>

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

    <script>
        //1。创建组件构造器
        const cpnC = Vue.extend({
            template: `
            <div>
              <h2>我是标题</h2>
              <p>我是内容,哈哈哈</p>
            </div>`
        })

        // 2.注册组件(全局组件, 意味着可以在多个Vue的实例下面使用)
        // Vue.component('cpn', cpnC)

        // 疑问: 怎么注册的组件才是局部组件了?
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                // cpn使用组件时的标签名
                cpn: cpnC
            }
        })

        const app2 = new Vue({
            el: '#app2'
        })
    </script>

二、父组件和子组件

  • 在前面我们看到了组件树
  • 组件和组件之间存在层级关系
  • 而其中一种非常重要的关系就是父子组件的关系
  • 父子组件错误用法:以子标签的形式在Vue实例中使用
  • 因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
  • 该模板的内容已经决定了父组件将要渲染的html(相当于父组件中已经有了子组件中的内容了)
  • <child-cpn></child-cpn>是只能在父组件中被识别的
  • 类似这种用法, <child-cpn></child-cpn>是会被浏览器忽略的

在这里插入图片描述

 <div id="app">
        <cpn2></cpn2>
        <!--<cpn1></cpn1>-->
        <!-- 会被浏览器忽略的 -->
    </div>
    <script src="../js/vue.js"></script>
    <script>
        // 1.创建第一个组件构造器(子组件)
        const cpnC1 = Vue.extend({
                template: `
      <div>
        <h2>我是标题1</h2>
        <p>我是内容, 哈哈哈哈</p>
      </div>
    `
            })
            // 2.创建第二个组件构造器(父组件)
        const cpnC2 = Vue.extend({
            template: `
      <div>
        <h2>我是标题2</h2>
        <p>我是内容, 呵呵呵呵</p>
        <cpn1></cpn1>
      </div>
    `,
            components: {
                cpn1: cpnC1
            }
        })

        // root组件
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                cpn2: cpnC2
            }
        })
    </script>



三、注册组件语法糖

  • 在上面注册组件的方式,可能会有些繁琐
  • Vue为了简化这个过程,提供了注册的语法糖
  • 主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替
    <div id="app">
        <cpn1></cpn1>
        <cpn2></cpn2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        //1.全局组件注册的语法糖
        //1.创建组件构造器
        //const cpn1=Vue.extend()

        //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>我是内容,hhehe1</p>
                </div>`
                }
            }
        })
    </script>



四、模板的分离写法

  • 刚才,我们通过语法糖简化了Vue组件的注册过程,另外还有一个的写法比较麻烦,就是template模板写法
  • 如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰
  • Vue提供了两种方案来定义HTML模板内容:
  • 使用<script>标签
  • 使用<template>标签
    在这里插入图片描述
    在这里插入图片描述
    <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
    </div>

    <!-- 1.第一种方法script标签,注意:类型必须是text/x-template -->
    <!-- <script type="text/x-template" id="cpn"></script> -->
    <!-- <div>
        <h2>我是标题</h2>
        <p>我是内容,hhhh</p>
    </div>
  </script> -->

    <!-- 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>



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

  • 组件是一个单独功能模块的封装:
  • 这个模块有属于自己的HTML模板,也应该有属性自己的数据data
  • 组件中的数据是保存在哪里呢?顶层的Vue实例中吗?
  • 我们先来测试一下,组件中能不能直接访问Vue实例中的data

解析:组件去访问message,message定义在Vue,我们发现最终并没有显示结果
结论:组件是不能直接访问Vue实例中的data数据

在这里插入图片描述



六、组件数据的存放

  • 组件自己的数据存放在哪里呢?
  • 组件对象也有一个data属性(也可以有methods等属性,下面我们会用到)
  • 只是这个data属性必须是一个函数
  • 而且这个函数返回一个对象,对象内部保存着数据
    <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
    </div>

    <!--1.script标签, 注意:类型必须是text/x-template-->
    <!--<script type="text/x-template" id="cpn">-->
    <!--<div>-->
    <!--<h2>我是标题</h2>-->
    <!--<p>我是内容,哈哈哈</p>-->
    <!--</div>-->
    <!--</script>-->

    <!--2.template标签-->
    <template id="cpn">
    <div>
      <h2>{{title}}</h2>
      <p>我是内容,呵呵呵</p>
    </div>
  </template>

    <script src="../js/vue.js"></script>
    <script>
        //1.注册一个全局事件
        Vue.component("cpn", {
            template: "#app",
            data() {
                return {
                    title: "abc"
                }
            }
        })
        const app = new Vue({
            el: "#app",
            data: {
                message: "你好啊",
            }
        })
    </script>

在这里插入图片描述

在这里插入图片描述

七、为什么data是一个函数呢?

  • 为什么data在组件中必须是一个函数呢?
  • 首先,如果不是一个函数,Vue直接就会报错
  • 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响
    <!--组件实例对象-->
    <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>

    <script>
        // const obj = {
        //   name: 'why',
        //   age: 18
        // }
        //
        // function abc() {
        //   return obj
        // }
        //
        // let obj1 = abc()
        // let obj2 = abc()
        // let obj3 = abc()
        //
        // obj1.name = 'kobe'
        // console.log(obj2);
        // console.log(obj3);
    </script>

在这里插入图片描述



八、父子组件的通信

  • 在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的
  • 但是,在开发中,往往一些数据确实需要从上层传递到下层:
  • 比如在一个页面中,我们从服务器请求到了很多的数据
  • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

在这里插入图片描述

  • 如何进行父子组件间的通信呢?
  • 通过props向子组件传递数据
  • 通过事件向父组件发送信息

在下面的代码中,会将Vue实例当做父组件,并且其中包含子组件来简化代码
真实的开发中,Vue实例和子组件的通信父组件和子组件的通信过程是一样的



九、props基本用法

  • 在组件中,使用选项props来声明需要从父级接收到的数据
  • props的值有两种方式:
  • 方式一:字符串数组,数组中的字符串就是传递时的名称
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
  • 我们先来看一个最简单的props传递

在这里插入图片描述



十、props数据验证

  • 在前面,我们的props选项是使用一个数组
  • 除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了
  • 验证都支持哪些数据类型呢?
    String
    Number
    Boolean
    Array
    Object
    Date
    Function
    Symbol
   <script>
        Vue.component("my-component", {
            props: {
                //基础的类型检查(`null`匹配任何类型)
                propA: Number,
                //多个可能的类型
                propB: [String, Number],
                //必填的字符串
                propC: {
                    type: String,
                    required: true
                },
                //带有默认值的数字
                propD: {
                    type: Number,
                    default: 100
                },
                //带有默认值的对象
                propE: {
                    type: Object,
                    //对象或数组默认值必须从一个工厂函数获取
                    default: function() {
                        return {
                            message: "hello"
                        }
                    }
                },
                //自定义验证函数
                propF: {
                    validator: function(value) {
                        //这个值必须匹配下列字符串中的一个
                        return ["success", "warning", "danger"].indexOf(value) !== -1
                    }
                }
            }
        })
    </script>
  • 当我们有自定义构造函数时,验证也支持自定义的类型
<script>
        function Person(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName
        }
        Vue.component("blog-post", {
            props: {
                author: Person
            }
        })
    </script>



十一、子级向父级传递

  • props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中
  • 这个时候,我们需要使用自定义事件来完成
  • 什么时候需要自定义事件呢?
  • 当子组件需要向父组件传递数据时,就要用到自定义事件了
  • 自定义事件的流程:
  • 在子组件中,通过$emit()来触发事件
  • 在父组件中,通过v-on来监听子组件事件



  • 我们来看一个简单的例子
  • 之前做过的那个两个按钮+1和-1,点击后修改counter
  • 整个操作的过程还是在子组件中完成,但是之后的展示交给父组件
  • 这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total

在这里插入图片描述

  <!--父组件模板-->
    <div id="app">
        <cpn @item-click="cpnClick"></cpn>
    </div>

    <!--子组件模板-->
    <template id="cpn">
  <div>
    <button v-for="item in categories"
            @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

    <script src="../js/vue.js"></script>
    <script>
        // 1.子组件
        const cpn = {
            template: '#cpn',
            data() {
                return {
                    categories: [{
                        id: 'aaa',
                        name: '热门推荐'
                    }, {
                        id: 'bbb',
                        name: '手机数码'
                    }, {
                        id: 'ccc',
                        name: '家用家电'
                    }, {
                        id: 'ddd',
                        name: '电脑办公'
                    }, ]
                }
            },
            methods: {
                btnClick(item) {
                    // 发射事件: 自定义事件
                    this.$emit('item-click', item)
                }
            }
        }

        // 2.父组件
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                cpn
            },
            methods: {
                cpnClick(item) {
                    console.log('cpnClick', item);
                }
            }
        })
    </script>

以上是关于vue.js学习笔记六的主要内容,如果未能解决你的问题,请参考以下文章

vue.js学习笔记六

vue.js 源代码学习笔记 ----- helpers.js

vue.js 源代码学习笔记 ----- decoder

vue.js 源代码学习笔记 ----- codegenEvents.js

vue.js 源代码学习笔记 ----- Dep

vue.js学习笔记八