当下的新封装Composition API:突破性思维

“别忘了在使用时引入相应的 CSS 哦!最近我正在开发一个基于 Vue3 的后台管理模板,自然少不了 NProgress。如果你没用过,可以参考 Instagram、YouTube 这类网站,它们都有顶部的加载条,这已成为当今最流行的网页加载指示器之一。

我发现了 @vueuse/integrations 中的 useNProgress,是 Anthony Fu 大神的杰作。我们先看看他是如何封装的,然后再试着自己动手实现。

安装和基本使用

首先,我们通过 npm 安装 NProgress 和 @vueuse/integrations:

npm i nprogress @vueuse/integrations</code>

在定义 Vue Router 时,我们可以这样使用:

<code><strong>import</strong> { createRouter, createWebHashHistory } <strong>from</strong> 'vue-router' <strong>import</strong> { useNProgress } <strong>from</strong> '@vueuse/integrations/useNProgress' <em>// 通过 useNProgress,导出一个 isLoading</em> <strong>const</strong> { isLoading } = <strong>useNProgress</strong>(null, { showSpinner: false }) <strong>const</strong> router = <strong>createRouter</strong>({ history: <strong>createWebHashHistory</strong>(), routes: [<em>/* 省略路由 */</em>] }) <em>// 在路由进入和离开时,改变 isLoading</em> router.<strong>beforeEach</strong>((to, <strong>from</strong>, next) => { isLoading.value = true <strong>next</strong>() }) router.<strong>afterEach</strong>((to, <strong>from</strong>, next) => { isLoading.value = false <strong>next</strong>() }) <strong>export</strong> <strong>default</strong> router</code>

这样就实现了一个基本的进度条,具体效果你可以自行尝试。

我对此感到惊讶,从未想过第三方库也能通过 Composition API 与 Vue 结合得如此紧密。而且,这样的封装理论上也适用于跨框架使用。

自行实现一个

首先,我们定义一个入口函数,并使用 ref 来定义一个 isLoading。为了兼容 Vue 2,我们可以使用 vue-demi 这个库:

<code><strong>import</strong> { ref, watch } <strong>from</strong> 'vue-demi' <strong>import</strong> nprogress <strong>from</strong> 'nprogress' <strong>export</strong> <strong>const</strong> <strong>useNProgress</strong> = () => { <strong>const</strong> isLoading = <strong>ref</strong>(false) <strong>watch</strong>(isLoading, (newVal) => { <strong>if</strong> (newVal) { <strong>return</strong> nprogress.<strong>start</strong>() } <strong>return</strong> nprogress.<strong>done</strong>() }) <strong>return</strong> { isLoading } } <strong>export</strong> <strong>default</strong> useNProgress</code>

这样就完成了基本的封装,但并不完美。watch 并不是最佳解决方案,我还有两种更好的方法。

  1. 使用 computed + set:computed 可以修改,只需指定其 set 方法即可。
  2. 摒弃变量控制,直接使用原始 API,如 start、done。但这样做没有必要封装。

接下来,我们修改代码,使用 computed。这样实现了与 watch 相同的效果,同时也吸收了 computed 的优点。同时,我们也将 NProgress 的一些原生 API 导出,以实现更高的灵活性:

<code><strong>import</strong> { ref, computed } <strong>from</strong> 'vue-demi' <strong>import</strong> nprogress <strong>from</strong> 'nprogress' <strong>export</strong> <strong>const</strong> <strong>useNProgress</strong> = (instance, options) => { <strong>const</strong> isLoading = <strong>computed</strong>({ get: () => nprogress.<strong>isStarted</strong>() && nprogress.value < 1, set: (status) => status ? nprogress.<strong>start</strong>() : nprogress.<strong>done</strong>() }) <strong>return</strong> { isLoading, start: nprogress.start, done: nprogress.done } }</code>

有时我们不仅使用默认样式或配置,这时需要传入配置。有时我们可能会自定义 NProgress 的实例,因此我将第一个参数命名为 instance,第二个参数为 options,这里无需过多考虑,直接调用相关 API 即可:

<code><strong>const</strong> progress = <strong>ref</strong>(instance) <strong>if</strong> (options) { nprogress.<strong>configure</strong>(options) }</code>

在 NProgress 中有一个有趣的 API inc,它表示每次前进一点。我们可以将这个功能封装进去:

<code><strong>import</strong> { ref, computed, watchEffect, unref } <strong>from</strong> 'vue-demi' <strong>import</strong> nprogress <strong>from</strong> 'nprogress' <strong>export</strong> <strong>const</strong> <strong>useNProgress</strong> = (instance, options) => { <strong>const</strong> isLoading = <strong>computed</strong>({ get: () => nprogress.<strong>isStarted</strong>() && nprogress.value < 1, set: (status) => status ? nprogress.<strong>start</strong>() : nprogress.<strong>done</strong>() }) <strong>const</strong> progress = <strong>ref</strong>(instance) <strong>if</strong> (options) { nprogress.<strong>configure</strong>(options) } <strong>watchEffect</strong>(() => { <strong>if</strong> (<strong>typeof</strong> <strong>unref</strong>(progress) === 'number') { nprogress.<strong>set</strong>(progress.value) } }) <strong>return</strong> { isLoading, progress, step: nprogress.inc, start: nprogress.start, done: nprogress.done, reset: () => progress.value = 0 } }</code>

这样,当我们使用时,可以像一开始那样直接使用 isLoading,也可以使用 step() 实现逐步加载,也可以直接传入一个数字:

<code><strong>const</strong> { isLoading, progress, step } = <strong>useNProgress</strong>(0.1, null) progress.value = 0.2 <em>// 进度条加载到 0.2</em> <strong>step</strong>() <em>// 每次自动前进一点</em> isLoading.value = false <em>// 直接结束加载</em></code>

这样的封装使用起来更方便了。而且,由于我没有把 Vue Router 和这个耦合在一起,理论上 React 也可以使用。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容