Vue3 Composition API教程及示例

发布于2/25/2020 来自:「前端知否」微信公众号

Vue引入了Composition API(基于功能的API)作为当前基于Option的API的补充。该API将随Vue 3一起发布,但是现在您可以通过将Vue 3 Composition API添加到您的Vue 2应用程序中来进行尝试。在本教程中,我们将向您展示:

  • 新的Vue Composition API概述以及与基于经典Vue Options的API的比较
  • 使用新API实现Vue组件的示例:Props,data,watch,生命周期钩子
  • 充分利用新的Vue 3 Composition API(基于函数的API)的示例:将代码拆分为函数

添加Vue Composition API会发生什么变化?

一切仍将像以前一样工作。几乎没有任何变化:

  • 命令行界面
  • 模板语法
  • 对象格式
  • 数据响应
  • 计算属性,观察者和组件生命周期的概念
  • SFC格式
  • Vue框架的渐进性

基于选项的API与组合API

当前基于选项的API概念与新的合成API(基于函数的API)概念的区别在于:

  • 基于选项的API:组件包含属性/方法/选项的类型。
  • 组合API:组件将逻辑封装到函数中。

组件选项可能变得组织起来复杂且难以维护(怪异的组件)。逻辑可能涉及props和data()的属性,某些方法,某个钩子(beforeMount/mounted)以及值班的watch。因此,一个逻辑将分散在多个选项中。

使用Composition API,每个功能都是大型组件的一部分,它封装了与逻辑相关的所有代码(属性,方法,钩子,watch观察者)。现在,较小的代码(函数)可以重复使用,并且组织得很好。

在当前的Vue 2项目中使用Vue 3 Composition API

通过安装@vue/composition-api模块,我们可以在当前的Vue 2.x项目中使用新的Vue 3 Composition API。

非常简单,只需运行以下命令:

npm install @vue/composition-api

or

yarn add @vue/composition-api

然后将其导入main.js。

import Vue from 'vue';
import CompositionApi from '@vue/composition-api';

Vue.use(CompositionApi);

Vue setup()函数

setup()是新的组件选项,我们将使用新的Vue Composition API设置组件的逻辑。如果setup()函数变得复杂,我们可以轻松地将其拆分为多个具有逻辑主题的函数。

何时调用setup()?

创建组件实例时,在props解析后调用它。

现在,使用setup()函数查看Vue组件:

const MyComponent = {
props: {
name: String
},

setup(props, context) {
console.log(props.name);
// context.attrs
// context.slots
// context.emit
// context.parent
// context.root
}
}

该函数有2个参数:

props

context

context具有与this.$attrs,this.$slots,this.$emit,this.$parent,this.$root对应的属性(属性,插槽,emit,parent,root)。

即使在更新后,我们也可以使用解构attrs获取最新值

const MyComponent = {
setup(props, { attrs }) {

function onClick() {
attrs.foo // 自动更新为最新的值
}
}
}

*注意:this关键字在setup()函数中不可用。

所以this.$emit不能这样使用

setup() {
function onClick() {
this.$emit // 无效
}
}

带有新的Composition API的this.$refs

为了获得对模板中元素或组件实例的引用,我们使用ref API,以便setup()可以为渲染上下文返回可响应和可变对象。

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

const MyComponent = {
setup(props) {
const name = ref('bezkoder.com')
const appendName = () => {
name.value = `hello ${props.name}`
}
return {
name,
appendName
}
},

template: `<div @click="appendName">{{ name }}</div>`
}

ref会自动解包为内部值,因此我们无需在模板中附加.value:。{{name}}就足够了,而不是{{name.value}}

Vue Composition API计算值

基于Vue2选项的API语法:

export default {
props: {
title: String
},

computed: {
vTitle() {
return '-' + this.title + '-';
},
itemsQuantity() {
return this.items.length;
}
},

data() {
return {
items: ['This', 'is'],
};
},
}

Vue 3 Composition API语法:

import { ref, computed } from '@vue/composition-api';

export default {
props: {
title: String
},
setup(props) {
const vTitle = computed(() => '-' + props.title + '-');

const items = ref(['This', 'is']);
const itemsQuantity = computed(() => items.value.length);

return {
vTitle,
items,
itemsQuantity,
};
}
};

使用新的计算API,我们可以使用get和set函数创建可写的ref对象。

const count = ref(1)
const double = computed({
get: () => count.value * 2,
set: val => { count.value = val - 1 }
})

double.value = 3 // set: count.value = 3 - 1 = 2
console.log(count.value) // 2
console.log(double.value) // get: count.value * 2 = 4

Vue Composition API Watch

这就是我们使用基于Vue2选项的API语法的方式:

export default {
data() {
return {
items: ['This', 'is'],
append: ''
};
},

watch: {
items: {
handler: function(value, oldValue) {
this.append = '';
value.forEach(item => {
this.append += item + ' ';
});
},
immediate: true
}
},
}

就像watch选项一样,每次状态改变时,我们都可以使用新的Vue watch API来执行副作用逻辑代码。

