【前端Vue】04 - 组件开发 [v-model + 父子组件通信]

1. 表单绑定 v-model

  1. 表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。

  2. Vue 中使用 v-model 指令来实现表单元素和数据的双向绑定。

1.1 案例分析

vue中v-model的双向绑定
  1. 当我们在输入框输入内容时。

  2. 因为input中的v-model绑定了message,所以会实时将输入的内容传递给messagemessage发生改变。

  3. message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。

  4. 所以,通过 v-model 实现了双向的绑定。

<div id="app">
  <!-- v-model 是一個双向绑定 -->
  <input type="text" v-model="message">
  <h2>{{message}}</h2>
</div>
<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            message: ''
        },
        methods: {}
    });

1.2 v-model 原理

  1. v-model 指令就是 v-click:inputv-bind:value 指令的结合,再使用 $eventtarget 获取到当前输入框中输入的值。
<div id="app">
  <!--
     v-model = v-on:input + v-bind:value
  -->
  <input type="text" name="username" id="username" :value="message" @input="valueChange">
  <input type="text" name="email" id="email" :value="message" @input="message = $event.target.value">
  <h2>{{message}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            message: '您好啊!'
        },
        methods: {
            valueChange(event) {
                this.message = event.target.value;
            }
        }
    });

1.3 v-model 结合 radio 类型

  1. 单选框的实现需要多个input使用相同的name属性。
<div id="app">
  <label for="male">
    <input type="radio" name="sex" id="male" value="1" v-model="sex">男
  </label>
  <label for="female">
    <input type="radio" name="sex" id="female" value="0" v-model="sex">女
  </label>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            sex: 1
        }
    });

1.4 v-model 结合CheckBox

<div id="app">
  <input type="checkbox" value="篮球" name="basketball" id="basketball" v-model="hobbies">篮球
  <input type="checkbox" value="羽毛球" name="badminton" id="badminton" v-model="hobbies">羽毛球
  <input type="checkbox" value="毽子" name="shuttlecock" id="shuttlecock" v-model="hobbies">毽子
  <input type="checkbox" value="足球" name="football" id="football" v-model="hobbies">足球
  <h2>{{hobbies}}</h2>
</div>
<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            hobbies: []
        }
    });

1.5 v-model 结合select

  1. select可以实现单选和多选 。使用 multiple 属性。
<div id="app">

  <!-- 1. 选择一个 -->
  <select name="fruit" id="fruit" v-model="fruit">
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="芒果">芒果</option>
    <option value="李子">李子</option>
  </select>

  <h2>{{fruit}}</h2>
  <br>
  <br>
  <br>
  <!-- 2. 选择多个 multiple 属性-->
  <select name="fruit" id="fruits" multiple v-model="fruits">
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="芒果">芒果</option>
    <option value="李子">李子</option>
  </select>
  <h2>{{fruits}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            fruit: '香蕉',
            fruits: []
        }
    });

1.6 v-model 的值绑定

  1. 所谓的值绑定就是将数据动态的绑定到每一个操作项上。
<div id="app">
  <label v-for="hobby in hobbies" :for="hobby">
    <input type="checkbox" name="hobby" :id="hobby" :value="hobby" v-model="selected">{{hobby}}
  </label>
  <h2>您的爱好是 : {{selected}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            hobbies: ['篮球', '足球', '羽毛球', '乒乓球', '高尔夫球'],
            selected: []
        }
    });

1.7 v-model 修饰符的使用

  1. lazy: 可以让双向绑定的元素,减少实时渲染次数,只有当鼠标失去焦点或者当用户按下回车的时候才渲染。

  2. number:可以将绑定的数据的数据类型转换为 number 类型,方便一些需要使用数字的地方。

  3. trim: 去除字符串两边的空格。

