使用 Vue Demi 构建通用的 Vue 组件库

前端全栈开发者

共 6383字,需浏览 13分钟

 · 2022-02-10

在本文中,我们通过考虑其功能、工作原理以及如何开始使用它来了解 Vue Demi。

Vue Demi 是一个很棒的包,具有很多潜力和实用性。我强烈建议在创建下一个 Vue 库时使用它。

根据创建者 Anthony Fu 的说法,Vue Demi 是一个开发实用程序,它允许用户为 Vue 2 和 Vue 3 编写通用的 Vue 库,而无需担心用户安装的版本。

以前,要创建支持两个目标版本的 Vue 库,我们会使用不同的分支来分离对每个版本的支持。对于现有库来说,这是一个很好的方法,因为它们的代码库通常更稳定。

缺点是,你需要维护两个代码库,这让你的工作量翻倍。对于想要支持Vue的两个目标版本的新Vue库来说,我不推荐这种方法。实施两次功能请求和错误修复根本就不理想。

这就是 Vue Demi 的用武之地。Vue Demi 通过为两个目标版本提供通用支持来解决这个问题,这意味着您只需构建一次即可获得两个目标版本的所有优点,从而获得两全其美的优势。

在本文中,我们将了解 Vue Demi 是做什么的,它是如何工作的,以及如何开始构建一个通用的 Vue 组件库。

Vue Demi 中的额外 API

除了 Vue 已经提供的 API 之外,Vue Demi 还引入了一些额外的 API 来帮助区分用户的环境和执行特定于版本的逻辑。让我们仔细看看它们!

请注意,Vue Demi 还包括 Vue 中已经存在的标准 API,例如 ref、onMounted 和 onUnmounted 等。

isVue2 and isVue3

在 Vue Demi 中,isvue2isvue3 API 允许用户在创建 Vue 库时应用特定于版本的逻辑。

例如:

import { isVue2, isVue3 } from 'vue-demi' 
if (isVue2) {
// Vue 2 only
} else {
// Vue 3 only
}

vue2

Vue Demi 提供了 vue2 API,它允许用户访问 Vue 2 的全局 API,如下所示:

import { Vue2 } from 'vue-demi' 
// in Vue 3 `Vue2` will return undefined
if (Vue2) {
Vue2.config.devtools = true
}

install()

在 Vue 2 中,Composition API 作为插件提供,在使用它之前需要安装在 Vue 实例上:

import Vue from 'vue' 
import VueCompositionAPI from '@vue/composition-api'

Vue.use(VueCompositionAPI)

Vue Demi 会尝试自动安装它,但是对于您想要确保插件安装正确的情况,提供了 install() API 来帮助您。

它作为 Vue.use(VueCompositionAPI) 的安全版本公开:

import { install } from 'vue-demi' 

install()

Vue Demi 入门

要开始使用 Vue Demi,您需要将其安装到您的应用程序中。在本文中,我们将创建一个集成 Paystack 支付网关的 Vue 组件库。

你可以像这样安装 Vue Demi:

// Npm 
npm i vue-demi

// Yarn
yarn add vue-demi

您还需要添加 vue@vue/composition-api 作为库的对等依赖项,以指定它应该支持的版本。

现在我们可以将 Vue Demi 导入我们的应用程序:

<script lang="ts"> 
import {defineComponent, PropType, h, isVue2} from "vue-demi"

export default defineComponent({
// ...
})
script
>

如此处所示,我们可以使用已经存在的标准 Vue API,例如 defineComponentPropTypeh

现在我们已经导入了Vue Demi,让我们来添加我们的props。这些是用户在使用组件库时需要(或不需要,取决于你的口味)传入的属性。

<script lang="ts">
import {defineComponent, PropType, h, isVue2} from "vue-demi"
// Basically this tells the metadata prop what kind of data is should accept
interface MetaData {
[key: string]: any
}

export default defineComponent({
props: {
paystackKey: {
type: String as PropType,
required: true,
},
email: {
type: String as PropType,
required: true,
},
firstname: {
type: String as PropType,
required: true,
},
lastname: {
type: String as PropType,
required: true,
},
amount: {
type: Number as PropType,
required: true,
},
reference: {
type: String as PropType,
required: true,
},
channels: {
type: Array as PropType,
default: () => ["card", "bank"],
},
callback: {
type: Function as PropType<(response: any) => void>,
required: true,
},
close: {
type: Function as PropType<() => void>,
required: true,
},
metadata: {
type: Object as PropType,
default: () => {},
},
currency: {
type: String as PropType,
default: "",
},
plan: {
type: String as PropType,
default: "",
},
quantity: {
type: String as PropType,
default: "",
},
subaccount: {
type: String as PropType,
default: "",
},
splitCode: {
type: String as PropType,
default: "",
},
transactionCharge: {
type: Number as PropType,
default: 0,
},
bearer: {
type: String as PropType,
default: "",
},
}
script
>

上面看到的属性是使用 Paystack 的 Popup JS 所必需的。

Popup JS 提供了一种将 Paystack 集成到我们的网站并开始接收付款的简单方法:

data() {
return {
scriptLoaded: false,
}
},
created() {
this.loadScript()
},
methods: {
async loadScript(): Promise<void> {
const scriptPromise = new Promise((resolve) => {
const script: any = document.createElement("script")
script.defer = true
script.src = "https://js.paystack.co/v1/inline.js"
// Add script to document head
document.getElementsByTagName("head")[0].appendChild(script)
if (script.readyState) {
// IE support
script.onreadystatechange = () => {
if (script.readyState === "complete") {
script.onreadystatechange = null
resolve(true)
}
}
} else {
// Others
script.onload = () => {
resolve(true)
}
}
})
this.scriptLoaded = await scriptPromise
},
payWithPaystack(): void {
if (this.scriptLoaded) {
const paystackOptions = {
key: this.paystackKey,
email: this.email,
firstname: this.firstname,
lastname: this.lastname,
channels: this.channels,
amount: this.amount,
ref: this.reference,
callback: (response: any) => {
this.callback(response)
},
onClose: () => {
this.close()
},
metadata: this.metadata,
currency: this.currency,
plan: this.plan,
quantity: this.quantity,
subaccount: this.subaccount,
split_code: this.splitCode,
transaction_charge: this.transactionCharge,
bearer: this.bearer,
}
const windowEl: any = window
const handler = windowEl.PaystackPop.setup(paystackOptions)
handler.openIframe()
}
},
},

scriptLoaded 状态帮助我们知道是否添加了 Paystack Popup JS 脚本,并且 loadScript 方法加载 Paystack Popup JS 脚本并将其添加到我们的文档头部。

payWithPaystack 方法用于在调用时使用 Paystack Popup JS 初始化交易:

render() {
if (isVue2) {
return h(
"button",
{
staticClass: ["paystack-button"],
style: [{display: "block"}],
attrs: {type: "button"},
on: {click: this.payWithPaystack},
},
this.$slots.default ? this.$slots.default : "PROCEED TO PAYMENT"
)
}
return h(
"button",
{
class: ["paystack-button"],
style: [{display: "block"}],
type: "button",
onClick: this.payWithPaystack,
},
this.$slots.default ? this.$slots.default() : "PROCEED TO PAYMENT"
)
}

render 函数帮助我们创建没有