Article 三月 15, 2021

vue3入门——插件开发

Words count 6.6k Reading time 6 mins. Read count 0

效果图

在这里插入图片描述

点击这里直接查看 DEMO

前言

Vue 3.0 正式版发布到现在已经有半年了,官方插件 vue-router 和 vuex 也都是稳定版的了。一个相当好用的组件库 ant-design-vue 也出了正式版2.0,它是全面兼容 Vue3 的,同样好用的移动端组件库 Vant 也已经早早兼容 Vue3 。嗯🤔,距离全面拥抱 Vue3 的开发已经不远了…

最近主要研究了一下它的新特性,并结合 ant-design-vue 写了一个试水 demo。本篇介绍 Vue3 中是如何开发组件的,下面先上代码:

template

<div class="d-input" :class="rootClass">
  <div class="d-input-placeholder">{{ placeholder }}</div>
  <input
    ref="root"
    autocomplete="new-password"
    :type="type"
    :readonly="readonly"
    :disabled="disabled"
    :value="modelValue"
    @focus="focusHandler"
    @blur="blurHandler"
    @input="inputHandler"
  >
</div>

script

import { ref, reactive, computed, watchEffect } from 'vue'
import { useInputStatus } from './useInput'

export default {
  name: 'DInput',
  inheritAttrs: false,
  props: {
    modelValue: {
      type: [String, Number],
      default: ''
    },
    placeholder: {
      type: String,
      default: '请输入内容...'
    },
    readonly: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    // 仅支持文本框和密码框
    type: {
      type: String,
      default: 'text',
      validator: (value: string) => ['text', 'password'].includes(value)
    }
  },
  emits: ['update:modelValue', 'focus', 'blur'],
  setup (props: any, { emit }: any) {
    const root = ref(null)
    const status = reactive({
      active: false, // 聚焦 || 有值,placeholder上移,边框变红
      filled: true // 失焦,边框变灰(会同时placeholder上移,边框变灰)
    })
    const isFocus = ref(false)

    const rootClass = computed(() => ({
      active: status.active,
      filled: status.filled
    }))

    watchEffect(() => {
      status.active = isFocus.value || !!props.modelValue
    })

    function focusHandler (e: Event) {
      isFocus.value = true
      useInputStatus(status, props.modelValue, 'focus')
      emit('focus', e)
    }

    function blurHandler (e: Event) {
      isFocus.value = false
      useInputStatus(status, props.modelValue, 'blur')
      emit('blur', e)
    }

    function inputHandler (event: { target: { value: any } }) {
      const { value } = event.target

      emit('update:modelValue', value)
    }

    return {
      root,
      status,
      rootClass,
      focusHandler,
      blurHandler,
      inputHandler
    }
  }
}

以上代码是我直接将之前写的 vue-dz-ui 中的 组件 移植过来的,模板绑定语法几乎没有区别。比较大的区别就是从 Vue2 的选项式 api 变成了现在的 Vue3 组合式 api 。然后这样:

  • prop:value -> modelValue
  • event:input -> update:modelValue

另外,在 Vue3 中新增了一个 emits 选项,用来定义我们触发事件的名称,跟 props 很像,支持验证功能。

当然 Vue3 中也不是非要写组合式 api ,它同样保留了以前 Vue2 中的选项式 api,对于钟爱它的人又可以愉快的玩耍了。

JSX版

某些时候使用 jsx 能更灵活的编写我们的组件,以上的 .vue 组件可以很快的转为 jsx 组件,只需要以下几步:

  • 新建 .tsx 文件

  • 将 .vue 中 script 标签内容直接复制过来

  • 将 setup 函数返回值改成:

    return () => (
      <div class={rootClass.value}>
        <div class="d-input-placeholder">{props.placeholder}</div>
        <input
          ref={root}
          autocomplete="new-password"
          type={props.type}
          readonly={props.readonly}
          disabled={props.disabled}
          value={props.modelValue}
          onFocus={focusHandler}
          onBlur={blurHandler}
          onInput={inputHandler}
        />
      </div>
    )

install 函数

import { App } from 'vue'
import DInput from './d-input/DInput.vue'
import DInputJSX from './d-input-jsx'

const components = [DInput, DInputJSX]

const install = (app: App) => {
  components.forEach(component => app.component(component.name, component))
}

export default install

如何引入

由于 Vue3 中废弃了 Vue.use() 方法,采用了一个 App 的概念,使用 createApp 创建一个实例,然后像这样:

import { createApp } from 'vue'
import vueDzUi from '@/components/vue3-dz-ui/lib'

createApp(App).use(vueDzUi)

这样就能在 该应用实例 内使用这个组件了。对于函数式调用的组件(例如:message 组件)在 Vue2 中是直接挂在 Vue 的 prototype 属性上,因为每个组件都是 Vue 的实例,所以能够访问到原型上的属性。Vue3 不再推荐这种方式了,它变成这样:

import { message } from 'ant-design-vue'
createApp(App).config.globalProperties.$message = message

另外,在 setup 函数中没有 this 的概念,所以不能随心所欲的使用 this 来调用几乎所有的功能了,它改成这样:

import { getCurrentInstance, ComponentInternalInstance } from 'vue'

setup () {
    const { globalProperties } = (getCurrentInstance() as ComponentInternalInstance).appContext.config
    globalProperties.$message.success('Hello Vue3')
}

源码

更多细节参考源码

0%