在vue中的一些另类的方法/思路...(持续更新)

791 阅读2分钟

前言

分享一些个人在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中的挂载的东西,可能也用不了。

未完待续