vue组件

本文详细介绍了Vue.js中组件的定义、使用及数据传递方法,包括全局与局部组件的创建,组件间的通信机制,以及如何利用组件实现动态内容切换。

vue组件

全局组件定义的三种方式

1. 使用 Vue.extend 配合 Vue.component 方法:
//第一种
   var login = Vue.extend({
      template: '<h1>登录</h1>'
    });
    Vue.component('login', login);
//第二种
     Vue.component('mycom1', Vue.extend({
      template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
    }))
2. 直接使用 Vue.component 方法:
Vue.component('register', {
      template: '<h1>注册</h1>'
    });
3. 将模板字符串,定义到script标签种:
<script id="tmpl" type="x-template">
      <div><a href="#">登录</a> | <a href="#">注册</a></div>
    </script>

同时,需要使用 Vue.component 来定义组件:

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

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

局部组件


<div id="app">
    <runoob></runoob>
</div>
 
<script>
var Child = {
  template: '<h1>自定义组件!</h1>'
}
 
// 创建根实例
new Vue({
  el: '#app',
  components: {
    // <runoob> 将只在父模板可用
    'runoob': Child
  }
})
</script>

组件中展示数据和响应事件

  • 组件可以有自己的 data 数据
  • 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
  • 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
  • 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!

在组件中,data需要被定义为一个方法,例如:

 <script>
   Vue.component('mycom1', {
     template: '<h1>这是全局组件 --- {{msg}}</h1>',
     data: function () {
       return {
         msg: '这是组件的中data定义的数据'
       }
     }
   })

   // 创建 Vue 实例,得到 ViewModel
   var vm = new Vue({
     el: '#app',
     data: {},
     methods: {}
   });
 </script>

使用components属性定义局部子组件

组件实例定义方式:

<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: { // 定义子组件
        account: { // account 组件
          template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>', // 在这里使用定义的子组件
          components: { // 定义子组件的子组件
            login: { // login 组件
              template: "<h3>这是登录组件</h3>"
            }
          }
        }
      }
    });
  </script>

引用组件:

<div id="app">
    <account></account>
  </div>

重点】为什么组件中的data属性必须定义为一个方法并返回一个对象

通过计数器案例演示

<div id="app">
    <counter></counter>
    <hr>
    <counter></counter>
    <hr>
    <counter></counter>
  </div>
  <template id="tmpl">
    <div>
      <input type="button" value="+1" @click="increment">
      <h3>{{count}}</h3>
    </div>
  </template>
  <script>
    var dataObj = { count: 0 }

    // 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
    Vue.component('counter', {
      template: '#tmpl',
      data: function () {
        // return dataObj
        return { count: 0 }
      },
      methods: {
        increment() {
          this.count++
        }
      }
    })
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });
  </script>

在这里插入图片描述

使用flag标识符结合v-ifv-else切换组件

页面结构:

<div id="app">
    <input type="button" value="toggle" @click="flag=!flag">
    <my-com1 v-if="flag"></my-com1>
    <my-com2 v-else="flag"></my-com2>
  </div>

Vue实例定义:

<script>
    Vue.component('myCom1', {
      template: '<h3>奔波霸</h3>'
    })

    Vue.component('myCom2', {
      template: '<h3>霸波奔</h3>'
    })

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        flag: true
      },
      methods: {}
    });
  </script>

使用:is属性来切换不同的子组件,并添加切换动画

组件实例定义方式:
  // 登录组件
    const login = Vue.extend({
      template: `<div>
        <h3>登录组件</h3>
      </div>`
    });
    Vue.component('login', login);

    // 注册组件
    const register = Vue.extend({
      template: `<div>
        <h3>注册组件</h3>
      </div>`
    });
    Vue.component('register', register);

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: { comName: 'login' },
      methods: {}
    });
使用component标签,来引用组件,并通过:is属性来指定要加载的组件:

页面结构:

 <div id="app">
    <a href="" @click.prevent="comName='login'">登录</a>
    <a href="" @click.prevent="comName='register'">注册</a>
    // Vue提供了 component ,来展示对应名称的组件
    //component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称
    <component :is="comName"></component>
  </div>

Vue实例定义:

<script>
    // 组件名称是 字符串
    Vue.component('login', {
      template: '<h3>登录组件</h3>'
    })

    Vue.component('register', {
      template: '<h3>注册组件</h3>'
    })

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
      },
      methods: {}
    });
  </script>
添加切换样式:

css样式:

  <style>
    .v-enter,
    .v-leave-to {
      opacity: 0;
      transform: translateX(30px);
    }

    .v-enter-active,
    .v-leave-active {
      position: absolute;
      transition: all 0.3s ease;
    }

    h3{
      margin: 0;
    }
  </style>