<div id="app">
  <!-- 1. 使用lazy修饰符的v-model 当输入完成之后敲回车或者是当鼠标失去焦点的时候更新数据 -->
  <input type="text" name="username" id="username" v-model.lazy="username">
  <h2>您输入的用户名是 : {{username}}</h2>
  <!-- 2. 使用number修饰符修饰的v-model 文本框中的数据类型会转换为string 后期不方便处理使用number修饰符的文本框文本类型是 number 的 -->
  <input type="number" name="phoneNum" id="phoneNum" v-model.number="phoneNum">
  <h2> 文本框的类型 : {{typeof phoneNum}} </h2>
  <!--3. 使用trim 去除字符串两边的空格-->
  <input type="text" name="content" id="content" v-model.trim="content">
  <h2>{{content}}</h2>
</div>
    const app = new Vue({
        el: '#app',
        data: {
            username: '',
            phoneNum: 0,
            content: ''
        }
    });

2. 组件化

  1. 组件化是Vue.js 中的重要思想,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。
Vue组件树

2.1 注册组件的基本步骤

  1. 创建组件构造器;

  2. 注册组件;

  3. 使用组件。

注册组件的基本步骤
创建组件的基本步骤
<div id="app">
  <cpn></cpn>
</div>
<script src="../../js/vue.js"></script>
// 1. 创建组件构造器 使用Vue.extend 创建组件构造器
    const cpnC = Vue.extend({
        template: `
                <div>
                  <h1>我是标题</h1>
                  <p>我是段落一</p>
                  <p>我是段落二</p>
                </div>
              `
    });
    // 2. 注册组件
    Vue.component('cpn', cpnC);
    // 3. 挂载 app
    const app = new Vue({
        el: '#app',
        data: {}
    });

2.2 全局组件和局部组件

  1. 全局组件是使用 Vue.component('组件名称' , 组件构造器) 注册的组件;

  2. 局部组件时在 , Vue({}) 对象中使用 components属性声明的组件。

<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpna></cpna>
  <hr>
</div>

<div id="app2">
  <cpn></cpn>
  <!-- app注冊的局部组件在app中不能使用 <cpna></cpna>-->
</div>

<script src="../../js/vue.js"></script>
    /**
     * 全局组件可以在多个Vue的实例下面使用
     */
        // 1. 使用Vue.extend 创建一个组件构造器
    const cpnC = Vue.extend({
            template: `
                  <div>
                    <h2>我是标题</h2>
                    <p>我是段落1</p>
                    <p>我是段落2</p>
                  </div>>
                 `
        });


    /**
     * 局部组件构造器声明
     */
    const cpnCa = Vue.extend({
        template: `
                  <div>
                    <h2>我是局部组件的标题</h2>
                    <p>我是段落1</p>
                    <p>我是段落2</p>
                  </div>>
                 `
    });

    // 2. 将组件注册为全局组件
    Vue.component('cpn', cpnC);

    const app = new Vue({
        el: '#app',
        data: {},
        components: {
            cpna: cpnCa
        }
    });

    const app2 = new Vue({
        el: '#app2',
        data: {},
        components: {}
    });
全局组件和局部组件

2.3 父组件和子组件

  1. 组件和组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系。
父组件和子组件的关系

2.4 注册组件的语法糖简写

注册组件的语法糖写法
<div id="app">
  <cpn1></cpn1>
  <cpn2></cpn2>
  <cpn3></cpn3>
  <cpn4></cpn4>
  <hr>
</div>

<div id="app2">
  <cpn1></cpn1>
  <cpn2></cpn2>
  <cpn3></cpn3>
  <cpn4></cpn4>
</div>
<script src="../../js/vue.js"></script>
/**
     *  (一) 传统方式注册组件
     */
        // 1. 创建组件构造器
    const cpnC1 = Vue.extend({
            template: `
        <div>
          <h1>我是全局组件一</h1>
        </div>
       `
        });

    /**
   * 注册一个全局组件
   * */
    Vue.component('cpn1', cpnC1);

    /**
     * (二) 使用语法糖的方式注册组件 : 主要是省去调用Vue.extend的步骤,而是可以直接使用一个对象来代替
     */
    Vue.component('cpn2', {
        template: `
        <div>
          <h1>我是全局组件二(使用语法糖方式创建)</h1>
        </div>
       `
    });

    /**
     * 创建一个组件构造器用于局部组件
     */
    const cpnC4 = Vue.extend({
        template: `
        <div>
          <h1>我是局部组件四</h1>
        </div>
       `
    });

    /**
     * 挂载app
     */
    const app = new Vue({
        el: '#app',
        data: {},
        components: {
            /* 创建局部组件 */
            cpn3: {
                template: `
        <div>
          <h1>我是局部组件三(使用语法糖方式创建)</h1>
        </div>
       `
            },

            cpn4: cpnC4
        }
    });

    /**
     * 挂载app2
     */
    const app2 = new Vue({
        el: '#app2',
        data: {}
    });

