vue入门笔记(三)

Vue Day 03

一、v-model的使用(表单绑定)

表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
Vue中使用v-model指令来实现表单元素和数据的双向绑定

1.1.v-model的基本使用
<div id="app">
  <input type="text" v-model="message">
  {{message}}
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
</script>

在这里插入图片描述

1.2.v-model的原理
1. v-bind绑定一个value属性
2. v-on指令给当前元素绑定input事件
<div id="app">
  <!--<input type="text" v-model="message">-->
  <!--<input type="text" :value="message" @input="valueChange">-->
  <input type="text" :value="message" @input="message = $event.target.value">
  <h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      valueChange(event) {
        this.message = event.target.value;
      }
    }
  })
</script>
1.3.v-model结合radio的类型使用

当存在多个单选框时

<div id="app">
  <label for="male">
    <input type="radio" id="male" value="" v-model="sex"></label>
  <label for="female">
    <input type="radio" id="female" value="" v-model="sex"></label>
  <h2>您选择的性别是: {{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      sex: '女'
    }
  })
</script>

在这里插入图片描述

1.4.v-model结合CheckBox的类型使用
  • 复选框分为两种情况:单个勾选框和多个勾选框
  1. 单个勾选框:
    • v-model即为布尔值。
    • 此时input的value并不影响v-model的值
  2. 多个复选框:
    • 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
    • 当选中某一个时,就会将input的value添加到数组中。
  3. 最后面是:value动态值绑定(就是动态的给value赋值)
    这里的value是动态绑定的 从下面的数组中取,代码有点乱 没有注释上面的input
<div id="app">
  <!--1.checkbox单选框-->
  <!--<label for="agree">-->
    <!--<input type="checkbox" id="agree" v-model="isAgree">同意协议-->
  <!--</label>-->
  <!--<h2>您选择的是: {{isAgree}}</h2>-->
  <!--<button :disabled="!isAgree">下一步</button>-->
  <!--2.checkbox多选框-->
  <input type="checkbox" value="篮球" v-model="hobbies">篮球
  <input type="checkbox" value="足球" v-model="hobbies">足球
  <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
  <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
  <h2>您的爱好是: {{hobbies}}</h2>
  <label v-for="item in originHobbies" :for="item">
    <input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
  </label>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isAgree: false, // 单选框
      hobbies: [], // 多选框,
      originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
    }
  })
</script>

在这里插入图片描述

1.5.v-model结合select的类型使用
  • 和checkbox一样,select也分单选和多选两种情况
  1. 单选:只能选中一个值
    • v-model绑定的是一个值。
    • 当我们选中option中的一个时,会将它对应的value赋值到mySelect中
  2. 多选:可以选中多个值
    • v-model绑定的是一个数组。
    • 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中`
  <!--1.选择一个-->
  <select name="abc" v-model="fruit">
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="榴莲">榴莲</option>
    <option value="葡萄">葡萄</option>
  </select>
  <h2>您选择的水果是: {{fruit}}</h2>
  <!--2.选择多个-->
  <select name="abc" v-model="fruits" multiple>
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="榴莲">榴莲</option>
    <option value="葡萄">葡萄</option>
  </select>
  <h2>您选择的水果是: {{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      fruit: '香蕉',
      fruits: []
    }
  })
</script>
1.5.v-model修饰符的使用
  • lazy修饰符:
    1. 默认情况下,v-model默认是在input事件中同步输入框的数据的。
    2. 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
    3. lazy修饰符可以让数据在失去焦点或者回车时才会更新:
  • number修饰符:
    1. 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
    2. 但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
    3. number修饰符可以让在输入框中输入的内容自动转成数字类型:
  • trim修饰符:
    1. 如果输入的内容首尾有很多空格,通常我们希望将其去除
    2. trim修饰符可以过滤内容左右两边的空格
<div id="app">
 <!--1.修饰符: lazy-->
 <input type="text" v-model.lazy="message">
 <h2>{{message}}</h2>
 <!--2.修饰符: number-->
 <input type="number" v-model.number="age">
 <h2>{{age}}-{{typeof age}}</h2>
 <!--3.修饰符: trim-->
 <input type="text" v-model.trim="name">
 <h2>您输入的名字:{{name}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
 const app = new Vue({
   el: '#app',
   data: {
     message: '你好啊',
     age: 0,
     name: ''
   }
 })
 var age = 0
 age = '1111'
 age = '222'
</script>

二、组件化开发

2.1.什么是组件化思想
  • 组件化是Vue.js中的重要思想

    1. 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
    2. 任何的应用都会被抽象成一颗组件树
      在这里插入图片描述
  • 组件化思想的应用:

    1. 有了组件化的思想,我们在之后的开发中就要充分的利用它。
    2. 尽可能的将页面拆分成一个个小的、可复用的组件。
    3. 这样让我们的代码更加方便组织和管理,并且扩展性也更强
2.2.注册组件的基本步骤
  • 组件的使用分成三个步骤:

    1. 创建组件构造器
    2. 注册组件
    3. 使用组件

在这里插入图片描述
在这里插入图片描述

2.3.组件的基本使用
<my-cpn></my-cpn>
<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器对象
  const cpnC = Vue.extend({
    template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容, 哈哈哈哈</p>
        <p>我是内容, 呵呵呵呵</p>
      </div>`
  })
  // 2.注册组件
  Vue.component('my-cpn', cpnC)
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
</script>
2.4.全局组件和局部组件
  • 当我们通过调用Vue.component()注册组件时,组件的注册是全局的

    • 这意味着该组件可以在任意Vue示例下使用。
  • 如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

  • Vue.component(‘my-cpn’, cpnC)这样是注册全局 下面的代码可以注册局部

  • 注意:开发中局部组件用的最多 而且一般只用一个vue实例 const app = new Vue({})就是一个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>
