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

\u003cp\u003e“组件”是 Vue 中比较基础的概念,但我发现,许多同学对 Vue 组件的概念和由来并不是清楚。因此,我希望通过这个专题,带大家换个角度来分析,最终让大家更清楚组件开发。\u003c/p\u003e\n\u003cp\u003e首先,我们先不谈组件,我想问大家一个问题:\u003c/p\u003e\n\u003cp\u003e我们平常用任何编程语言写方法(method)的时候,当一个方法里的逻辑过多时,我们会怎么办?当多个方法里有很多相似的逻辑时,我们该怎么办?\u003c/p\u003e\n\u003cp\u003e答案很明了:拆分成一个独立的方法。\u003c/p\u003e\n\u003cp\u003e如果拆分后还是有类似问题呢?那就继续拆分。\u003c/p\u003e\n\u003cp\u003e类比一下,HTML 也一样。当我们写了一大堆的 HTML后,发现有不少类似或重复的地方,我们也可以按照拆分的方法,拆分 HTML。除了拆分 HTML 之外,我们还可以拆分针对这段HTML书写的逻辑,甚至是样式。拆分后的HTML,逻辑,样式组合在一起,我们就称之为组件。\u003c/p\u003e\n\u003cp\u003e这么讲似乎有点抽象,我们来举个例子吧。假如我们要做一个简单的 TodoList 项目,代码如下:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e\u0026lt;ul\u0026gt;\n​ \u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;学习 Vue 属性\u0026lt;/span\u0026gt;\n​ \u0026lt;button\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;\n​ \u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;学习 Vue 事件\u0026lt;/span\u0026gt;\n​ \u0026lt;button\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;\n​ \u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;学习 Vue 插槽\u0026lt;/span\u0026gt;\n​ \u0026lt;button\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;\n \u0026lt;/ul\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv id=\"app\"\u003e\n\u003cp\u003e可以看到,我们 li 标签内的内容越来越多,似乎可以把它独立出来,在 Vue 中,通过以下代码就可以将这部分代码独立出来.\u003c/p\u003e\n\u003cp\u003e通过 Vue.component 定义(注册)一个组件,起名为 todo-item, 组件的 HTML 写在 template 字段上:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003eVue.component('todo-item', {\n template: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;学习 Vue 属性\u0026lt;/span\u0026gt;\n​ \u0026lt;button\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e然后,你可以通过下面这样的方式来使用这个组件:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e \u0026lt;ul\u0026gt;\n​ \u0026lt;todo-item\u0026gt;\u0026lt;/todo-item\u0026gt;\n​ \u0026lt;todo-item\u0026gt;\u0026lt;/todo-item\u0026gt;\n​ \u0026lt;todo-item\u0026gt;\u0026lt;/todo-item\u0026gt;\n \u0026lt;/ul\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv id=\"app\"\u003e\n\u003cp\u003e当然前提条件是你要先 new Vue 一个实例,并添加挂载点\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003enew Vue({\n el: '#app' // 提供一个挂载点,这样我们就可以在里面使用 todo-item 了\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这样一来清爽了许多,可是这样就变成三个“学习 Vue 属性” 事项了,我们还缺“学习 Vue 事件”和“学习 Vue 插槽” ,怎么办呢,这就要用到 Vue 的属性了。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://time.geekbang.org/course/detail/163-86424\"\u003e点击此处,开始你的第一个 Vue 程序\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://time.geekbang.org/course/detail/163-87489\"\u003e点击此处,了解单文件组件\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003e属性\u003c/h2\u003e\n\u003cp\u003e我们接着改改,调用方法时,可以传递不同的参数,方法也可以接收参数,执行不同的逻辑,加载组件时同样也可以传递不同的参数(属性),组件也可以接收参数(属性)来显示不同的内容:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003eVue.component('todo-item', {\n props: ['item'], // 声明能接收的参(属)数(性)\n // {{item}} 使用传递过来的 item\n template: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;{{item}}\u0026lt;/span\u0026gt;\n​ \u0026lt;button\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`\n})\n\n\u0026lt;div id=\u0026quot;app\u0026quot;\u0026gt;\n \u0026lt;ul\u0026gt;\n​ \u0026lt;todo-item item=\u0026quot;学习 Vue 属性\u0026quot;\u0026gt;\u0026lt;/todo-item\u0026gt;\n​ \u0026lt;todo-item item=\u0026quot;学习 Vue 事件\u0026quot;\u0026gt;\u0026lt;/todo-item\u0026gt;\n​ \u0026lt;todo-item item=\u0026quot;学习 Vue 插槽\u0026quot;\u0026gt;\u0026lt;/todo-item\u0026gt;\n \u0026lt;/ul\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e我们再精简一下:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e \u0026lt;ul\u0026gt;\n​ \u0026lt;todo-item v-for=\u0026quot;item in list\u0026quot; :item=\u0026quot;item\u0026quot;\u0026gt;\u0026lt;/todo-item\u0026gt;\n \u0026lt;/ul\u0026gt;\n\u0026lt;/div\u0026gt;\nnew Vue({\n el: '#app',\n data() {\n​ return {\n​ list: ['学习 Vue 属性', '学习 Vue 事件', '学习 Vue 插槽']\n​ }\n }\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv id=\"app\"\u003e这样就可以了,是不是很简单?\n\u003cp\u003e\u003ca href=\"https://time.geekbang.org/course/detail/163-86426\"\u003e点击此处,深入学习 Vue 中组件的属性\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003e事件\u003c/h2\u003e\n\u003cp\u003e我现在已经学完了 Vue 属性,想要从 todolist 里面把它删除掉,这好像并不太容易。这时,我需要给 button 绑定一个事件,当然 Vue 提供给我们了一个简单的方式进行绑定事件,用\u003ca href=\"https://github.com/xxx\"\u003e@xxx\u003c/a\u003e 就可以进行事件绑定了(这里的xxx指的任一字符串,根据你的实际需要来命名就行)\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003eVue.component('todo-item', {\n props: ['item'],\n template: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;{{item}}\u0026lt;/span\u0026gt;\n​ \u0026lt;button @click=\u0026quot;handleClick\u0026quot;\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`,\n methods: {\n​ handleClick() {\n​ }\n }\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e然后我们需要把点击事件告诉我们的上层(父组件),Vue 同样给我们提供了一个 API:this.$emit(‘xxx’, …),我们既然是做删除操作,那就是叫delete好了,我们还可以传递更多的参数,如 this.item:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003ehandleClick() {\n this.$emit('delete', this.item)\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e上层组件还缺少了一个用来接收 delete 的地方,我们可以通过 @delete 的方式来绑定一个用来接收 delete 事件的方法:\u003c/p\u003e\n\u003cp\u003e\u0026lt;todo-item v-for=“item in list” :item=“item” \u003ca href=\"https://github.com/delete\"\u003e@delete\u003c/a\u003e=“handleDelete”\u0026gt;\u003c/todo-item\u003e\u003c/p\u003e\n\u003cp\u003e最后只需要在 methods 字段上定义一个 handleDelete 方法,改变 list 数组就完成了我们的删除操作:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003enew Vue({\n el: '#app',\n data() {\n return {\n list: ['学习 Vue 属性', '学习 Vue 事件', '学习 Vue 插槽']\n }\n },\n methods: {\n handleDelete(item) {\n const index = this.list.findIndex(text=\u0026gt;text === item);\n this.list.splice(index, 1);\n }\n }\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e以上就是 Vue 中事件的用法。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://time.geekbang.org/course/detail/163-86427\"\u003e点击此处,深入学习 Vue 中组件的事件\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003e插槽\u003c/h2\u003e\n\u003cp\u003e现在我想让这个TodoList中的“学习 Vue XXX”前加个图标 Icon,怎么办呢?还好 Vue 早就帮我想到了,我们不能再通过属性传递这些带有标签的内容,而是通过一种名叫“插槽”的东西进行传递:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e\u0026lt;todo-item v-for=\u0026quot;item in list\u0026quot; :item=\u0026quot;item\u0026quot; @delete=\u0026quot;handleDelete\u0026quot;\u0026gt;\n \u0026lt;span\u0026gt;我是Icon\u0026lt;/span\u0026gt;\n\u0026lt;/todo-item\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e当然我们也不能再用双括号来解析,我们需要使用 \u003cslot\u003e\u003c/slot\u003e 这种写法来解析:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003etemplate: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;span\u0026gt;{{item}}\u0026lt;/span\u0026gt;\n​ \u0026lt;slot\u0026gt;\u0026lt;/slot\u0026gt;\n​ \u0026lt;button @click=\u0026quot;handleClick\u0026quot;\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`,\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这种我们称之为默认插槽。\u003c/p\u003e\n\u003cp\u003e现在我想更进一步,添加两个图标,一个在文字前面,一个在文字后面,没问题:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e\u0026lt;todo-item v-for=\u0026quot;item in list\u0026quot; :item=\u0026quot;item\u0026quot; @delete=\u0026quot;handleDelete\u0026quot;\u0026gt;\n \u0026lt;span slot=\u0026quot;prefixIcon\u0026quot;\u0026gt;我是前缀Icon\u0026lt;/span\u0026gt;\n \u0026lt;span slot=\u0026quot;suffixIcon\u0026quot;\u0026gt;我是后缀Icon\u0026lt;/span\u0026gt;\n\u0026lt;/todo-item\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e同样template需要更改:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003etemplate: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt;\n​ \u0026lt;slot name=\u0026quot;prefixIcon\u0026quot;\u0026gt;\u0026lt;/slot\u0026gt;\n​ \u0026lt;span\u0026gt;{{item}}\u0026lt;/span\u0026gt;\n​ \u0026lt;slot name=\u0026quot;suffixIcon\u0026quot;\u0026gt;\u0026lt;/slot\u0026gt;\n​ \u0026lt;button @click=\u0026quot;handleClick\u0026quot;\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`,\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这便是我们的具名插槽。\u003c/p\u003e\n\u003cp\u003e如果想让功能更加丰富的话,比如我想根据我的 input checkbox 的是否选中来改变图标的颜色,该怎么做?\u003c/p\u003e\n\u003cp\u003e第一步,记录我们input的选中状态,我们使用 Vue 的 v-model 进行 input 的双向绑定:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003eVue.component('todo-item', {\n props: ['item'],\n data() {\n​ return {\n​ checked: false, // 默认不选中\n​ }\n },\n template: `\u0026lt;li\u0026gt;\n​ \u0026lt;input type=\u0026quot;checkbox\u0026quot; v-model=\u0026quot;checked\u0026quot;\u0026gt;\n​ \u0026lt;slot name=\u0026quot;prefixIcon\u0026quot;\u0026gt;\u0026lt;/slot\u0026gt;\n​ \u0026lt;span\u0026gt;{{item}}\u0026lt;/span\u0026gt;\n​ \u0026lt;slot name=\u0026quot;suffixIcon\u0026quot;\u0026gt;\u0026lt;/slot\u0026gt;\n​ \u0026lt;button @click=\u0026quot;handleClick\u0026quot;\u0026gt;删除\u0026lt;/button\u0026gt;\n​ \u0026lt;/li\u0026gt;`,\n methods: {\n​ handleClick() {\n​ this.$emit('delete', this.item)\n​ }\n }\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e状态有了,现在需要把这个状态传递给上层:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e\u0026lt;slot name=\u0026quot;prefixIcon\u0026quot; v-bind=\u0026quot;{checked}\u0026quot;\u0026gt;我是前缀Icon\u0026lt;/slot\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e接收到状态后并根据状态提供不同颜色的图标:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003e\u0026lt;span slot=\u0026quot;prefixIcon\u0026quot; slot-scope=\u0026quot;props\u0026quot; :style=\u0026quot;{color: props.checked ?'red':'blue'}\u0026quot;\u0026gt;我是前缀Icon\u0026lt;/span\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这就是我们的作用域插槽。\u003c/p\u003e\n\u003cp\u003e为了方便理解,插槽使用方式我们使用了 Vue 2.5 版本的语法进行讲解,Vue 2.6 的语法可查看视频教程。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://time.geekbang.org/course/detail/163-86428\"\u003e点击此处,深入学习 Vue 中组件的插槽\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e如果你已经阅读到了这里,那么恭喜你,你已经可以进行简单的 Vue 组件开发了。\u003c/p\u003e\n\u003cp\u003e如果还想学习更多的 Vue 实战技巧,欢迎订阅我的视频课程\u003ca href=\"https://time.geekbang.org/course/intro/163?utm_term=zeus1LREE\u0026amp;utm_source=website\u0026amp;utm_medium=infoq\"\u003e《Vue 开发实战》\u003c/a\u003e。\u003c/p\u003e\n

你可能感兴趣的:(3个概念,入门 Vue 组件开发)