在这篇文章中,我将讲讲 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. 使用 Promise 实现请求自动重试

    使用 Promise 实现请求自动重试 "use strict"; /** * * @author xgqfrms * @license MIT * @copyright xgqf ...

  2. TensorFlow & Machine Learning

    TensorFlow & Machine Learning TensorFlow 实战 传统方式 规则 + 数据集 => 答案 无监督学习 机器学习 神经元网络 答案 + 数据集 =&g ...

  3. The Weekly Web Dev Challenge: Emoji Ratings

    The Weekly Web Dev Challenge: Emoji Ratings /* DESCRIPTION: You job is to enable users to give a rat ...

  4. disable html input & pointer-events

    disable html input & pointer-events css https://developer.mozilla.org/en-US/docs/Web/CSS/pointer ...

  5. Build your own React

    Build your own React https://pomb.us/build-your-own-react/ https://github.com/pomber/didact demo htt ...

  6. 全球首发—鸿蒙开源平台OpenGL

    目录: 前言 背景 鸿蒙OpenGL-ISRC的结构 OpenGL-ISRC和鸿蒙SDK OpenGL的区别 OpenGL-ISRC的使用 前言 基于安卓平台的OpenGL(androidxref.c ...

  7. Markdown简单语法的使用

    Markdown简单语法的使用 目录 Markdown简单语法的使用 前言 标题的设置 字体的设置 1.字体加粗 2.斜体 3.字体加粗斜体 3.删除线 引用 列表的使用 插入图片 分割线 代码的书写 ...

  8. JVM 字节码之 int 入栈指令

    本文转载自JVM 字节码之 int 入栈指令(iconst.bipush.sipush.ldc) 前言 本文介绍 int 入栈指令 iconst.bipush.sipubh.Idc. 当 int 取值 ...

  9. 推荐一款好用的免费远程控制软件——ToDesk

    创作立场声明:我在本文中评测的软件为自用,感觉不错并且全免费,第一时间发出来和大家分享,欢迎理性观点交流碰撞. 疫情刚开始的时候,待在家里不能上班,但是还是有很多工作需要在线完成,常常需要跑回办公室拿 ...

  10. springboot对数据库密码加密

    第一步:maven引jar包 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifa ...