5
头图

In this article, we take a look at Vue Demi by considering its features, how it works, and how to get started with it.

Vue Demi is a great package with a lot of potential and utility. I highly recommend using it when creating your next Vue library.

According to creator Anthony Fu, Vue Demi is a development utility that allows users to write common Vue libraries for Vue 2 and Vue 3 without worrying about the user-installed version.

Previously, to create a Vue library that supported two target versions, we would use a different branch to separate support for each version. This is a good approach for existing libraries, as their codebases are generally more stable.

The downside is that you need to maintain two codebases, which doubles your workload. I don't recommend this approach for new Vue libraries that want to support both target versions of Vue. Implementing two feature requests and bug fixes is not ideal at all.

That's where Vue Demi comes in. Vue Demi solves this problem by providing common support for both target versions, which means you only need to build once and get all the benefits of both target versions for the best of both worlds.

In this article, we'll learn what Vue Demi does, how it works, and how to get started building a general-purpose Vue component library.

Additional APIs in Vue Demi

In addition to the APIs already provided by Vue, Vue Demi introduces some additional APIs to help differentiate the user's environment and execute version-specific logic. Let's take a closer look at them!

Note that Vue Demi also includes standard APIs already present in Vue, such as ref, onMounted and onUnmounted, etc.

isVue2 and isVue3

In Vue Demi, the isvue2 and isvue3 APIs allow users to apply version-specific logic when creating a Vue library.

For example:

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

vue2

Vue Demi provides the vue2 API, which allows users to access Vue 2's global API as follows:

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

install()

In Vue 2, the Composition API is available as a plugin and needs to be installed on a Vue instance before using it:

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

Vue.use(VueCompositionAPI)

Vue Demi will try to install it automatically, but in case you want to make sure the plugin is installed correctly, the install() API is provided to help you.

It is exposed as a secure version of Vue.use(VueCompositionAPI) :

import { install } from 'vue-demi' 

install()

Getting Started with Vue Demi

To start using Vue Demi, you need to install it into your application. In this article, we will create a Vue component library that integrates the Paystack payment gateway.

You can install Vue Demi like this:

// Npm 
npm i vue-demi 

// Yarn 
yarn add vue-demi

You also need to add vue and @vue/composition-api as peer dependencies of the library to specify the version it should support.

Now we can import Vue Demi into our application:

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

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

As shown here, we can use the standard Vue APIs that already exist, such as defineComponent , PropType , and h .

Now that we've imported Vue Demi, let's add our props. These are the attributes that users need (or don't need, depending on your taste) to pass in when using the component library.

<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<string>,
      required: true,
    },
    email: {
      type: String as PropType<string>,
      required: true,
    },
    firstname: {
      type: String as PropType<string>,
      required: true,
    },
    lastname: {
      type: String as PropType<string>,
      required: true,
    },
    amount: {
      type: Number as PropType<number>,
      required: true,
    },
    reference: {
      type: String as PropType<string>,
      required: true,
    },
    channels: {
      type: Array as PropType<string[]>,
      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<MetaData>,
      default: () => {},
    },
    currency: {
      type: String as PropType<string>,
      default: "",
    },
    plan: {
      type: String as PropType<string>,
      default: "",
    },
    quantity: {
      type: String as PropType<string>,
      default: "",
    },
    subaccount: {
      type: String as PropType<string>,
      default: "",
    },
    splitCode: {
      type: String as PropType<string>,
      default: "",
    },
    transactionCharge: {
      type: Number as PropType<number>,
      default: 0,
    },
    bearer: {
      type: String as PropType<string>,
      default: "",
    },
  }
</script>

The properties seen above are required to use Paystack's Popup JS.

Popup JS provides an easy way to integrate Paystack to our website and start receiving payments:

data() {
    return {
      scriptLoaded: false,
    }
  },
  created() {
    this.loadScript()
  },
  methods: {
    async loadScript(): Promise<void> {
      const scriptPromise = new Promise<boolean>((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 status helps us know if the Paystack Popup JS script is added, and the loadScript method loads the Paystack Popup JS script and adds it to the head of our document.

payWithPaystack method is used to initialize the transaction using Paystack Popup JS when called:

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"
    )
}

The render function helps us create a component without the <template> tag and returns a virtual DOM node.

If you notice, we use one of Vue Demi's APIs, isVue2 , in the conditional statement to conditionally render our button. Without this, if we wanted to use our component library in a Vue 2 app, we might run into errors because Vue 2 doesn't support some of Vue 3's APIs.

Now, when we build our library, it will be accessible in Vue 2 and Vue 3.

The full source code for is available here: https://github.com/ECJ222/vue-paystack2


杭州程序员张张
11.8k 声望6.7k 粉丝

Web/Flutter/独立开发者/铲屎官