前言
分享一些个人在vue3 / vue2+compositionApi的另类的方法/思路,组件采用tsx写法。
不注册组件直接使用
可以不注册组件,不把组件写入父组件模版中,一般用于Dialog等组件。
例如我有一个显示用户信息的组件,需要放到Dialog中,但是我又不想在父组件中再写入Dialog组件,会显得很臃肿。例如用户组件如下。
type UserInfo = {
username: string
realname: string
description: string
}
const UserInfo = defineComponent({
props: ['username', 'changeUsername'],
setup(props) {
const user = ref<UserInfo>()
const loadUser = (username: string) => {
Promise.resolve().then(() => {
user.value = {
username,
realname: '最后的hibana',
description: 'hello world'
}
})
}
watchEffect(() => {
if (props.username) {
loadUser(props.username)
}
})
return () => (
<div>
<p>用户名:{user.value?.username}</p>
<p>姓名:{user.value?.realname}</p>
<p>描述:{user.value?.description}</p>
<button onClick={() => props.changeUsername()}>切换用户</button>
</div>
)
}
})
如果直接写入到父组件中,一般会这样写:
export default defineComponent({
setup(){
//...其他代码
const visible = ref(false)
const username = ref('hibana')
function changeUsername() {
username.value = 'hibana' + Math.random().toFixed(1)
}
return () => (
// ...其他代码
// 把Dialog写入到tsx中
<MyDialog visible={visible.value} title="用户信息">
<UserInfo username={username.value} changeUsername={changeUsername}>
</MyDialog>
)
},
})
如果父组件中已经有很多代码了,或者也有一些其他dialog,再写入dialog的话,就会显得臃肿,查看代码起来也不方便。
如果说有一种方法,可以不在父组件中写dialog,直接在setup中用的话,岂不是很舒服。
例如下面这样:
export default defineComponent({
setup(props, ctx) {
const username = ref('hibana')
function changeUsername() {
username.value = 'hibana' + Math.random().toFixed(1)
}
const { openDialog, closeDialog } = useDialog(
UserInfo,
{ username, changeUsername },
{ title: '用户信息' }
)
return () => (
// ...其他代码
)
}
})
这样就不需要在父组件中写入dialog了。那么接下来就来简单的实现一下这个useDialog方法。
export const useDialog = (
Component: any,
props: Record<string, any> = {},
dialogProps: Record<string, any> = {}
) => {
const visible = ref(false)
const openDialog = () => {
visible.value = true
}
const closeDialog = () => {
visible.value = false
}
const DiaologComponent = () => (
<MyDialog visible={visible.value} {...{ ...dialogProps }}>
<Component {...{ ...props }}></Component>
</MyDialog>
)
const app = createRender(DiaologComponent)
return {
visible,
openDialog,
closeDialog,
app
}
}
function createRender(component: any, props?: Record<string, any>) {
const container = document.createElement('div')
const app = createApp(component, props)
app.mount(container)
document.body.appendChild(container)
return app
}
其实思路就是在内部将需要dialog的组件传入dialog组件中,然后通过createApp挂载到dom上,最后通过闭包导出方法。
目前这个useDialog方法是比较简单的,也有缺点,由于是创建了又一个app,所以用户组件用inject是接收不到父组件的provide的,在原来app中的挂载的东西,可能也用不了。