点击这里直接查看 DEMO
Vue 3.0 正式版发布到现在已经有半年了,官方插件 vue-router 和 vuex 也都是稳定版的了。一个相当好用的组件库 ant-design-vue 也出了正式版2.0,它是全面兼容 Vue3 的,同样好用的移动端组件库 Vant 也已经早早兼容 Vue3 。嗯,距离全面拥抱 Vue3 的开发已经不远了…
最近主要研究了一下它的新特性,并结合 ant-design-vue 写了一个试水 demo。本篇介绍 Vue3 中是如何开发组件的,下面先上代码:
<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>
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 。然后这样:
value
-> modelValue
input
-> update:modelValue
另外,在 Vue3 中新增了一个 emits
选项,用来定义我们触发事件的名称,跟 props
很像,支持验证功能。
当然 Vue3 中也不是非要写组合式 api ,它同样保留了以前 Vue2 中的选项式 api,对于钟爱它的人又可以愉快的玩耍了。
某些时候使用 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>
)
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')
}
更多细节参考源码