在这篇文章中,我将讲讲 Vue 的 Composition API 为什么比之前的 Options API 要好,以及它是如何工作的。

Options API 有什么问题

首先,这里不是要大家放弃 Options API,如果你觉得 Options API 还不错,已经习惯了,就继续使用它。但我希望你能明白为什么 Composition API 是一种更好的选择。

当我刚开始接触 Vue 时,我喜欢它用匿名对象来构建组件的方式。简单易学,也很自然。但随着使用的时间越长,就会遇到一些很奇怪的问题。比如下面这段代码:

export default {
mounted: async function () {
this.load().then(function () {
this.name = 'Foo'
})
},
}

这里的第二个this的问题我想很多人都遇到过。在不调试的情况下,很难知道两个this为什么不一样。我们只知道要解决这个问题,反正得用箭头函数,例如:

mounted: async function () {
this.load().then(() => {
this.name = 'Foo';
});
}

此外,从data函数返回成员后,Vue 会包装它们(通常使用Proxy),以便能够监听对象的修改。但如果你尝试直接替换对象(比如数组),就可能监听不到对象修改了。比如:

export default {
data: () => {
return {
items: [],
}
},
mounted: async function () {
this.load().then((result) => {
this.items = result.data
})
},
methods: {
doSomeChange() {
this.items[0].foo = 'foo'
},
},
}

这是 Vue 最常见的问题之一。尽管可以很简单地解决这些问题,但毕竟给学习 Vue 工作原理造成了障碍。

最后,Options API 很难实现组件间的功能共享,为了解决这个问题,Vue 使用了mixins的概念。例如,你可以创建一个 mixin 来扩展组件,如下所示:

export default {
data: function () {
return { items: [] };
}, methods: {
...
}
}

mixin 看起来很像 Options API。它只是对对象进行合并,以允许你添加到特定组件上:

import dataService from "./dataServiceMixin";
export default {
mixins: [dataService],
...

mixin 最大问题是名称冲突。

鉴于 Options API 这些不足,Composition API 诞生了。

了解 Composition API

现在来介绍一下 Composition API。Vue 2 也可以使用 Composition API。首先,你需要引入 Composition API 库:

> npm i -s @vue/composition-api

要启用 Composition API,你只需在main.js/ts中注册一下:

import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)

让我们来写一个 Composition API 组件。首先,组件仍然是匿名对象:

export default {
setup() {
return {}
},
}

Composition API 的关键就是这个setup方法,所有所需的数据都从setup中返回。这非常类似于 Options API 的data方法:

export default {
setup() {
const name = 'Foo'
return { name }
},
}

与其只在return中赋值,不如在setup里面用一个局部变量创建。为什么要这样做呢?因为 JavaScript 的闭包。让我们扩展一下,再写一个函数。

export default {
setup() {
const name = 'Foo' function save() {
alert(`Name: ${name}`)
}
return { name, save }
},
}

该函数可以访问name,因为它们在同一个作用域内。这就是 Composition API 的神奇之处。没有魔术对象,只有 JavaScript。这个模式可以支持更复杂的场景,更灵活,这一切的基础只是闭包,仅此而已。

在这个例子中name是永远不会改变的。为了让 Vue 能够处理绑定,它需要知道name的变化。在这个例子中,如果你在save()中修改了代码来改变名称,它不会反映在 UI 中:

export default {
setup() {
const name = 'Foo' function save() {
name = name + ' saved'
alert(`Name: ${name}`)
}
return { name, save }
},
}

响应式

要使对象具有响应式,Vue 提供了两种方法:refreactive包装器。

你可以将name包装成一个ref对象:

import { ref } from '@vue/composition-api'
export default {
setup() {
const name = ref('Foo') function save() {
name.value = name.value + ' saved'
alert(`Name: ${name.value}`)
}
return { name, save }
},
}

这是一个简单的响应式,ref 对象的 value 属性才是实际的值。

这与简单对象可以很好地配合,但是具有自身状态的对象(例如类或数组),对值的更改是不够的。你真正需要的是使用一个 proxy 函数以便监听该对象内部发生的任何更改。

import { ref, reactive } from '@vue/composition-api'
export default {
setup() {
const name = ref('Foo')
const items = reactive([]) function save() {
// Change Array
items.splice(0, items.length)
name.value = name.value + ' saved'
alert(`Name: ${name.value}`)
}
return { name, save, items }
},
}

在此示例中,使用了splice更改数组,Vue 需要了解此更改。你可以通过使用另一个称为reactive的包装器包装该对象(在本例中为数组)来实现。

Composition API 中的 reactive 包装器与 Vue2 的 Vue.observable 包装器相同。