语法为:watch(源,回调,选项)

  • source:可以是getter函数,值包装器或包含上述两种类型的数组(如果要查看多个源)
  • callback:是类似于Vue2 watcher处理程序的函数,带有2个参数:newVal,oldVal。每个参数都可以是一个数组(用于观察多个源): [newVal1,newVal2,... newValN],[oldVal1,oldVal2,... oldValN]
  • options(可选):用于配置观察者类型,其中包括:lazy,deep,flush。

有关WatchOption的更多详细信息,请访问:api#watch。

import { ref, watch } from '@vue/composition-api';

export default {
setup(props) {
const items = ref(['This', 'is']);
const append = ref('');

watch(
// getter
() => items.value,

// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},

// watch Options
{
lazy: false // immediate: true
}
)

return {
items,
append
};
}
};

如果我们想观察多个源:

watch([aRef, bRef], ([a, b], [prevA, prevB]) => {
/* ... */
})

我们还可以将多个源观察程序拆分为较小的观察程序。这有助于我们组织代码并创建具有不同选项的观察程序:

watch(
// getter
() => items.value,

// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},

// watch Options
{
lazy: false // immediate: true
}
)

watch(
// getter
() => todo.value.length,

// callback
(length, oldLength) => {
todoLength.value = length;
},

// watch Options
{
lazy: true // immediate: false
}
)

Vue Composition API生命周期挂钩

使用Vue2,我们通过以下方式实现Lifecycle Hooks函数:

export default {
beforeMount() {
console.log('V2 beforeMount!')
},

mounted() {
console.log('V2 mounted!')
}
};

新的Vue 3 Composition API具有等效的功能,我们可以在setup()函数内使用带前缀的功能:

import { onBeforeMount, onMounted } from '@vue/composition-api';

export default {
setup() {
onBeforeMount(() => {
console.log('V3 beforeMount!');
})

onMounted(() => {
console.log('V3 mounted!');
})
}
};

您可以在下表中看到Lifecycle Vue2 Options与Composition API之间的映射:

xxx

将逻辑封装到函数中

现在,我们将以上所有代码组合到一个Vue组件中,您可以看到一个包含多个逻辑的复杂组件。我们需要为标题,待办事项和项目实现逻辑:

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

export default {
props: {
title: String,
initInput: String
},

setup(props) {
const vTitle = computed(() => '-' + props.title + '-');

const todo = ref(props.initInput);
const todoLength = ref(0);

const items = ref(['This', 'is']);
const itemsQuantity = computed(() => items.value.length);
const append = ref('');

watch(
// getter
() => items.value,

// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},

// watch Options
{
lazy: false // immediate: true
}
)

watch(
// getter
() => todo.value.length,

// callback
(length, oldLength) => {
todoLength.value = length;
},

// watch Options
{
lazy: false // immediate: true
}
)

const add = () => {
if (todo.value) {
items.value.push(todo.value);
todo.value = '';
}
};

const remove = index => {
items.value.splice(index, 1);
};

onBeforeMount(() => {
console.log('V3 beforeMount!');
})

onMounted(() => {
console.log('V3 mounted!');
})

return {
vTitle,
todo,
todoLength,
items,
itemsQuantity,
append,
add,
remove
};
}
};

是时候利用Vue Composition API了:将复杂的组件拆分为多个对应于不同逻辑的函数:

import { ref, computed, watch, onBeforeMount, onMounted } from "@vue/composition-api";

function useTitle(props) {
const vTitle = computed(() => "-" + props.title + "-");

return {
vTitle
};
}

function useTodoLength(todo) {
const todoLength = ref(0);

watch(
// getter
() => todo.value.length,

// callback
(length, oldLength) => {
todoLength.value = length;
},

// watch Options
{
lazy: false // immediate: true
}
);

return {
todoLength
};
}

function useItems(todo) {
const items = ref(["This", "is"]);
const itemsQuantity = computed(() => items.value.length);
const append = ref("");

watch(
// getter
() => items.value,

// callback
(items, oldItems) => {
append.value = "";
items.forEach(item => {
append.value += item + " ";
});
},

// watch Options
{
lazy: false // immediate: true
}
);

const add = () => {
if (todo.value) {
items.value.push(todo.value);
todo.value = "";
}
};

const remove = index => {
items.value.splice(index, 1);
};

return {
items,
itemsQuantity,
append,
add,
remove
};
}

export default {
props: {
title: String,
initInput: String
},

setup(props) {
const todo = ref(props.initInput);

onBeforeMount(() => {
console.log("V3 beforeMount!");
});

onMounted(() => {
console.log("V3 mounted!");
});

return {
todo,
...useTitle(props),
...useTodoLength(todo),
...useItems(todo)
};
}
};

最后

使用旧的基于Vue选项的API时,您可能会感到熟悉舒适;或者,您不希望将所有内容都作为函数,而是继续保持OOP思维方式的属性/方法。这些想法都没问题。同时开发者们也在积极开发Vue,并使其逐年变得更好,并为我们提供更多选择。何不尝试一下,感觉也很不错哦。

源代码:

您可以在Github上找到此"Vue Composition Api示例”的完整源代码。