js 动态加载

js 脚本懒加载,类似于webpack import()方法,可以适用于静态js文件的加载

脚本文件

type FetchSuccess = {
  success: boolean;
  loading: boolean;
  name: string;
  script: any;
};

// 加载脚本
// name 名字,用来判断是否加载过
// src 地址
// timeOut 超时时间
const fetchScript = (() => {
  const fetchSuccessList: FetchSuccess[] = [];
  return function fetchFn(
    name: string,
    src: string,
    timeOut: number = 5000,
  ): Promise<{ success: boolean }> {
    return new Promise(resolve => {
      const isCaching = fetchSuccessList.find(item => item.name === name);
      if (isCaching === undefined) {
        fetchSuccessList.push({ name, loading: true, success: false, script: null });
      }
      const current = fetchSuccessList.find(item => item.name === name) as any;
      // 代表之前已经获取过,就直接返回成功
      if (current.success) {
        resolve({ success: true });
        return;
      }
      if (current.script) {
        /**
         * 这里可能并发调用这个函数,同时调用三次,第一个先进来创建 script,
         * 后面的就不应该在创建 script,
         * 避免重复创建script, 等待第一次 onload
         */
        // setTimeout 实现
        function callback() {
          return new Promise(newResolve => {
            setTimeout(() => {
              // 超时
              if (new Date().getTime() - current.time >= timeOut) {
                newResolve();
                return;
              }
              // loading 结束
              if (current.loading === false) {
                newResolve();
                return;
              }
              // 这里需要递归执行自己进行等待,直到前面两个断言进去后才执行最外层的 resolve
              callback().then(() => callback());
            }, 50);
          }).then(() => {
            resolve({ success: current.success });
          });
        }
        callback();
        // setInterval 实现
        // new Promise(newResolve => {
        //   const timer = setInterval(() => {
        //     // 超时
        //     if (new Date().getTime() - current.time >= timeOut){
        //       newResolve(timer);
        //       return;
        //     }
        //     // loading 结束
        //     if (current.loading === false) {
        //       newResolve(timer);
        //       return;
        //     }
        //   }, 50);
        // }).then((timer: any) => {
        //   clearInterval(timer);
        //   resolve({ success: current.success });
        // });
        return;
      }
      current.script = document.createElement('script');
      current.loading = true;
      current.time = new Date().getTime();
      const { script } = current;
      script.src = src;
      document.body.appendChild(script);
      console.time(name + ':脚本加载时间');
      script.onload = () => {
        console.timeEnd(name + ':脚本加载时间');
        current.success = true;
        current.loading = false;
        resolve({ success: true });
      };
      script.onerror = () => {
        console.timeEnd(name + ':脚本加载时间');
        document.body.removeChild(script);
        current.script = null;
        current.success = false;
        current.loading = false;
        resolve({ success: false });
      };
    });
  };
})();

export default fetchScript;

使用方式,封装echarts高阶组件,echarts从props里面取,统一管理echarts的加载方式,也可以用webpack
import()替换,webpack的需要增加包下载和打包时间和修改publicPath

import React, { PureComponent } from 'react';
import fetchScript from '@/util/fetchScript';

const echartsUrl = 'xxx'; // 你的静态文件地址

const echartsHoc = Component => {
  return class Index extends PureComponent {
    static echarts = null;

    state = {
      echarts: Index.echarts,
    };

    async componentDidMount() {
      // 防止多 setState 一次
      if (Index.echarts) {
        return;
      }
      const { success } = await fetchScript('echarts', echartsUrl);
      if (!success) {
        return;
      }
      Index.echarts = window.echarts || null;
      this.setState({
        echarts: Index.echarts,
      });
    }

    render() {
      const { echarts } = this.state;
      if (echarts === null) {
        return null;
      }
      return <Component {...this.props} echarts={echarts} />;
    }
  };
};

export default echartsHoc;

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。