Thinking--Promise解决动态挂载静态资源重复问题

news2024/12/26 9:18:08

Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想。

js
异步
css
异步
获取资源loadResources
script
追加body
link
初始化 initVideoPlayer

为减少页面包体积,videojs 相关资源,动态追加到页面。

使用 vue 封装组件 <VideoPlayer>

<template>
    <video ref="videoPlayer" style="width: 100%; height: 100%" class="video-js vjs-big-play-centered" />
</template>
<script>
export default {
  name: 'VideoPlayer',
  data () {
    return {
      player: null
    }
  },
  methods: {
    // 获取资源
    loadResources() {
      if (window.videojs) return Promise.resolve();
      return Promise.all([
        // 见下述,被封装成了Promise
        loadScript('//xxx/js/video-umdV7.18.1-711dd1be5e.js'),
        loadScript('//xxx/js/videojs-ccs-umdV7.18.1-59190bdaf5.css')
      ])
    },
    // 初始化
  	initVideoPlayer() {
      // 需要依赖 videojs 构造函数
      this.player = window.videojs(this.$refs.videoPlayer, {...}, () => {});
    }
  },
  async mounted() {
    await this.loadResources();
    this.initVideoPlayer();
  },
  beforeDestroy() {
    this.player && this.player.dispose();
  },
}
</script>

调用方:

🐬单个组件调用:运转良好

<VideoPlayer v-bind="$attrs"></VideoPlayer>

🐋 循环调用:展示没啥问题,但加载了多份

<div v-for="videoItem in list" :key="videoItem.key">
  <VideoPlayer v-bind="$attrs"></VideoPlayer>
</div>

⚠️ 问题出现了:js 和 css 都被加载了多份(list.length)!

在这里插入图片描述

🐾 原因分析:在 loadResources() 中,已经追加了前置判断 if (window.videojs) return Promise.resolve(); 来避免重复资源的加载,从而达到只加载一份的目的。没有生效❓

在这里插入图片描述
video.js 资源加载&解析执行完,会在 window 上挂载 videojs 属性。

在这里插入图片描述

循环过程会产生多个 <VideoPlayer> 组件实例,进而产生多个获取资源 loadResources() 的调用。加载资源是异步过程window.videojs 资源加载完才会追加。

在这里插入图片描述

⚠️注意:在 Chrome 浏览器下,只会发送一个。怀疑 Chrome 做了对应的缓存机制或脚本去重机制,欢迎大家补充&指正~~~

🐬 解决思路:

  1. 将实例创建改成串行,确保第二个初始化时,第一个已经处理完毕(存在),这样 window.videojs 的判断可以生效。=> 可以解决,但不是最佳方案,整体页面渲染效率下降
  2. 初始化多个 <VideoPlayer> 实例(并行加载),确保资源只加载一份。=> ⭐ 资源全局单例

在这里插入图片描述

// 借助window实现单例
window.resourceLoader = {
  loadingPromise: null,
  loadResources: function () {
    if (!window.resourceLoader.loadingPromise) {
      window.resourceLoader.loadingPromise = new Promise((resolve, reject) => {
        Promise.all([
          // 见下述,被封装成了Promise
        	loadScript('//xxx/js/video-umdV7.18.1-711dd1be5e.js'),
        	loadScript('//xxx/js/videojs-ccs-umdV7.18.1-59190bdaf5.css')
        ]).then(() => {
          resolve();
        });
      });
    }
    return window.resourceLoader.loadingPromise;
  },
};

export default {
  name: 'VideoPlayer',
  data () {
    return {
      player: null
    }
  },
  methods: {
    // 获取资源(这里使用window)
    loadResources() {
      return window.resourceLoader.loadResources();
    },
    // 初始化
  	initVideoPlayer() {
      // 需要依赖 videojs 构造函数
      this.player = window.videojs(this.$refs.videoPlayer, {...}, () => {});
    }
  },
  async mounted() {
    await this.loadResources();
    this.initVideoPlayer();
  },
  beforeDestroy() {
    this.player && this.player.dispose();
  },
}

🌲 window 单例,确保了全局 window.resourceLoader.loadingPromise 的唯一性!
🌲 loadingPromise:资源加载管理标志,以确保资源只被加载一次。

  • 如果 loadingPromisenull,则意味着资源尚未加载过,加载资源,返回 loadingPromise
  • 否则,返回现有的 loadingPromise,防止冗余加载。

附:body 追加资源

