Webpack 代码分割 动态导入的实践例子

代码分割,动态导入的场景

简单来说,就是例如我们的页面中,有一个组件内部实现逻辑非常庞大,引入了
许多其他的包,导致该页面整体体积过大,影响首屏渲染速度,此时我们期望优先加载
其他内容,最后再去加载这块组件内容,避免首屏渲染时间过长。也就是从chunks再拆分出
部分逻辑,单独生成一个script文件,在我们需要的时机进行请求该模块的script文件,再将
它显示出来,这样就是代码分割,动态引入的一个经典场景。

动态导入的实现原理

动态导入,本质上是来自于ECMA关于异步import语法的提案,该语法是让import支持异步引入。
例如import('./xxxfile').then(res=>{}),使得import语法promise异步化,res中就可以拿到
该模块的语法资源,再由开发者对其进行处理,例如,在webpack的实现中,引入一个react组件的res是这样的
一个JS对象:
image

其中default便是该模块默认到处的内容,也就是我们默认导出的组件了。

实践例子

1.模拟一下场景,我们有一个需要被拆分的 react 组件 DynamicTest.js

import React from 'react';
import styles from './DynamicTest.less';

function DynamicTest() {
  // 实际案例中,这里的实现逻辑可能非常复杂,我们想把它单独拎出来
  return <div className={styles.root}>动态加载组件内容</div>;
}

export default DynamicTest;

以及其组件样式文件 ./DynamicTest.less,
现在我们期望把这个文件和其样式文件单独从打包chunk中拆分出来。

原来的webpack输出:

chunk.js      
chunk.css

期望的webpack输出:

chunk.js
chunk.css          
DynamicTest.js               
DynamicTest.chunk.css        

2.对动态导入的逻辑进行封装

在对DynamicTest进行动态导入的时候,我们期望能实现以下功能 createLoadable:

  • 资源正在加载的时候,显示Loading占位符组件
  • 资源加载错误的时候,显示Error占位符组件
  • 资源加载完成的时候,显示DynamicTest组件
  • 允许设置一个delay,来延迟加载的时机

也就是以下的调用例子,createLoadable传入以上需求的配置,返回一个可动态导入的react组件:

const DynamicTest = createLoadable({
  // loader(func),返回动态导入的文件路径
  // 其中 /* webpackChunkName: "DynamicTest" */ 是webpack规定的,来命名打包后文件的名字
  loader: () => import(/* webpackChunkName: "DynamicTest" */ './DynamicTest'),
  // loading(reactComponent) react组件,props是loading(正在加载)以及err(发生错误)
  // 我们可以根据其props进行样式定制化
  loading: ({loading,err})=> loading ? 'loading' : err ? 'error': null,
  // delay(ms) 延迟加载的时间
  delay: 5000,
});

在我们的页面中,调用DynamicTest组件

export default () => { 
  return (
    <Wrapper>
      <DynamicTest/>
    </Wrapper>
  );
};
  1. createLoadable的实现逻辑
import React, { useState, useEffect } from 'react';

// loader组件,控制动态引入的状态
function Loader({ loader, loading, delay, loadedComponentProps }) {
  // import进来的模块内容
  const [loaded, setLoaded] = useState(null);
  // err引入发生的错误,isLoading是否正在加载中
  const [err, setErr] = useState(null);
  const [isLoading, setLoading] = useState(false);

  const load = () => {
    loader()
      .then(_loaded => {
        // log =>{__esModule: true, Symbol(Symbol.toStringTag): "Module", default: ƒ}
        setLoaded(_loaded);
      })
      .catch(_err => {
        setErr(_err);
      })
      .finally(() => {
        setLoading(false);
      });
  };
  useEffect(() => {
    setLoading(true);
    let h = null;
    if (delay) {
      h = setTimeout(load, delay);
    } else {
      load();
    }
    return () => {
      clearTimeout(h);
    };
  }, []);

  // 加载中或者错误
  // 返回占位符组件
  if (isLoading || err) {
    return React.createElement(loading, { isLoading, err });
  }

  // 加载成功
  // 返回加载成功后的组件
  if (loaded) {
    const loadedComponent =
      loaded && loaded.__esModule ? loaded.default : loaded;

    return React.createElement(loadedComponent, loadedComponentProps);
  }

  return null;
}

// 默认的占位符组件
const DefaultLoading = ({ loading, error }) => {
  // 组件正在异步加载的时候
  if (loading) return <div>Loading</div>;
  // 组件加载发生了错误的时候
  if (error) return <div>Unknown error occurred</div>;
  return null;
};

/**
 * @method createLoadable
 * @desc 通过import动态导入的语法,返回一个React组件,在合适的时机展示loading与err以及异步加载组件的内容
 * @param {loadr|fn}  () => import('./DynamicTest') 动态引入
 * @param {loading|component}  占位符组件,会被注入isLoading、err的props
 * @param {delay|ms}  延迟加载的时间
 * */
const createLoadable = ({ loader, loading = DefaultLoading, delay = 0 }) => {
  return props =>
    React.createElement(Loader, {
      loader,
      loading,
      delay,
      loadedComponentProps: props,
    });
};

export default createLoadable;

createLoadable的实现简而言之,就是创建了一个react组件loader,该组件帮助我们来管理import动态导入时的状态,
加载中、加载失败、以及加载完成,再去选择展示的组件,以及加上了延迟加载的功能需要。

预览

  1. 正在加载的时候
    image

  2. 加载完成,展示组件内容

  3. network中单独出来的chunk

在这里插入图片描述

其他:如何修改打包后的分片文件名

1.使用webpack,修改

output: {
 chunkFilename: '[name].bundle.js',
}

2.使用webpack-chain

config.output.chunkFilename(’[name].bundle.js’);

output中的chunkFilename其实就是entry中没有的其他chunk的命名规则。

总结

以上便是在react中如何使用webpack的动态导入特性的一个实践例子了,其实就是利用了webpack对import异步化语法的支持,
文件拆分的逻辑由webpack完成,我们通过编写一个组件来管理动态引入可能出现的几种状态,来显示我们期望的内容,甚至还可以
在导入失败的时候重新加载。

参考

y源码参考-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
webpack代码分割是一种优化技术,它可以将代码分割成多个小块,以减小单个文件的大小并提高加载速度。在webpack中,有两种常见的代码分割方法可以使用:代码分割和公共代码提取。 代码分割是指将代码分割成多个不同的块,避免将所有的代码打包到一个文件中。这样可以实现按需加载,只加载当前需要的代码。在webpack中,可以使用动态导入(import())或旧版本的require.ensure来实现代码分割动态导入是一种符合ECMAScript提案的语法,可以在运行时动态加载模块。而require.ensure是webpack的遗留功能,可以实现异步模块加载。 公共代码提取是指将多个入口文件中的共享代码提取出来,以减少重复加载和减小文件大小。webpack提供了SplitChunksPlugin插件来实现公共代码提取。通过配置webpack.config.js文件中的optimization.splitChunks选项,可以将共享依赖模块提取成一个单独的文件,供多个入口文件共享使用。 在项目结构中,可以通过Webpack配置文件(webpack.config.js)来指定代码分割的行为。通过配置entry和output选项,可以指定入口文件和输出文件的位置。同时,通过配置optimization选项,可以进行代码分割和公共代码提取的配置。 总结起来,webpack代码分割是通过将代码分割成多个小块来优化性能的一种技术。可以使用动态导入或require.ensure来实现代码分割,也可以使用SplitChunksPlugin插件来实现公共代码提取。通过合理配置webpack.config.js文件,可以实现代码分割和公共代码提取的优化效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值