@aimwhy/vue-function-api
Version:
Vue2 plugin for the function-based RFC
345 lines (262 loc) • 7.72 kB
Markdown
# Vue Function API
> [Function-based Component API RFC](https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md)
Future-Oriented Programming, `@aimwhy/vue-function-api` provides function api from `Vue3.x` to `Vue2.x` for developing next-generation Vue applications.
[**中文文档**](./README.zh-CN.md)
---
# Navigation
- [Changelog](https://github.com/aimwhy/vue-function-api/blob/master/CHANGELOG.md)
- [Installation](#Installation)
- [Usage](#Usage)
- [Example](#Example)
- [Single-File Component](#single-file-Component)
- [Demo](https://codesandbox.io/s/vue-template-szw4b)
- [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 @aimwhy/vue-function-api --save
```
**yarn**
```bash
yarn add @aimwhy/vue-function-api
```
**CDN**
```html
<script src="https://unpkg.com/@aimwhy/vue-function-api@0.0.6/dist/index.js"></script>
```
By using the global variable `window.vueFunctionApi`
# Usage
You must explicitly install `@aimwhy/vue-function-api` via `Vue.use()`:
```js
import Vue from 'vue'
import { plugin } from '@aimwhy/vue-function-api'
Vue.use(plugin)
```
After installing the plugin you can use the new [function API](#API) to compose your component.
# Example
## 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 '@aimwhy/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>
```
# 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 '@aimwhy/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 '@aimwhy/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 '@aimwhy/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) .
### 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 '@aimwhy/vue-function-api'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
```
## provide, inject
▸ **provide**(value: *`Object`*)
▸ **inject**(key: *`string` | `symbol`*)
Equivalent with `provide` and `inject` from `2.x`
Example:
```js
import { provide, inject } from '@aimwhy/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
- `@aimwhy/vue-function-api` will keep updated with `Vue3.x` API. When `3.0` released, you can replace this library seamlessly.
- `@aimwhy/vue-function-api` only relies on `Vue2.x` itself. Wheather `Vue3.x` is released or not, it's not affect you using this library.
- Due the the limitation of `Vue2.x`'s public API. `@aimwhy/vue-function-api` inevitably introduce some extract workload. It doesn't concern you if you are now working on extreme environment.
[wrapper]: https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md#why-do-we-need-value-wrappers