异步组件

在现代前端应用中,按需加载是优化加载体验与渲染性能的重要手段。异步组件(Async Component)正是为此而生的一种机制:它允许你在真正需要某个组件时再加载,而不是在初始阶段就将其打包进主应用中。这种方式不仅能有效减少初次加载体积,还能改善资源加载速度,并提升首屏渲染性能;同时也便于配合路由或条件渲染进行更高效的资源管理。

在 Qingkuai 中,渲染异步组件有两种方式:

直接渲染时,组件标签可以直接绑定返回组件的 Promise动态导入的结果,编译器会把组件标签统一交由运行时处理,在 Promise 解析后取出其中的组件函数进行渲染。


动态导入

很多构建工具会对 动态导入 的模块进行代码分割优化。对于组件,我们同样可以利用这一点,把 import() 返回的 Promise 直接作为组件标签:

qk
<lang-js>
    import SyncPanel from "./SyncPanel.qk"

    // 手动构造 Promise,返回已导入的组件
    const AsyncPanel = new Promise(resolve => {
        setTimeout(() => {
            resolve(SyncPanel)
        }, 1000)
    })

    const AsyncModule = import("./AsyncModule.qk")
</lang-js>

<AsyncPanel />
<AsyncModule />

若需要展示加载中或加载失败的状态,则需要搭配使用 异步处理 指令:

qk
<div #await={import("./Component.qk")}>
    Loading...
</div>
<qk:spread #then={Module}>
    <Module.default />
</qk:spread>
<div #catch>Fail to load Component.qk</div>

这里省略 default 属性访问或在 #then 中解构出组件标识符后再使用都是支持的:

qk
<qk:spread #then={Module}>
    <Module />
</qk:spread>
qk
<qk:spread #then={{default: Component}}>
    <Component />
</qk:spread>

异步返回

除了直接使用动态导入,我们还可以在自定义的异步逻辑中返回组件,例如根据某个条件决定加载哪个组件:

qk
<lang-js>
    import Comp1 from "./Comp1.qk"
    import Comp2 from "./Comp2.qk"

    async function getComponent() {
        return (await isOk()) ? Comp1 : Comp2
    }

    const CurrentView = getComponent()
</lang-js>

<CurrentView />

搭配异步处理指令:

qk
<lang-js>
    import Comp1 from "./Comp1.qk"
    import Comp2 from "./Comp2.qk"

    async function getComponent() {
        return (await isOk()) ? Comp1 : Comp2
    }
</lang-js>

<div #await={getComponent()}>Loading...</div>
<qk:spread #then={Comp}>
    <Comp />
</qk:spread>
<div #catch={err}>Error: {err}</div>

动态切换

把异步组件赋值给 let 声明的响应式变量,即可在运行时动态切换组件:

qk
<lang-js>
    let CurrentView = import("./AsyncOne.qk")

    const switchView = () => {
        CurrentView = import("./AsyncTwo.qk")
    }
</lang-js>

<CurrentView />
<button @click={switchView}>Switch</button>

搭配异步处理指令:

qk
<lang-js>
    let Module = import("./AsyncOne.qk")

    const switchView = () => {
        Module = import("./AsyncTwo.qk")
    }
</lang-js>


<div #await={Module}>Loading...</div>
<qk:spread #then={View}>
    <View />
</qk:spread>
<div #catch={err}>Error: {err}</div>
<button @click={switchView}>Switch</button>

实例获取

与普通组件标签一样,异步组件标签同样支持 &handle 引用属性,可以正常获取组件实例并访问其导出的成员:

qk
<lang-js>
    import { onAfterMount } from "qingkuai"

    const AsyncView = import("./AsyncOne.qk")
    let asyncView

    onAfterMount(() => {
        console.log(asyncView) // 异步组件实例
    })
</lang-js>

<AsyncView &handle={asyncView} />

搭配异步处理指令:

qk
<lang-js>
    import { onAfterMount } from "qingkuai"

    let asyncView

    onAfterMount(() => {
        console.log(asyncView) // 异步组件实例
    })
</lang-js>

<div #await={import("./AsyncOne.qk")}>Loading...</div>
<qk:spread #then={{default: Component}}>
    <Component &handle={asyncView} />
</qk:spread>

在github上编辑此页面