2.5 组件模板抽离

  1. 在编写组件模板的时候可以使用,script 或者 template标签定义标签模板。将其从对象中抽离出来。
组件模板抽离
<div id="app">
  <cpn></cpn>
  <cpn1></cpn1>
</div>

<!-- 1. 抽离template模板 -->
<script type="text/x-template" id="cpn">
  <div>
    <h1>我是抽离的标签的标题</h1>
    <p>我是一个段落</p>
  </div>
</script>

<!-- 2. 使用template标签包裹-->
<template id="cpn1">
  <div>
    <h1>我是抽离的标签的标题使用template标签</h1>
    <p>我是一个段落</p>
  </div>
</template>

<script src="../../js/vue.js"></script>
Vue.component('cpn', {
        template: '#cpn'
    });

    Vue.component('cpn1', {
        template: '#cpn1'
    });

    const app = new Vue({
        el: '#app',
        data: {}
    });

2.6 组件中可以访问Vue实例中的数据吗?

  1. 组件是一个单独功能模块的封装:这个模块有属于自己的HTML模板,也应该有属性自己的数据data

  2. 答案:不能。而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。

  3. 结论:Vue组件应该有自己保存数据的地方。

2.7 组件自己的数据存放在哪里呢?

  1. 组件对象也有一个data属性(也可以有methods等属性)。

  2. 只是这个data属性必须是一个函数。而且这个函数返回一个对象,对象内部保存着数据。

<template id="cpn">
  <div>
    <h1>当前计数: {{counter}}</h1>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<template id="com">
  <div>
    <h1>{{title}}</h1>
  </div>
</template>

<div id="app">
  <!-- 组件实例 -->
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
  <com></com>
</div>

<script src="../../js/vue.js"></script>
/**
     * 语法糖方式注册组件
     */
    Vue.component('cpn', {
        template: '#cpn',
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            increment() {
                this.counter++
            },
            decrement() {
                this.counter--
            }
        }
    });

    /**
     * 使用语法糖方式注册组件
     */
    Vue.component('com', {
        template: '#com',
        data() {
            return {
                title: '我是标题'
            };
        }
    });

    const app = new Vue({
        el: '#app',
        data: {}
    });

2.8 为什么组件中的data是一个函数

  1. 如果不同组件他们共享一个共同的对象将会出大问题。
    const obj = {
        name: '张三',
        age: 23
    }

    /**
     * 每次调用函数返回一个对象
     */
    function data() {
        return obj;
    }

    let obj1 = data();
    obj1.name = '李四';
    let obj2 = data();
    console.log(obj1); // {name: "李四", age: 23}
    console.log(obj2); // {name: "李四", age: 23}
为什么组件中的data是一个函数

2.9 父子组件之间的通信

  1. 在开发中,往往一些数据确实需要从上层传递到下层,比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
如何进行父子组件间的通信呢?Vue官方提到
  1. 父组件向子组件传递数据 : 通过props向子组件传递数据。

  2. 子组件向父组件传递数据: 通过事件向父组件发送消息。

Vue父子组件之间的通信
props 的基本用法
  1. 在组件中,使用选项props 来声明需要从父级接收到的数据。props的值有两种方式:

方式一:字符串数组,数组中的字符串就是传递时的名称。

方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

我们先来看一个最简单的props传递:
简单的props传递
在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码。
  1. 真实的开发中,Vue 实例和子组件的通信和父组件和子组件的通信过程是一样的。
