三、Vue组件

参考菜鸟教程

一、简介

  • 组件(Component)是 Vue.js 最强大的功能之一。
  • 组件可以扩展 HTML 元素,封装可重用的代码。
  • 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件

在这里插入图片描述
注册一个全局组件语法格式如下:

Vue.component(tagName, options)

tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

<tagName></tagName>
注册组件的基本步骤

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

1.Vue.extend():
调用Vue.extend()创建的是一个组件构造器。
通常在创建组件构造器时,传入template代表我们自定义组件的模板。
该模板就是在使用到组件的地方,要显示的HTML代码。
事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

2.Vue.component():
调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

3.组件必须挂载在某个Vue实例下,否则它不会生效。
我们来看下面我使用了三次
而第三次其实并没有生效:
在这里插入图片描述

注册组件语法糖

在上面注册组件的方式,可能会有些繁琐。
Vue为了简化这个过程,提供了注册的语法糖。
主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
在这里插入图片描述
在这里插入图片描述

模板的分离写法

如果写成变量形式的话,无法自动对齐,并且比较乱。Vue提供了两种方案来定义HTML模块内容:

使用<script>标签
使用<template>标签

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

二、示例、全局组件

例如下面设置一个名称为wangxiaoyu的自定义全局组件,在script中Vue.component('wangxiaoyu', { template: '<h1>自定义组件!</h1>' })方法进行注册,然后在html中直接<wangxiaoyu></wangxiaoyu>使用

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

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

<script>
// 注册
Vue.component('wangxiaoyu', {
  template: '<h1>自定义组件!</h1>'
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>


</body>

{% endverbatim %}


</html>

访问页面
在这里插入图片描述

三、示例、局部组件

在vue对象中使用components: { 'wangxiaoyu': { template: '<h1>自定义组件!</h1>' } }进行注册,然后在html中直接<wangxiaoyu></wangxiaoyu>使用,这个组件只能在这个实例中使用。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

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

<script>


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


</body>

{% endverbatim %}


</html>

访问页面
在这里插入图片描述

四、组件数据存放

1、测试是否能直接访问实例中的data

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

我们先来测试一下,组件中能不能直接访问Vue实例中的data
在这里插入图片描述
我们发现不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。
结论:Vue组件应该有自己保存数据的地方。

2、组件数据的存放

组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)
只是这个data属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据。
在data函数中返回的数据变量,可以在组件的template中进行使用

在这里插入图片描述

3、为什么组件data是一个函数呢

首先,如果不是一个函数,Vue直接就会报错。
其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

五、父组件和子组件

vue实例可以看做是最顶层的一个root父组件

1、基础实例1

子组件在父组件中注册,而不是在vue实例或全局中注册,并且在父组件的template中可以使用子组件,并且子组件要写在父组件之前。

如果子组件要在其他组件使用,需要在其他组件中注册或者在全局注册,否则只能在它注册的父组件中使用。

再往上,其实parent-cpn这个组件同时也是app这个vue实例的子组件
在这里插入图片描述

2、父传子 Prop自定义属性

在这里插入图片描述
父组件通过props向子组件传递数据
子组件通过事件向父组件发送消息

props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的变量名称。
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

示例1

props: [‘message’]为子组件增加了message这个属性变量,也就是说,子组件中存在了message变量,可以在使用组件时直接message="hello!"用字符串给该属性赋值,也可以使用在使用组件时v-bind:message="xxx"来为这个属性变量赋值,这里的xxx也是个变量,是父组件data中的变量。
在这里插入图片描述

示例2

如下使用props: ['message']定义子组件中的message属性变量,然后在template: '<span>{{ message }}</span>'使用message属性,在html代码中使用<child message="hello!"></child>直接赋值message变量。将vue实例作为父组件,其中包含子组件来简化代码。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

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

</body>

{% endverbatim %}


</html>

访问页面
在这里插入图片描述

示例3、动态 Prop

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

如下使用props: ['message']定义子组件中的message属性,然后在template: '<span>{{ message }}</span>'使用message属性,在html代码中使用<child v-bind:message="parentMsg"></child>,用 v-bind 动态绑定 props 的message属性值和父组件的parentMsg变量,然后在父组件中定义parentMsg数据值data: { parentMsg: '父组件内容' }

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

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

</body>

{% endverbatim %}


</html>

访问页面
在这里插入图片描述
这里将input<input v-model="parentMsg">绑定了,所以修改input中的内容会跟着变化
在这里插入图片描述

示例4、使用 v-bind 指令将 todo 传到每一个重复的组件中

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

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

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

</body>

{% endverbatim %}


</html>

访问页面
在这里插入图片描述

prop数据验证

在前面,我们的props选项是使用一个数组。
除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。
在这里插入图片描述

六、子传父 组件 - 自定义事件,例如点击事件

父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!

我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:

  • 父组件中使用 v-on(eventName) 监听事件

  • 子组件中使用 $emit(eventName) 触发事件

  • 子组件先提交事件 this.$emit(“事件名称”,值)

  • 在父组件中给子组件绑定事件 <child @事件名称=“父亲处理的方法”>

另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

如下

  • 定义button-counter这个组件,template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
  • 点击响应click事件执行incrementHandler方法,使用counter变量,counter默认设为0function () { return { counter: 0 } },每点击一下则调用incrementHandler方法进行加一
  • 然后在使用组件的时候绑定increment事件,执行incrementTotal方法,表示点击的时候将total变量也加一。所以每点击一下都会调用incrementHandler和incrementTotal
    说不太清楚就这样吧。。
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src={% static "vue.js" %}></script>

</head>
{% verbatim %}
<body>

<div id="app">
    <div id="counter-event-example">
      <p>{{ total }}</p>
      <button-counter v-on:increment="incrementTotal"></button-counter>
      <button-counter v-on:increment="incrementTotal"></button-counter>
    </div>
</div>

<script>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementHandler: function () {
      this.counter += 1
      //在子组件的点击方法incrementHandler中执行increment事件,该事件会再去执行incrementTotal方法
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
</script>

</body>

{% endverbatim %}


</html>

访问页面并点击,可以看到上面的值为下面两个相加,每点一次则加一
在这里插入图片描述

在这里插入图片描述

data 必须是一个函数
上面例子中,可以看到 button-counter 组件中的 data 不是一个对象,而是一个函数,这样的好处就是每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象则会影响到其他实例。

data: function () {
  return {
    count: 0
  }
}

七、父子组件的访问方式

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
父组件访问子组件:使用 c h i l d r e n 或 children或 childrenrefs
子组件访问父组件:使用$parent

$children

this.$children是一个数组类型,它包含所有子组件对象。
我们这里通过一个遍历,取出所有子组件的message状态。
在这里插入图片描述

$refs的使用:

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

$refs和ref指令通常是一起使用的。
首先,我们通过ref给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID就可以访问到该组件了。
在这里插入图片描述
在这里插入图片描述

$parent

如果我们想在子组件中直接访问父组件,可以通过$parent

注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
在这里插入图片描述

八、插槽

slot基本使用

我们通过一个简单的例子,来给子组件定义一个插槽:
中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容
在这里插入图片描述
在这里插入图片描述

具名插槽slot

当子组件的功能复杂时,子组件的插槽可能并非是一个。
比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
这个时候,我们就需要给插槽起一个名字只要给slot元素一个name属性即可

<slot name='myslot'></slot>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值