22 / 02 / 12
我们在使用 Vue 或 React 等框架构建应用程序时,经常需要编写大量组件,我们并没有将这些组件都写到一个文件中,而是将组件分开,放在各自文件中,这样便是为每个组件创建一个模块。
以 React 为例,通常在某个模块中使用其他模块时,是通过这样的方式来导入并使用的。
import React from 'react'; import Count1 from './Count1.jsx'; import Count2 from './Count2.jsx'; const App = () => { const shouldShowCountIndex = 1; return ( <div> {shouldShowCountIndex === 1 ? <Count1 /> : <Count2 />} </div> ) };
在文件顶部导入所有模块时,所有模块都会在文件的其余部分之前加载。在某些情况下,我们只需要根据某个条件导入一个模块。通过动态导入,我们可以按需导入模块。
import React, { useState, useEffect } from 'react'; const App = () => { const [CountComponent, setCountComponent] = useState(null); useEffect(() => { const loadDynamicComponent = async () => { const module = await import('./Count1.jsx'); setCountComponent(module.default); } loadDynamicComponent(); }, []); return ( <div> {CountComponent ? <CountComponent /> : null} </div> ) };
通过动态导入,我们可以减少页面加载时间。只需要在用户需要的时候,加载真正需要的代码。
另外, import()
还可以使用模版字符串,以此来实现根据动态变量值来动态加载模块。
const module = await import(`./Count${count}.jsx`);
下面我们来看两个场景,是否适合。
场景一:根据不同的变量值使用不同的图片
import React from 'react'; import { fetchImageIndex } from '../api'; import Image1 from '../assets/image1.png'; import Image2 from '../assets/image2.png'; import Image3 from '../assets/image3.png'; import Image4 from '../assets/image4.png'; const App = () => { const [imageIndex, setImageIndex] = useState(1); useEffect(() => { const getImageIndex = async () => { const imageIndex = await fetchImageIndex(); setImage(imageIndex); } getImageIndex(); }, [count]); const image = imageIndex === 1 ? Image1 : imageIndex === 2 ? Image2 : imageIndex === 3 ? Image3 : Image4; return ( <div> // 使用图片资源 image </div> ) };
换成动态导入,我们可以这样写
import React, { useState, useEffect } from 'react'; import { fetchImageIndex } from '../api'; const App = () => { const [image, setImage] = useState(null); useEffect(() => { const loadImage = async () => { const index = await fetchImageIndex(); const resource = await import(`../assets/image${index}.png`); setImage(resource); } loadImage(); }, []); return ( <div> // 使用图片资源 image </div> ) }; }
这样,我们不依赖硬编码的模块路径,如果有 10+ 张的图片,也不会增加页面的加载时间,同时如果新增图片,也不用修改代码。
同样地,也适合动态导入有限个组件的场景。
场景二:动态使用组件库所有 Icon 组件
有一个场景,是根据服务端返回的 icon key ,动态加载对应的 Icon 组件。但是组件库中的 Icon 组件包含 1000+ 个,如果将所有的 Icon 组件都加载进来,然后通过 key 来匹配,那么页面的加载时间就会很长。那么是否可以通过动态导入来解决呢?
很遗憾,如果我们像上面那样,通过动态导入来解决,所有的 Icon 组件都会打入到 JS 文件中,尽管可以通过配置打包策略来分割 JS 文件,但会增加额外的工程复杂度和文件数量开销。
所以这里我们可以采用相对”传统“的字体图标的方式来实现动态图标的需求。通过将 Icon 打包成字体文件,可以实现更好的压缩来减小字体体积。另外,使用方式也相对方便。
<i class="icon icon-add" />
一个 1600 个图标的字体文件测试:
当你遇到性能问题时再去考虑使用动态导入,尤其是某些模块的加载不是页面呈现的关键点时。
当使用动态导入时,需要考虑其额外带来的工程成本和体积成本。有时候,可以通过其他方案来替代。例如动态 Icon 场景。
你有其他的见解吗?
“如果你想有任何想法,欢迎在 X/Twitter 上联系我。”