3 个概念,入门 Vue 组件开发

“组件”是 Vue 中比较基础的概念,但我发现,许多人对 Vue 组件的概念和由来并不是清楚。因此,我希望通过这个专题,带大家换个角度来分析,最终让大家更清楚组件开发。
image

首先,我们先不谈组件,我想问大家一个问题:

我们平常用任何编程语言写方法(method)的时候,当一个方法里的逻辑过多时,我们会怎么办?当多个方法里有很多相似的逻辑时,我们该怎么办?

答案很明了:拆分成一个独立的方法。

如果拆分后还是有类似问题呢?那就继续拆分。

类比一下,HTML 也一样。当我们写了一大堆的 HTML 后,发现有不少类似或重复的地方,我们也可以按照拆分的方法,拆分 HTML。除了拆分 HTML 之外,我们还可以拆分针对这段 HTML 书写的逻辑,甚至是样式。拆分后的 HTML,逻辑,样式组合在一起,我们就称之为组件。

这么讲似乎有点抽象,我们来举个例子吧。假如我们要做一个简单的 TodoList 项目,代码如下:

<ul>​    <li>​      <input type="checkbox">​      <span> 学习 Vue 属性 </span>​      <button> 删除 </button>​    </li>​    <li>​      <input type="checkbox">​      <span> 学习 Vue 事件 </span>​      <button> 删除 </button>​    </li>​    <li>​      <input type="checkbox">​      <span> 学习 Vue 插槽 </span>​      <button> 删除 </button>​    </li>  </ul></div>

可以看到,我们 li 标签内的内容越来越多,似乎可以把它独立出来,在 Vue 中,通过以下代码就可以将这部分代码独立出来.

通过 Vue.component 定义 (注册) 一个组件,起名为 todo-item, 组件的 HTML 写在 template 字段上:

Vue.component('todo-item', {  template: `<li>​      <input type="checkbox">​      <span> 学习 Vue 属性 </span>​      <button> 删除 </button>​    </li>`})

然后,你可以通过下面这样的方式来使用这个组件:

  <ul>​    <todo-item></todo-item>​    <todo-item></todo-item>​    <todo-item></todo-item>  </ul></div>

当然前提条件是你要先 new Vue 一个实例,并添加挂载点

