【前端项目优化】React&Vue使用异步组件+代码分包+Suspense减少首页白屏时间

使用异步组件+Suspense的形式来实现代码分包是前端项目优化的有效&常用手段之一。

何为代码分包?

未进行代码分包的打包文件结构:
image.png
如果项目里用了第三方库的话(echarts、elementUI…),理论上还会有一个vendor.xxx.js文件。 index.xxx.js里是我们项目打包后的主文件代码。
进行代码分包的打包文件结构:

image.png
通过上述两个图的对比,可以发现,代码分包指的是打包后的项目js文件有多个。
代码分包的直观好处就是减少项目线上环境的首页白屏时间。
首页白屏时间是衡量项目的重要指标之一。首页白屏时间越短,用户体验越好。
代码分包把那些需要异步加载的组件单独形成一个js文件,在使用这些组件时才动态引入。
在首页加载过程中只会引入主js文件,从而实现减少首页白屏时间的目的。

何为异步组件?

所谓异步组件,指的是组件内的数据需要等待服务器响应结果后才能显示在页面上。
在没有收到服务器响应的结果之前,页面不会渲染这个组件。
乍一听,这件事同步组件也可以干呀,同步组件在第一次渲染的时候因为服务器还没有返回数据,因此页面渲染是空的,等前端接收到服务器返回的数据后,可以通过重新渲染把数据填充到页面呀~
但是有追求的程序员应该发现了:用同步组件干异步组件的事,会导致页面的不必要渲染。

Vue3生成异步组件&进行代码分包

通过defineAsyncComponent加载异步配合import函数模式导入异步组件,在Template模块使用Suspense控制异步组件的显示,完成异步组件的生成后,打包时就会自动实现分包。
我们可以生成一个json文件来模拟服务器端的响应结果:

// data.json
[
    {
        "name": "苹果"
    },
    {
        "name": "菠萝"
    },
    {
        "name": "西瓜"
    },
    {
        "name": "葡萄"
    }
]

然后编写一个简易版的axios来处理响应结果:

// server.ts
type NameList = {
    name: string
}

export const axios = (url:string):Promise<NameList[]> => {
    return new Promise((resolve) => {
        let xhr: XMLHttpRequest = new XMLHttpRequest()

        xhr.open('GET', url)

        xhr.onreadystatechange = () => {
            if(xhr.readyState === 4 && xhr.status === 200){
                setTimeout(() => {
                    resolve(JSON.parse(xhr.responseText))
                }, 2000)
            }
        }

        xhr.send()
    })
}

在异步组件中发起请求:

// 子组件.vue
<template>
    <div>
       <div v-for="(item, index) in list" :key="index">
            {{item.name}}
       </div>
    </div>
</template>

<script lang='ts' setup>
import { axios } from './server'

// 这个请求在项目跑起来之后,data.json文件默认就会去项目的public文件夹下面找
const list = await axios('./data.json')
</script>
 
<style lang = "less" scoped>
    
</style>

在父组件内通过defineAsyncComponent加载异步配合import引入,再使用Suspense控制显示即可

// 父组件.vue
<template>
    <div class="content">
        <Suspense>
            <template #default>
                <A></A>
            </template>
            <template #fallback>
                <div>
                    loading
                </div>
            </template>
        </Suspense>
    </div>
</template>

<script lang='ts' setup>
import { reactive, ref, defineAsyncComponent } from 'vue'

const A = defineAsyncComponent(() => import('../../DynamicComponent/A/A.vue'))
</script>

<style lang = "less" scoped>
    
</style>

页面过了定时2秒后成功加载异步组件:

image.png
然后执行npm run build,webpack或vite等打包工具就会帮我们自动分包:

image.png

需要注意的是,对于异步组件,在相关被引用的父组件里,应该都以defineAsyncComponent的方式进行引入。不能在某些父组件里以异步的方式进行引入,另一些又以同步的方式(直接使用import)进行引入。否则打包时会出现粘包问题,即没有把异步代码进行分离。

React生成异步组件&进行代码分包

按照React官网的说法:

在你的应用中引入代码分割的最佳方式是通过动态import()语法。

使用之前:

import { add } from './math';

console.log(add(16, 26));

使用之后:

import("./math").then(math => {
  console.log(math.add(16, 26));
});

当Webpack解析到该语法时,会自动进行代码分割(代码分包)。
React使用异步组件实现代码分包的代码示例如下:
和Vue重复的data.json和server.ts就不重复写了~直接上子组件和父组件:

// 异步子组件
import React, {useEffect, useState} from 'react'
import {axios} from "./server";

type NameList = {
    name: string
}
const ChildrenComponent = () => {
    const [list, setList] = useState<NameList[]>([]);

    const getData = async () => {
        return await axios("./data.json")
    }

    useEffect(() => {
        getData().then(data => setList(data))
    }, []);

    return (
        <div>
            {list.map((item) => (
                <p>{item.name}</p>
            ))}
        </div>
    )
}

export default ChildrenComponent;
// 父组件
import React,{lazy, Suspense} from 'react'

export const FatherComponent = () => {
    const ChildrenComponent = lazy(() => import('./ChildrenComponent'));

    return (
        <div>
            <Suspense fallback={<div>Loading...</div>}>
                <ChildrenComponent />
            </Suspense>
        </div>
    )
}

在实践React异步组件时,踩得一个坑是:异步子组件的导出形式必须使用default,否则父组件使用import导入时会标红。鼠标移上去,错误提示的意思就是子组件必须用default进行导出~
后面发现官网针对这点在文章最后也做了解释:

React.lazy 目前只支持默认导出(default exports)。如果你想被引入的模块使用命名导出(named exports),你可以创建一个中间模块,来重新导出为默认模块。这能保证 tree shaking 不会出错,并且不必引入不需要的组件

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

总结

Vue&React使用异步组件进行代码分包总结如下:

  • 相同点
    通过上述代码示例,可以发现Vue和React在引入异步组件时,都使用了Suspense这个组件,都采用动态import的方式进行异步组件的导入。
  • 不同点
    Vue引入动态import采用的是defineAsyncComponent,而React使用的是lazy。

项目优化的手段有很多,除了异步组件,还可以通过路由懒加载的方式来进行代码分包,从而实现减少首页白屏时间的目的,感觉真不错~

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coding101

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值