Vue学习笔记(四):深入了解组件(上)
🚥Vue2 系列文章导航🚥
一、组件基础
1.引子
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。当我们使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的.vue
文件中,这被叫做单文件组件(简称 SFC)。实际上,我们前面的代码示例也都是在一个单独的组件内实现的。
2.组件组成结构
一个最基本的组件,其组成结构如下:
1 | <template> |
-
<template>
用来承载所有的 HTML 标签; -
<script>
用于承载所有的业务逻辑; -
<style>
用于承载各种样式;
可以看到,一个完整的 Vue 组件包含所有我们在网页上需要呈现的内容:HTML,JavaScript 以及 CSS。
有些人可能会注意到这里的<style>
标签里有一个scoped
,它的作用是什么呢?
答案很简单,scoped
的作用是让当前样式只在当前组件中生效,如果不加scoped
,这里的样式就为成为全局样式,对于任何组件都会生效,所以一般为了避免各个组件之间相互干扰,我们都会选择给<style>
标签加上一个scoped
,对其作用域进行限制。
3.组件的引用
组件的引用分为三个步骤:
首先,在<script>
标签中使用 ES6 所提供的import
去引入这个组件,格式为import MyComponent from "./components.MyComponent.vue"
。前面的MyComponent
是用户自己为所引入组件所起的名字,可以不与文件名相同,后面的引号内容则是所要引入组件的文件地址。然后,在export default
中与data
同级的components
里注册组件。这个组件将会以其注册时的名字作为模板中的标签名。最后,我们在<template>
中以标签的形式将组件显示出来。代码示例如下:
1 | <template> |
注意事项:
1.这里的<MyComponent/>
我们也可以写成<my-component/>
,两者的关系是等价的。因为 Vue 支持将模板中使用 kebab-case
的标签解析为使用 PascalCase
注册的组件。
2.同一个组件可以被我们使用多次,但要注意是,每一个组件都在维护着各自的状态,是不同的 count,因为每当我们使用一个组件,就创建了一个新的实例,具体理解有点类似于我们在 c++ 中学到的对象。
二、组件嵌套关系
在实际应用中,组件常常被我们组织成层层嵌套的树状结构:
上方这张图来自于官方文档,就让我们以这张图所提供的嵌套方式为例,进行页面创建:
App.vue:
1 | <template> |
Main.vue:
1 | <template> |
Article.vue:
1 | <template> |
Aside.vue:
1 | <template> |
Item.vue:
1 | <template> |
这里我们单独建立了一个 pages 网页用于存储各个组件,读者可根据实际情况自行调整文件路径。最后我们就能得到如下的网页了,怎么样,是不是和官方文档的图片很像呢?记得要仔细观察各个组件之间的嵌套逻辑以及嵌套实现方式哦!
三、组件注册方式
一个 Vue 组件在被我们使用前需要先被“注册”,这样 Vue 才能在渲染模版的时候找到其对应的实现,组件的注册方式有两种:全局注册和局部注册。
1.全局注册
一般情况下,我们使用 Vue 应用实例的.component()
方法来让组件全局可用,打开我们项目中的main.js
文件,我们会发现它存在下面这样的默认内容:
1 | import { createApp } from 'vue' |
我们首先使用import
将组件文件引入,接着将此处的createApp(App).mount('#app')
拆分为两部分,最后在他们之间写入我们要全局注册的内容:
1 | import { createApp } from 'vue' |
这里的app.component("Header",Header)
中第一个"Header"
是我们为注册的组件取得变量名,即日后我们引入该组件时使用的名字,第二个Header
则是我们要所注册的组件的名字。通过这种方式注册的组件就可以直接在全局中使用了。
.component()
方法也可以被链式调用:
1 | app |
2.局部注册
实际上,前面我们讲到的组件的引用就是采用的局部注册方式。对于局部注册,我们要注意的是,局部注册的组件在其后代组件中不可用,只能使用于当前组件。与局部注册相比,全局注册似乎有着天然的优势,那么我们为什么又要选择局部注册呢?前者的劣势又是什么呢?
3.全局注册的劣势
-
全局注册,但没有被使用的组件无法在生产打包时被自动移除(也叫做 tree-shaking),仍然会出现在打包后的 JS 文件中。
-
全局注册在大型项目中使项目的依赖关系变得不这么明确,在父组件中使用子组件时,不容易定位子组件的实现,和使用过多的全局变量一样,可能会影响应用长期的可维护性。
与之相对应的也就是局部注册的优势啦,这里就不再一一赘述。
四、组件传递数据(Props)
1.Props 传递静态数据
我们先创建出有着嵌套关系的Parent和Child文件,然后结合示例代码详细讲解数据传递的过程:
Parent.vue:
1 | <template> |
Child.vue:
1 | <template> |
首先我们在Parent文件的<Child/>
标签中以key = value
的形式写下我们想要传递给 Child 的数据,然后在 Child 的props
中接收数据,注意这里要以数组的形式,且key
要加双引号。之后,我们就可以在 Child 文件中正常使用该数据了。
实际上props
能够传递任何数据类型,包括数字,字符串,数组,对象等。
props 也支持同时传递多个数据:
1 | Parent文件: |
2.props 传递动态数据
1 | <template> |
要注意Props
传递动态数据时,我们需要借助v-bind
指令,这里我们采用的是其简写形式。
3.使用一个对象绑定多个 prop
如果我们想要将一个对象的所有属性都当作 props 传入,可以选择使用没有参数的v-bind
,即只使用v-bind
而非: prop-name
。示例如下:
存在对象:
1 | pika: { |
我们可以使用以下方式为其每一个属性都绑定一个prop
1 | <Child v-bind = "pika" /> |
这种实现方式实际上等价于:
1 | <Child :id = "pika.id" :title = "pika.title" /> |
4. 单向数据流
所有的props都遵循着单向绑定的原则,即只能父组件给子组件传递数据,不能反其道而行之。这样props
因父组件的更新而变化,自然地就会将新的状态向下流往子组件,而不会逆向传递,避免了子组件意外修改父组件的状态。(当然,我们其实还是有方法实现逆向传递数据的,这点我们后面会讲到)
其次,pros
是只读的,这也就意味着我们不能在子组件中去更改一个prop
,否则 Vue 会在程序台上报错。
5.Prop 校验
Vue 组件可以更加细致地声明对传入的props
的校验要求:
1 | <template> |
上面这段代码的含义就是要求接受到的test
的数据类型为Number
(注意此处的 N 要大写,否则会显示number
未定义),如果不是Number
类型,控制台就会出现警告。
同样的Prop
还能校验String
,Array
,Object
等数据类型,此外还能兼容多个数据类型,例如:
1 | type: [Number, String, Array] |
表示允许接受的数据类型为这三种的任何一种。
6.默认值
有时候我们在子组件中选择接收某个数据,但是实际上父组件并没有传递该数据,这时网页上将会直接不显示该数据,而当网页的内容多起来时我们往往很难注意到这一点。想要解决这个问题我们可以通过设置默认值的方式来填补这块空白,以便我们观察或达成格式的统一。
1 | <template> |
此时,我们取消父组件的数据传递,我们发现,原本应该出现“雷希拉姆”(原test
数据内容)的地方变成了我们设置的默认值:未接收。
需要注意的是,如果默认值是字符串或者数字,我们可以直接写在default:
的后面,但如果是数组或对象,我们必须通过函数返回值的形式来设置默认值。例如:
1 | default() { |
7.必选项
在上一小节中我们了解到当子组件没有接受到来自父组件的数据时,会选择直接不在页面中显示对应模块,我们可以通过设置必选项的方式来让某个数据必须被接收到,否则控制台就会弹出警告。具体实现方式如下:
1 | export default { |