xiaochengzi6 / Blog

个人博客
GNU Lesser General Public License v2.1
0 stars 0 forks source link

关于 script 标签的想法与实践 #71

Open xiaochengzi6 opened 1 year ago

xiaochengzi6 commented 1 year ago

近期突发奇想想搞一下 关于在页面中动态加载脚本的 hook 关于这个 hook 不同的开源作品都给出了不同的方案,可以说都大同小异每个的区别和侧重点都不一样。

script 标签

首先先看一下关于 script 标签的相关的语法及特性参考文章,主要有注意它的 asyncdefer 属性,这两个属性的标记也代表其请求资源也不一样。

普通的 script 标签只能放到 head || body 这两个标签中,html 解析是自上而下的当遇到 script 标签就会尝试加载由于并不知道是否在这个过程操作了 dom 所以会阻塞 html 的渲染,当 js 处理完后继续进行 html 的解析工作,所以通常会将 script 标签放置在 body 标签的最下面,也就是解析完html 后才能去加载 js 文件

现在来看一下 script 中比较重要的属性

  1. 使用 async 属性的 script 标签,在被加载时会去请求的资源但这个过程并不会阻塞 html 的加载,当加载完成后立刻开始执行文件
  2. 使用 defer 属性的请求资源时同样并不会阻塞 HTML 的加载,加载完成后并不会立即执行而是等到 整个 HTML 解析完后执行

    使用 defer 的标签加载文件的顺序会按照放置的顺序去加载

在面对不能使用 <script> 标签的情况通常会在页面放入 <noscript> 标签来提示用户允许浏览器加载 js 文件

useScriptDom

1、关于这个 hook 我做出了一个简易的版本 可以参考我对其的提议

我搜索一些关于这个 hook 的开源作品,比如 ahook 中 它关于资源的加载使用 useExternal hook 去请求 js/css 资源,比较方便,它的传出值为 加载、成功、失败、未定义这四种,传入参数上来看, path 参数用来指定 url 地址,options 参数用来设置 额外的值具体的参考官方文档

从源码上来看,主要大致的思路就是先根据你传入的参数来创建 script 标签(针对 js) 然后将创建好的 dom 元素返回,并绑定事件确保能够在合适的时机触发事件

// ...
const scriptDom = createScript(...args)
useEffect(() => {
  script.addEventListener('load', handleLoad)
  script.addEventListener('error', handleError)
  document.body.appendChild(script);

  return () => {
    script.removeEventListener('load', handleLoad)
    script.removeEventListener('error', handleError)
  }
})

在此基础上添加了一个 EXTERNAL_USED_COUNT 的标志,当组件第一次被请求且成功时就 +1 多次请求就一直累加,当请求次数为 0时就移除这个 script 标签

2、react-script-hook它的源码也非常简单,大致看了一下也和 ahook 的思路差不多,它采用的是使用一个对象来缓存 script 标签,使用的时候根据你传递的参数去创建,不过在创建之前要判断缓存中是否缓存过这个script 标签,没有缓存过就重新船舰一个设置他的属性,然后通过和 ahook 同样的方法去监听事件

3、react-recipes开源作品useScripthook 它也采取了一个缓存的方法 不过它的更加简单,只缓存 src 的usl地址,当在缓存中有保留过相同的就直接返回 状态 true 否则就重新创建 script 标签,然后去定义自己的事件并注册上,成功就返回 loading: true 失败就卸载 节点并返回 error: true

最后

这三个开源库对与动态加载文件的处理方式都大同小异,我打算采取一个更加自由的方式,这三个hook的它们的属性和url 都是由用户去定义的但监听事件触发的函数都是自己去实现用户可以不用处理这里面的逻辑,但我想在这个脚本加载成功或者失败的基础上在额外做一些事情,比如失败后我在请求一下,成功后我打印一下状态或者执行某个操作等。。。

除此之外 <script> 标签还有一些需要注意的属性 module && nomodule

关于脚本的加载之后的拓展方向还可以继续升入,从文件的加载再到 js 模块的起源、规范、都可以深入进去。 推荐文章:

  1. 270写一个 amd 模块加载器
  2. 阮一峰对模块化的理解这里有三篇文章只放第一篇
  3. 关于一些常见的知识点也可以主动的搜索阮一峰的博客
  4. 关于 script 对于ie的兼容的考虑
  5. 现代脚本的加载-这篇文章属实有点看不懂涉及到了太多的知识盲区
  6. 270 行写出 amd 加载器的仓库可以研究一下