父组件向子组件传值

Prop

prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。
父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

<div id="app">
    <child message="hello!"></child>
</div>
<script>
// 注册
Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 同样也可以在 vm 实例中像 "this.message" 这样使用
  template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>

动态 Prop

类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:

<div id="app">
    <div>
      <input v-model="parentMsg">
      <br>
      <child v-bind:message="parentMsg"></child>
    </div>
</div>
<script>
// 注册
Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 同样也可以在 vm 实例中像 "this.message" 这样使用
  template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
  el: '#app',
  data: {
    parentMsg: '父组件内容'
  }
})
</script>

以下实例中使用 v-bind 指令将 todo 传到每一个重复的组件中:


<div id="app">
    <ol>
    <todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
      </ol>
</div>
 
<script>
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
new Vue({
  el: '#app',
  data: {
    sites: [
      { text: 'Runoob' },
      { text: 'Google' },
      { text: 'Taobao' }
    ]
  }
})
</script>

注意: prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。

Prop 验证

组件可以为 props 指定验证要求。

为了定制 prop 的验证方式,可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    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
      }
    }
  }
})

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

type 可以是下面原生构造器:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

type 也可以是一个自定义构造器,使用 instanceof 检测。

子组件向父组件传值

  1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  2. 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
<son @func="getMsg"></son>
  1. 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
<div id="app">
    <!-- 引用父组件 -->
    <son @func="getMsg"></son>
    <!-- 组件模板定义 -->
    <script type="x-template" id="son">
      <div>
        <input type="button" value="向父组件传值" @click="sendMsg" />
      </div>
    </script>
  </div>

  <script>
    // 子组件的定义方式
    Vue.component('son', {
      template: '#son', // 组件模板Id
      methods: {
        sendMsg() { // 按钮的点击事件
          this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去
        }
      }
    });

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
          alert(val);
        }
      }
    });
  </script>

评论列表案例

<div id="app">
    <cmt-box @func="loadComments"></cmt-box>
    <ul class="list-group">
      <li class="list-group-item" v-for="item in list" :key="item.id">
        <span class="badge">评论人: {{ item.user }}</span>
        {{ item.content }}
      </li>
    </ul>
  </div>
  <template id="tmpl">
    <div>
      <div class="form-group">
        <label>评论人:</label>
        <input type="text" class="form-control" v-model="user">
      </div>
      <div class="form-group">
        <label>评论内容:</label>
        <textarea class="form-control" v-model="content"></textarea>
      </div>
      <div class="form-group">
        <input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
      </div>
    </div>
  </template>
  <script>
    var commentBox = {
      data() {
        return {
          user: '',
          content: ''
        }
      },
      template: '#tmpl',
      methods: {
        postComment() { // 发表评论的方法
          // 分析:发表评论的业务逻辑
          // 1. 评论数据存到哪里去???   存放到了 localStorage 中  localStorage.setItem('cmts', '')
          // 2. 先组织出一个最新的评论数据对象
          // 3. 想办法,把 第二步中,得到的评论对象,保存到 localStorage 中:
          //  3.1 localStorage 只支持存放字符串数据, 要先调用 JSON.stringify 
          //  3.2 在保存 最新的 评论数据之前,要先从 localStorage 获取到之前的评论数据(string), 转换为 一个  数组对象, 然后,把最新的评论, push 到这个数组
          //  3.3 如果获取到的 localStorage 中的 评论字符串,为空不存在, 则  可以 返回一个 '[]'  让 JSON.parse 去转换
          //  3.4  把 最新的  评论列表数组,再次调用 JSON.stringify 转为  数组字符串,然后调用 localStorage.setItem()

          var comment = { id: Date.now(), user: this.user, content: this.content }

          // 从 localStorage 中获取所有的评论
          var list = JSON.parse(localStorage.getItem('cmts') || '[]')
          list.unshift(comment)
          // 重新保存最新的 评论数据
          localStorage.setItem('cmts', JSON.stringify(list))

          this.user = this.content = ''

          // this.loadComments() // ?????
          this.$emit('func')
        }
      }
    }

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        list: [
          { id: Date.now(), user: '李白', content: '天生我材必有用' },
          { id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
          { id: Date.now(), user: '小马', content: '我姓马, 风吹草低见牛羊的马' }
        ]
      },
      beforeCreate(){ // 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好

      },
      created(){
        this.loadComments()
      },
      methods: {
        loadComments() { // 从本地的 localStorage 中,加载评论列表
          var list = JSON.parse(localStorage.getItem('cmts') || '[]')
          this.list = list
        }
      },
      components: {
        'cmt-box': commentBox
      }
    });
  </script>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值