最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一种简单实用的 JS 动态加载方案

    正文概述 掘金(傅剑寒)   2021-04-21   462

    背景

    在做 Web 应用的时候,你或许遇到过这样的场景:为了实现一个使用率很低的功能,却引入了超大的第三方库,导致项目打包后的 JS bundle 体积急剧膨胀。

    我们有一些具体的案例,例如:

    产品要求在项目中增加一个导出数据为 Excel 文件的功能,这个功能其实只有管理员才能看到,而且最多一周才会使用一次,绝对属于低频操作。

    团队里的小伙伴为了实现这个功能,引入了 XLSX 这个库,JS bundle 体积因而增加了一倍,所有用户的体验都受到影响了。

    XLSX 用来做 Excel 相关的操作是不错的选择,但因为新增低频操作影响全部用户却不值得。

    除了导出 Excel 这种功能外,类似的场景还有使用 html2canvas 生成并下载海报,使用 fabric 动态生成图片等。

    针对这种情况,你觉得该如何优化呢?

    自动分包和动态加载

    机智如你很快就想到使用 JS 动态加载,如果熟悉 React,还知道可以使用 react-loadable 来解决。

    原理就是利用 React Code-Splitting,配合 Webpack 自动分包,动态加载。

    这种方案可以,React 也推荐这么做,但是对于引用独立的第三方库这样的场景,还有更简单的方案。

    更简单的方案

    这些第三方库往往都提供了 umd 格式的 min.js,我们动态加载这些 min.js 就可以了。比如 XLSX,引入其 min.js 文件之后,就可以通过 window.XLSX 来实现 Excel 相关的操作。

    此方案的优点有:

    • 与框架无关,不需要和 React 等框架或 Webpack 等工具绑定
    • 精细控制,React Code-Splitting 之类的方案只能到模块级别,想要在点击按钮后才动态加载较难实现

    具体实现

    我们重点需要实现一个 JS 动态加载器 AsyncLoader,代码如下:

    function initLoader() {
      // key 是对应 JS 执行后在 window 中添加的变量
      const jsUrls = {
        html2canvas: 'https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.min.js',
        XLSX: 'https://cdn.jsdelivr.net/npm/xlsx@0.16.9/dist/xlsx.min.js',
        flvjs: 'https://cdn.jsdelivr.net/npm/flv.js@1.5.0/dist/flv.min.js',
        domtoimage: 'https://cdn.jsdelivr.net/npm/dom-to-image@2.6.0/src/dom-to-image.min.js',
        fabric: 'https://cdn.jsdelivr.net/npm/fabric@4.3.1/dist/fabric.min.js',
      };
    
      const loadScript = (src) => {
        return new Promise((resolve, reject) => {
          const script = document.createElement('script');
          script.type = 'text/javascript';
          script.onload = resolve;
          script.onerror = reject;
          script.crossOrigin = 'anonymous';
          script.src = src;
          if (document.head.append) {
            document.head.append(script);
          } else {
            document.getElementsByTagName('head')[0].appendChild(script);
          }
        });
      };
    
      const loadByKey = (key) => {
        // 判断对应变量在 window 是否存在,如果存在说明已加载,直接返回,这样可以避免多次重复加载
        if (window[key]) {
          return Promise.resolve();
        } else {
          if (Array.isArray(jsUrls[key])) {
            return Promise.all(jsUrls[key].map(loadScript));
          }
          return loadScript(jsUrls[key]);
        }
      };
    
      // 定义这些方法只是为了方便使用,其实 loadByKey 就够了。
      const loadHtml2Canvas = () => {
        return loadByKey('html2canvas');
      };
    
      const loadXlsx = () => {
        return loadByKey('XLSX');
      };
    
      const loadFlvjs = () => {
        return loadByKey('flvjs');
      };
    
      window.AsyncLoader = {
        loadScript,
        loadByKey,
        loadHtml2Canvas,
        loadXlsx,
        loadFlvjs,
      };
    }
    
    initLoader();
    

    使用方式

    以 XLSX 为例,使用这种方式之后,我们不需要在顶部 import xlsx from 'xlsx',只有当用户点击 导出Excel 按钮的时候,才从 CDN 动态加载 xlsx.min.js,加载成功后使用 window.XLSX 即可,代码如下:

    await window.AsyncLoader.loadXlsx().then(() => {
      const XLSX = window.XLSX;
      if (resp.data.signList && resp.data.signList.length > 0) {
        const new_workbook = XLSX.utils.book_new();
    
        resp.data.signList.map((item) => {
          const header = ['班级/学校/单位', '姓名', '帐号', '签到时间'];
          const { signRecords } = item;
          signRecords.unshift(header);
    
          const worksheet = XLSX.utils.aoa_to_sheet(signRecords);
          XLSX.utils.book_append_sheet(new_workbook, worksheet, item.signName);
        });
    
        XLSX.writeFile(new_workbook, `${resp.data.fileName}.xlsx`);
      } else {
        const new_workbook = XLSX.utils.book_new();
        const header = [['班级/学校/单位', '姓名', '帐号']];
        const worksheet = XLSX.utils.aoa_to_sheet(header);
        XLSX.utils.book_append_sheet(new_workbook, worksheet, '');
        XLSX.writeFile(new_workbook, `${resp.data.fileName}.xlsx`);
      }
    });
    

    另一个动态加载 domtoimage 的示例:

    window.CommonJsLoader.loadByKey('domtoimage').then(() => {
      const scale = 2;
      window.domtoimage
        .toPng(poster, {
          height: poster.offsetHeight * scale,
          width: poster.offsetWidth * scale,
          style: {
            zoom: 1,
            transform: `scale(${scale})`,
            transformOrigin: 'top left',
            width: `${poster.offsetWidth}px`,
            height: `${poster.offsetHeight}px`,
          },
        })
        .then((dataUrl) => {
          copyImage(dataUrl, liveData?.planName);
          message.success(`${navigator.clipboard ? '复制' : '下载'}成功`);
        });
    });
    

    AsyncLoader 方案使用方便、理解简单,而且可以很好地利用 CDN 缓存,多个项目可以共用同样的 URL,进一步提高加载速度。而且这种方式使用的是原生 JS,在任何框架中都可以使用。

    注意,如果你用 TypeScript 开发,这种方案或许会丢失一些智能提示,如果引入了对应的 @types/xxx 应该没影响。如果你特别在意开发时的智能提示,也可以在开发的过程中 import 对应的包,开发完成后才换成 AsyncLoader 方案。


    起源地下载网 » 一种简单实用的 JS 动态加载方案

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元