From 7e6598cddebfc2c7ec307838b3eae8a1fcc0ce8b Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 20 Jun 2020 21:26:40 +0800 Subject: [PATCH 01/14] docs: update readme --- README.md | 89 ++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 5af16bab..ab4d7de7 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,19 @@ -# Vue Composition API +# @vue/composition-api -Vue 2 plugin for **Composition API** in Vue 3. +Vue 2 plugin for **Composition API** [![npm](https://img.shields.io/npm/v/@vue/composition-api)](https://www.npmjs.com/package/@vue/composition-api) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/vuejs/composition-api/Build%20&%20Test)](https://github.com/vuejs/composition-api/actions?query=workflow%3A%22Build+%26+Test%22) -English | [**中文文档**](./README.zh-CN.md) / [**Composition API RFC**](https://composition-api.vuejs.org/) +English | [中文](./README.zh-CN.md) · [**Composition API Docs**](https://composition-api.vuejs.org/) +

-**Note: the primary goal of this package is to allow the community to experiment with the API and provide feedback before it's finalized. The implementation may contain minor inconsistencies with the RFC as the latter gets updated. We do not recommend using this package for production yet at this stage.** - ---- - -# Navigation - -- [Installation](#Installation) -- [Usage](#Usage) -- [TypeScript](#TypeScript) - - [TSX](#tsx) -- [Limitations](#Limitations) -- [Changelog](https://github.com/vuejs/composition-api/blob/master/CHANGELOG.md) - -# Installation - -**npm** +## Installation ```bash npm install @vue/composition-api -``` - -**yarn** - -```bash +# or yarn add @vue/composition-api ``` @@ -41,9 +23,9 @@ yarn add @vue/composition-api ``` -By using the global variable `window.vueCompositionApi` +The package will be exposed to global variable `window.vueCompositionApi` -# Usage +## Usage You must install `@vue/composition-api` via `Vue.use()` before using other APIs: @@ -54,28 +36,28 @@ import VueCompositionApi from '@vue/composition-api'; Vue.use(VueCompositionApi); ``` -After installing the plugin you can use the [Composition API](https://vue-composition-api-rfc.netlify.com/) to compose your component. +After installing the plugin you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. -# TypeScript +## TypeScript Support -**This plugin requires TypeScript version >3.5.1. If you are using vetur, make sure to set `vetur.useWorkspaceDependencies` to `true`.** +> TypeScript version **>3.5.1** is required To let TypeScript properly infer types inside Vue component options, you need to define components with `defineComponent`: ```ts import { defineComponent } from '@vue/composition-api'; -const Component = defineComponent({ +const ComponentA = defineComponent({ // type inference enabled -}); +}) -const Component = { +const ComponentB = { // this will NOT have type inference, // because TypeScript can't tell this is options for a Vue component. -}; +} ``` -## TSX +### TSX :rocket: An Example [Repository](https://github.com/liximomo/vue-composition-api-tsx-example) with TS and TSX support is provided to help you start. @@ -102,13 +84,23 @@ declare global { } ``` -# Limitations +## Limitations + +> :white_check_mark: +> Support     :x: Not Supported + +### Performance Impact + +Due the the limitation of Vue2's public API. `@vue/composition-api` inevitably introduced some extract costs. This should not concern you in most of the cases. -## `Ref` Unwrap +You can check the [benchmarks](https://antfu.github.io/vue-composition-api-benchmark-results/) that comparing with Vue 2's option API and vue-next. -`Unwrap` is not working with Array index. -### **Should not** store `ref` as a **direct** child of `Array`: +### `Ref` Unwrap + +:x: `Unwrap` is not working with Array index. + +#### **Should NOT** store `ref` as a **direct** child of `Array`: ```js const state = reactive({ @@ -122,7 +114,7 @@ state.list.push(ref(1)); state.list[1].value === 1; // true ``` -### **Should not** use `ref` in a plain object when working with `Array`: +#### **Should NOT** use `ref` in a plain object when working with `Array`: ```js const a = { @@ -149,7 +141,7 @@ const b = reactive({ b.list[0].count.value === 0; // true ``` -### **Should** always use `ref` in a `reactive` when working with `Array`: +#### **Should** always use `ref` in a `reactive` when working with `Array`: ```js const a = reactive({ @@ -170,23 +162,18 @@ b.list.push( b.list[1].count === 1; // true ``` -### ***Using*** `reactive` will mutate the origin object - -This is an limitation of using `Vue.observable` in Vue 2. -> Vue 3 will return an new proxy object. +### :warning: `reactive` ***mutates*** the original object ---- +`reactive` uses `Vue.observable` underneath which will ***mutate*** the original object. -## `watch()` API +> :bulb: Vue 3 will return an new proxy object. -`onTrack` and `onTrigger` are not available in `WatchOptions`. ---- +### `watch()` API -## Template Refs +:x: `onTrack` and `onTrigger` are not available in `WatchOptions`. -> :white_check_mark: -> Support     :x: Not Supported +### Template Refs :white_check_mark: String ref && return it from `setup()`: From 1878f00478e0befc1d35874167a71d71dd436753 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 20 Jun 2020 21:54:01 +0800 Subject: [PATCH 02/14] docs: update --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ab4d7de7..fb02202f 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ Vue 2 plugin for **Composition API** English | [中文](./README.zh-CN.md) · [**Composition API Docs**](https://composition-api.vuejs.org/) -