function loadScript(url) {
  // 已加载直接返回
  if (loadScript[url]) return Promise.resolve();
  return new Promise((resolve, reject) => {
    let dom;
    if (url.endsWith('.js')) { // js资源
      dom = document.createElement('script');
      dom.type = 'text/javascript';
      dom.src = url;
    } else { // css资源
      dom = document.createElement('link');
      dom.setAttribute('rel', 'stylesheet');
      dom.setAttribute('type', 'text/css');
      dom.setAttribute('href', url);
    }

    dom.setAttribute('ignore', 'true');
    document.head.appendChild(dom);
    dom.onload = function () {
      loadScript[url] = true;
      resolve();
    };
    dom.onerror = reject;
  });
}

🌴 缓存策略:通过将加载过的脚本URL作为对象的属性记录在loadScript上,实现加载过的资源复用,减少不必要的网络请求。
🌴 动态元素创建:根据URL后缀判断资源类型,如果是.js文件,则创建<script>标签;如果是其他类型(假设为CSS),则创建<link>标签。
🌴 资源加载与事件监听:为新创建的DOM元素添加 onloadonerror 事件处理器,分别在资源成功加载和加载失败时调用 resolvereject ,从而改变 Promise 状态,通知调用者加载结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1887503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式Linux系统编程 — 6.3 kill、raise、alarm、pause函数向进程发送信号

目录 1 kill函数 1.1 kill函数介绍 1.2 示例程序 2 raise函数 2.1 raise函数介绍 2.2 示例程序 3 alarm函数 3.1 alarm函数介绍 3.2 示例程序 4 pause函数 4.1 pause函数介绍 4.2 示例程序 与 kill 命令相类似&#xff0c; Linux 系统提供了 kill()系统调用&#…

仿全民飞机大战射击网页游戏源码

仿全民飞机大战设计网页游戏源码&#xff0c;画质精美的飞机大战手机端游戏源码 微信扫一扫免费下载源码

搞管理千万别犯这3大禁忌,否则活该你当不了领导,切忌!

搞管理千万别犯这3大禁忌&#xff0c;否则活该你当不了领导&#xff0c;切忌&#xff01; 禁忌一&#xff1a;不懂放权 美国总统罗斯福曾说过&#xff1a;“一个最好的管理者&#xff0c;很擅长知人善任。当下属在从事其职务时&#xff0c;管理者要懂得约束自己&#xff0c;不…

MyBatis入门案例

实施前的准备工作&#xff1a; 1.准备数据库表2.创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动、lombok&#xff09;3.在application.properties文件中引入数据库连接信息4.创建对应的实体类Emp&#xff08;实体类属性采用驼峰…

一文揭秘:CRM如何助力家居建材企业可持续发展?

01、家居建材行业业务高速发展&#xff0c;对数字化转型提出越来越高诉求 家居建材行业是国民经济的重要基础产业&#xff0c;是改善人居条件、治理生态环境和发展循环经济的重要支撑。家居建材是土木工程和建筑工程中使用材料的统称&#xff0c;包括天花板、瓷砖、门、窗、锁…

VGPU的使用

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/data-infra/cube-studio 训练AI模型以及部署模型推理服务时&#xff0c;GPU往往是必不可少的&#xff0c;但当我们机器上没有足够的GPU卡可使用时&#xf…

如何选择品牌推广公司?哪家好?收费标准及评价!

不管是什么品牌&#xff0c;推广对公司的成败起了很关键的作用。然而&#xff0c;面对市面上琳琅满目的品牌推广公司&#xff0c;如何选择一家既熟悉又靠谱的公司&#xff0c;成为许多企业主面临的难题。 作为一家手工酸奶品牌的创始人&#xff0c;目前全国也复制了100多家门店…

PyInstaller exe文件报错

文章目录 包找不到的问题去掉黑窗口 包找不到的问题 遇到的问题 : 打包好了之后exe文件报错: 没有找到这个文件 1.当时打包的 有这个文件main.spec 打开它找到hiddenimports ,填上差的包 2, 删除build和dist 3,在当前命令行下执行pyinstaller main.spec打包生成exe 去掉黑…

Polygon链的对接及使用

Polygon&#xff08;前身为Matic Network&#xff09;是一个基于以太坊的侧链&#xff0c;旨在解决以太坊网络拥堵和高昂 gas 费的问题。Polygon 使用侧链技术将交易从以太坊主网转移到自己的侧链上&#xff0c;从而提高交易速度和降低 gas 费。北京木奇移动技术有限公司&#…

