Vue的文档可以说已经相当全了。但是在加班是常态的情况下,我们很难有整块的时间去学习,要想把整个Vue文档都看一遍可能都是一件奢侈的事情。Vue3新出的Composition API的文档说明散落在Vue文档的各个角落,虽然也有专门的篇幅讲了Composition API,但是远不够全面。Composition API其中一个重要的特性就是让逻辑关注点聚焦,但是关于它的文档却不怎么聚焦。这篇文章就是聚焦Composition API,让大家能够快速的全面学习新特性。
Options API 和 Composition API
Vue官方解释Composition API是一种API风格,和他并列的是Options API。 Options API是Vue2中定义组件的方式,一个带有若干选项(属性或者方法)的对象,常用的有这些属性和方法:data、methods、mounted,定义在对象中的属性可以在方法中通过this访问,this指向组件的实例,具体代码的例子如下:
Options API版:
<script>
export default {
// 从data方法返回的对象属性变成了响应式的状态
// 可通过this访问
data() {
return {
count: 0
}
},
// methods中是函数,会修改状态触发UI更新
// 它们可以作为模版上绑定的事件处理函数
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在相应的阶段被调用
// mounted将在组件mount后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
相信有过Vue开发经验的人对以上代码再熟悉不过了。我们用这种方式做了太多的页面了。
Composition API是Vue3中新加的书写组件逻辑的方式。它并没有像Options API那样规定什么逻辑该写在什么地方,而是提供了一系列API给我们来构建组件的逻辑。Composition API总是和<script setup>一起使用。setup属性告诉Vue在编译期要对代码进行特殊处理,以便里面的顶层定义的变量可以供模版访问。示例代码如下: Composition API版:
<script setup>
import { ref, onMounted } from 'vue'
// 响应式的状态
const count = ref(0)
// 修改状态,触发更新。该函数可以直接被模版访问
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
这两种API风格的内部机制其实是一样的。Options API实际上是基于Composition API之上实现的,它们都能覆盖我们常见的使用场景。 Options API风格围绕着组件实例的概念构建,和基于类的面向对象思维模式一致,而且Options API对初学者比较友好,屏蔽了响应式的细节,各个option像脚手架一样,指导开发人员将代码有组织地写到指定的地方,这样的代码保证了在一定程度上不会一锅烩,只要业务逻辑不是特别复杂,代码就不会太差到哪儿去。 Composition API风格围绕着函数(Composable)构建,函数返回可供模版使用的状态。它是一个更自由的结构,自由的结构带来的好处就是可以更好的组织和复用逻辑。
Composition API的核心概念
抽象、封装
其实计算机软件业一直在做一件事情:抽象、封装、再抽象、再封装,如此循环往复,一层套一层,最终形成有价值的计算机软件给最终用户使用。举几个粗略的例子来理解这段话:
- 计算机软件的最底层是硬件驱动程序,抽象封装后形成操作系统,再抽象封装形成各种应用程序。这里每一层的每个细小的方面又可以划分出多个层
- Javascript编程语言的运行环境V8由C++编写,而C++又是由汇编语言搞出来的,汇编语言又是由二进制机器码搞出来的
我们不断找出可复用的东西,把它抽象出来,然后封装他们,把他们组织起来,再在这个基础上再次找到可复用的东西,再抽象,再封装,如此反复之后,开发和维护效率变得越来越高。发展到如今,整个计算机的技术体系已经非常庞大,我们大部分人只能工作在其中一两个层而已。如果我们也能在我们工作的这一层找到可复用的点,把它抽象出来,进行封装,使之可复用,我们的生产效率就会提高,价值也就产生了。
Vue其实也是在做一样的事情。当页面需要新增删除或者修改DOM节点时,我们需要大量繁复的代码操作DOM,在各种前端框架出现之前,前端的代码复杂度往往要大过后端。JQuery对DOM访问进行了封装,风靡一时,但是远远不够,后来Angular将数据与DOM直接绑定,操作DOM变成了直接操作数据(状态),这是人机交互领域的一次变革,其实就是把大家都在重复做的那部分工作给抽象出来了,形成了框架。数据与DOM的绑定概念催生出了React、Vue,从2010年左右的那次重大变革之后一直到今天,10多年过去了,人机交互领域一直没有革命性的抽象封装出现。虽然这段时间没有大的创新,小的进步却在持续进行,React Hook就是一系列小的革新中比较重量级的一个抽象封装,而Vue的Composition API的灵感就是来自于React Hook,千万不要觉得Composition API是后来者就认为它是山寨,它去除了React Hook的糟粕,Composition API至少从表面上看是要比React Hook更好用的,对,是更好用,因为React Hook有太多的坑,Composition API也有坑但是相比React Hook则要少不少,至于最终他们两个谁更厉害,就让历史去评判吧,或许永远没有结论。Composition API到底都做了哪些抽象和封装呢?我们下面开始讲解。
之前我们用Options API写一个组件时,一般需要做下面几件事:
- data中声明组件模版用到的响应式变量,即状态
- mounted生命周期中对页面进行初始化,获取服务端的各种数据
- 事件响应函数中响应用户交互
- 声明定义watch
- 声明定义计算属性
每一个功能点都需要在这几部分分别填上逻辑。当业务逻辑不复杂时,整个代码应该还是挺清晰的,但是随着业务的发展,业务逻辑变得很复杂时,这就带来些问题:
-
各个逻辑功能点由于混在一起,容易耦合
-
单个逻辑功能点分散在各个部分,增加了开发人员识别的难度。试想一下当你接手到这样的代码,需要改一个功能时,你需要从众多代码中找出若干分散但相关的逻辑,然后对他们进行改动,是不是一件让人头疼的事情? 下面的图来自官方文档,图中的组件使用Options API,相同颜色代表相同的逻辑点:
这张图很像Windows硬盘碎片整理功能里硬盘的文件分布图,文件碎片散落在硬盘的各个不连续的扇区,导致硬盘需要频繁寻址从而导致运行速度变慢。Vue Options API的逻辑点也是像碎片一样散落在各个Options选项中,随着业务复杂度的增加,维护会变得越来越困难。
因此组件内部如果能够按照逻辑功能点来进行模块化,应该是解决复杂组件问题的一个出路。
现成的方案
现成的有三个解决方案可以将组件内部模块化:
- 继续分成若干小组件
- 将逻辑放入若干renderless component(无渲染组件)
- mixin
方案1:继续分成若干小组件可能是可行的办法之一,但是组件UI有可能没有必要或者没法再分割了,即使组件UI分割了,但是单个组件的逻辑依然复杂
方案2:renderless component(无渲染组件),顾名思义,就是这个组件没有UI,只有一些逻辑。这可以解决方案1的UI无法再分割的问题。无渲染组件处理完逻辑之后,将需要返回的数据通过slot的形式返回给父组件。 我们以短信验证码的获取按钮为例: ```javascript // 无渲染组件:CountDown
}
// 父组件 ``` 上面的代码在做一个短信验证码按钮的功能,点击按钮手机会收到短信验证码,并且60秒内无法重复点击按钮获取验证码,倒计时会显示出来,提醒用户如果想重复获取验证码,则需要等待。 从上面的代码可以看到,CountDown组件中并没有UI,在处理数据之后,通过slot的机制返回给父组件的UI使用,父组件可以在slot返回的数据基础上对UI进行深度定制,这很适合UI简单多样,但是逻辑相对复杂的功能。 可以看到Renderless component确实可以做到对组件逻辑的模块化,我们可以把一个逻辑复杂的组件分解成若干的Renderless component。不过Vue官方并不推荐这种形式,一方面这样做会导致组件实例增多从而有可能影响性能,另一方面slot设计的初衷确实不是用来做这个的,这多少会让代码看起来有些奇怪,像是一种hack手法。
方案3:mixin已经是广为人知了,它的缺点也一样广为人知:
- 属性来源不清
- 命名冲突
- 通过共享属性来进行跨mixin的通讯,导致耦合
mixin一般会在框架层提供一些公共功能,但是它一般不是日常开发的常态,平时业务开发时,mixin会写得比较少。
Composition API方案
上面三种方案都不是很理想。现在来看看Composition API是怎么让组件逻辑模块化的。 继续以上面的短信验证码按钮为例:
<script setup>
import {ref, onMounted, onUnmounted} from 'vue';
const initialCountDownTime = 60;
const countDownTime = ref(initialCountDownTime);
const disabled = ref(false);
let intervalId;
onUnmounted(() => clearInterval(intervalId));
function sendVerificationCodeMessage() {
if(disabled) {
return;
}
// 发送请求
// 开始倒计时
intervalId = setInterval(() => {
countDownTime--;
if(countDownTime === 0) {
disabled = false;
countDownTime = initialCountDownTime;
clearInterval(intervalId);
}
}, 1000);
disabled = true;
}
</script>
<template>
<div>
<button @click="sendVerificationCodeMessage()" disabled="{{disabled}}">获取验证码</button>
{{countDownTime}}
</div>
</template>
和上面的renderless component对比,少了一些模版代码,可是把一堆代码放到一起不是更乱了吗?哪里有模块化? 我们再演进一下上面的代码:
// 组件
<script setup>
import {useVerificationCode} from './verificationCode';
// 顶层变量可以被模版访问
const {countDownTime, disabled, sendVerificationCodeMessage} = useVerificationCode();
</script>
<template>
<div>
<button @click="sendVerificationCodeMessage()" disabled="{{disabled}}">获取验证码</button>
{{countDownTime}}
</div>
</template>
// ./verificationCode文件
import {ref, onMounted, onUnmounted} from 'vue';
export function useVerificationCode() {
const initialCountDownTime = 60;
const countDownTime = ref(initialCountDownTime);
const disabled = ref(false);
let intervalId;
onUnmounted(() => clearInterval(intervalId));
function sendVerificationCodeMessage() {
if(disabled) {
return;
}
// 发送请求
// 开始倒计时
intervalId = setInterval(() => {
countDownTime--;
if(countDownTime === 0) {
disabled = false;
countDownTime = initialCountDownTime;
clearInterval(intervalId);
}
}, 1000);
disabled = true;
}
return {
countDownTime,
disabled,
sendVerificationCodeMessage
}
}
可以看到所有关于验证码的逻辑都被放到了useVerificationCode函数中。
组件的逻辑终于模块化了,以后有其他人来修改验证码相关的功能时,他基本可以一眼就看到useVerificationCode,如果只是为了修改验证码的逻辑,直接进useVerificationCode,心无旁骛的修改就可以了。
这里的useVerificationCode被叫做Composable,Vue官方对Composable的定义:Composable是一个函数,该函数利用Composition API封装并重用有状态的逻辑,所谓有状态就是这些数据要被模版访问。
那到底什么是Composition API呢?这里用到的ref、onMounted、onUnmounted就是若干Composition API中的几个。具体都有哪些Composition API可以看下图:
该图来自于英文官方文档,比较可惜的是Vue的中文官方文档要比英文文档落后很多,中文文档就没有这样归类。
从上面的例子可以看出Composable其实就是一个函数,没有特别的地方,通过调用Composition API创建响应式的变量(在Options API中,响应式变量需要提前在data中声明),把响应式变量作为函数返回值返回,Vue的编译器会把<script setup>中的顶层声明的变量编译成模版可访问的变量,因此Composable函数只需要把需要被模版访问的变量返回,然后在<script setup>的顶层声明变量接住Composable的返回值就可以了。
// 组件
<script setup>
import {useVerificationCode} from './verificationCode';
// 顶层变量可以被模版访问
const {countDownTime, disabled, sendVerificationCodeMessage} = useVerificationCode();
</script>
<template>
<div>
<button @click="sendVerificationCodeMessage()" disabled="{{disabled}}">获取验证码</button>
{{countDownTime}}
</div>
</template>
示例代码中,useVerificationCode返回值被解构到变量中,然后模版就可以直接访问了。
因此有了Composition API和<script setup>,我们便可以把状态化的逻辑封装起来,可能大家会想:“我们平时写的函数不也是在封装吗?把函数的返回值赋值给data中声明的变量就可以了,它们有什么区别?”,最大的区别就是Composable可以利用Composition API把生命周期中相关的逻辑、相关的计算属性、相关的watch都放到Composable中,形成逻辑的聚焦,而我们之前写的函数并不能直接去注册组件的生命周期。 之前的Vue做到了组件级别的抽象封装重用,现在终于更进一步,组件中的逻辑也可以抽象出来封装重用了。
下面的图来自官方,图中将Options API和Composition API进行了对比:
可以看到Composition API对逻辑碎片进行了整合,组件逻辑很好的模块化了。
至此大家应该已经了解Composition API是做什么的了,理念还不错,也很简单,实际动手也简单吗?我们的旅程才刚刚开始,下面我们来讲一下具体用法。
Composition API的用法
我们用一张图来描述一下Composition API风格的组件,它的代码结构是什么样子的:
从图中可以看出代码的业务逻辑部分由之前的对象导出变成了setup,setup由一系列composable组成,上面说过Composable其实并没有什么特别,就是普通函数,Vue编译器不会对它进行任何特殊处理,它只是一个概念。从使用者角度看,Vue3真正带给我们的是Composition API和<script setup>。如果我们不按照图示去组织代码,而是Composable中的逻辑都平铺在setup里,也不是不可以,如果业务逻辑简单则不会有什么问题,但是如果业务逻辑复杂的话,最好还是分成多个Composable。
在你准备切换到Vue3,并准备用VSCode写代码之前,强烈建议先把VSCode的Vue插件Volar装上,同时卸载Vetur,Volar是新一代的插件工具,支持Vue3也支持Vue2,而Vetur只支持Vue2。
<script setup>
<script setup>是单文件组件中开启Composition API风格的开关。它只能使用于单文件组件。这里有不少需要我们了解的东西,咱们一个一个说。
-
执行时机 每当组件实例被创建时,<script setup>中的代码都会被执行,执行时机是在mounted之前。
-
模版可访问顶层变量 <script setup>内部顶层的变量、函数声明和导入都可以直接被模版使用,示例代码:
<script setup>
import { capitalize } from './helpers'
// 变量
const msg = 'Hello!'
// 函数声明
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
<div>{{ capitalize('hello') }}</div>
</template>
-
如何创建响应式变量 在Options API中,在data中声明变量会自动变成响应式变量。在Composition API中,需要我们自己调用Reactive API来包装数据,如果不进行包装,当变量被重新赋值时,模版不会响应。 这里先介绍两个常用的响应式API:
-
ref 接受一个参数,可以是任何类型。 返回一个具有value属性的对象。该对象是响应式的。
先看如下代码:
<script setup> let commonVariable = 0; setInterval(() => { commonVariable++; }, 1000); </script> <template> <div> <!--不会变化--> <div>{{commonVariable}}</div> </div> </template>可以看到,没有经过ref包装的变量不具有响应变动的能力。 ref既可以包装简单内置类型变量(number、string等),还可以包装对象。
继续看下面的代码:
<script setup> const reactiveVariable = ref(0); setInterval(() => { reactiveVariable.value++; }, 1000); </script> <template> <div> <!--每隔1秒增加1--> <div>{{reactiveVariable}}</div> </div> </template>经过ref包装后,模版终于可以自动更新了。我们在<script>标签中访问ref对象时,需要使用.value,而在模版中则不需要,模版中可以直接使用ref对象,Vue会自动找到ref对象value属性的值。
继续看下面的代码:
<script setup> const reactiveVariable = ref(ref(ref(0))); setInterval(() => { reactiveVariable.value++; }, 1000); </script> <template> <div> <!--每隔1秒增加1--> <div>{{reactiveVariable}}</div> </div> </template>从上面的代码可以看到,ref经过多次重复调用并不会进行叠加,经过一次ref包装和多次是一样的。
继续看下面的代码:
<script setup> const reactiveObject = ref({a: {x: 1}}); setInterval(() => { reactiveObject.value.a.x++; }, 1000); </script> <template> <div> <!--每隔1秒增加1--> <div>{{reactiveObject.a.x}}</div> </div> </template>当ref的参数是普通对象时,ref会调用reactive方法,将对象深度转换成响应式,对象中任何层级的属性都将是响应式的。如果不想进行深度转换,可以使用shallowRef()。不过要注意的是,转换并不会修改原对象,而给原对象创建了一个代理。
继续看下面的代码:
<script setup>s const map = new Map(); map.set("a", 1); const reactiveMap = ref(map); setInterval(() => { reactiveMap.value.set("a", reactiveMap.value.get("a") + 1); }, 1000); </script> <template> <div> <!--每隔1秒增加1--> <div>reactiveMap: {{ reactiveMap.get("a") }}</div> </div> </template>可以看到Map经过ref包装后也变成了响应式。
现在咱们来看一下这个例子:
<script setup lang="ts"> import { ref } from "vue"; // 普通简单类型变量 let commonVariable = 9; setInterval(() => { commonVariable++; }); // 响应式变量 const reactiveVariable = ref(0); setInterval(() => { reactiveVariable.value++; }, 1000); // 普通对象 const o = { x: 0 }; setInterval(() => { o.x++; }, 1000); </script> <template> <div> <!--不会变化--> <div>{{ commonVariable }}</div> <!--每隔1秒增加1--> <div>{{ reactiveVariable }}</div> <!--每隔1秒增加1--> <div>{{ o.x }}</div> </div> </template>o对象并没有进行响应式包装,但是似乎模版也会自动更新,其实不然,o.x被更新是因为reactiveVariable被更新触发了重新渲染所致。我们在开发时可能很容易忘记进行响应式包装,而bug并不一定会立即显现,所以需要格外注意。
-
reactive reactive接受一个对象参数,不接受普通简单类型变量。 示例代码如下:
<script setup lang="ts"> import { reactive } from "vue"; const reactiveObject = reactive({ x: 0 }); setInterval(() => { reactiveObject.x++; }, 1000); </script> <template> <div> <!--每隔1秒增加1--> <div>{{ reactiveObject.x }}</div> </div> </template>reactive基于proxy机制实现,因此reactive返回的对象和初始对象已经不是一个对象。
当reactive遇到ref时,某些情况下ref会被自动解包,请看如下示例:
<script setup lang="ts"> import { reactive, ref } from "vue"; const count = ref(0); const reactiveObject = reactive({ count }); // true console.log(reactiveObject.count === count.value); const anotherRef= ref('a'); reactiveObject.another = anotherRef; // true console.log(reactiveObject.another === anotherRef.value); </script> <template> <div> </div> </template>可以看到,当ref变量变成reactive对象的属性成员时,即使是将ref赋值给一个新属性,ref都会被自动解包,因为这时确实没有ref存在的意义了。
但是当对一个ref数组或者map进行reactive响应式包装时,ref不会被自动解包,示例代码如下:
<script setup lang="ts"> import { reactive, ref } from "vue"; const reactiveArray = reactive([ ref('a'), ref('b'), ref('c') ]); // 需要.value才能访问 console.log(reactiveArray[0].value); const reactiveMap = reactive(new Map([['count', ref(0)]])); // 需要.value才能访问 console.log(reactiveMap.get('count').value); </script> <template> <div> </div> </template>
-
-
<script setup>中如何使用组件 在Options API中,如果要引入外部组件,需要在components属性中注册一下该组件,才能被使用。Composition API中不需要了,直接import,然后在模版中使用就可以了。 示例代码如下:
组件文件 ./FooBar.vue
<script setup> import MyComponent from './MyComponent.vue'; import {FooBar as FooBarAnotherName} from './components'; import * as Form from './form-components' </script> <template> <MyComponent /> <FooBarAnotherName /> <Form.Input> <Form.Label>label</Form.Label> </Form.Input> </template>上面例子有几个知识点:
- 组件在模版中也可以用连字符(kebab-case)的形式:,但是Vue官方强烈建议使用大驼峰的形式,这样就和变量名称一致了,而且也可以和原生的自定义元素区分开
- 在模版中组件可以用自己文件名称引用自己,例如本例中文件名称是FooBar,在该组件模版中,可以用引用自己。这在某些场景下还是有些用处的。如果组件名称和该组件引入的其他组件名称冲突,则可以利用import的as语法,给组件起一个别名
- 例子中的Form是一个组件集合,要想访问某个具体组件,可以使用<Form.*>的形式。这就相当于简单的命名空间
-
自定义指令 全局自定义指令相比之前的使用方式没有变化,局部自定义指令使用<script setup>后不需要再显式的声明了,直接用对象的形式定义顶层变量便可,指令变量必须遵循vName的命名规则,即首字母必须是v,采用小驼峰命名法,示例如下:
<script setup> const vMyDirective = { beforeMount: (el) => { // 对元素进行一些操作 } } </script> <template> <h1 v-my-directive>这是标题</h1> </template>如果是从其他地方引入的指令,且暴露出的指令名称不符合规则,则可以在import中直接重新命名:
<script setup> import { myDirective as vMyDirective } from './MyDirective.js' </script> -
属性和自定义事件 为了对类型推断进行支持,defineProps和defineEmits两个函数分别用来声明属性和自定义事件。 有如下知识点:
- 它们在<script setup>中可以直接被使用,不需要引入,这一点还是挺容易让人出错的,因为Reactive的API都需要从vue包中引入,而defineProps和defineEmits却不用,这种不一致会让人觉得找不到规律,其实这两个这两个API都是编译器宏,它们只能在<script setup>中使用,其实规律也不是没有,可以看到两个API都是以define开头。
- defineProps和defineEmits参数和Options API中props属性的值形式相同。
- Vue通过defineProps和defineEmits中的参数来进行类型推断。
- defineProps和defineEmits的参数中不能使用<script setup>中定义的变量,因为它们被提升到了setup(setup其实是个函数)的外层的模块级别,但是可以使用import导入的变量,因为import也同样被提升到了模块级别。
举例如下:
<script setup> const localV = 'text'; // 编译错误 defineProps([localV]); </script><script setup> import x from './x'; defineProps([x]); </script>
-
组件向外部暴露数据 <script setup>内部定义的变量数据默认是不向外暴露的。模版中的ref或者$parent链不会暴露任何<script setup>中的数据。 如果要想显式的暴露数据,可以使用defineExpose,可以看到该API使用define开头,说明它是一个编译期宏,示例如下:
<script setup> import { ref } from 'vue'; const a = 1; const b = ref(2); defineExpose({ a, b }); </script>
该示例中,父组件中通过模版的refs获取示例组件中暴露出的{a: 1, b: 2}。这里要注意的是ref会自动被解包。
- 获取槽对象、获取attr 在模版中可以直接访问attrs。<script setup>中使用useSlots和useAttrs来访问。这两个函数都是运行时函数,需要从vue导出。示例如下:
<script setup>
import {useSlots, useAttrs} from 'vue';
const slots = useSlots();
const attrs = useAttrs();
</script>
-
<script setup>和<script>一起使用 <script setup>可以和<script>一起使用,以下几种情况,我们需要这么做:
-
声明一些无法在<script setup>中声明的属性,例如inheritAttrs或者来自于插件的自定义的属性
-
声明命名式的导出
-
由于<script setup>中的代码在每次创建组件实例时都会被重复执行,当我们想执行一些只想执行一次的代码时,还是需要<script> 示例代码如下:
-
-
顶层await <script setup>中可以使用顶层的await,编译完的代码实际是:async setup(),示例代码如下:
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
await并不能阻挡住组件的创建,只是setup内部await之后的代码会被延迟执行。一旦使用了await,setup便成了async setup,最近版本的vue要求这种情况下必须和Suspense结合使用,Suspense是Vue自带的组件,不需要引入,可以在Suspense中定义async setup未执行完成时的UI效果。大部分情况下,setup中会有一个远程数据获取逻辑,在数据未返回给客户端时,Suspense会显示开发人员提供的中间态UI。
<script setup >到此就讲解结束了。看到这里你对Composition API还有兴趣吗?Vue之所以受欢迎是因为它学习成本低,较容易上手,即使新手也能驾驭起来,而现在Composition API的复杂程度可能会和简单的Vue这个概念有些背离,Vue的使用正在变得越来越复杂。不过好在Vue是一个渐进式的框架,如果你觉得无法驾驭Composition API,可以继续选择使用Options API,它依然是那么的简单明了。
Composition API风格是一套成体系的新概念,肯定不只是一个<script setup>,它还提供了一系列API,下面咱们继续,来研究一下这些API。
Composition API风格的API
Composition API广义上讲是一个风格和一个概念的总称,狭义上也可以理解为一系列具体的API。由于这部分篇幅会比较长,因此我们另到一片文章讲解,待该文章出炉时,会把链接放到这里。