Vue组件

组件就是可复用的Vue实例,在开发过程中,我们可以把重复使用的功能封装成自定义组件,以达到便捷开发的目的。

通常一个应用会以一棵嵌套的组件树的形式来组织:

你可能会有头部导航、内容区、侧边栏等组件,每个组件内部又包含了导航链接、博文之类的组件。

为了能在模板 <template> 中使用,这些组件必须先注册以便 Vue能够识别。

组件的注册

在 Vue 中,组件的注册分全局注册和局部注册两种:

  1. 全局注册:用 Vue.component 来创建组件,注册之后可以在任何新创建的 Vue 根实例中使用;
  2. 局部注册:在单个 Vue 格式的文件中创建组件,在需要用到的地方进行注册;

但通常我们都是基于 Vue 工程进行开发的,会用到 webpack 这样的构建系统,而通过全局注册的组件在构建系统中即使没被使用依然会存在于构建结果中,所以我们通常选择局部注册。

组件的创建

每个Vue格式的文件都可以作为一个组件来使用

组件的局部注册

首先我们需要创建一个Vue文件

然后需要定义组件名字

在需要使用的地方,注册组件、引入组件以及使用组件

组件内数据data必须是一个函数

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

组件单向数据流

而实际开发中,复用的组件里显示的内容往往是不同的,因此我们需要从父组件传递不同内容给子组件。

我们使用 prop

向prop传输数据:

<template>
  <div id="app">
    <!-- 注意!title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名 -->
    <HelloVue :title="title1"></HelloVue>
    <HelloVue :title="title2"></HelloVue>
  </div>
</template>

title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名

由于 title1 和 title2 是变量,所以 title 前要加 :

prop接受数据

<template>
  <div class="hello">
    <!-- 第二步:在页面上显示 title 的值,写法和显示 data 里定义的数据一样 -->
    <h1>{{ title }}</h1>
  </div>
</template>

<script>
  export default {
    name: 'HelloVue',
    // 第一步:在 prop 属性中接收 title
    props: ['title']
  };
</script>

因为传过来的数据可能不止一个,所以props中 title 是以数组的形式表达的。

同时,我们可以声明数据类型:

 props: {
      title: String
    }

当传入为对象,值有多个的时候,我们使用逗号隔开

props: {
  title: String,
  // 多类型
  likes: [String, Number],
  // 带有默认值
  isPublished: {
    type: Boolean,
    default: true
  },
  // 必填
  commentIds: {
    type: Array,
    required: true
  },
  author: Object,
  callback: Function,
  contactsPromise: Promise
}

需要注意的是,单向数据流是父对子的单向流通

父组件数据变化会通过prop传递到子组件,但子组件不能直接修改传递过来的prop

如果我们需要修改传递过来的prop

1.使用计算属性进行处理

props: ['initialTitle'],
computed: {
  normalizedTitle: function () {
    // 对传入的 initialTitle 进行去空格、字母小写化处理
    return this.initialTitle.trim().toLowerCase()
  }
}

2.将prop传入数据变为本地数据进行处理

props: ['initialTitle'],
data: function () {
  return {
    // 要读取 prop 里的 initialTitle,要在前面加 “this.”
    // 将传入的 initialTitle 作为本地属性 title 的初始值
    title: this.initialTitle
  }
}

自定义组件绑定原生事件

当我们在子组件内设置了事件(如点击事件)的同时,在父组件中引入的子组件标签上也添加了事件(如点击事件),当我们点击这个标签,会发现只有子组件(自定义组件)的事件被触发了,而父组件(原生组件)的事件没有触发。

这种情况,如果我们需要父组件事件一起触发,可以添加 Vue 修饰符

修饰符使用点开头的指令后缀表示的 如 .prevent.capture

而要让父组件内容被执行,我们需要添加 .native 修饰符:

<Article
  v-for="article in articleList"
  :key="article.title"
  :article="article"
  @click.native="print(article)"
></Article>

按键修饰符

当我们需要添加按键点击事件时,需要添加 keyup 事件,此时我们需要设定这个事件对应的键盘按键。

如对应回车键(enter按键):

<button @keyup.enter="print(article)">按回车键执行 print</button>

因为回车键的 ASCII码对应的是 13,所以也可以用:

<button @keyup.13="print(article)">按回车键执行 print</button>

自定义事件

举个例子,当我们需要提供 子组件里的点赞按钮来改变 父组件里的点赞数,此时我们需要通过子组件来修改父组件的数据,在之前的内容中可知,按之前的方法是无法实现的,此时我们需要使用自定义组件。

首先,我们需要在父组件中将子组件绑定一个自定义事件 v-on:upVote="handleLikes"

其中 upVote 是自定义事件的名称,类比于点击事件绑定 v-on:click 。 handleLikes 是事件绑定的方法,结合上述要求,即是父组件里改变点赞数的方法。

然后我们需要在子组件内调用自定义事件

<button @click="$emit('upVote')">点赞</button>

如果有多个事件可以

<button @click="childEvent">点赞</button>
methods: {
  childEvent: function() {
    // 调用自定义事件 upVote
    this.$emit('upVote');
    // do other things
  }
}

如果我们参数的传递如

// 在 `methods` 对象中定义方法
methods: {
  handleLikes(article) {
    article.likes++
  }
}

可以在子组件的 $emit 中写入多个参数表示参数传递

this.$emit('upVote', this.article);

如果我们添加 .sync 修饰符,可以表示双向绑定:

<MyCount class="count" :count.sync="count"></MyCount>
<div class="my-count">
  <button @click="$emit('update:count', count+1)">加一</button>
  {{ count }}
</div>

组件函数调用

父组件是可以直接访问子组件的函数的。

首先我们使用 ref 属性添加需要调用的组件

<template>
  <Modal ref="modal"></Modal>
</template>

然后我们就可以直接在父组件方法中调用子组件函数:

  methods: {
    showModal() {
      // 调用子组件中的 show 方法
      this.$refs.modal.show();
    }
  }

除了可以调用子组件函数,ref属性也可以调用子元素

<input ref="input" type="text" />

input是HTML原生的表单元素,它存在着自己的 focus() 方法来聚焦,可以用ref来调用这个方法:

 focusInput() {
      // this.$refs.input 访问输入框元素,并调用 focus() 方法使其获取焦点
      this.$refs.input.focus();
    }