实现简单的 JS 模块加载器

Posted gaollard

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现简单的 JS 模块加载器相关的知识,希望对你有一定的参考价值。

实现简单的 JS 模块加载器

按需加载是前端性能优化的一个重要手段,按需加载的本质是从远程服务器加载一段JS代码(这里主要讨论JS,CSS或者其他资源大同小异),该JS代码就是一个模块的定义,如果您之前有去思考过按需加载的原理,那你可能已经知道按需加载需要依赖一个模块加载器。它可以加载所有的静态资源文件,比如:

  • JS 脚本
  • CSS? 脚本
  • 图片 资源

如果你了解 webpack,那您可以发现在 webpack 内部,它实现了一个模块加载器。模块加载器本身需要遵循一个规范,当然您可以自定义规范,大部分运行在浏览器模块加载器都遵循 AMD 规范,也就是异步加载。

下面是一个基于AMD规范的简单的模块加载器:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  1. 相同模块的并发加载问题?
</body>
<script>
  // 模块加载配置
  var config = {
    baseDir: window.location.origin + '/module'
  }

  // loader 模块加载器
  var loader = {
    // 配置
    config: {
      baseDir: window.location.origin + '/module'
    },

    // 缓存
    modules: {},

    // 注册加载后的回调
    installed: {},

    // 标记某个模块是否已经加载
    status: {},

    // 定义模块
    define: function(name, fn) {
      this.modules[name] = fn ? fn() : undefined
    },

    // 加载模块
    require: function(name, fn) {
      if (this.modules[name]) {
        // 已经加载成功, 直接从缓存读取
        callback(this.modules[name])
      } else {
        if (this.status[name]) {
          // 加载过了, 但是还未加载成功
          this.installed[name].push(fn)
        } else {
          // 还未加载过
          this.installed[name] = []
          this.installed[name].push(fn)
          this.loadScript(name)
          this.status[name] = true
        }
      }
    },

    // 加载JS文件
    loadScript: function (name, callback) {
      let _this = this
      let script = document.createElement('script')

      script.src = this.config.baseDir + '/' + name + '.js'
      script.onload = function () {
        _this.installed[name].forEach(fn => fn(_this.modules[name]))
      }
      setTimeout(() => {
        // 模拟请求时间
        document.body.append(script)
      }, 200)
    }
  }

  loader.require('lazyload', function(lazyload){
    console.log(Date.now())
    lazyload()
  })

  loader.require('lazyload', function(lazyload){
    console.log(Date.now())
    lazyload()
  })

  loader.require('moment', function(moment){
    console.log(moment)
  })
</script>
</html>

主要的相关点在于:

  • 模块的定义方法
  • 模块的加载方法
  • 已经加载过的模块需要缓存
  • 同一个模块并行加载的处理

还未考虑的点包括:

  • 模块加载失败时异常处理
  • 定义模块时,该模块依赖其他模块
  • ......

可以看出的是,对于某个应用使用了模块加载器,那么首先需要加载该JS代码,然后有一个主模块,程序从主模块开始执行, requireJS 中使用main来标记,webpack 中叫?webpackBootstrap 模块。

以上代码有助于帮您理解 requireJS 和 webpack 应用的运行流程。

以上是关于实现简单的 JS 模块加载器的主要内容,如果未能解决你的问题,请参考以下文章

RequireJS简单介绍即使用

angularJS使用ocLazyLoad实现js延迟加载

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

Three.js 中的自定义纹理着色器

webpack总结

活动(加载器 - 下载)+ 3 个片段(使用加载器 - 计算)