2.5.父组件和子组件
  • 在前面我们看到了组件树:

    1. 组件和组件之间存在层级关系
    2. 而其中一种非常重要的关系就是父子组件的关系
    3. 下面这个例子root注册了父组件,父组件里面注册了子组件
    4. 注意:不能直接在vue实例里面注册子组件,代码注释部分 因为cnp1这个组件不是全局组件,而且没有在vue实例中注册
<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>
2.6.组件的语法糖注册方式
  • 在上面注册组件的方式,可能会有些繁琐

    1. Vue为了简化这个过程,提供了注册的语法糖
    2. 主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替
    3. 之前是写cnp:对象 现在是直接把内容写到对象这个位置
<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>我是内容, 呵呵呵</p>
          </div>
    `
      }
    }
  })
</script>
2.7.模板的分离写法
  • Vue提供了两种方案来定义HTML模块内容:

    1. 使用
<!--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>我是标题</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>
2.8.组件数据的存放
  • 组件自己的数据存放在哪里呢? 之前我们是存在vue实例里面 但是现在不行了,应该有自己得位置

    1. 组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)
    2. 只是这个data属性必须是一个函数
    3. 而且这个函数返回一个对象,对象内部保存着数据
<template id="cpn">
  <div>
    <h2>{{title}}</h2>
    <p>我是内容,呵呵呵</p>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn',
    data() {
      return {
        title: 'abc'
      }
    }
  })
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      // title: '我是标题'   这里会报错  用不了
    }
  })
</script>
  • 为什么data在组件中必须是一个函数呢?

    1. 首先,如果不是一个函数,Vue直接就会报错。

    2. 其次,原因是在于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>

上面这种方法的问题:用const定义一个对象后 她是不变的,每次调用都返回这个对象(同一个),所以生成的这几个组件一个内容改变其他的也改变了,毕竟都是一块内存,公用的。
在这里插入图片描述


用data(){return{ counter:0 } } 的方式就不会出现问题
在这里插入图片描述
而且用data:{counter : 0 } 一样的会出现问题 所以为什么我们选择用函数,就是为了解决组件复用

2.9.父子组件的通信
  • 子组件是不能引用父组件或者Vue实例的数据的。

  • 但是,在开发中,往往一些数据确实需要从上层传递到下层:

比如在一个页面中,我们从服务器请求到了很多的数据。
其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

  • 如何进行父子组件间的通信呢?Vue官方提到

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

在这里插入图片描述

父传子:

<div id="app">
  <!--<cpn v-bind:cmovies="movies"></cpn>-->
  <!--<cpn cmovies="movies" cmessage="message"></cpn>-->
  <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'],
    props: {
      // 1.类型限制
      // cmovies: Array,
      // cmessage: String,
      // 2.提供一些默认值, 以及必传值
      cmessage: {
        type: String,
        default: 'aaaaaaaa',
        required: true
      },
      // 类型是对象或者数组时, 默认值必须是一个函数
      cmovies: {
        type: Array,
        default() {
          return []
        }
      }
    },
    data() {
      return {}
    },
    methods: {
    }
  }
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      cpn
    }
  })
</script>

在这里插入图片描述

类型限制:

  • 验证都支持哪些数据类型呢?
  1. String
  2. Number
  3. Boolean
  4. Array
  5. Object
  6. Date
  7. Function
  8. Symbol

在这里插入图片描述

注意:v-bind:后面是不支持驼峰的 比如cInfo 那么用的时候需要转成 v-bind:c-info

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

子传父:

  • 我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。

  • 什么时候需要自定义事件呢?

  • 当子组件需要向父组件传递数据时,就要用到自定义事件了。

  • 我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

自定义事件的流程:

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

注意:这里还是建议不用驼峰的写法,会报错

<!--父组件模板-->
<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>
2.10.父子组件的通信案例
  • 案例一:

本案例是父传子通信时候加上双向绑定问题,不能直接绑定子组件,会报错

<div id="app">
  <cpn :number1="num1":number2="num2"/>
</div>
<template id="cpn">
  <div>
    <h2>{{number1}}</h2>
    <input type="text" v-model="number1">
    <h2>{{number2}}</h2>
    <input type="text" v-model="number2">
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
      }
    }
  })
</script>

在这里插入图片描述
在这里插入图片描述
解决办法:
1. 我们可以看到解决方法是用data或者computed来解决
2. 用data绑定父传过来的值就行

  • 用data()解决:
<div id="app">
  <cpn :number1="num1" :number2="num2"/>
</div>
<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <input type="text" v-model="dnumber1">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <input type="text" v-model="dnumber2">
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        }
      }
    }
  })
</script>

在这里插入图片描述
只有data的dnumber有变化



问题:当我们还想把这个改变的值传递给父组件的时候该怎么办呢?(改变num1和num2)

步骤:

  1. 用 :value 绑定dnumber1 和 @input 来解决 (因为这两个就是双向绑定的分解,前一节有讲过)
  2. 获取input中的值,然后子组件用$emit方法发射一个事件
  3. 父组件监听这个事件
<div id="app">
  <cpn :number1="num1"
       :number2="num2"
       @num1change="num1change"
       @num2change="num2change"/>
</div>
<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <!--<input type="text" v-model="dnumber1">-->
    <input type="text" :value="dnumber1" @input="num1Input">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <!--<input type="text" v-model="dnumber2">-->
    <input type="text" :value="dnumber2" @input="num2Input">
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    methods: {
      num1change(value) {
      //将字符串转为数字类型
        this.num1 = parseFloat(value)
      },
      num2change(value) {
        this.num2 = parseFloat(value)
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        },
        methods: {
          num1Input(event) {
            // 1.将input中的value赋值到dnumber中
            this.dnumber1 = event.target.value;
            // 2.为了让父组件可以修改值, 发出一个事件
            this.$emit('num1change', this.dnumber1)
          },
          num2Input(event) {
            this.dnumber2 = event.target.value;
            this.$emit('num2change', this.dnumber2)
          }
        }
      }
    }
  })
</script>

在这里插入图片描述

问题加强:只要上面的data改变 下面的data永远是下面的data数据的100倍

<div id="app">
  <cpn :number1="num1"
       :number2="num2"
       @num1change="num1change"
       @num2change="num2change"/>
</div>
<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <!--<input type="text" v-model="dnumber1">-->
    <input type="text" :value="dnumber1" @input="num1Input">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <!--<input type="text" v-model="dnumber2">-->
    <input type="text" :value="dnumber2" @input="num2Input">
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    methods: {
      num1change(value) {
      //将字符串转为数字类型
        this.num1 = parseFloat(value)
      },
      num2change(value) {
        this.num2 = parseFloat(value)
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        },
        methods: {
          num1Input(event) {
            // 1.将input中的value赋值到dnumber中
            this.dnumber1 = event.target.value;
            // 2.为了让父组件可以修改值, 发出一个事件
            this.$emit('num1change', this.dnumber1)
            // 3.同时修饰dnumber2的值
            this.dnumber2 = this.dnumber1 * 100;
            this.$emit('num2change', this.dnumber2);
          },
          num2Input(event) {
            this.dnumber2 = event.target.value;
            this.$emit('num2change', this.dnumber2)
            // 同时修饰dnumber2的值
            this.dnumber1 = this.dnumber2 / 100;
            this.$emit('num1change', this.dnumber1);
          }
        }
      }
    }
  })
</script>

在这里插入图片描述

注意:上面的这个例子很复杂,实际开发中可能不会用到,但是会对帮助我们更深的理解父子组件数据传递

2.11.父子组件的访问
  • 有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件

    1. 父组件访问子组件:使用 c h i l d r e n 或 children或 childrenrefs reference(引用)
    2. 子组件访问父组件:使用$parent
  • $children的访问

    1. this.$children是一个数组类型,它包含所有子组件对象。
    2. 我们这里通过一个遍历,取出所有子组件的message状态
<div id="app">
  <cpn></cpn>
  
  <button @click="btnClick">按钮</button>
</div>
<template id="cpn">
  <div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        console.log(this.$children);
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        methods: {
          showMessage() {
            console.log('showMessage');
          }
        }
      },
    }
  })
</script>

在这里插入图片描述

  • $children的缺陷:

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

  • $refs的使用:

    1. $refs和ref指令通常是一起使用的。
    2. 首先,我们通过ref给某一个子组件绑定一个特定的ID。
    3. 其次,通过this.$refs.ID就可以访问到该组件了。
<div id="app">
  <!-- 加一个属性 -->
  <cpn ref="aaa"></cpn>
  <button @click="btnClick">按钮</button>
</div>
<template id="cpn">
  <div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        // 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
        console.log(this.$refs.aaa.name);
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('showMessage');
          }
        }
      },
    }
  })
</script>
  • 子访问父(开发用的更少了)
<div id="app">
  <cpn></cpn>
</div>
<template id="cpn">
  <div>
    <h2>我是cpn组件</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
            methods: {
              btnClick() {
                // 1.访问父组件$parent
                console.log(this.$parent);
                console.log(this.$parent.name);
              }
            }
          }
        }
  })
</script>

在这里插入图片描述

2.12.slot的使用
  • 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
  • 是搜索框,还是文字,还是菜单。由调用者自己来决定。
  • 1.插槽的基本使用
<!--
1.插槽的基本使用 <slot></slot>  <cpn>这里面加入想替换的东西</cpn>
2.插槽的默认值 <slot>button</slot>
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
-->
<div id="app">
  <cpn></cpn>
  <cpn><span>哈哈哈</span></cpn>
  <cpn><i>呵呵呵</i></cpn>
  <cpn>
  <!-- 这里面加了三个  都加入进去了-->
    <i>呵呵呵</i>
    <div>我是div元素</div>
    <p>我是p元素</p>
  </cpn>
</div>
<template id="cpn">
  <div>
    <h2>我是组件</h2>
    <p>我是组件, 哈哈哈</p>
    <slot><button>按钮</button></slot>
    <!--<button>按钮</button>-->
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn'
      }
    }
  })
</script>

在这里插入图片描述
在这里插入图片描述
2.具名插槽

<div id="app">
  <cpn><span slot="center">标题</span></cpn>
  <cpn><button slot="left">返回</button></cpn>
</div>
<template id="cpn">
  <div>
    <slot name="left"><span>左边</span></slot>
    <slot name="center"><span>中间</span></slot>
    <slot name="right"><span>右边</span></slot>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn'
      }
    }
  })
</script>

在这里插入图片描述
3.作用域插槽

这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:
父组件替换插槽的标签,但是内容由子组件来提供。

<div id="app">
  <cpn></cpn>
  <cpn>
    <!--目的是获取子组件中的pLanguages-->
    <template slot-scope="slot">
      <!--<span v-for="item in slot.data"> - {{item}}</span>-->
      <span>{{slot.data.join(' - ')}}</span>
    </template>
  </cpn>
  
  <cpn>
    <!--目的是获取子组件中的pLanguages-->
    <template slot-scope="slot">
      <!--<span v-for="item in slot.data">{{item}} * </span>-->
      <span>{{slot.data.join(' * ')}}</span>
    </template>
  </cpn>
</div>

<template id="cpn">
  <div>
    <slot :data="pLanguages"><!--这里定义了插槽后上面可以根据自己的方式调用-->
      <ul>
        <li v-for="item in pLanguages">{{item}}</li>
      </ul>
    </slot>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
          }
        }
      }
    }
  })
</script>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值