实现父组件向子组件传递数据的步骤如下 :(使用字符串数组的方式)
  1. 第一步: 在子组件将props定义为一个字符串数组用于接收父组件传递的数据。

  2. 第二步:将父组件的值和子组件定义的属性进行绑定。

  3. 第三步:值已经传递过来了,在子组件中使用父组件的数据。

父组件向子组件传递数据
<!-- 父子组件之间的通信
      1. 父组件向子组件传通过props;
      2. 子组件向父组件传通过自定义事件;
    在真实开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。
  -->
<div id="app">
  <!-- 第二步 将父组件的值和子组件定义的属性进行绑定 -->
  <cpn :cmovies="movies" :cmessage="message"></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>
 /* 父传子通过 props */
  const cpn = {
    template:'#cpn',
    data() {
        return{

      }
    },
    // 字符串数组写法
    // 第一步
    props:['cmovies','cmessage']
  }
    const app = new Vue({
        el: '#app',
        data: {
            message:'我是父组件的message',
      movies:['陆小凤传奇之决战紫禁之巅','陆小凤传奇之剑神一笑','陆小凤传奇之凤舞九天']
    },
        components: {
            /* 使用属性的增强写法 */
            cpn
        }
    });
实现父组件向子组件传递数据的步骤如下 :(使用对象的方式)
  1. props除了使用数组的方式之外还能使用对象的方式接收父组件传递的数据。当需要对props进行类型验证的时候,就需要使用对象的写法。

  2. 对象中属性非自定义的数据类型可以使用 type属性验证该属性的数据类型,使用required标识该属性是否为必须传递的,使用default属性设置默认值。

props中的属性
验证都支持如下类型:
  1. String

  2. Number

  3. Boolean

  4. Array:当是数组类型的时候,在设置默认值时需要注意,在某些版本之后不能使用 default:[]这样的默认值,而是需要一个函数,在函数中返回数组的默认值 ,如 : default:() => { return []; }default:() => [] 或者 default() {return [];}

  5. Object

  6. Date

  7. Function

  8. Symbol

  9. 当我们有自定义构造函数时,验证也支持自定义的类型。

peops为对象类型
父组件传递值给子组件props属性驼峰标识
  1. 在进行父组件传递数据给子组件的时候,在props中的属性如果使用了驼峰标识,那么在子组件标签中使用该属性的时候就需要使用 横线分隔的形式,绑定父组件传递的数据。
子组件中props的属性使用驼峰标识

2.10 子组件向父组件传递数据

子组件向父组件传递数据的步骤
  1. 第一步 : 定义一个,比如按钮事件 将子组件的数据传递给父组件。
    <!-- 第一步 : 定义一个,比如按钮事件 将子组件的数据传递给父组件 -->
    <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
  1. 第二步 : 将子组件的自定义事件发射给父组件 并传递携带的参数。
  btnClick(item) {
                /* 第二步 : 将子组件的自定义事件发射给父组件 并传递携带的参数 */
                this.$emit('customevent', item);
            }
  1. 第三步 : 在父组件中处理子组件自定义的事件。
<!-- 第三步 : 在父组件中处理子组件自定义的事件  -->
  <cpn @customevent="getSonCpnData"></cpn>
  1. 第四步 : 处理触发子组件自定义事件之后需要执行的方法。
  /* 第四步 : 处理触发子组件自定义事件之后需要执行的方法 */
            getSonCpnData(item) {
                console.log(item);
            }
子组件向父组件传递数据
<div id="app">
  <!-- 第三步 : 在父组件中处理子组件自定义的事件  -->
  <cpn @customevent="getSonCpnData"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>我是子组件</h2>
    <!-- 第一步 : 定义一个,比如按钮事件 将子组件的数据传递给父组件 -->
    <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
  </div>
</template>

