From ec78d5b1d9f315e374680d12a79720300bc58f2f Mon Sep 17 00:00:00 2001 From: neverland <chenjiahan@youzan.com> Date: Sun, 3 Jul 2022 13:39:27 +0800 Subject: [PATCH] refactor(Notify): redesign function-call API (#10782) * refactor(Notify): redesign function-call API * docs: update --- .../docs/markdown/migrate-from-v3.zh-CN.md | 52 +++++++++++ packages/vant/src/icon/demo/index.vue | 4 +- packages/vant/src/notify/README.md | 60 +++++++----- packages/vant/src/notify/README.zh-CN.md | 91 +++++++------------ packages/vant/src/notify/demo/index.vue | 18 ++-- packages/vant/src/notify/function-call.tsx | 75 ++++++--------- packages/vant/src/notify/index.ts | 12 ++- packages/vant/src/notify/test/index.spec.ts | 48 +++++----- packages/vant/src/notify/types.ts | 7 -- 9 files changed, 192 insertions(+), 175 deletions(-) diff --git a/packages/vant/docs/markdown/migrate-from-v3.zh-CN.md b/packages/vant/docs/markdown/migrate-from-v3.zh-CN.md index 22a0df264..3c1e7837a 100644 --- a/packages/vant/docs/markdown/migrate-from-v3.zh-CN.md +++ b/packages/vant/docs/markdown/migrate-from-v3.zh-CN.md @@ -120,6 +120,58 @@ declare module '@vue/runtime-core' { } ``` +### Notify 调用方式调整 + +Vant 4 中,`Notify` 组件的调用方式也进行了调整,与 `Dialog` 组件的改动一致: + +```js +// Vant 3 +Notify(); // 函数调用 +Notify.Component; // 组件对象 + +// Vant 4 +showNotify(); // 函数调用 +Notify; // 组件对象 +``` + +`Notify` 上挂载的其他方法也进行了重命名,新旧 API 的映射关系如下: + +```js +Notify(); // -> showNotify() +Notify.clear(); // -> hideNotify() +Notify.setDefaultOptions(); // -> setNotifyDefaultOptions() +Notify.resetDefaultOptions(); // -> resetNotifyDefaultOptions() +``` + +同时,Vant 4 将不再在 `this` 对象上全局注册 `$notify` 方法,这意味着 `this` 对象上将无法访问到 `$notify`。 + +```js +export default { + mounted() { + // 无效代码 + this.$notify({ + message: '内容', + }); + }, +}; +``` + +如果需要全局方法,可以手动在 `app` 对象上注册: + +```js +import { showNotify } from 'vant'; + +// 注册 $notify 方法 +app.config.globalProperties.$notify = showNotify; + +// 添加 TS 类型定义 +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $notify: typeof showNotify; + } +} +``` + ### 事件命名调整 从 Vant 4 开始,所有的事件均采用 Vue 官方推荐的**驼峰格式**进行命名。 diff --git a/packages/vant/src/icon/demo/index.vue b/packages/vant/src/icon/demo/index.vue index fa111dd7e..a0e7dc089 100644 --- a/packages/vant/src/icon/demo/index.vue +++ b/packages/vant/src/icon/demo/index.vue @@ -7,7 +7,7 @@ import VanCol from '../../col'; import icons from '@vant/icons'; import { ref } from 'vue'; import { cdnURL, useTranslate } from '../../../docs/site'; -import { Notify } from '../../notify'; +import { showNotify } from '../../notify'; // from https://30secondsofcode.org function copyToClipboard(str: string) { @@ -82,7 +82,7 @@ const copy = (icon: string, option: Record<string, unknown> = {}) => { tag = `${tag} />`; copyToClipboard(tag); - Notify({ + showNotify({ type: 'success', duration: 1500, className: 'demo-icon-notify', diff --git a/packages/vant/src/notify/README.md b/packages/vant/src/notify/README.md index 7dac56de8..fd3212c02 100644 --- a/packages/vant/src/notify/README.md +++ b/packages/vant/src/notify/README.md @@ -2,7 +2,7 @@ ### Intro -The display message prompt is at the top of the page, and supports two methods: function call and component call. +The display message prompt is at the top of the page, and supports two methods: component call and function call. ### Install @@ -16,55 +16,65 @@ const app = createApp(); app.use(Notify); ``` +### Function Call + +Vant provides some utility functions that can quickly evoke global `Notify` components. + +For example, calling the `showNotify` function will render a Dialog directly in the page. + +```js +import { showNotify } from 'vant'; + +showNotify('Notify Message'); +``` + ## Usage ### Basic Usage ```js -Notify('Notify Message'); +import { showNotify, hideNotify } from 'vant'; + +// auto close after 3s +showNotify('Message'); + +// manually close +hideNotify(); ``` ### Notify Type ```js -Notify({ type: 'primary', message: 'Notify Message' }); -Notify({ type: 'success', message: 'Notify Message' }); -Notify({ type: 'danger', message: 'Notify Message' }); -Notify({ type: 'warning', message: 'Notify Message' }); +import { showNotify } from 'vant'; + +showNotify({ type: 'primary', message: 'Notify Message' }); +showNotify({ type: 'success', message: 'Notify Message' }); +showNotify({ type: 'danger', message: 'Notify Message' }); +showNotify({ type: 'warning', message: 'Notify Message' }); ``` ### Custom Notify ```js -Notify({ +import { showNotify } from 'vant'; + +showNotify({ message: 'Custom Color', color: '#ad0000', background: '#ffe1e1', }); -Notify({ +showNotify({ message: 'Custom Position', position: 'bottom', }); -Notify({ +showNotify({ message: 'Custom Duration', duration: 1000, }); ``` -### Global Method - -After registering the Notify component through `app.use`, the `$notify` method will be automatically mounted on all subcomponents of the app. - -```js -export default { - mounted() { - this.$notify('Notify Message'); - }, -}; -``` - ### Component Call ```html @@ -103,10 +113,10 @@ export default { | Methods | Attribute | Return value | Description | | --- | --- | --- | --- | -| Notify | `options \| message` | notify instance | Show notify | -| Notify.clear | - | `void` | Close notify | -| Notify.setDefaultOptions | `options` | `void` | Set default options of all notifies | -| Notify.resetDefaultOptions | - | `void` | Reset default options of all notifies | +| showNotify | `options \| message` | notify instance | Show notify | +| hideNotify | - | `void` | Close notify | +| setNotifyDefaultOptions | `options` | `void` | Set default options of all notifies | +| resetNotifyDefaultOptions | - | `void` | Reset default options of all notifies | ### Options diff --git a/packages/vant/src/notify/README.zh-CN.md b/packages/vant/src/notify/README.zh-CN.md index 11fb4c6cb..30f148f7b 100644 --- a/packages/vant/src/notify/README.zh-CN.md +++ b/packages/vant/src/notify/README.zh-CN.md @@ -2,51 +2,30 @@ ### 介绍 -在页面顶部展示消息提示,支持函数调用和组件调用两种方式。 +在页面顶部展示消息提示,支持组件调用和函数调用两种方式。 -### 函数调用 +### 引入 -Notify 是一个函数,调用后会直接在页面中弹出相应的消息提示。 - -```js -import { Notify } from 'vant'; - -Notify('通知内容'); -``` - -### 组件调用 - -通过组件调用 Notify 时,可以通过下面的方式进行注册: +通过以下方式来全局注册组件,更多注册方式请参考[组件注册](#/zh-CN/advanced-usage#zu-jian-zhu-ce)。 ```js import { createApp } from 'vue'; import { Notify } from 'vant'; -// 全局注册 const app = createApp(); app.use(Notify); - -// 局部注册 -export default { - components: { - [Notify.Component.name]: Notify.Component, - }, -}; ``` -在 `script setup` 中,可以通过以下方式使用: +### 函数调用 -```html -<script setup> - const VanNotify = Notify.Component; -</script> +为了便于使用 `Notify`,Vant 提供了一系列辅助函数,通过辅助函数可以快速唤起全局的消息提示。 -<template> - <!-- 中划线命名 --> - <van-notify /> - <!-- 也支持大驼峰命名 --> - <VanNotify> -</template> +比如使用 `showNotify` 函数,调用后会直接在页面中渲染对应的提示。 + +```js +import { showNotify } from 'vant'; + +showNotify({ message: '提示' }); ``` ## 代码演示 @@ -54,7 +33,13 @@ export default { ### 基础用法 ```js -Notify('通知内容'); +import { showNotify, hideNotify } from 'vant'; + +// 3 秒后自动关闭 +showNotify('通知内容'); + +// 主动关闭 +hideNotify(); ``` ### 通知类型 @@ -62,17 +47,19 @@ Notify('通知内容'); 支持 `primary`、`success`、`warning`、`danger` 四种通知类型,默认为 `danger`。 ```js +import { showNotify } from 'vant'; + // 主要通知 -Notify({ type: 'primary', message: '通知内容' }); +showNotify({ type: 'primary', message: '通知内容' }); // 成功通知 -Notify({ type: 'success', message: '通知内容' }); +showNotify({ type: 'success', message: '通知内容' }); // 危险通知 -Notify({ type: 'danger', message: '通知内容' }); +showNotify({ type: 'danger', message: '通知内容' }); // 警告通知 -Notify({ type: 'warning', message: '通知内容' }); +showNotify({ type: 'warning', message: '通知内容' }); ``` ### 自定义通知 @@ -80,37 +67,25 @@ Notify({ type: 'warning', message: '通知内容' }); 自定义消息通知的颜色、位置和展示时长。 ```js -Notify({ +import { showNotify } from 'vant'; + +showNotify({ message: '自定义颜色', color: '#ad0000', background: '#ffe1e1', }); -Notify({ +showNotify({ message: '自定义位置', position: 'bottom', }); -Notify({ +showNotify({ message: '自定义时长', duration: 1000, }); ``` -### 全局方法 - -通过 `app.use` 全局注册 Notify 组件后,会自动在 app 的所有子组件上挂载 `$notify` 方法,便于在组件内调用。 - -```js -export default { - mounted() { - this.$notify('提示文案'); - }, -}; -``` - -> Tips: 由于 setup 选项中无法访问 this,因此不能使用上述方式,请通过 import 引入。 - ### 组件调用 如果需要在 Notify 内嵌入组件或其他自定义内容,可以使用组件调用的方式。 @@ -151,10 +126,10 @@ export default { | 方法名 | 说明 | 参数 | 返回值 | | --- | --- | --- | --- | -| Notify | 展示提示 | `options \| message` | notify 实例 | -| Notify.clear | 关闭提示 | - | `void` | -| Notify.setDefaultOptions | 修改默认配置,对所有 Notify 生效 | `options` | `void` | -| Notify.resetDefaultOptions | 重置默认配置,对所有 Notify 生效 | - | `void` | +| showNotify | 展示提示 | `options \| message` | notify 实例 | +| hideNotify | 关闭提示 | - | `void` | +| setNotifyDefaultOptions | 修改默认配置,影响所有的 `showNotify` 调用 | `options` | `void` | +| resetNotifyDefaultOptions | 重置默认配置,影响所有的 `showNotify` 调用 | - | `void` | ### Options diff --git a/packages/vant/src/notify/demo/index.vue b/packages/vant/src/notify/demo/index.vue index 449fcba48..95ef3ee43 100644 --- a/packages/vant/src/notify/demo/index.vue +++ b/packages/vant/src/notify/demo/index.vue @@ -2,11 +2,9 @@ import VanCell from '../../cell'; import VanIcon from '../../icon'; import { ref } from 'vue'; -import { Notify, type NotifyType } from '..'; +import { showNotify, Notify as VanNotify, type NotifyType } from '..'; import { useTranslate } from '../../../docs/site'; -const VanNotify = Notify.Component; - const t = useTranslate({ 'zh-CN': { primary: '主要通知', @@ -38,12 +36,12 @@ const t = useTranslate({ const show = ref(false); -const showNotify = () => { - Notify(t('content')); +const showBasicNotify = () => { + showNotify(t('content')); }; const showCustomColor = () => { - Notify({ + showNotify({ color: '#ad0000', message: t('customColor'), background: '#ffe1e1', @@ -51,21 +49,21 @@ const showCustomColor = () => { }; const showCustomDuration = () => { - Notify({ + showNotify({ message: t('customDuration'), duration: 1000, }); }; const showCustomPosition = () => { - Notify({ + showNotify({ message: t('customPosition'), position: 'bottom', }); }; const showType = (type: NotifyType) => { - Notify({ + showNotify({ message: t('content'), type, }); @@ -81,7 +79,7 @@ const showComponentCall = () => { <template> <demo-block card :title="t('basicUsage')"> - <van-cell is-link :title="t('basicUsage')" @click="showNotify" /> + <van-cell is-link :title="t('basicUsage')" @click="showBasicNotify" /> </demo-block> <demo-block card :title="t('notifyType')"> diff --git a/packages/vant/src/notify/function-call.tsx b/packages/vant/src/notify/function-call.tsx index eac843c32..0fc8599af 100644 --- a/packages/vant/src/notify/function-call.tsx +++ b/packages/vant/src/notify/function-call.tsx @@ -1,11 +1,4 @@ -import { App } from 'vue'; -import { - extend, - isObject, - inBrowser, - withInstall, - type ComponentInstance, -} from '../utils'; +import { extend, isObject, inBrowser, type ComponentInstance } from '../utils'; import { mountComponent, usePopupState } from '../utils/mount-component'; import VanNotify from './Notify'; import type { NotifyMessage, NotifyOptions } from './types'; @@ -25,27 +18,6 @@ function initInstance() { })); } -function Notify(options: NotifyMessage | NotifyOptions) { - if (!inBrowser) { - return; - } - - if (!instance) { - initInstance(); - } - - options = extend({}, Notify.currentOptions, parseOptions(options)); - - instance.open(options); - clearTimeout(timer); - - if (options.duration! > 0) { - timer = window.setTimeout(Notify.clear, options.duration); - } - - return instance; -} - const getDefaultOptions = (): NotifyOptions => ({ type: 'danger', color: undefined, @@ -60,27 +32,38 @@ const getDefaultOptions = (): NotifyOptions => ({ background: undefined, }); -Notify.clear = () => { +let currentOptions = getDefaultOptions(); + +export const hideNotify = () => { if (instance) { instance.toggle(false); } }; -Notify.currentOptions = getDefaultOptions(); +export function showNotify(options: NotifyMessage | NotifyOptions) { + if (!inBrowser) { + return; + } -Notify.setDefaultOptions = (options: NotifyOptions) => { - extend(Notify.currentOptions, options); + if (!instance) { + initInstance(); + } + + options = extend({}, currentOptions, parseOptions(options)); + + instance.open(options); + clearTimeout(timer); + + if (options.duration! > 0) { + timer = window.setTimeout(hideNotify, options.duration); + } + + return instance; +} + +export const setNotifyDefaultOptions = (options: NotifyOptions) => + extend(currentOptions, options); + +export const resetNotifyDefaultOptions = () => { + currentOptions = getDefaultOptions(); }; - -Notify.resetDefaultOptions = () => { - Notify.currentOptions = getDefaultOptions(); -}; - -Notify.Component = withInstall(VanNotify); - -Notify.install = (app: App) => { - app.use(Notify.Component); - app.config.globalProperties.$notify = Notify; -}; - -export { Notify }; diff --git a/packages/vant/src/notify/index.ts b/packages/vant/src/notify/index.ts index 973c8839f..983ee5cf4 100644 --- a/packages/vant/src/notify/index.ts +++ b/packages/vant/src/notify/index.ts @@ -1,7 +1,15 @@ -import { Notify } from './function-call'; +import { withInstall } from '../utils'; +import _Notify from './Notify'; +export const Notify = withInstall(_Notify); export default Notify; -export { Notify }; +export { + showNotify, + hideNotify, + setNotifyDefaultOptions, + resetNotifyDefaultOptions, +} from './function-call'; + export type { NotifyProps } from './Notify'; export type { NotifyType, NotifyOptions } from './types'; diff --git a/packages/vant/src/notify/test/index.spec.ts b/packages/vant/src/notify/test/index.spec.ts index 369d54f3e..afe657ee5 100644 --- a/packages/vant/src/notify/test/index.spec.ts +++ b/packages/vant/src/notify/test/index.spec.ts @@ -1,20 +1,23 @@ -import { createApp } from 'vue'; import { later } from '../../../test'; -import { Notify } from '../function-call'; -import NotifyComponent from '../Notify'; +import { + showNotify, + hideNotify, + setNotifyDefaultOptions, + resetNotifyDefaultOptions, +} from '../function-call'; test('should not throw error if calling clear method before render notify', () => { - Notify.clear(); + hideNotify(); }); test('should render Notify correctly', async () => { - Notify('test'); + showNotify('test'); await later(); expect(document.querySelector('.van-notify')).toMatchSnapshot(); }); test('should add "van-notify--success" class when type is success', async () => { - Notify({ + showNotify({ message: 'test', type: 'success', }); @@ -24,28 +27,23 @@ test('should add "van-notify--success" class when type is success', async () => expect(notify.classList.contains('van-notify--success')).toBeTruthy(); }); -test('should register component to app', () => { - const app = createApp({}); - app.use(Notify); - expect(app.component(NotifyComponent.name)).toBeTruthy(); -}); +test('should change default options after calling setDefaultOptions method', async () => { + setNotifyDefaultOptions({ message: 'foo' }); + showNotify({}); + await later(); + const notify = document.querySelector('.van-notify') as HTMLElement; + expect(notify.innerHTML.includes('foo')).toBeTruthy(); -test('should change default duration after calling setDefaultOptions method', () => { - Notify.setDefaultOptions({ duration: 1000 }); - expect(Notify.currentOptions.duration).toEqual(1000); - Notify.resetDefaultOptions(); - expect(Notify.currentOptions.duration).toEqual(3000); -}); - -test('should reset to default duration after calling resetDefaultOptions method', () => { - Notify.setDefaultOptions({ duration: 1000 }); - Notify.resetDefaultOptions(); - expect(Notify.currentOptions.duration).toEqual(3000); + resetNotifyDefaultOptions(); + showNotify({}); + await later(); + const notify2 = document.querySelector('.van-notify') as HTMLElement; + expect(notify2.innerHTML.includes('foo')).toBeFalsy(); }); test('should call onClose option when closing', async () => { const onClose = jest.fn(); - Notify({ + showNotify({ message: 'test', onClose, duration: 1, @@ -57,7 +55,7 @@ test('should call onClose option when closing', async () => { test('should call onClick option when clicked', async () => { const onClick = jest.fn(); - Notify({ + showNotify({ message: 'test', onClick, }); @@ -69,7 +67,7 @@ test('should call onClick option when clicked', async () => { }); test('should align to bottom when position option is bottom', async () => { - Notify({ + showNotify({ message: 'test', position: 'bottom', }); diff --git a/packages/vant/src/notify/types.ts b/packages/vant/src/notify/types.ts index 8dfbd5bb5..cb4e6373a 100644 --- a/packages/vant/src/notify/types.ts +++ b/packages/vant/src/notify/types.ts @@ -1,4 +1,3 @@ -import { Notify } from './function-call'; import type { Numeric } from '../utils'; export type NotifyMessage = Numeric; @@ -20,9 +19,3 @@ export type NotifyOptions = { onClose?: () => void; onOpened?: () => void; }; - -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $notify: typeof Notify; - } -}