• 小程序优化:第三方SDK过大解决方案


    【前言】

    小程序开发中,有时会遇到下面这种情况,项目目录中存放过大的js包,会被警告影响手机端性能,同时让开发编译启动变得很慢。慢是其次,单是影响性能这一点,就需要解决一下。

     

     

    【云资源】

    将项目js包放入公司的oss、obs之类的云存储上,通过https链接来访问。

    https链接不能使用node的require加载,会抛错,但是可以通过其他两种方式进行访问:

      1. request请求

      2. fileSystemManager文件管理器

     

    方法1简单,但是不可取,原因如下:

    1) 获得的文件信息,没有较好的保存方法,既不能存在store中,也不能存在local中,不仅是数据存储格式问题,更重要的同样会造成性能缺点,总不能解决了一个问题,又创造新的问题

    2) 由于1)描述的那样,如果不能存储文件,那么每次需要依赖这个文件时,就需要每次请求接口,这就造成了资源浪费

     

    故此,基于以上两点,只能负重前行,选择较为麻烦和让人头疼的方案2(fileSystemManager文件管理器),虽然麻烦,但是却可以一劳永逸,并且可以抽离逻辑封装成一套方法,可以在以后复用。

     

     

    【前期准备】

    实现方案前,有几个注意事项:

    1. 首先要将存放云资源的oss或obs的域名配置在白名单中,这里就需要配置request合法域名和downloadFile合法域名。

     

    2. 需要勾选“不校验合法域名...”,不勾选的话,真机会遇到意想不到的问题。

     

     

    3. 将js文件转成json文件,如果没办法,就自行抽离js包,拆出一个json文件,因为微信fileSystemManager不支持读取js文件,js文件会变成string文本,但是支持json。

    当然,如果你觉得可以使用JavaScript 解释器来破这个局,那么你又一次要碰壁了,微信官方对此做了限制,禁止eval5、estime、evil-eval等动态执行代码的js解释器。

    原文地址:关于禁止小程序JavaScript解释器使用规范要求

     

     

    【方案思考】

    fileSystemManager,它是getFileSystemManager返回的对象,给我们暴露出了多个方法,下面为部分截图

    原文地址:FileSystemManager

     

    为保证性能和可靠性,这里我们采用下面这种方案:

    首次:下载 + 保存 + 读取,非首次:直接读取

    流程图如下:

     

     

     

    【方法封装】

    我这里提供了两种封装写法:

    1. 链式写法,方便回调处理,不需要回调可以采用写法2
    2. 解耦式写法,降低了函数颗粒度,每个方法独立,更加灵活,可以单独使用某一个函数

     

    千言万语,前面的铺垫已完成,直接上代码:

    链式写法

    复制代码
    /**
     * 读取静态资源,如果没有该资源,则触发下载,并保存至用户本地
     * 注意:此方法为链式调用,如果需要单独的方法,可选择下面拆分过的独立方法
     * @param {String} name 文件名 -必传
     * @param {String} url 文件路径 -必传
     * @param {Function} callback 是否需回调操作 -非必传
     */
    export async function getSystemFileInfoToChain(name = 'jn', url, callback = null) {
      if (!url) {
        return;
      }
      const fileManager = uni.getFileSystemManager();
      // 用户文件路径
      let filePath = `${uni.env.USER_DATA_PATH}/${name}`;
      // 读取静态资源,如果没有该资源,则触发下载
      try {
        let res = await fileManager.readFileSync(filePath, 'utf-8');
        const resData = res && JSON.parse(res);
        let resFile = await fileManager.readFileSync(url, 'utf-8');
        console.log('🆒 fileManager 111', resData, name, url, resFile);
        if (callback && typeof callback === 'function') {
          callback(resData);
        }
        // 暴露出文件内容
        return resData;
      } catch (err) {
        console.log('getSystemFileInfoToChain err', err);
        // 下载文件至本地
        try {
          const res = await uni.downloadFile({
            url
          });
          let tempFilePath = res?.[1]?.tempFilePath;
          console.log('🆒 fileManager 222', tempFilePath);
          if (!tempFilePath) {
            return;
          }
          // 保存文件至本地
          try {
            await fileManager.saveFileSync(tempFilePath, filePath);
            console.log('🆒 fileManager 333', url);
            // 再次触发,获得需要的数据
            getSystemFileInfoToChain(name, url, callback);
          } catch (err) {
            console.log('saveFileSync err', err);
          }
        } catch (err) {
          console.log('downloadFile err', err);
        }
      }
    }
    复制代码

      

    解耦式写法

    复制代码
    /**
     * 读取静态资源,如果没有该资源,则触发下载,并保存至用户本地
     * @param {String} name 文件名 -必传
     * @param {String} url 文件路径 -必传
     * @param {Boolean} next 是否下一步(如果读取失败,则下载文件至本地) -非必传
     */
    export async function getSystemFileInfo(name = 'jn', url, next = false) {
      const fileManager = uni.getFileSystemManager();
      // 用户文件路径
      let filePath = `${uni.env.USER_DATA_PATH}/${name}`;
      try {
        let res = await fileManager.readFileSync(filePath, 'utf-8');
        const resData = res && JSON.parse(res);
        console.log('🆒 fileManager 111', resData);
        // 暴露出文件内容
        return resData;
      } catch (err) {
        // 允许下一步(下载文件),且有url,进行下载保存
        next && url && onDownloadFile(name, url, next);
        console.log('readFileSync err', err);
      }
      // console.log('🆒 fileManager', fileManager, uni.env.USER_DATA_PATH);
    }
    
    /** 
     * 下载文件至本地
     * @param {String} name 文件名 -必传
     * @param {String} url 文件路径 -必传
     * @param {Boolean} next 是否下一步(文件内容读取) -非必传
     */
    export async function onDownloadFile(name = 'jn', url, next = false) {
      if (!url) {
        return;
      }
      try {
        const res = await uni.downloadFile({
          url
        });
        let tempFilePath = res?.[1]?.tempFilePath;
        console.log('🆒 fileManager 222', tempFilePath);
        onSaveFile({ name, url }, tempFilePath, next);
      } catch (err) {
        console.log('downloadFile err', err);
      }
    }
    
    /**
     * 保存文件至本地
     * @param {String} params[0].name 文件名 -必传
     * @param {String} params[0].url 文件路径 -非必传
     * @param {String} tempFilePath 临时文件路径 -必传
     * @param {Boolean} next 是否下一步(文件内容读取) -非必传
     */
    export async function onSaveFile({
      name = 'jn',
      url
    }, tempFilePath, next = false) {
      if (!tempFilePath) {
        return;
      }
      const fileManager = uni.getFileSystemManager();
      // 用户文件路径
      let filePath = `${uni.env.USER_DATA_PATH}/${name}`;
      try {
        await fileManager.saveFileSync(tempFilePath, filePath);
        // 允许下一步(文件内容读取),且有url,进行文件内容读取
        next && url && getSystemFileInfo(name, url, false);
        console.log('🆒 fileManager 333', url);
      } catch (err) {
        console.log('saveFileSync err', err);
      }
    }
    复制代码

     

    到此,就封装完成了,后面使用看具体场景,来选择链式、解耦式写法。

  • 相关阅读:
    浏览器中的页面循环系统(三)async await使用同步方式写异步代码
    《Java-SE-第十八章》之HashMap(jdk8)
    详解删除链表的倒数第N个结点
    java计算机毕业设计框架的企业机械设备智能管理系统的设计与实现源码+数据库+系统+lw文档+mybatis+运行部署
    解决IDEA中SpringBoot项目创建多个子模块时配置文件小绿叶图标异常问题
    Qlik Sense 内联加载详解(Load * Inline)
    C语言开发过程中段错误处理方法之经典
    腾讯云国际站服务器端口开放失败怎么办?
    六分科技CEO李阳:精准定位助力汽车智能化普及
    NXOPEN二次开发-CAM Operation转OperationBuilder对加工操作修改一些进给速度参数
  • 原文地址:https://www.cnblogs.com/wx3091/p/17925976.html