<script src="../../js/vue.js"></script>
const cpn = {
        template: '#cpn',
        data() {
            return {
                categories: [
                    {id: 1, name: '手机数码'},
                    {id: 2, name: '家用电器'},
                    {id: 3, name: '电脑办公'},
                    {id: 4, name: '生鲜蔬菜'}
                ]
            }
        },
        methods: {
            btnClick(item) {
                /* 第二步 : 将子组件的自定义事件发射给父组件 并传递携带的参数 */
                this.$emit('customevent', item);
            }
        }
    };

    /* 子组件产生了一些事件希望父组件知道 */
    const app = new Vue({
        el: '#app',
        components: {
            cpn
        },
        methods: {
            /* 第四步 : 处理触发子组件自定义事件之后需要执行的方法 */
            getSonCpnData(item) {
                console.log(item);
            }
        }
    });

2.11 父子组件通信-结合双向绑定

  1. 需求1: 当子组件中的 data 数据 dNumber1 dNumber2 改变的时候 子组件 props 中的 number1 number2 也需要发生改变,利用的是 v-model的原理 是由 v-on:inputv-bind:value 组合。

  2. 需求2: 当number1 的值改变的时候 将 number2 的值变为 number1100 倍。

Vue父子组件通信
简单图解
<div id="app">

  <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>

</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dNumber1}}</h2>
    <input type="number" @input="dnumber1change" :value="dNumber1">

    <h2>props:{{number2}}</h2>
    <h2>data:{{dNumber2}}</h2>
    <input type="number" @input="dnumber2change" :value="dNumber2">
  </div>
</template>

<!--
  1. 需求1: 当子组件中的 data数据 dNumber1 dNumber2 改变的时候 子组件 props 中的  number1 number2 也需要发生改变
      1.1 利用的是 v-model的原理 是由 v-on:input 和 v-bind:value 组合

  2. 需求2: 当 number1 的值改变的时候 将 number2 的值变为 number1 的 100 倍
 -->

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            num1: 0,
            num2: 0
        },
        methods: {
            num1change(value) {
                this.num1 = parseFloat(value);
            },
            num2change(value) {
                this.num2 = parseFloat(value);
            }
        },
        components: {
            'cpn': {
                template: '#cpn',
                props: {
                    /* 这里的值有父组件的 num1 或 num2 决定 */
                    number1: Number,
                    number2: Number
                },
                data() {
                    return {
                        dNumber1: this.number1,
                        dNumber2: this.number2
                    };
                },
                methods: {
                    dnumber1change(event) {
                        // 从event中获取当前改变的值
                        this.dNumber1 = event.target.value;
                        // 发送一个自定义事件给父组件让父组件的值发生改变
                        this.$emit('num1change', this.dNumber1);
                        this.dNumber2 = this.dNumber1 * 100;
                        this.$emit('num2change', this.dNumber2);
                    },
                    dnumber2change(event) {
                        this.dNumber2 = event.target.value;
                        this.$emit('num2change', this.dNumber2);
                        this.dNumber1 = this.dNumber2 / 100;
                        this.$emit('num1change', this.dNumber1);
                    }
                }
            }
        }
    });

2.12 父子组件通信-结合双向绑定 (watch实现)

<div id="app">
  <cpn
    :number1="num1"
    :number2="num2"
    @num1change="num1Change"
    @num2change="num2Change"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{pNumber1}}</h2>
    <input type="number" v-model="pNumber1">
    <!--<input type="text" :value="pNumber1" @input="number1Change">-->

    <h2>props:{{number2}}</h2>
    <h2>data:{{pNumber2}}</h2>
    <input type="number" v-model="pNumber2">
    <!--<input type="text" :value="pNumber2" @input="number2Change">-->
  </div>
</template>

<script src="../../js/vue.js">
const app = new Vue({
        el: '#app',
        data: {
            num1: 0,
            num2: 0
        },
        methods: {
            num1Change(number1) {
                this.num1 = parseFloat(number1);
            },
            num2Change(number2) {
                this.num2 = parseFloat(number2);
            }
        },
        components: {
            'cpn': {
                template: '#cpn',
                props: {
                    number1: Number,
                    number2: Number
                },
                data() {
                    return {
                        pNumber1: this.number1,
                        pNumber2: this.number2
                    }
                },
                methods: {},
                /* 用于监视一个属性的改变 */
                watch: {
                    pNumber1(newValue, oldValue) {
                        this.pNumber2 = newValue * 100
                        // 子组件发送一个自定义事件给父组件
                        this.$emit('num1change', newValue);
                    },
                    pNumber2(newValue, oldValue) {
                        this.pNumber1 = newValue / 100
                        this.$emit('num2change', newValue);
                    }
                }
            }
        }
    });