- ## Installation ```bash @@ -27,22 +25,21 @@ The package will be exposed to global variable `window.vueCompositionApi` ## Usage -You must install `@vue/composition-api` via `Vue.use()` before using other APIs: +You must install `@vue/composition-api` via `Vue.use()` before you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. ```js -import Vue from 'vue'; -import VueCompositionApi from '@vue/composition-api'; +import Vue from 'vue' +import VueCompositionApi from '@vue/composition-api' -Vue.use(VueCompositionApi); +Vue.use(VueCompositionApi) ``` -After installing the plugin you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. ## TypeScript Support > TypeScript version **>3.5.1** is required -To let TypeScript properly infer types inside Vue component options, you need to define components with `defineComponent`: +To let TypeScript properly infer types inside Vue component options, you need to define components with `defineComponent` ```ts import { defineComponent } from '@vue/composition-api'; @@ -91,9 +88,9 @@ declare global { ### Performance Impact -Due the the limitation of Vue2's public API. `@vue/composition-api` inevitably introduced some extract costs. This should not concern you in most of the cases. +Due the the limitation of Vue2's public API. `@vue/composition-api` inevitably introduced some extract costs. -You can check the [benchmarks](https://antfu.github.io/vue-composition-api-benchmark-results/) that comparing with Vue 2's option API and vue-next. +You can check the [benchmark results](https://antfu.github.io/vue-composition-api-benchmark-results/) for more details. ### `Ref` Unwrap @@ -295,7 +292,7 @@ import VueCompositionApi from '@vue/composition-api'; Vue.use(VueCompositionApi); -declare module '@vue/composition-api/dist/component/component' { +declare module '@vue/composition-api' { interface SetupContext { readonly refs: { [key: string]: Vue | Element | Vue[] | Element[] }; } From 01a036f4598b6a698826558a63451c48badd7778 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 21 Jun 2020 23:53:48 +0800 Subject: [PATCH 03/14] docs: update CDN guide, resolve #161 --- README.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fb02202f..2273bdc4 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,29 @@ English | [中文](./README.zh-CN.md) · [**Composition API Docs**](https://comp ## Installation +### NPM + ```bash npm install @vue/composition-api # or yarn add @vue/composition-api ``` -**CDN** +You must install `@vue/composition-api` via `Vue.use()` before you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. + +```js +import Vue from 'vue' +import VueCompositionApi from '@vue/composition-api' + +Vue.use(VueCompositionApi) +``` + +```js +// in components +import { ref, reactive } from '@vue/composition-api' +``` + +### CDN ```html @@ -23,15 +39,14 @@ yarn add @vue/composition-api The package will be exposed to global variable `window.vueCompositionApi` -## Usage - -You must install `@vue/composition-api` via `Vue.use()` before you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. - ```js -import Vue from 'vue' -import VueCompositionApi from '@vue/composition-api' +// install the plugin +Vue.use(vueCompositionApi.default) +``` -Vue.use(VueCompositionApi) +```js +// use the APIs +const { ref, reactive } = vueCompositionApi ``` From 1c52d2f8a32e8119d0ea8dff693eb86932ae0d29 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 22 Jun 2020 01:32:27 +0800 Subject: [PATCH 04/14] chore: update JSX/TSX doc, close #91 --- README.md | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2273bdc4..8da2553e 100644 --- a/README.md +++ b/README.md @@ -69,32 +69,9 @@ const ComponentB = { } ``` -### TSX +### JSX/TSX -:rocket: An Example [Repository](https://github.com/liximomo/vue-composition-api-tsx-example) with TS and TSX support is provided to help you start. - -To support TSX, create a declaration file with following content in your project. - -```ts -// file: shim-tsx.d.ts -import Vue, { VNode } from 'vue'; -import { ComponentRenderProxy } from '@vue/composition-api'; - -declare global { - namespace JSX { - // tslint:disable no-empty-interface - interface Element extends VNode {} - // tslint:disable no-empty-interface - interface ElementClass extends ComponentRenderProxy {} - interface ElementAttributesProperty { - $props: any; // specify the property name to use - } - interface IntrinsicElements { - [elem: string]: any; - } - } -} -``` +To make JSX/TSX work with `@vue/composition-api`, check out [babel-preset-vca-jsx](https://github.com/luwanquan/babel-preset-vca-jsx) by [@luwanquan](https://github.com/luwanquan). ## Limitations From 65b4ca7bda210a9e62aaa3415b49def519746798 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 22 Jun 2020 13:43:20 +0800 Subject: [PATCH 05/14] docs: clean up --- README.md | 106 +++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 2273bdc4..f78efedf 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ const { ref, reactive } = vueCompositionApi To let TypeScript properly infer types inside Vue component options, you need to define components with `defineComponent` ```ts -import { defineComponent } from '@vue/composition-api'; +import { defineComponent } from '@vue/composition-api' const ComponentA = defineComponent({ // type inference enabled @@ -77,8 +77,8 @@ To support TSX, create a declaration file with following content in your project ```ts // file: shim-tsx.d.ts -import Vue, { VNode } from 'vue'; -import { ComponentRenderProxy } from '@vue/composition-api'; +import Vue, { VNode } from 'vue' +import { ComponentRenderProxy } from '@vue/composition-api' declare global { namespace JSX { @@ -87,10 +87,10 @@ declare global { // tslint:disable no-empty-interface interface ElementClass extends ComponentRenderProxy {} interface ElementAttributesProperty { - $props: any; // specify the property name to use + $props: any // specify the property name to use } interface IntrinsicElements { - [elem: string]: any; + [elem: string]: any } } } @@ -117,13 +117,13 @@ You can check the [benchmark results](https://antfu.github.io/vue-composition-ap ```js const state = reactive({ list: [ref(0)], -}); +}) // no unwrap, `.value` is required -state.list[0].value === 0; // true +state.list[0].value === 0 // true -state.list.push(ref(1)); +state.list.push(ref(1)) // no unwrap, `.value` is required -state.list[1].value === 1; // true +state.list[1].value === 1 // true ``` #### **Should NOT** use `ref` in a plain object when working with `Array`: @@ -131,13 +131,13 @@ state.list[1].value === 1; // true ```js const a = { count: ref(0), -}; +} const b = reactive({ list: [a], // `a.count` will not unwrap!! -}); +}) // no unwrap for `count`, `.value` is required -b.list[0].count.value === 0; // true +b.list[0].count.value === 0 // true ``` ```js @@ -147,10 +147,10 @@ const b = reactive({ count: ref(0), // no unwrap!! }, ], -}); +}) // no unwrap for `count`, `.value` is required -b.list[0].count.value === 0; // true +b.list[0].count.value === 0 // true ``` #### **Should** always use `ref` in a `reactive` when working with `Array`: @@ -158,20 +158,20 @@ b.list[0].count.value === 0; // true ```js const a = reactive({ count: ref(0), -}); +}) const b = reactive({ list: [a], -}); +}) // unwrapped -b.list[0].count === 0; // true +b.list[0].count === 0 // true b.list.push( reactive({ count: ref(1), }) -); +) // unwrapped -b.list[1].count === 1; // true +b.list[1].count === 1 // true ``` ### :warning: `reactive` ***mutates*** the original object @@ -198,18 +198,18 @@ String ref && return it from `setup()`: ``` @@ -219,22 +219,22 @@ String ref && return it from `setup()` && Render Function / JSX: ```jsx export default { setup() { - const root = ref(null); + const root = ref(null) onMounted(() => { // the DOM element will be assigned to the ref after initial render - console.log(root.value); //
- }); + console.log(root.value) //
+ }) return { root, - }; + } }, render() { // with JSX - return () =>
; + return () =>
}, -}; +} ``` :x: Function ref: @@ -247,13 +247,13 @@ export default { ``` @@ -262,17 +262,17 @@ export default { ```jsx export default { setup() { - const root = ref(null); + const root = ref(null) return () => h('div', { ref: root, - }); + }) // with JSX - return () =>
; + return () =>
}, -}; +} ``` If you really want to use template refs in this case, you can access `vm.$refs` via `SetupContext.refs`. @@ -282,34 +282,34 @@ If you really want to use template refs in this case, you can access `vm.$refs` ```js export default { setup(initProps, setupContext) { - const refs = setupContext.refs; + const refs = setupContext.refs onMounted(() => { // the DOM element will be assigned to the ref after initial render - console.log(refs.root); //
- }); + console.log(refs.root) //
+ }) return () => h('div', { ref: 'root', - }); + }) // with JSX - return () =>
; + return () =>
}, -}; +} ``` You may also need to augment the `SetupContext` when working with TypeScript: ```ts -import Vue from 'vue'; -import VueCompositionApi from '@vue/composition-api'; +import Vue from 'vue' +import VueCompositionApi from '@vue/composition-api' -Vue.use(VueCompositionApi); +Vue.use(VueCompositionApi) declare module '@vue/composition-api' { interface SetupContext { - readonly refs: { [key: string]: Vue | Element | Vue[] | Element[] }; + readonly refs: { [key: string]: Vue | Element | Vue[] | Element[] } } } ``` @@ -319,19 +319,19 @@ declare module '@vue/composition-api' { Even if there is no definitive Vue 3 API for SSR yet, this plugin implements the `onServerPrefetch` lifecycle hook that allows you to use the `serverPrefetch` hook found in the classic API. ```js -import { onServerPrefetch } from '@vue/composition-api'; +import { onServerPrefetch } from '@vue/composition-api' export default { setup (props, { ssrContext }) { - const result = ref(); + const result = ref() onServerPrefetch(async () => { - result.value = await callApi(ssrContext.someId); - }); + result.value = await callApi(ssrContext.someId) + }) return { result, - }; + } }, -}; +} ``` From ff0143e42e39dd29452f36ec97b9a4b99ca6cda4 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 22 Jun 2020 23:02:40 +0800 Subject: [PATCH 06/14] docs: guide users to use versioned CDN (#397) --- README.md | 15 +++++++++++---- README.zh-CN.md | 14 ++++---------- package.json | 4 +++- scripts/update-readme.js | 29 +++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 scripts/update-readme.js diff --git a/README.md b/README.md index 782eeb37..a6364eca 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ npm install @vue/composition-api yarn add @vue/composition-api ``` -You must install `@vue/composition-api` via `Vue.use()` before you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. +You must install `@vue/composition-api` as a plugin via `Vue.use()` before you can use the [Composition API](https://composition-api.vuejs.org/) to compose your component. ```js import Vue from 'vue' @@ -27,17 +27,24 @@ Vue.use(VueCompositionApi) ``` ```js -// in components +// use the APIs import { ref, reactive } from '@vue/composition-api' ``` +> :bulb: When you migrate to Vue 3, just replacing `@vue/composition-api` to `vue` and your code should just work. + ### CDN +Add the following lines in your `` to import Vue and `@vue/composition-api`. + + ```html - + + ``` + -The package will be exposed to global variable `window.vueCompositionApi` +`@vue/composition-api` will be exposed to global variable `window.vueCompositionApi` and you have to install it before using the APIs. ```js // install the plugin diff --git a/README.zh-CN.md b/README.zh-CN.md index 157ab140..c790c759 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -11,15 +11,6 @@ Vue 2 插件用于提供 Vue 3 中的 **组合式 API**. --- -# 导航 - -- [安装](#安装) -- [使用](#使用) -- [TypeScript](#TypeScript) - - [TSX](#tsx) -- [限制](#限制) -- [更新日志](https://github.com/vuejs/composition-api/blob/master/CHANGELOG.md) - # 安装 **npm** @@ -36,9 +27,12 @@ yarn add @vue/composition-api **CDN** + ```html - + + ``` + 通过全局变量 `window.vueCompositionApi` 来使用。 diff --git a/package.json b/package.json index ddb0d457..d6205506 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "main": "dist/vue-composition-api.js", "umd:main": "dist/vue-composition-api.umd.js", + "browser": "dist/vue-composition-api.umd.js", "module": "dist/vue-composition-api.module.js", "typings": "dist/index.d.ts", "author": { @@ -31,10 +32,11 @@ "test": "yarn test-dts && yarn test-unit", "test-unit": "cross-env NODE_ENV=test jest", "test-dts": "tsc -p ./test-dts/tsconfig.json && yarn build && tsc -p ./test-dts/tsconfig.build.json", + "update-readme": "node ./scripts/update-readme.js", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "prepublish": "yarn test", "postpublish": "yarn release-gh", - "version": "yarn changelog && git add CHANGELOG.md", + "version": "yarn changelog && yarn update-readme && git add CHANGELOG.md README.*", "release": "yarn version && git push --follow-tags && yarn publish --non-interactive", "release-gh": "conventional-github-releaser -p angular" }, diff --git a/scripts/update-readme.js b/scripts/update-readme.js new file mode 100644 index 00000000..364b5a9c --- /dev/null +++ b/scripts/update-readme.js @@ -0,0 +1,29 @@ +const { promises: fs } = require('fs') +const path = require('path') +const { version } = require('../package.json') + +const files = ['../README.md', '../README.zh-CN.md'] + +const MakeLinks = (version, vueVersion = '2.6') => + ` +\`\`\`html + + +\`\`\` +` + +;(async () => { + const links = MakeLinks(version) + + for (const file of files) { + const filepath = path.resolve(__dirname, file) + const raw = await fs.readFile(filepath, 'utf-8') + + const updated = raw.replace( + /([\s\S]*)/g, + `${links}` + ) + + await fs.writeFile(filepath, updated, 'utf-8') + } +})() From b11f14cce6137a2ab735bb606170f8968b124749 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 22 Jun 2020 23:38:04 +0800 Subject: [PATCH 07/14] docs: update --- README.md | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a6364eca..206cd254 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ import { ref, reactive } from '@vue/composition-api' ### CDN -Add the following lines in your `` to import Vue and `@vue/composition-api`. +Include `@vue/composition-api` after Vue and it will install itself automatically. ```html @@ -44,15 +44,9 @@ Add the following lines in your `` to import Vue and `@vue/composition-api ``` -`@vue/composition-api` will be exposed to global variable `window.vueCompositionApi` and you have to install it before using the APIs. +`@vue/composition-api` will be exposed to global variable `window.vueCompositionApi`. -```js -// install the plugin -Vue.use(vueCompositionApi.default) -``` - -```js -// use the APIs +```ts const { ref, reactive } = vueCompositionApi ``` @@ -66,14 +60,9 @@ To let TypeScript properly infer types inside Vue component options, you need to ```ts import { defineComponent } from '@vue/composition-api' -const ComponentA = defineComponent({ +export default defineComponent({ // type inference enabled }) - -const ComponentB = { - // this will NOT have type inference, - // because TypeScript can't tell this is options for a Vue component. -} ``` ### JSX/TSX @@ -259,11 +248,18 @@ export default { } ``` -If you really want to use template refs in this case, you can access `vm.$refs` via `SetupContext.refs`. +
+$refs accessing workaround + + +
> :warning: **Warning**: The `SetupContext.refs` won't exist in `Vue 3.0`. `@vue/composition-api` provide it as a workaround here. -```js +If you really want to use template refs in this case, you can access `vm.$refs` via `SetupContext.refs`. + + +```jsx export default { setup(initProps, setupContext) { const refs = setupContext.refs @@ -287,9 +283,6 @@ You may also need to augment the `SetupContext` when working with TypeScript: ```ts import Vue from 'vue' -import VueCompositionApi from '@vue/composition-api' - -Vue.use(VueCompositionApi) declare module '@vue/composition-api' { interface SetupContext { @@ -298,6 +291,9 @@ declare module '@vue/composition-api' { } ``` +
+ + ## SSR Even if there is no definitive Vue 3 API for SSR yet, this plugin implements the `onServerPrefetch` lifecycle hook that allows you to use the `serverPrefetch` hook found in the classic API. From a1792de1f5ff9f2e4d45fadeadebee7b04d71b93 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 23 Jun 2020 13:06:08 +0800 Subject: [PATCH 08/14] BREAKING CHANGE: drop createComponent (#398) --- src/component/component.ts | 40 ------------------------------ src/component/index.ts | 1 - src/index.ts | 1 - test/types/defineComponent.spec.ts | 35 -------------------------- 4 files changed, 77 deletions(-) diff --git a/src/component/component.ts b/src/component/component.ts index 8751a22b..c08d657e 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -119,43 +119,3 @@ export function defineComponent< export function defineComponent(options: any) { return options as any } - -// overload 1: object format with no props -export function createComponent( - options: ComponentOptionsWithoutProps -): VueProxy -// overload 2: object format with array props declaration -// props inferred as { [key in PropNames]?: any } -// return type is for Vetur and TSX support -export function createComponent< - PropNames extends string, - RawBindings = Data, - PropsOptions extends ComponentPropsOptions = ComponentPropsOptions ->( - // prettier-ignore - options: ( - ComponentOptionsWithArrayProps) & - Omit, keyof ComponentOptionsWithProps> -): VueProxy, RawBindings> -// overload 3: object format with object props declaration -// see `ExtractPropTypes` in ./componentProps.ts -export function createComponent< - Props, - RawBindings = Data, - PropsOptions extends ComponentPropsOptions = ComponentPropsOptions ->( - // prettier-ignore - options: ( - // prefer the provided Props, otherwise infer it from PropsOptions - HasDefined extends true - ? ComponentOptionsWithProps - : ComponentOptionsWithProps) & - Omit, keyof ComponentOptionsWithProps> -): VueProxy -// implementation, deferring to defineComponent, but logging a warning in dev mode -export function createComponent(options: any) { - if (__DEV__) { - Vue.util.warn('`createComponent` has been renamed to `defineComponent`.') - } - return defineComponent(options) -} diff --git a/src/component/index.ts b/src/component/index.ts index a880a467..21217b8b 100644 --- a/src/component/index.ts +++ b/src/component/index.ts @@ -1,6 +1,5 @@ export { Data, - createComponent, defineComponent, SetupFunction, SetupContext, diff --git a/src/index.ts b/src/index.ts index c891f3d1..bea3a2f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,6 @@ export default plugin export { default as createElement } from './createElement' export { SetupContext } export { - createComponent, defineComponent, ComponentRenderProxy, PropType, diff --git a/test/types/defineComponent.spec.ts b/test/types/defineComponent.spec.ts index d4fe4b31..21f6e62f 100644 --- a/test/types/defineComponent.spec.ts +++ b/test/types/defineComponent.spec.ts @@ -1,5 +1,4 @@ import { - createComponent, defineComponent, createElement as h, ref, @@ -217,38 +216,4 @@ describe('defineComponent', () => { }) }) }) - - describe('retro-compatible with createComponent', () => { - it('should still work and warn', () => { - const warn = jest - .spyOn(global.console, 'error') - .mockImplementation(() => null) - const Child = createComponent({ - props: { msg: String }, - setup(props) { - return () => h('span', props.msg) - }, - }) - - const App = createComponent({ - setup() { - const msg = ref('hello') - return () => - h('div', [ - h(Child, { - props: { - msg: msg.value, - }, - }), - ]) - }, - }) - const vm = new Vue(App).$mount() - expect(vm.$el.querySelector('span').textContent).toBe('hello') - expect(warn.mock.calls[0][0]).toMatch( - '[Vue warn]: `createComponent` has been renamed to `defineComponent`.' - ) - warn.mockRestore() - }) - }) }) From 23e90f56d1757d1940f20346e6c5f06e8be2343d Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 23 Jun 2020 13:06:42 +0800 Subject: [PATCH 09/14] BREAKING CHANGE: rename createElement to h (#400) --- src/apis/computed.ts | 4 ++-- src/apis/inject.ts | 4 ++-- src/apis/lifecycle.ts | 8 ++++++-- src/apis/watch.ts | 4 ++-- src/createElement.ts | 6 +++--- src/helper.ts | 4 ++-- src/index.ts | 10 ++++------ src/runtimeContext.ts | 2 +- src/setup.ts | 4 ++-- test/setup.spec.js | 2 +- test/setupContext.spec.js | 2 +- test/templateRefs.spec.js | 2 +- test/types/defineComponent.spec.ts | 2 +- test/v3/runtime-core/apiLifecycle.spec.ts | 2 +- 14 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/apis/computed.ts b/src/apis/computed.ts index bdc2c856..7edda5c7 100644 --- a/src/apis/computed.ts +++ b/src/apis/computed.ts @@ -1,4 +1,4 @@ -import { getCurrentVue, getCurrentVM } from '../runtimeContext' +import { getCurrentVue, getCurrentInstance } from '../runtimeContext' import { createRef, Ref } from '../reactivity' import { defineComponentInstance } from '../helper' import { warn } from '../utils' @@ -22,7 +22,7 @@ export function computed(options: Option): WritableComputedRef export function computed( options: Option['get'] | Option ): ComputedRef | WritableComputedRef { - const vm = getCurrentVM() + const vm = getCurrentInstance() let get: Option['get'], set: Option['set'] | undefined if (typeof options === 'function') { get = options diff --git a/src/apis/inject.ts b/src/apis/inject.ts index 9b8e533b..82f504cc 100644 --- a/src/apis/inject.ts +++ b/src/apis/inject.ts @@ -1,7 +1,7 @@ import { ComponentInstance } from '../component' import { currentVMInFn } from '../helper' import { hasOwn, warn } from '../utils' -import { getCurrentVM } from '../runtimeContext' +import { getCurrentInstance } from '../runtimeContext' const NOT_FOUND = {} export interface InjectionKey extends Symbol {} @@ -48,7 +48,7 @@ export function inject( return defaultValue } - const vm = getCurrentVM() + const vm = getCurrentInstance() if (vm) { const val = resolveInject(key, vm) if (val !== NOT_FOUND) { diff --git a/src/apis/lifecycle.ts b/src/apis/lifecycle.ts index 62457d1b..b7467fc4 100644 --- a/src/apis/lifecycle.ts +++ b/src/apis/lifecycle.ts @@ -1,6 +1,10 @@ import { VueConstructor } from 'vue' import { ComponentInstance } from '../component' -import { getCurrentVue, setCurrentVM, getCurrentVM } from '../runtimeContext' +import { + getCurrentVue, + setCurrentVM, + getCurrentInstance, +} from '../runtimeContext' import { currentVMInFn } from '../helper' const genName = (name: string) => `on${name[0].toUpperCase() + name.slice(1)}` @@ -26,7 +30,7 @@ function injectHookOption( function wrapHookCall(vm: ComponentInstance, fn: Function) { return (...args: any) => { - let preVm = getCurrentVM() + let preVm = getCurrentInstance() setCurrentVM(vm) try { return fn(...args) diff --git a/src/apis/watch.ts b/src/apis/watch.ts index a4b94633..0639439c 100644 --- a/src/apis/watch.ts +++ b/src/apis/watch.ts @@ -2,7 +2,7 @@ import { ComponentInstance } from '../component' import { Ref, isRef, isReactive } from '../reactivity' import { assert, logError, noopFn, warn, isFunction } from '../utils' import { defineComponentInstance } from '../helper' -import { getCurrentVM, getCurrentVue } from '../runtimeContext' +import { getCurrentInstance, getCurrentVue } from '../runtimeContext' import { WatcherPreFlushQueueKey, WatcherPostFlushQueueKey } from '../symbols' import { ComputedRef } from './computed' @@ -95,7 +95,7 @@ function getWatchEffectOption(options?: Partial): WatchOptions { } function getWatcherVM() { - let vm = getCurrentVM() + let vm = getCurrentInstance() if (!vm) { if (!fallbackVM) { fallbackVM = defineComponentInstance(getCurrentVue()) diff --git a/src/createElement.ts b/src/createElement.ts index 42ee20a8..cf3e8c35 100644 --- a/src/createElement.ts +++ b/src/createElement.ts @@ -7,7 +7,9 @@ type CreateElement = Vue['$createElement'] let fallbackCreateElement: CreateElement -const createElement: CreateElement = function createElement(...args: any) { +export const createElement: CreateElement = function createElement( + ...args: any +) { if (!currentVM) { warn('`createElement()` has been called outside of render function.') if (!fallbackCreateElement) { @@ -20,5 +22,3 @@ const createElement: CreateElement = function createElement(...args: any) { return currentVM.$createElement.apply(currentVM, args) } as any - -export default createElement diff --git a/src/helper.ts b/src/helper.ts index 97040c04..be5ba135 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -1,10 +1,10 @@ import Vue, { VNode, ComponentOptions, VueConstructor } from 'vue' import { ComponentInstance } from './component' -import { currentVue, getCurrentVM } from './runtimeContext' +import { currentVue, getCurrentInstance } from './runtimeContext' import { warn } from './utils' export function currentVMInFn(hook: string): ComponentInstance | null { - const vm = getCurrentVM() + const vm = getCurrentInstance() if (__DEV__ && !vm) { warn( `${hook} is called when there is no active component instance to be ` + diff --git a/src/index.ts b/src/index.ts index bea3a2f5..2833d88d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import Vue, { VueConstructor } from 'vue' -import { Data, SetupFunction, SetupContext } from './component' +import { Data, SetupFunction } from './component' import { currentVue } from './runtimeContext' import { install } from './install' import { mixin } from './setup' @@ -21,17 +21,15 @@ if (currentVue && typeof window !== 'undefined' && window.Vue) { } export default plugin -export { default as createElement } from './createElement' -export { SetupContext } +export { createElement as h } from './createElement' +export { getCurrentInstance } from './runtimeContext' export { defineComponent, ComponentRenderProxy, PropType, PropOptions, + SetupContext, } from './component' -// For getting a hold of the interal instance in setup() - useful for advanced -// plugins -export { getCurrentVM as getCurrentInstance } from './runtimeContext' export * from './apis/state' export * from './apis/lifecycle' diff --git a/src/runtimeContext.ts b/src/runtimeContext.ts index f6894377..a0b7b503 100644 --- a/src/runtimeContext.ts +++ b/src/runtimeContext.ts @@ -17,7 +17,7 @@ export function setCurrentVue(vue: VueConstructor) { currentVue = vue } -export function getCurrentVM(): ComponentInstance | null { +export function getCurrentInstance(): ComponentInstance | null { return currentVM } diff --git a/src/setup.ts b/src/setup.ts index bcb93b7d..365d2c51 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -6,7 +6,7 @@ import { Data, } from './component' import { Ref, isRef, isReactive, markRaw } from './reactivity' -import { getCurrentVM, setCurrentVM } from './runtimeContext' +import { getCurrentInstance, setCurrentVM } from './runtimeContext' import { resolveSlots, createSlotProxy } from './helper' import { hasOwn, isPlainObject, assert, proxy, warn, isFunction } from './utils' import { ref } from './apis/state' @@ -112,7 +112,7 @@ function activateCurrentInstance( fn: (vm_: ComponentInstance) => any, onError?: (err: Error) => void ) { - let preVm = getCurrentVM() + let preVm = getCurrentInstance() setCurrentVM(vm) try { return fn(vm) diff --git a/test/setup.spec.js b/test/setup.spec.js index 5d0ad820..8199af57 100644 --- a/test/setup.spec.js +++ b/test/setup.spec.js @@ -2,7 +2,7 @@ const Vue = require('vue/dist/vue.common.js') const { ref, computed, - createElement: h, + h, provide, inject, reactive, diff --git a/test/setupContext.spec.js b/test/setupContext.spec.js index 017af58b..1d33cb55 100644 --- a/test/setupContext.spec.js +++ b/test/setupContext.spec.js @@ -1,5 +1,5 @@ const Vue = require('vue/dist/vue.common.js') -const { ref, watch, createElement: h } = require('../src') +const { h } = require('../src') describe('setupContext', () => { it('should have proper properties', () => { diff --git a/test/templateRefs.spec.js b/test/templateRefs.spec.js index 8745a82c..10bbc566 100644 --- a/test/templateRefs.spec.js +++ b/test/templateRefs.spec.js @@ -1,5 +1,5 @@ const Vue = require('vue/dist/vue.common.js') -const { ref, watchEffect, watch, createElement: h } = require('../src') +const { ref, watchEffect } = require('../src') describe('ref', () => { it('should work', (done) => { diff --git a/test/types/defineComponent.spec.ts b/test/types/defineComponent.spec.ts index 21f6e62f..709fda70 100644 --- a/test/types/defineComponent.spec.ts +++ b/test/types/defineComponent.spec.ts @@ -1,6 +1,6 @@ import { defineComponent, - createElement as h, + h, ref, SetupContext, PropType, diff --git a/test/v3/runtime-core/apiLifecycle.spec.ts b/test/v3/runtime-core/apiLifecycle.spec.ts index f952bdc4..418c282a 100644 --- a/test/v3/runtime-core/apiLifecycle.spec.ts +++ b/test/v3/runtime-core/apiLifecycle.spec.ts @@ -4,7 +4,7 @@ import { onBeforeMount, onMounted, ref, - createElement as h, + h, onBeforeUpdate, onUpdated, onBeforeUnmount, From 4c4213d07d89dc43c255e302f4339f7b3a7220c7 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 23 Jun 2020 13:08:14 +0800 Subject: [PATCH 10/14] BREAKING CHANGE: Change umd exported name to `VueCompositionAPI` (#399) --- README.md | 6 +++--- README.zh-CN.md | 10 +++++----- rollup.config.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 206cd254..acc76fbd 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ You must install `@vue/composition-api` as a plugin via `Vue.use()` before you c ```js import Vue from 'vue' -import VueCompositionApi from '@vue/composition-api' +import VueCompositionAPI from '@vue/composition-api' -Vue.use(VueCompositionApi) +Vue.use(VueCompositionAPI) ``` ```js @@ -44,7 +44,7 @@ Include `@vue/composition-api` after Vue and it will install itself automaticall ``` -`@vue/composition-api` will be exposed to global variable `window.vueCompositionApi`. +`@vue/composition-api` will be exposed to global variable `window.VueCompositionAPI`. ```ts const { ref, reactive } = vueCompositionApi diff --git a/README.zh-CN.md b/README.zh-CN.md index c790c759..ce805743 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -34,7 +34,7 @@ yarn add @vue/composition-api ``` -通过全局变量 `window.vueCompositionApi` 来使用。 +通过全局变量 `window.VueCompositionAPI` 来使用。 # 使用 @@ -42,9 +42,9 @@ yarn add @vue/composition-api ```js import Vue from 'vue'; -import VueCompositionApi from '@vue/composition-api'; +import VueCompositionAPI from '@vue/composition-api'; -Vue.use(VueCompositionApi); +Vue.use(VueCompositionAPI); ``` 安装插件后,您就可以使用新的 [Composition API](https://vue-composition-api-rfc.netlify.com/) 来开发组件了。 @@ -294,9 +294,9 @@ export default { ```ts import Vue from 'vue'; -import VueCompositionApi from '@vue/composition-api'; +import VueCompositionAPI from '@vue/composition-api'; -Vue.use(VueCompositionApi); +Vue.use(VueCompositionAPI); declare module '@vue/composition-api/dist/component/component' { interface SetupContext { diff --git a/rollup.config.js b/rollup.config.js index 679708c9..6dea1609 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -54,7 +54,7 @@ function genConfig({ outFile, format, mode }) { vue: 'Vue', }, exports: 'named', - name: format === 'umd' ? 'vueCompositionApi' : undefined, + name: format === 'umd' ? 'VueCompositionAPI' : undefined, }, external: ['vue'], onwarn, From 56332c174493b6c3cbca4c7517068b60e4f4600d Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 23 Jun 2020 13:12:03 +0800 Subject: [PATCH 11/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acc76fbd..c7da5b54 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Include `@vue/composition-api` after Vue and it will install itself automaticall `@vue/composition-api` will be exposed to global variable `window.VueCompositionAPI`. ```ts -const { ref, reactive } = vueCompositionApi +const { ref, reactive } = VueCompositionAPI ``` From a6af7d487a6faf04174a50d7049defe3872a1de3 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 28 Jun 2020 11:07:39 +0800 Subject: [PATCH 12/14] docs: update Chinese README --- README.md | 13 +- README.zh-CN.md | 350 +++++++++++++++++++++++++++++++----------------- 2 files changed, 231 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index ebc92d76..d88b7b81 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,6 @@ Include `@vue/composition-api` after Vue and it will install itself automaticall ```ts const { ref, reactive } = VueCompositionAPI ``` - ## TypeScript Support @@ -95,8 +94,7 @@ export default { ## Limitations -> :white_check_mark: -> Support     :x: Not Supported +> :white_check_mark: Support     :x: Not Supported ### `Ref` Unwrap @@ -107,15 +105,6 @@ export default { ❌ Should NOT store ref as a direct child of Array -You can check the [benchmark results](https://antfu.github.io/vue-composition-api-benchmark-results/) for more details. - - -### `Ref` Unwrap - -:x: `Unwrap` is not working with Array index. - -#### **Should NOT** store `ref` as a **direct** child of `Array`: - ```js const state = reactive({ list: [ref(0)], diff --git a/README.zh-CN.md b/README.zh-CN.md index ce805743..3eab32a2 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,31 +1,43 @@ -# Vue Composition API +# @vue/composition-api Vue 2 插件用于提供 Vue 3 中的 **组合式 API**. [![npm](https://img.shields.io/npm/v/@vue/composition-api)](https://www.npmjs.com/package/@vue/composition-api) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/vuejs/composition-api/Build%20&%20Test)](https://github.com/vuejs/composition-api/actions?query=workflow%3A%22Build+%26+Test%22) -[**English**](./README.md) | 中文文档 / [**组合式 API RFC**](https://composition-api.vuejs.org/zh) +[English](./README.md) | 中文 ・ [**组合式 API 文档**](https://composition-api.vuejs.org/zh) -**请注意:此插件的主要目的是让社区尝试新的API并在其最终确定之前提供反馈。随着RFC的更新,该实现可能包含与RFC有细微的不一致。现阶段,我们暂不建议将此插件用于生产环境。** ---- +## 安装 -# 安装 - -**npm** +### NPM ```bash -npm install @vue/composition-api --save +npm install @vue/composition-api +# or +yarn add @vue/composition-api ``` -**yarn** +在使用 `@vue/composition-api` 前,必须先先通过 `Vue.use()` 进行安装后方可使用使用新的 [**组合式 API**](https://composition-api.vuejs.org/zh) 进行组件开发。 -```bash -yarn add @vue/composition-api +```js +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' + +Vue.use(VueCompositionAPI) ``` -**CDN** +```js +// 使用 API +import { ref, reactive } from '@vue/composition-api' +``` + +> :bulb: 当迁移到 Vue 3 时,只需简单的将 `@vue/composition-api` 替换成 `vue` 即可。你现有的代码几乎无需进行额外的改动。 + + +### CDN + +在 Vue 之后引入 `@vue/composition-api` ,插件将会自动完成安装。 ```html @@ -34,96 +46,95 @@ yarn add @vue/composition-api ``` -通过全局变量 `window.VueCompositionAPI` 来使用。 - -# 使用 +`@vue/composition-api` 将会暴露在全局变量 `window.VueCompositionAPI` 中。 -在使用任何 `@vue/composition-api` 提供的能力前,必须先通过 `Vue.use()` 进行安装: - -```js -import Vue from 'vue'; -import VueCompositionAPI from '@vue/composition-api'; - -Vue.use(VueCompositionAPI); +```ts +const { ref, reactive } = VueCompositionAPI ``` -安装插件后,您就可以使用新的 [Composition API](https://vue-composition-api-rfc.netlify.com/) 来开发组件了。 +## TypeScript 支持 -# TypeScript - -**本插件要求使用 TypeScript 3.5.1 以上版本,如果你正在使用 `vetur`,请将 `vetur.useWorkspaceDependencies` 设为 `true`。** +> 本插件要求使用 TypeScript **3.5.1** 或以上版本 为了让 TypeScript 在 Vue 组件选项中正确地推导类型,我们必须使用 `defineComponent` 来定义组件: ```ts -import { defineComponent } from '@vue/composition-api'; - -const Component = defineComponent({ - // 启用类型推断 -}); +import { defineComponent } from '@vue/composition-api' -const Component = { - // 无法进行选项的类型推断 - // TypeScript 无法知道这是一个 Vue 组件的选项对象 -}; +export default defineComponent({ + // 类型推断启用 +}) ``` -## TSX +### JSX/TSX -:rocket: 这里有一个配置好 TS/TSX 支持的[示例仓库](https://github.com/liximomo/vue-composition-api-tsx-example)来帮助你快速开始. +要使得 `@vue/composition-api` 支持 JSX/TSX,请前往查看由 [@luwanquan](https://github.com/luwanquan) 开发的 Babel 插件[babel-preset-vca-jsx](https://github.com/luwanquan/babel-preset-vca-jsx)。 -要支持 TSX,请创建一个类型定义文件并提供正确的 JSX 定义。内容如下: +## SSR -```ts -// 文件: `shim-tsx.d.ts` -import Vue, { VNode } from 'vue'; -import { ComponentRenderProxy } from '@vue/composition-api'; - -declare global { - namespace JSX { - // tslint:disable no-empty-interface - interface Element extends VNode {} - // tslint:disable no-empty-interface - interface ElementClass extends ComponentRenderProxy {} - interface ElementAttributesProperty { - $props: any; // 定义要使用的属性名称 - } - interface IntrinsicElements { - [elem: string]: any; +尽管 Vue 3 暂时没有给出确定的 SSR 的 API,这个插件实现了 `onServerPrefetch` 生命周期钩子函数。这个钩子允许你使用在传统 API 中的 `serverPrefetch` 函数。 + +```js +import { onServerPrefetch } from '@vue/composition-api' + +export default { + setup (props, { ssrContext }) { + const result = ref() + + onServerPrefetch(async () => { + result.value = await callApi(ssrContext.someId) + }) + + return { + result, } - } + }, } ``` -# 限制 +## 限制 + +> :white_check_mark: 支持     :x: 不支持 + -## `Ref` 自动展开 (unwrap) +### `Ref` 自动展开 (unwrap) 数组索引属性无法进行自动展开: -### **不要**使用 `Array` 直接存取 `ref` 对象: + +
+ +❌ 不要 使用数组直接存取 ref 对象 + ```js const state = reactive({ list: [ref(0)], -}); +}) // 不会自动展开, 须使用 `.value` -state.list[0].value === 0; // true +state.list[0].value === 0 // true -state.list.push(ref(1)); +state.list.push(ref(1)) // 不会自动展开, 须使用 `.value` -state.list[1].value === 1; // true +state.list[1].value === 1 // true ``` -### **不要**在数组中使用含有 `ref` 的普通对象: +
+ + +
+ +❌ 不要 在数组中使用含有 ref 的普通对象 + + ```js const a = { count: ref(0), -}; +} const b = reactive({ list: [a], // `a.count` 不会自动展开!! -}); +}) // `count` 不会自动展开, 须使用 `.value` b.list[0].count.value === 0; // true @@ -136,51 +147,47 @@ const b = reactive({ count: ref(0), // 不会自动展开!! }, ], -}); +}) // `count` 不会自动展开, 须使用 `.value` b.list[0].count.value === 0; // true ``` -### **应该**总是将 `ref` 存放到 `reactive` 对象中: +
+ + +
+ +✅ 在数组中,应该 总是将 ref 存放到 reactive 对象中 + ```js const a = reactive({ count: ref(0), -}); +}) const b = reactive({ list: [a], -}); +}) // 自动展开 -b.list[0].count === 0; // true +b.list[0].count === 0 // true b.list.push( reactive({ count: ref(1), }) -); +) // 自动展开 b.list[1].count === 1; // true ``` -### `reactive` 会返回一个修改过的原始的对象 - -此行为与 Vue 2 中的 `Vue.observable` 一致 -> Vue 3 中会返回一个新的的代理对象. - ---- - -## `watch()` API - -不支持 `onTrack` 和 `onTrigger` 选项。 +
---- +### 模板 Refs -## 模板 Refs - -> :white_check_mark: 支持     :x: 不支持 - -:white_check_mark: 字符串 ref && 从 `setup()` 返回 ref: +
+ +✅ 字符串 ref && 从 setup() 返回 ref + ```html