在上一章节中,咱们将vue-cli
命令行工具生成的代码骨架中的src目录清理了一遍,而后从头开始配置和编写了一个能够运行的小程序页面,算是正真走上了使用mpvue开发小程序的第一步。今天咱们将进一步来了解和学习mpvue / Vue
的其余重要功能。html
既然mpvue
是基于Vue的,那么就没有理由不进一步学习一下Vue最核心的东西:组件。组件系统是Vue应用开发中最具价值的特性之一,在前文中其实咱们就已经有在使用组件了,好比App.vue
和首页index.vue
就是两个Vue组件。vue
组件是一种抽象,容许咱们使用小型、独立和一般可复用的组件构建大型应用。仔细想一想,几乎任意类型的应用界面均可以抽象为一个组件树,若干的小组件能够聚合成一个完整的界面:vue-cli
一个好的组件系统必定会有这些特色:封装性、复用性、扩展性。对于Vue的组件来讲,这几点都算是实现的比较的优秀的。npm
Vue组件的写法能够避免将属于一个独立逻辑单位的代码散落在各处,能够将界面(DOM)、样式(CSS)、行为(JS)三部分的代码很好的组织在一块儿(推荐的实践是使用.vue
文件)。在设计编写一个组件时,咱们要记住的原则就是:编程
避免向外部暴露过多的东西,只暴露必要的外部交互接口(组件属性、事件、方法等)。小程序
下面咱们来在原先的代码基础上,建立一个简单的按钮点击计数器组件,它将实现的功能是:点击按钮并展现已点击按钮次数、点击清零按钮实现点击次数的归零。在src/components
目录下,新建一个click-counter.vue
组件文件,并编写以下代码:微信
<template> <div class="click-counter"> <div class="counter-num">次数:{{num}}</div> <button class="counter-btn" @click="handleClick">点我呀!</button> <button class="counter-reset-btn" @click="handleResetClick">清零</button> </div> </template> <script> export default { data() { return { num: 0 }; }, methods: { handleClick() { this.num += 1; }, handleResetClick() { this.num = 0; } } }; </script> <style> .click-counter { display: flex; align-items: center; justify-content: center; border: 1px solid red; background-color: #ffffff; padding: 10px; } .counter-num, .counter-btn, .counter-reset-btn { flex: 1; margin: 3px; } </style>
编写完这个组件后,咱们来尝试在首页组件src/pages/index/index.vue
文件中使用它:微信开发
<template> <div class="container" @click="clickHandle"> <div class="message">{{msg}}</div> <!-- 使用 click-counter 组件 --> <click-counter /> </div> </template> <script> // 导入 click-counter 组件 import ClickCounter from "@/components/click-counter"; export default { // 声明在当前组件下使用 counter-click 组件 components: { ClickCounter }, data() { return { msg: "Hello" }; }, methods: { clickHandle() { this.msg = "Clicked!!!!!!"; } } }; </script> <style scoped> .message { color: red; padding: 10px; text-align: center; } </style>
完成上面两个步骤后,记得从新运行一下命令行npm run dev
(注意点:新增文件必须从新运行该命令,编译器不会自动检测新加入的文件)。成功后经过微信开发者工具的模拟器查看,结果界面将会是这样的:函数
点击“点我呀!”按钮,计数器就会累加点击次数并更新界面上的数字;而点击“清零”按钮,则会将统计数字归零。工具
回到代码上来看,对于click-counter.vue
的使用者index.vue
来讲,它并不关心太多click-counter.vue
的实现细节,引入该组件文件并进行声明,就能够经过标签的形式来使用它了,很是简单明了。并且,这样一个click-counter.vue
组件也能够被拿到其余的Vue/mpvue代码中使用,其余使用者也并不须要关注它的实现细节,而只须要关心它能实现什么功能就好了。这就是组件封装带来的好处。
不过,目前的这个click-counter组件尚未跟它的父组件之间有什么交互或通讯,没有体现出“暴露接口”的特性,那让咱们来增长点代码,了解下这一特性。首先解释一下咱们要实现的功能:组件能够接收一个外部设置的初始点击次数值,在点击“点我呀!”按钮的时候,从这个初始值开始进行累加;而且点击按钮后,能够通知组件的使用者(即父组件)当前的点击统计值。
修改click-counter.vue
的代码:
<template> <div class="click-counter"> <div class="counter-num">次数:{{num}}</div> <button class="counter-btn" @click="handleClick">点我呀!</button> <button class="counter-reset-btn" @click="handleResetClick">清零</button> </div> </template> <script> export default { // 增长一个可从外部传入的属性initNum props: { initNum: { type: Number, default: 0 } }, data() { return { num: this.initNum //使用传入的initNum值做为初始的点击数 }; }, methods: { handleClick() { this.num += 1; this.notifyNum(); }, handleResetClick() { this.num = 0; this.notifyNum(); }, notifyNum() { //触发自定义事件 clicknum this.$emit("clicknum", { num: this.num }); } } }; </script> <style scoped> .click-counter { display: flex; align-items: center; justify-content: center; border: 1px solid red; background-color: #ffffff; padding: 10px; } .counter-num, .counter-btn, .counter-reset-btn { flex: 1; margin: 3px; } </style>
修改index.vue
的代码:
<template> <div class="container" @click="clickHandle"> <div class="message">{{msg}}</div> <!-- 使用 click-counter 组件 --> <click-counter :init-num="10" @clicknum="handleClickNum" /> </div> </template> <script> // 导入 click-counter 组件 import ClickCounter from "@/components/click-counter"; export default { // 声明在当前组件下使用 counter-click 组件 components: { ClickCounter }, data() { return { msg: "Hello" }; }, methods: { clickHandle() { this.msg = "Clicked!!!!!!"; }, handleClickNum(data) { console.log(">>>>>>", data.num); } } }; </script> <style scoped> .message { color: red; padding: 10px; text-align: center; } </style>
观察以上修改后的代码能够发现,在click-couter.vue
中的主要变化是:
使用props
定义了一个名为initNum
的数字型组件属性(且初始值为0)。它可用于接收使用组件外部传入的值。而后,这个initNum
值被赋值到data
中的属性num
上做为它的初始值。
在两个按钮的click事件处理方法中,额外调用了一个notifyNum()
方法,它向组件触发了一个自定义事件clicknum
并携带了当前点击次数值。
而在index.vue
中的主要变化是实例化click-counter
组件的这行代码:
<click-counter :init-num="10" @clicknum="handleClickNum" />
实例化组件的时候,为组件传入了initNum
属性值10
;而且添加了一个对自定义事件clicknum
的监听方法。
这样一个结构实现了数据进入组件/数据传出组件的机制,父子组件之间就能实现数据通讯。经过有限的通讯点进行数据互换,而不是直接进行函数调用,可使得代码结构更优雅、更易维护。
组件的复用性就好理解的多了,建立组件的目的,大多数时候就是但愿这个组件能够被多个地方、屡次使用,避免编写重复的代码。好比咱们前面的计数器组件,有可能一个项目中的多个页面会用到,也可能一个页面就会使用屡次。
Vue组件的复用也是很容易的,好比咱们要在前面例子中的index.vue
中复用计数器组件,建立3个计数器,那么直接在模板部分编写3个标签就好了:
<template> <div class="container" @click="clickHandle"> <div class="message">{{msg}}</div> <!-- 建立 3个 click-counter 组件 --> <click-counter :init-num="10" @clicknum="handleClickNum" /> <click-counter :init-num="20" @clicknum="handleClickNum" /> <click-counter :init-num="30" @clicknum="handleClickNum" /> </div> </template>
运行后的效果以下图所示,这三个计数器都能独立统计各自的点击数量:
谈到扩展性,有面向对象编程经验的开发者就会想到“继承(extends)”。继承是一种比较有效的扩展机制,不过随着继承的层次变深,代码也会变得难以理解。在Vue组件中,没有采用继承的机制,而是推荐使用“组合”的方式。
在组合理念下,咱们尽可能将想复用性高的组件设计到最小可拆分单位,好比按钮、输入框、单选框等等,而后再将这些低层组件放入更高层组件中,一层一层,慢慢拼装出知足需求的业务界面。
除了组合,Vue组件还提供了插槽(Slot)功能,至关于在一个组件中挖出了一个或多个坑,在具体使用这些具备插槽的组件时,能够选择往坑里面填什么内容(其余组件)。
举个例子,在计数器组件中,咱们在清零按钮后面用<slot></slot>
挖了一个坑:
<template> <div class="click-counter"> <div class="counter-num">次数:{{num}}</div> <button class="counter-btn" @click="handleClick">点我呀!</button> <button class="counter-reset-btn" @click="handleResetClick">清零</button> <slot></slot> </div> </template>
然后,在index.vue
中使用计数器组件时,在<click-counter>
标签体中放入了额外的内容,会被传入该组件中去用于填坑:
<template> <div class="container" @click="clickHandle"> <div class="message">{{msg}}</div> <!-- 使用 click-counter 组件 --> <click-counter :init-num="10" @clicknum="handleClickNum"> <!-- 填坑用... --> <input type="checkbox" /> 禁用 </click-counter> </div> </template>
从运行结果能够看到,清零按钮后面已经多出了咱们传入的复选框和文字内容:
插槽其实能够理解为是另外一种形式的组件属性:普通组件属性传入的是比较简单类型的数据;而插槽传入的能够是更复杂的界面组件而已。
本文咱们初步学习了一下Vue组件的相关理念和特性,但愿你们花点时间去熟悉和掌握这些比较核心的知识点,相信无论在以后使用Vue进行Web应用开发,仍是mpvue小程序开发,都会更加驾轻就熟、事半功倍的!