watch 的使用
  1. watch : 是属于Vue实例的一个 options参考文档,它可以监控一个属性的变化,在watch中可以将属性名称作为方法名称 属性名称(newValue,oldValue)newValue参数就是该属性改变之后的值。watch可以监控data中属性的变化。
<div id="app">
  <input type="text" v-model="number">
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            number: 123
        },
        watch: {
            number(newValue, oldValue) {
                console.log('number从' + oldValue + '变为了' + newValue);
            }
        }
    });

2.13 父组件直接访问子组件使用 $children$refs

父组件访问子组件
  1. $children 获取到的是父组件中的所有子组件,結果是一个数组。包含所有子组件对象。$children的缺陷:通过$children 访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
父组件通过this.$children获取到子组件对象
  1. $refs 获取到的是一个类似Map集合的对象。但是使用$refs获取之前需要在组件标签上使用ref属性指定。$refsref 指令通常是一起使用的。首先,我们通过ref给某一个子组件绑定一个特定的ID。其次,通过this.$refs.ID就可以访问到该组件了。
<cpn ref="aaa"></cpn>
<cpn ref="bbb"></cpn>
父组件通过this.$refs获取子组件的对象
<div id="app">
  <cpn ref="aaa"></cpn>
  <cpn ref="bbb"></cpn>
  <button @click="btnClick">点我</button>
</div>

<template id="cpn">
  <div>
    <h2>{{message}}</h2>
  </div>
</template>

<script src="../../js/vue.js"></script>
/* $children 可以拿到所有的子组件 */
    /* $refs 拿到指定 ref 的组件 */
    const app = new Vue({
        el: '#app',
        data: {},
        methods: {
            btnClick() {
                // console.log(this.$children);
                // console.log(this.$children[0].message);
                // this.$children[0].showMessage();
                console.log(this.$refs);
                console.log(typeof this.$refs);
                // let map = new Map();
                // map.set('aaa', {name: '张三'});
                // console.log(map);
                console.log(this.$refs.aaa.message);
                this.$refs.bbb.showMessage();
            }
        },
        components: {
            'cpn':
                {
                    template: '#cpn',
                    data() {
                        return {
                            message: '我是子组件'
                        }
                    },
                    methods: {
                        showMessage() {
                            console.log(this.message);
                        }
                    }
                }
        }
    });

2.14 子组件中访问父组件 $parent$root

  1. $parent : 可以获取组件的父组件。

  2. $root : 访问的是根组件,Vue实例。

子组件访问父组件
<div id="app">
  <cpn></cpn>
  <hr>
</div>

<template id="cpn">
  <div>
    <h2>我是cpn组件</h2>
    <button @click="btnClick"> cpn的按钮</button>
    <hr>
    <!-- 这是定义的一个局部子组件 -->
    <ccpn></ccpn>
  </div>
</template>

<template id="ccpn">
  <div>
    <h2>我是cpn的子组件ccpn</h2>
    <button @click="btnCcpnClick">ccpn的按钮</button>
  </div>
</template>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: {
            'cpn': {
                template: '#cpn',
                data() {
                    return {}
                },
                methods: {
                    btnClick() {
                        console.log(this.$parent); //
                    }
                },
                components: {
                    'ccpn': {
                        template: '#ccpn',
                        methods: {
                            btnCcpnClick() {
                                console.log(this.$parent); // 获取的是其父组件对象
                                console.log(this.$root); // 获取的是根组件对象
                            }
                        }
                    }
                }
            }
        }
    });
子组件通过$parent访问父组件注意事项
  1. 尽管在Vue开发中,我们允许通过$parent 来访问父组件,但是在真实开发中尽量不要这样做。

  2. 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。

  3. 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。

  4. 另外,更不好做的是通过$parent 直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容