1.6 Vue 组件

1. 组件基础
为什么要使用组件

组件(Component)是对数据和方法的简单封装。Vue组件可以扩展HTML元素,提高重用性的,让代码可复用。

组件的使用
  • 组件命名 两种方式:
    1)使用kebab-case(短横线分隔命名)<my-component>
    2)使用PascalCase(首字母大写命名)<MyComponent>
  • 组件复用 可以将组件进行任意次数的复用,每个组件都会独立维护它的实例数据
  • 组件注册 两种方式:(demo中有)
    1)全局注册
    2)局部注册
    demo
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>Document</title>
          <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
      </head>
      <body>
          <h4>全局注册</h4>
          <div id="app1">
              <my-component1></my-component1>
              <my-component1></my-component1>
              <my-component1></my-component1>
          </div>
          <hr>
          <h4>局部注册</h4>
          <div id="app2">
              <my-component2></my-component2>
              <my-component2></my-component2>
              <my-component2></my-component2>
          </div>
      </body>
      <script>
    
          // 全局注册
          // 第一个参数是取得组件名,第二个参数是配置项
          Vue.component('my-component1',{
              // 写data的好处就是每一个组件都有自己的私有作用域
              data:function () {
                  return {
                      title: '我是全局注册的组件'
                  }
              },
              template: '<div>{{title}}</div>'
          })
          new Vue({
              el: '#app1'
          })
          // 局部注册
          var ComponentA = {
              data:function () {
                  return {
                      title: '我是局部注册的组件'
                  }
              },
              template: '<div>{{title}}</div>'
          }
          new Vue({
              el: '#app2',
              components: {// 采用键值对的形式
                  'my-component2': ComponentA
              }
          })
      </script>
      </html>
    
使用prop向子组件传递数据
  • prop书写规则 使用DOM中的模板时,驼峰命名的 prop 名需要使用其等价的短横线分隔命名
  • prop基本用法 传递静态或动态prop
      <body>
          <div id="app">
              <my-component :message='fuji'></my-component>
              <my-component message="2"></my-component>
              <my-component message="3"></my-component>
          </div>
      </body>
      <script>
      Vue.component('my-component',{
          // 数据message就是通过props从父级传递过来的,在组件的自定义标签上直接写该props的名称
          // 大白话就是,用props可以取到  该组件标签上  需要传进来的变量,例如message
          // 第一个标签中的message对应的是父级上的 fuji 这个变量
          // 如果要传递多个数据,就在props中添加就可以
          props: ['message'],
          data:function () {
              return {
                  title: '我是全局注册的组件'
              }
          },
          template: '<div><div>{{title}}</div><p>message={{message}}</p></div>'
      })
      new Vue({
          el: '#app',
          data: {
              fuji: '父组件传的动态值1'
          }
      })
      </script>
    
  • 传递特性 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定(单向数据流)
  • 组件prop的数据验证
    • 当组件需要提供给别人使用时,推荐都进行数据验证
    • 验证的type类型可以是:String、Number、Boolean、Object、Array、Function、Symbol、Date。type也可以是一个自定义构造器,使用instanceof检测。
    • 当prop验证失败时,在 开发版本 下会在控制台抛出一条警告
      这里用的是开发版本,这样验证时如果传了空字符或者其他格式,控制台会显示报错
      <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
      <body>
          <div id="app">
              <my-component :message='fuji' :message1='fuji1'></my-component>
          </div>
      </body>
      <script>
      
          Vue.component('my-component',{
              // props 可以做数据验证
              props: {
                  message: String,
                  message1: {
                      type: Object,
                      required: true
                  }
              },
              data:function () {
                  return {
                      title: '我是全局注册的组件',
                      defaultMsg: this.message,// 这样子组件可以随意调用父组件中的值,非常灵活
                      defaultMsg1: this.message1
                  }
              },
              template: '<div><div>{{title}}</div><p>{{defaultMsg}}</p><p>{{defaultMsg1.name}}</p></div>'
          })
          new Vue({
              el: '#app',
              data: {
                  fuji: '父组件传的动态值1',
                  fuji1: {name: 'xiaoming'}
              }
          })
      </script>
      