ref 和 react 之间的区别在于,reactive 用 proxy 包装对象,而 ref 是简单的值包装器。

因此,你不需要将返回的 reactive 对象再包装,不需要像 ref 对象一样通过 value 属性访问其值。

一旦有了 ref 和 reactive 对象,就可以观察更改。有两种方法可以做到这一点:watchwatchEffect。watch 允许你监听单个对象的更改:

 setup() {
const name = ref("Shawn");
watch(() => name, (before, after) => {
console.log("name changes");
});
return { name };
},

watch函数有两个参数:将数据返回到watch的回调以及发生更改时要调用的回调。它提供两个参数分别是修改前的值before和修改后的值after

或者,你可以使用watchEffect 监听 reactive 对象的任何更改。它只需要一个回调:

watchEffect(() => {
console.log('Ch..ch...ch...changes.')
})

组合组件

有时候你希望把一个已有组件的功能组合到另一个组件,以便代码重用和数据交互。当组合组件时,Composition API 只是使用作用域和闭包解决问题。例如,你可以创建一个简单的 service 服务:

import axios from 'axios'

export let items = []

export async function load() {
let result = await axios.get(API_URL)
items.splice(0, items.length, ...result.data)
} export function removeItem(item) {
let index = items.indexOf(item)
if (index > -1) {
items.splice(index, 1)
}
}

然后你可以按需将需要的功能(对象或函数) import 到你的组件中:

import { ref, reactive, onMounted } from '@vue/composition-api'

import { load, items, removeItem } from './dataService'

export default {
setup() {
const name = ref('Foo') function save() {
alert(`Name: ${this.name}`)
} return {
load, // from import
removeItem, // from import
name,
save,
items, // from import
}
},
}

你可以使用 Composition API 更明显式地组合你的组件。接下来我们来谈谈组件的使用。

使用 Props

在 Composition API 中定义 Props 的方式与 Options API 相同:

export default {
name: 'WaitCursor',
props: {
message: {
type: String,
required: false,
},
isBusy: {
type: Boolean,
required: false,
},
},
setup() {},
}

你可以通过向setup添加可选参数来访问 Props:

setup(props) {
watch(() => props.isBusy, (b,a) => {
console.log(`isBusy Changed`);
});
}

此外,你可以添加第二个参数,可以访问emitslotsattrs对象,和 Options API 上 this 指针上的对象对应:

setup(props, context) {
watch(() => props.isBusy, (b,a) => context.emit("busy-changed", a));
}

使用组件

在 Vue 中有两种使用组件的方法。你可以全局注册组件(用作通用组件),以便可以在任何地方使用它们:

import WaitCursor from './components/WaitCursor'
Vue.component('wait-cursor', WaitCursor)

通常,你可以将组件添加到正在使用的特定组件中。在 Composition API 中,与 Options API 相同:

import WaitCursor from './components/waitCursor'
import store from './store'
import { computed } from '@vue/composition-api' export default {
components: {
WaitCursor, // Use Component
},
setup() {
const isBusy = computed(() => store.state.isBusy)
return { isBusy }
},
}

在 Composition API 中指定了组件后,就可以使用它:

<div>
<WaitCursor message="Loading..." :isBusy="isBusy"></WaitCursor>
<div class="row">
<div class="col">
<App></App>
</div>
</div>
</div>

使用组件的方式与 Options API 中的方式没有什么不同。

在 Vue 3 中使用 Composition API

如果你使用的是 Vue 3,则无需单独引用 Composition API。Vue 3 已经集成好了。该@vue/composition-api库仅用于 Vue 2 项目。Vue 3 的真正变化是,当你需要导入 Composition API 时,只需要直接从 Vue 获取它们:

import {
ref,
reactive,
onMounted,
watch,
watchEffect,
//from "@vue/composition-api";
} from 'vue'

其他一切都一样。只需从“ vue”导入即可。在 Vue 3 中,使用 Composition API 只是简单一些,因为它是默认行为。

Vue 3 的主要目标之一是改善 TypeScript 体验,所有内容都有类型库。但是要增加类型安全性,你必须进行一些小的更改才能使用 TypeScript。在创建组件时,需使用defineComponent

import { defineComponent, reactive, onMounted, ref } from "vue";
import Customer from "@/models/Customer"; export default defineComponent({
name: "App",
setup() {
const customers = reactive([] as Array<Customer>);
const customer = ref(new Customer());
const isBusy = ref(false);
const message = ref("");
return {
customers, customer, isBusy, message
}
}
});