new Vue({  el: '#app'  // 提供一个挂载点,这样我们就可以在里面使用 todo-item 了})

这样一来清爽了许多,可是这样就变成三个“学习 Vue 属性” 事项了,我们还缺“学习 Vue 事件”和“学习 Vue 插槽” ,怎么办呢,这就要用到 Vue 的属性了。

属性

我们接着改改,调用方法时,可以传递不同的参数,方法也可以接收参数,执行不同的逻辑,加载组件时同样也可以传递不同的参数 (属性),组件也可以接收参数 (属性) 来显示不同的内容:

Vue.component('todo-item', {  props: ['item'], // 声明能接收的参 (属) 数 (性)  // {{item}} 使用传递过来的 item  template: `<li>​      <input type="checkbox">​      <span>{{item}}</span>​      <button> 删除 </button>​    </li>`}) <div id="app">  <ul>​    <todo-item item=" 学习 Vue 属性 "></todo-item>​    <todo-item item=" 学习 Vue 事件 "></todo-item>​    <todo-item item=" 学习 Vue 插槽 "></todo-item>  </ul></div>

我们再精简一下:

 <ul>​    <todo-item v-for="item in list" :item="item"></todo-item>  </ul></div>new Vue({  el: '#app',  data() {​    return {​      list: ['学习 Vue 属性', '学习 Vue 事件', '学习 Vue 插槽']​    }  }})

这样就可以了,是不是很简单?

事件

我现在已经学完了 Vue 属性,想要从 todolist 里面把它删除掉,这好像并不太容易。这时,我需要给 button 绑定一个事件,当然 Vue 提供给我们了一个简单的方式进行绑定事件,用@xxx 就可以进行事件绑定了 (这里的 xxx 指的任一字符串,根据你的实际需要来命名就行)

Vue.component('todo-item', {  props: ['item'],  template: `<li>​      <input type="checkbox">​      <span>{{item}}</span>​      <button @click="handleClick"> 删除 </button>​    </li>`,  methods: {​    handleClick() {​    }  }})

然后我们需要把点击事件告诉我们的上层 (父组件),Vue 同样给我们提供了一个 API:this.$emit(‘xxx’, …),我们既然是做删除操作,那就是叫 delete 好了,我们还可以传递更多的参数,如 this.item:

handleClick() {  this.$emit('delete', this.item)}

上层组件还缺少了一个用来接收 delete 的地方,我们可以通过 @delete 的方式来绑定一个用来接收 delete 事件的方法:

<todo-item v-for=“item in list” :item=“item” @delete=“handleDelete”>

最后只需要在 methods 字段上定义一个 handleDelete 方法,改变 list 数组就完成了我们的删除操作:

new Vue({  el: '#app',  data() {    return {      list: ['学习 Vue 属性', '学习 Vue 事件', '学习 Vue 插槽']    }  },  methods: {    handleDelete(item) {      const index = this.list.findIndex(text=>text === item);      this.list.splice(index, 1);    }  }})

以上就是 Vue 中事件的用法。

点击此处,深入学习 Vue 中组件的事件

插槽

现在我想让这个 TodoList 中的“学习 Vue XXX”前加个图标 Icon,怎么办呢?还好 Vue 早就帮我想到了,我们不能再通过属性传递这些带有标签的内容,而是通过一种名叫“插槽”的东西进行传递:

<todo-item v-for="item in list" :item="item" @delete="handleDelete">  <span> 我是 Icon</span></todo-item>

当然我们也不能再用双括号来解析,我们需要使用 这种写法来解析:

template: `<li>​      <input type="checkbox">​      <span>{{item}}</span>​      <slot></slot>​      <button @click="handleClick"> 删除 </button>​    </li>`,

这种我们称之为默认插槽。

现在我想更进一步,添加两个图标,一个在文字前面,一个在文字后面,没问题:

<todo-item v-for="item in list" :item="item" @delete="handleDelete">  <span slot="prefixIcon"> 我是前缀 Icon</span>  <span slot="suffixIcon"> 我是后缀 Icon</span></todo-item>

同样 template 需要更改:

template: `<li>​      <input type="checkbox">​      <slot name="prefixIcon"></slot>​      <span>{{item}}</span>​      <slot name="suffixIcon"></slot>​      <button @click="handleClick"> 删除 </button>​    </li>`,

这便是我们的具名插槽。

如果想让功能更加丰富的话,比如我想根据我的 input checkbox 的是否选中来改变图标的颜色,该怎么做?

第一步,记录我们 input 的选中状态,我们使用 Vue 的 v-model 进行 input 的双向绑定:

Vue.component('todo-item', {  props: ['item'],  data() {​    return {​      checked: false, // 默认不选中​    }  },  template: `<li>​      <input type="checkbox" v-model="checked">​      <slot name="prefixIcon"></slot>​      <span>{{item}}</span>​      <slot name="suffixIcon"></slot>​      <button @click="handleClick"> 删除 </button>​    </li>`,  methods: {​    handleClick() {​      this.$emit('delete', this.item)​    }  }})

状态有了,现在需要把这个状态传递给上层:

<slot name="prefixIcon" v-bind="{checked}"> 我是前缀 Icon</slot>

接收到状态后并根据状态提供不同颜色的图标:

<span slot="prefixIcon" slot-scope="props" :style="{color: props.checked ?'red':'blue'}"> 我是前缀 Icon</span>

这就是我们的作用域插槽。

为了方便理解,插槽使用方式我们使用了 Vue 2.5 版本的语法进行讲解

如果您对这个文章有任何异议,那么请在文章评论处写上你的评论。

如果您觉得这个文章有意思,那么请分享并转发,或者也可以关注一下表示您对我们文章的认可与鼓励。

愿大家都能在编程这条路,越走越远。

猜你喜欢

转载自blog.csdn.net/ZIYUSHUO/article/details/89519052