vue-function-api-sd
Version:
Provide logic composition capabilities for Vue.
395 lines (293 loc) • 9.05 kB
Markdown
# Vue Function API
> [Function-based Component API RFC](https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md)
`vue-function-api` provides a way to use **function api** from `Vue3` in `Vue2.x`.
[**中文文档**](./README.zh-CN.md)
---
# Navigation
- [Changelog](https://github.com/vuejs/vue-function-api/blob/master/CHANGELOG.md)
- [Installation](#Installation)
- [Usage](#Usage)
- [Example](#Example)
- [Todo App Compare with Vue2 API](https://codesandbox.io/s/todo-example-6d7ep)
- [CodePen Live Demo](https://codepen.io/liximomo/pen/dBOvgg)
- [Single-File Component](#single-file-Component)
- [TypeScript](#TypeScript)
- [API](#API)
- [setup](#setup)
- [value](#value)
- [state](#state)
- [computed](#computed)
- [watch](#watch)
- [lifecycle](#lifecycle)
- [provide, inject](#provide-inject)
- [Misc](#Misc)
# Installation
**npm**
```bash
npm install vue-function-api --save
```
**yarn**
```bash
yarn add vue-function-api
```
**CDN**
```html
<script src="https://unpkg.com/vue-function-api/dist/vue-function-api.umd.js"></script>
```
By using the global variable `window.vueFunctionApi`
# Usage
You must explicitly install `vue-function-api` via `Vue.use()`:
```js
import Vue from 'vue';
import { plugin } from 'vue-function-api';
Vue.use(plugin);
```
After installing the plugin you can use the new [function API](#API) to compose your component.
# Example
## [Todo App Compare with Vue2 API](https://codesandbox.io/s/todo-example-6d7ep)
## [CodePen Live Demo](https://codepen.io/liximomo/pen/dBOvgg)
## Single-File Component
```html
<template>
<div>
<span>count is {{ count }}</span>
<span>plusOne is {{ plusOne }}</span>
<button @click="increment">count++</button>
</div>
</template>
<script>
import Vue from 'vue';
import { value, computed, watch, onMounted } from 'vue-function-api';
export default {
setup() {
// reactive state
const count = value(0);
// computed state
const plusOne = computed(() => count.value + 1);
// method
const increment = () => {
count.value++;
};
// watch
watch(
() => count.value * 2,
val => {
console.log(`count * 2 is ${val}`);
}
);
// lifecycle
onMounted(() => {
console.log(`mounted`);
});
// expose bindings on render context
return {
count,
plusOne,
increment,
};
},
};
</script>
```
# TypeScript
To let TypeScript properly infer types inside Vue component options, you need to define components with `createComponent`:
```ts
import Vue from 'vue';
const Component = createComponent({
// type inference enabled
});
const Component = {
// this will NOT have type inference,
// because TypeScript can't tell this is options for a Vue component.
};
```
If the component doesn't use a template, `setup()` can also directly return a render function instead(`render` option is not supported in `createComponent`):
```ts
import { value, createComponent, createElement as h } from 'vue-function-api';
const MyComponent = createComponent({
setup(initialProps) {
const count = value(0);
const increment = () => {
count.value++;
};
return props =>
h(
'button',
{
on: {
click: this.clickHandler,
},
},
count.value
);
},
});
```
# API
## setup
▸ **setup**(props: _`Props`_, context: _[`Context`](#Context)_): `Object|undefined`
A new component option, `setup()` is introduced. As the name suggests, this is the place where we use the function-based APIs to setup the logic of our component. `setup()` is called when an instance of the component is created, after props resolution. The function receives the resolved props as its first argument.
The second argument provides a `context` object which exposes a number of properties that were previously exposed on this in 2.x APIs.
```js
const MyComponent = {
props: {
name: String,
},
setup(props, context) {
console.log(props.name);
// context.attrs
// context.slots
// context.refs
// context.emit
// context.parent
// context.root
},
};
```
## value
▸ **value**(value: _`any`_): [`Wrapper`][wrapper]
Calling `value()` returns a **value wrapper** object that contains a single reactive property: `.value`.
Example:
```js
import { value } from 'vue-function-api';
const MyComponent = {
setup(props) {
const msg = value('hello');
const appendName = () => {
msg.value = `hello ${props.name}`;
};
return {
msg,
appendName,
};
},
template: `<div @click="appendName">{{ msg }}</div>`,
};
```
## state
▸ **state**(value: _`any`_)
Equivalent with [`Vue.observable`](https://vuejs.org/v2/api/index.html#Vue-observable).
Example:
```js
import { state } from 'vue-function-api';
const object = state({
count: 0,
});
object.count++;
```
## computed
▸ **computed**(getter: _`Function`_, setter?: _`Function`_): [`Wrapper`][wrapper]
Equivalent with computed property from `vue 2.x`.
Example:
```js
import { value, computed } from 'vue-function-api';
const count = value(0);
const countPlusOne = computed(() => count.value + 1);
console.log(countPlusOne.value); // 1
count.value++;
console.log(countPlusOne.value); // 2
```
## watch
▸ **watch**(source: _`Wrapper | () => any`_, callback: _`(newVal, oldVal)`_, options?: _[`WatchOption`](#WatchOption)_): `Function`
▸ **watch**(source: _`Array<Wrapper | () => any>`_, callback: _`([newVal1, newVal2, ... newValN], [oldVal1, oldVal2, ... oldValN])`_, options?: _[`WatchOption`](#WatchOption)_): `Function`
The `watch` API provides a way to perform side effect based on reactive state changes.
**Returns** a `Function` to stop the `watch`.
> [effect-cleanup](https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md#effect-cleanup) is NOT supported currently.
### WatchOption
| Name | Type | Default | Description |
| ----- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| lazy | `boolean` | `false` | The opposite of 2.x's `immediate` option |
| deep | `boolean` | `false` | Same as 2.x |
| flush | `"pre"` \| `"post"` \| `"sync"` | `"post"` | `"post"`: fire after renderer flush; `"pre"`: fire before renderer flush; `"sync"`: fire synchronously |
Example:
```js
watch(
// getter
() => count.value + 1,
// callback
(value, oldValue) => {
console.log('count + 1 is: ', value);
}
);
// -> count + 1 is: 1
count.value++;
// -> count + 1 is: 2
```
Example (Multiple Sources):
```js
watch([valueA, () => valueB.value], ([a, b], [prevA, prevB]) => {
console.log(`a is: ${a}`);
console.log(`b is: ${b}`);
});
```
## lifecycle
▸ **onCreated**(cb: _`Function`_)
▸ **onBeforeMount**(cb: _`Function`_)
▸ **onMounted**(cb: _`Function`_)
▸ **onXXX**(cb: _`Function`_)
All current lifecycle hooks will have an equivalent `onXXX` function that can be used inside `setup()`
Example:
```js
import { onMounted, onUpdated, onUnmounted } from 'vue-function-api';
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!');
});
onUpdated(() => {
console.log('updated!');
});
onUnmounted(() => {
console.log('unmounted!');
});
},
};
```
## provide, inject
▸ **provide**(key: _`string` | `symbol`_, value: _`any`_)
▸ **inject**(key: _`string` | `symbol`_)
Equivalent with `provide` and `inject` from `2.x`
Example:
```js
import { provide, inject } from 'vue-function-api';
const CountSymbol = Symbol();
const Ancestor = {
setup() {
// providing a value can make it reactive
const count = value(0);
provide(CountSymbol, count);
},
};
const Descendent = {
setup() {
const count = inject(CountSymbol);
return {
count,
};
},
};
```
## Context
The `context` object exposes a number of properties that were previously exposed on this in 2.x APIs:
```js
const MyComponent = {
setup(props, context) {
context.attrs;
context.slots;
context.refs;
context.emit;
context.parent;
context.root;
},
};
```
Full properties list:
- parent
- root
- refs
- slots
- attrs
- emit
# Misc
- Due the the limitation of `Vue2.x`'s public API. `vue-function-api` inevitably introduces some extra workload. This shouldn't concern you unless are already pushing your environment to the extreme.
[wrapper]: https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md#why-do-we-need-value-wrappers