在这些示例中,变量被推断为类型实例(例如,Customers对象是Reactive<Customer[]>类型的实例)。此外,使用强类型可以降低传递错误数据的机会。当然,如果你使用 TypeScript(尤其是在 Visual Studio 或 VS Code 中),则会有非常友好的智能提示。

了解 Vue 的 Compsition API的更多相关文章

  1. Vue学习笔记-API调试工具--->国产apipost按装(比postman好按装好用)

    一  使用环境: windows 7 64位操作系统 二   Vue学习笔记-API调试工具--->apipost按装 1.下载: https://www.apipost.cn/ (比postm ...

  2. vue中部分api解释 ($nextTick)

    1:this.$nextTick(function(){ }) 传如的参数是一个函数 这个API主要是获取dom元素 为什么需要这个api,在vue框架开发中,更新dom是一个异步操作,如果更新完do ...

  3. vue + 百度地图api

    主要分解为如下步骤: (1)在html文件中引入百度地图, <script type="text/javascript" src="http://api.map.b ...

  4. vue+ElementUI+高德API地址模糊搜索(自定义UI组件)

    开发环境描述: Vue.js ElementUI 高德地图API 需求描述: 在新增地址信息的时候,我们需要根据input输入的关键字调用地图的输入提示API,获取到返回的数据,并根据这些数据生成下拉 ...

  5. Vue.js(25)之 vue全局配置api介绍

    本文介绍的全局api并不在Vue的构造函数内,而是在Vue构造器外面提供这些方法,让我们扩展新功能. 1. vue.extend(options) 参考:https://www.w3cplus.com ...

  6. 蒲公英 &#183; JELLY技术周刊 Vol.21 -- 技术周刊 &#183; React Hooks vs Vue 3 + Composition API

    蒲公英 · JELLY技术周刊 Vol.21 选 React 还是 Vue,每个人心中都会有自己的答案,有很多理由去 pick 心水的框架,但是当我们扪心自问,我们真的可以公正的来评价这两者之间的差异 ...

  7. 一起学Vue:访问API(axios)

    目标 使用Vue+ElementUI+axios构建一个非常简单CRUD应用程序,以便您更好地了解它的工作方式. 什么是 axios? Axios 是一个基于 promise 的 HTTP 库,可以用 ...

  8. vue & $router & History API

    vue & $router gotoTemplateManage(e) { e.preventDefault(); this.$router.push({ path: `/operate-to ...

  9. vue.js中英文api

    全局配置 Vue.config is an object containing Vue's global configurations. You can modify its properties l ...

随机推荐

  1. Virtual Reality In Action

    Virtual Reality In Action VR WebXR immersive 沉浸式 https://github.com/immersive-web/webxr https://imme ...

  2. React Native 真机调试(iOS / Android)

    React Native 真机调试(iOS / Android) https://reactnative.dev/docs/running-on-device https://developer.ap ...

  3. DoH & DNS over HTTPS

    DoH & DNS over HTTPS DNS over HTTPS(DoH)服务 http://mozilla.com.cn/thread-422231-1-1.html https:// ...

  4. py django

    创建项目 $ django-admin startproject server 运行项目 $ cd server $ python manage.py runserver 创建一个模块 $ pytho ...

  5. 基本的DOS命令

    基本的DOS命令 打开CMD的方法 菜单+系统+命令提示符 WIN+R后输入CMD 桌面或文件夹任意位置按住SHIFT后鼠标右键 常见的DOS命令 #盘符切换 直接输入要切换的盘符名称 如输入&quo ...

  6. node初体验(一)

    1.node.js是一个构建在chrome V8引擎上的javascript运行环境 2.node.js特点:单线程.事件驱动.非阻塞IO模型.轻量 3.node.js是单线程的(多个请求都是一个线程 ...

  7. DisplayFormat属性

    DataFormatString="{0:格式字符串}" 在DataFormatString 中的 {0} 表示数据本身,而在冒号后面的格式字符串代表所们希望数据显示的格式: 数字 ...

  8. CentOS7安装Kibana7.9.2

    1:下载 wget https://artifacts.elastic.co/downloads/kibana/kibana-7.9.2-linux-x86_64.tar.gz 点击进入官网 安装手册 ...

  9. 并发\并行,同步\异步,阻塞\非阻塞,IO多路复用解释

    并发.并行 并发:是指一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行.由于CPU的运行速度极快,可以在多个程序之间切换,这样造成一个假象就是多个程序同时在运行.并 ...

  10. GMS的概述

    1 GMS GMS全称为GoogleMobile Service,即谷歌移动服务. GMS是Google所提供的一系列移动服务,包括开发用的一系列服务和用户所用的Google Apps. Maps与L ...