了解 Vue 的 Compsition API
在这篇文章中,我将讲讲 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 提供了两种方法:ref
和reactive
包装器。
你可以将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 对象,就可以观察更改。有两种方法可以做到这一点:watch
和watchEffect
。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`);
});
}
此外,你可以添加第二个参数,可以访问emit
,slots
和attrs
对象,和 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的更多相关文章
- Vue学习笔记-API调试工具--->国产apipost按装(比postman好按装好用)
一 使用环境: windows 7 64位操作系统 二 Vue学习笔记-API调试工具--->apipost按装 1.下载: https://www.apipost.cn/ (比postm ...
- vue中部分api解释 ($nextTick)
1:this.$nextTick(function(){ }) 传如的参数是一个函数 这个API主要是获取dom元素 为什么需要这个api,在vue框架开发中,更新dom是一个异步操作,如果更新完do ...
- vue + 百度地图api
主要分解为如下步骤: (1)在html文件中引入百度地图, <script type="text/javascript" src="http://api.map.b ...
- vue+ElementUI+高德API地址模糊搜索(自定义UI组件)
开发环境描述: Vue.js ElementUI 高德地图API 需求描述: 在新增地址信息的时候,我们需要根据input输入的关键字调用地图的输入提示API,获取到返回的数据,并根据这些数据生成下拉 ...
- Vue.js(25)之 vue全局配置api介绍
本文介绍的全局api并不在Vue的构造函数内,而是在Vue构造器外面提供这些方法,让我们扩展新功能. 1. vue.extend(options) 参考:https://www.w3cplus.com ...
- 蒲公英 · JELLY技术周刊 Vol.21 -- 技术周刊 · React Hooks vs Vue 3 + Composition API
蒲公英 · JELLY技术周刊 Vol.21 选 React 还是 Vue,每个人心中都会有自己的答案,有很多理由去 pick 心水的框架,但是当我们扪心自问,我们真的可以公正的来评价这两者之间的差异 ...
- 一起学Vue:访问API(axios)
目标 使用Vue+ElementUI+axios构建一个非常简单CRUD应用程序,以便您更好地了解它的工作方式. 什么是 axios? Axios 是一个基于 promise 的 HTTP 库,可以用 ...
- vue & $router & History API
vue & $router gotoTemplateManage(e) { e.preventDefault(); this.$router.push({ path: `/operate-to ...
- vue.js中英文api
全局配置 Vue.config is an object containing Vue's global configurations. You can modify its properties l ...
随机推荐
- Virtual Reality In Action
Virtual Reality In Action VR WebXR immersive 沉浸式 https://github.com/immersive-web/webxr https://imme ...
- React Native 真机调试(iOS / Android)
React Native 真机调试(iOS / Android) https://reactnative.dev/docs/running-on-device https://developer.ap ...
- DoH & DNS over HTTPS
DoH & DNS over HTTPS DNS over HTTPS(DoH)服务 http://mozilla.com.cn/thread-422231-1-1.html https:// ...
- py django
创建项目 $ django-admin startproject server 运行项目 $ cd server $ python manage.py runserver 创建一个模块 $ pytho ...
- 基本的DOS命令
基本的DOS命令 打开CMD的方法 菜单+系统+命令提示符 WIN+R后输入CMD 桌面或文件夹任意位置按住SHIFT后鼠标右键 常见的DOS命令 #盘符切换 直接输入要切换的盘符名称 如输入&quo ...
- node初体验(一)
1.node.js是一个构建在chrome V8引擎上的javascript运行环境 2.node.js特点:单线程.事件驱动.非阻塞IO模型.轻量 3.node.js是单线程的(多个请求都是一个线程 ...
- DisplayFormat属性
DataFormatString="{0:格式字符串}" 在DataFormatString 中的 {0} 表示数据本身,而在冒号后面的格式字符串代表所们希望数据显示的格式: 数字 ...
- CentOS7安装Kibana7.9.2
1:下载 wget https://artifacts.elastic.co/downloads/kibana/kibana-7.9.2-linux-x86_64.tar.gz 点击进入官网 安装手册 ...
- 并发\并行,同步\异步,阻塞\非阻塞,IO多路复用解释
并发.并行 并发:是指一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行.由于CPU的运行速度极快,可以在多个程序之间切换,这样造成一个假象就是多个程序同时在运行.并 ...
- GMS的概述
1 GMS GMS全称为GoogleMobile Service,即谷歌移动服务. GMS是Google所提供的一系列移动服务,包括开发用的一系列服务和用户所用的Google Apps. Maps与L ...