在这篇文章中,我将讲讲 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. SQL All In One

    SQL All In One Structured Query Language SQL is an ANSI (American National Standards Institute) stan ...

  2. 你所不知道的 cnblogs

    你所不知道的 cnblogs cnblogs 学院 cnblogs 班级 refs https://academy.cnblogs.com/ https://academy.cnblogs.com/b ...

  3. dart 匹配基本数组

    List<dynamic> evalList(String text) { var r = []; var i = 0; var isList = false; void parseSta ...

  4. Union international INC:VR线下娱乐市场巨大

    联合国际公司认为众多企业追着VR的风口不断加码,导致VR在经历了一个爆炸式的发展,如今部分VR公司开始觉得日子不好过了,一个重要表现就是现金流紧张.VR如何能够普及,何时能够及早变现,成为业内关注的焦 ...

  5. 2021-2-25:对于 Java MMAP,如何查看文件映射脏页,如何统计MMAP的内存大小?

    我们写一个测试程序: public static void main(String[] args) throws Exception { RandomAccessFile randomAccessFi ...

  6. tomcat部署项目问题

    tomcat部署项目的时候,报内存溢出,一种解决方案是直接添加内存,网上都有教程,如下: Windows下,在文件/bin/catalina.bat,Linux下,在文件/bin/catalina.s ...

  7. 微信小程序(六)-项目实例(原生框架 MINA基配搭建)==01-头搜索框tabbar

    项目实例(原生框架 MINA) 1.新建小程序项目 1.用自已的小程序APPID 2.清除整理项目中初建默认无关的代码 1.app.json 中删除logs,同时删除pages下的losgs文件夹 2 ...

  8. java算法题

    1.下面输出结果是什么? public class Test { public static void main(String[] args) { Person person=new Person(& ...

  9. SpringBoot以war包形式部署到外部Tomcat

    SpringBoot 项目打包时能打成 .jar 与 .war包文件,.jar使用 java -jar xx.jar 就可以启动,而 .war 可以部署到tomcat的 webapps 中,随tomc ...

  10. 小白养成记——Java比较器Comparable和Comparator

    一.使用情景 1.  调用Arrays.sort()方法或Collections.sort()方法对自定义类的对象排序 以Arrays.sort()为例.假定有如下自定义的Person类 1 publ ...