自定义事件
  • 使用场景 当子组件需要向父组件传递数据时,就要用到自定义事件
  • 基本用法 子组件用 $emit() 来触发事件,父组件用 $on() 来监听子组件的事件。
  • 自定义组件的v-model 一个组件上的v-model默认会利用名为value的prop和名为input的事件
  • 将原生事件绑定到组件 使用v-on的 .native 修饰符监听原生事件
  • .sync修饰符 父组件监听自定义事件按需更新数据
      <div id="app">
          <h4>demo1</h4>
          <!-- 在父组件直接用v-model绑定,就可以获取到子组件的值,不用像demo2再用@接受 -->
          <my-component v-model="total"></my-component>
          <button @click="handleReduce">减1按钮</button>
          <p>总共:{{total}}</p>
          <hr>
          <h4>demo2</h4>
          <!-- 通过子组件中的child-input接受来调用getVal函数-->
          <my-component1 @child-input='getVal'></my-component1>
          <div>子组件传值:{{msgVal}}</div>   
      </div>
      <script>
          // demo1
          Vue.component('my-component', {
              props: ['value'],
              template: '<input :value="value" @input="updateValue">',
              methods: {
                  updateValue(e) {
                      // 一个组件上的v-model默认会利用名为value的prop和名为input的事件
                      this.$emit('input', e.target.value)
                  }
              }
          })
          // demo2
          Vue.component('my-component1', {
              template: '<button @click="updateValue">点击发送{{changeVal}}到父组件</button>',
              data () {
                  return {
                      changeVal: ''
                  } 
              },
              methods: {
                  updateValue() {
                      if (!this.changeVal) {
                          changeVal = 0
                      }
                      this.changeVal ++
                      // 通过$emit向父组件传值
                      this.$emit('child-input', this.changeVal)
                  }
              }
          })
          // 父组件
          new Vue({
              el: "#app",
              data: {
                  total: 0,
                  msgVal: ''
              },
              methods: {
                  // demo1
                  handleReduce() {
                      this.total --;
                  },
                  // demo2
                  getVal(val) {
                      this.msgVal = val
                  }
              }
          })
      </script>
    
2. Slot分发内容
什么是Slot
  • ** 插槽(Slot) Vue提岀来的一个概念,插槽用于决定将所携带的內容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
  • 当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到Slot。
单个slot
  • 在子组件内使用特殊的<slot>元素,就可以为这个子组件开启一个slot(插槽)。
  • 在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容。
具名slot
  • <slot>元素指定一个name后,可以分发多个内容。
  • 具名Slot可以与单个Slot共存。
作用域slot
  • 作用域插槽是一种特殊的slot,使用一个可以复用的模板替换己渲染元素
    demo
      <div id="app">
          <h4>单个slot</h4>
          <child-component>
              <!-- 如果没有内容,则默认显示子组件中的内容 -->
              <p>slot分发的内容</p>
              <p>slot分发的内容</p>
          </child-component>
          <h4>具名slot</h4>
          <child-component1>
              <p slot="header">slot分发的header内容</p>
              <p>slot分发的内容</p>
              <p>slot分发的内容</p>
              <div slot="footer">底部footer内容</div>
          </child-component1>
          <h4>作用域slot</h4>
          <scope-component>
              <template scope="props">
                  <p>来自父组件的内容</p>
                  <h1>{{props.msg}}</h1>
              </template>
          </scope-component>
      </div>
      <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
      <script>
      // 单个slot
      Vue.component('child-component', {
          template: '<div><slot><p>如果父组件没有插入内容,该内容会默认显示</p></slot></div>'
      })
      // 具名slot
      Vue.component('child-component1', {
          template: '<div>\
              <slot name="header"><p>如果父组件没有插入内容,该内容会默认显示header</p></slot>\
              <slot><p>如果父组件没有插入内容,该内容会默认显示</p></slot>\
              <slot name="footer"><p>如果父组件没有插入内容,该内容会默认显示footer</p></slot>\
          </div>'
      })
      // 作用域slot
      Vue.component('scope-component', {
          template: '<div>\
              <slot msg="我是子组件传递的内容"></slot>\
          </div>'
      })
      new Vue({
          el: "#app"
      })
      </script>
    
3. 单文件组件及自定义组件
单文件组件
  • 一个后缀名为.vue的文件,使用.vue文件需要先安装vue-loader、vue- style-loader 等加载器并做 webpack配置。因为要使用ES6语法,还需要安装配置 babel 和 babel-loader等编译器。
  • 每个.vue文件包含三种类型的顶级语言块——<template>``<script><style>,还允许添加可选的自定义块。
自定义组件
  • 类似 element-u这类组件库,都是为了完成一些自定义或者特定业务的,这一类组件都可以称之为自定义组件。

实例可参考博客:搭建webpack +babel + vue项目以及编写单文件和自定义组件

4. 动态组件及异步组件
动态组件

Vue.js提供了一个特殊的元素<component>用来动态地挂载不同的组件,使用is特性来选择要挂载的组件,这样的组件叫动态组件。另外可以使用<keep-alive>标签来使组件进行缓存。

demo
动态改变currentView的值,就可以动态挂载组件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
<div id="app">
    <component :is="currentView"></component>
    <button @click="changeView('A')">切换到A</button>
    <button @click="changeView('B')">切换到B</button>
    <button @click="changeView('C')">切换到C</button>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
    new Vue({
        el: '#app',
        components: {
            comA: { template: '<div>我是A</div>' },
            comB: { template: '<div>我是B</div>' },
            comC: { template: '<div>我是C</div>' },
        },
        data: {
            currentView: 'comA'
        },
        methods: {
            changeView(com) {
                this.currentView = 'com' + com
            }
        },
    })
</script>
</body>
</html>
异步组件

Vue.js允许将组件定义为一个工厂函数,动态地解析组件。Vue只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。我们把这类组件称之为异步组件。
demo
工厂函数接收一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <my-component></my-component>
    </div>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script>
        Vue.component('my-component', function(resolve, reject){
            setTimeout( () => {
                resolve({
                    template:"<div>我是异步渲染的</div>"
                })
            }, 2000)
        })
        new Vue({
            el: '#app'
        })
    </script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

痴心的萝卜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值