DFS练习

105 从前序与中序遍历序列构造二叉树 import java.util.HashMap; import java.util.Map;class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int val) {this.val val;} }public class Letcode105 {public TreeNode bulidTree(int[] preOrder, int[] inOrd…

【RabbitMQ实战】邮件发送(直连交换机、手动ack)

一、实现思路 二、异常情况测试现象及解决 说明:本文涵盖了关于RabbitMQ很多方面的知识点, 如: 消息发送确认机制 、消费确认机制 、消息的重新投递 、消费幂等性, 二、实现思路 1.简略介绍163邮箱授权码的获取 2.编写发送邮件工具类 3.编写RabbitMQ配置文件 4.生产者发起调用…

linux内核驱动第一课(基于RK3568)

学习Linux驱动需要以下基础知识&#xff1a; C语言编程&#xff1a;掌握C语言是开发Linux驱动程序的基本要求。操作系统原理&#xff1a;了解操作系统的基本概念和原理&#xff0c;如进程管理、内存管理、中断处理等。Linux内核&#xff1a;熟悉Linux内核的结构和工作机制&…

DreamTech联合南大和牛津发布最强3D内容生成大模型——Direct3D

文章链接&#xff1a;https://arxiv.org/pdf/2405.14832 github链接&#xff1a;https://nju-3dv.github.io/projects/Direct3D/ 从文本和图像生成高质量的3D资产一直是一项挑战&#xff0c;主要是由于缺乏能够捕捉复杂几何分布的可扩展3D表示。在这项工作中&#xff0c;介绍…

力扣61. 旋转链表(java)

思路&#xff1a;用快慢指针找到最后链表k个需要移动的节点&#xff0c;然后中间断开节点&#xff0c;原尾节点连接原头节点&#xff0c;返回新的节点即可&#xff1b; 但因为k可能比节点数大&#xff0c;所以需要先统计节点个数&#xff0c;再取模&#xff0c;看看k到底需要移…

网络爬虫基础知识

文章目录 网络爬虫基础知识爬虫的定义爬虫的工作流程常用技术和工具爬虫的应用1. 抓取天气信息2. 抓取新闻标题3. 抓取股票价格4. 抓取商品价格5. 抓取博客文章标题 网络爬虫基础知识 爬虫的定义 网络爬虫&#xff08;Web Crawler 或 Spider&#xff09;是一种自动化程序&…

gitee项目上不同的项目分别使用不用的用户上传

最近使用根据需要&#xff0c;希望不同的项目使用不同的用户上传&#xff0c;让不同的仓库展示不同的用户名&#xff01;&#xff01;&#xff01; 第一步查看全局的用户信息&#xff1a; # 查看目前全局git配置信息 git config -l #会输出全局的git配置信息 第二步进入到要设…

【java计算机毕设】高校学生管理系统MySQL springboot vue3 Maven 源码 代码

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】高校学生管理系统MySQL springboot vue3 Maven 小组项目设计代码源码 2项目介绍 系统功能&#xff1a; 高校学生管理系统主要功能包含&#xff1a;学生管理&#xff0c;班主任信息管理&#xff0c;家长信息…

仓库管理系统26--权限设置

原创不易&#xff0c;打字不易&#xff0c;截图不易&#xff0c;多多点赞&#xff0c;送人玫瑰&#xff0c;留有余香&#xff0c;财务自由明日实现 1、权限概述 在应用软件中&#xff0c;通常将软件的功能分为若干个子程序&#xff0c;通过主程序调用。那么&#xff0c;通过…

Python的matplotlib简单操作及图像闪屏问题

1.显示一个sinx的图像 import matplotlib.pyplot as plt import numpy as np xnp.linspace(0,10,100)#生成0到10 之间 分成100份等间隔 ynp.sin(x) # # plt.plot(x,y)#放入x与y plt.title("ysin(x)")#给图像命名 plt.xlabel("x")#设置x位置的名字 plt.yl…

HarmonyOS开发实战:UDP通讯示例规范

1. UDP简介 UDP协议是传输层协议的一种&#xff0c;它不需要建立连接&#xff0c;是不可靠、无序的&#xff0c;相对于TCP协议报文更简单&#xff0c;在特定场景下有更高的数据传输效率&#xff0c;在现代的网络通讯中有广泛的应用&#xff0c;以最新的HTTP/3为例&#xff0c;…