• JavaScript设计模式(六):架构型设计模式-同步模块模式(SMD)、异步模块模式(AMD)


    同步异步

    因为JavaScript的单线程,因此同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务,但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着,拿ajax来说,当用户向后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验。

    因此,JavaScript在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载成功,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务,因此,任务就可以分为同步任务和异步任务。

    • 同步任务
      • 指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务
    • 异步任务
      • 指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

    死心眼-同步模块模式

    同步模块模式——SMD(Synchronous Module Definition)

    模块化:将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块的复用率

    请求发出后,无论模块是否存在,立即执行后续的逻辑,实现模块开发中对模块的立即引用

    创建一个导航模块

    在这里插入图片描述

    <style>
        #nav {
            list-style: none;
            margin: 0;
            padding: 0;
        }
    
        #nav li {
            float: left;
            width: 80px;
            height: 32px;
            line-height: 32px;
            text-align: center;
            background-color: #eee;
            cursor: pointer;
            color: #0051ff;
            margin: 0 2px;
        }
    
        #nav li span {
            margin-left: 3px;
            color: red;
            font-size: 12px;
        }
    
        #panel {
            display: none;
            position: absolute;
            top: 40px;
            width: 200px;
            height: 150px;
            line-height: 150px;
            text-align: center;
            background-color: #ccc;
        }
    style>
    
    <body>
        <ul id="nav">ul>
        <div id="panel">面板div>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    /**
     * 创建导航逻辑(由A工程师创建)
     * @param {HTMLElement} dom 上下文DOM
     * @param {Array} data 数据
     */
    function createNav(dom, data) {
        /* ============== A工程师加入 创建了导航模块并添加了导航的内容 ============== */
        {
            for (let i = 0; i < data.length; i++) {
                dom.append(`
  • ${data[i].label}
  • `
    ) } } /* ============== B工程师加入 在A工程师的代码里边为导航添加引导 ============== */ { let li = $('li', dom); for (let i = 0; i < data.length; i++) { if (data[i].isGuide) { $(li[i]).append('[hot]'); } } } /* ============== C工程师加入 在A工程师的代码里边为导航添加事件 ============== */ { let li = $('li', dom); li.on('mouseover', function () { $('#panel').css({ left: this.offsetLeft + 'px', display: 'block' }); }).on('mouseout', function () { $('#panel').css({ display: 'none' }); }); } }; // 模拟请求,获取导航引导数据 setTimeout(function () { let data = [ { label: '首页', isGuide: false }, { label: '推荐', isGuide: true }, { label: '我的', isGuide: false } ]; // 创建的导航模块 createNav($('#nav'), data); }, 2000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 缺点
      • 由于B、C工程师是在A工程师写的代码的内部添加的自己的逻辑,当后续需求要求A工程师修改逻辑时,就很有可能影响B、C工程师的逻辑,导致A工程时修改时要照顾B、C写的逻辑。
    • 解决方案
      • 使用模块化开发(包括模块管理器和模块调用器)

    模块管理器

    // 模块管理器
    const F = {};
    
    /**
     * 定义模块管理器
     * @param {string} str 模块路由
     * @param {Function} fn 模块方法
     * @returns F模块管理器对象
     */
    F.define = function (str, fn) {
        // 过滤掉第一个元素为F的字符串数组
        let keys = str.replace(/^F\./, '').split('.');
        // 屏蔽对define与module模块方法的重写
        if (keys[0] === 'define' || keys[0] === 'module') return this;
        // 遍历字符串数组,赋值到当前对象内
        let obj = this;
        for (let i = 0; i < keys.length; i++) {
            // 防止后续模块覆盖以前定义的模块
            if (typeof obj[keys[i]] === 'undefined') {
                obj[keys[i]] = {};
                if (i === keys.length - 1) {
                    obj[keys[i]] = fn && fn();
                }
            }
            obj = obj[keys[i]];
        }
        // 当前对象
        return this;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    // 创建一个字符串模块
    F.define('F.string.fn', function () {
        return {
            // 定义一个去除字符串空格的方法
            trim(str) {
                return str.replace(/\s+/g, '');
            }
        };
    });
    F.string.fn.trim(' P r o s p e r  L e e '); // 'ProsperLee'
    
    F.define('create.dom', function () {
        let dom = null;
        return function (el) {
            dom = document.createElement(el);
            dom.innerHTML = `创建的DOM元素-${el}`;
            document.body.appendChild(dom);
            return dom;
        }
    });
    F.create.dom('li'); // 
  • 创建的DOM元素-li
  • F.define('a', function () { return function () { } }); F.define('a.b', function () { return function () { } }); F.define('a.b.c', function () { return function () { } }); F.define('a.b.c', function () { return { a: {}, b: {}, c: {}, d: {}, e: {} } });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    模块调用器

    /**
     * 模块调用方法
     * @param  {...any} args 需要最后的参数为function
     */
    F.module = function (...args) {
        // 依赖模块列表
        let modules = [];
        // 判断最后一个参数是否为函数
        let fn = typeof args[args.length - 1] === 'function' ? args.pop() : null;
        // 将参数转换为数组
        let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
        // 遍历参数
        let i = 0;
        while (i < arr.length) {
            // 判断参数是否是字符串(通过define创建的)
            if (typeof arr[i] === 'string') {
                let keys = arr[i].replace(/^F\./, '').split('.');
                // 获取当前F对象
                let mod = this;
                for (let j = 0; j < keys.length; j++) {
                    // 替换当前的mod,查询到最里层对象
                    mod = mod[keys[j]];
                }
                modules.push(mod);
            } else {
                modules.push(arr[i])
            }
            i++;
        }
        fn && fn.apply(this, modules);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    F.module(['create.dom', 'F.string.fn.trim', document], function (dom, trim, doc) {
        console.log(this); // F
        dom('li').innerText = trim(' P r o s p e r  L e e '); // 
  • ProsperLee
  • console.log(doc); // #document });
    • 1
    • 2
    • 3
    • 4
    • 5

    解决创建导航模块问题

    在这里插入图片描述

    // 模块管理器
    const F = {};
    
    /**
     * 定义模块管理器
     * @param {string} str 模块路由
     * @param {Function} fn 模块方法
     * @returns F模块管理器对象
     */
    F.define = function (str, fn) {
        // 过滤掉第一个元素为F的字符串数组
        let keys = str.replace(/^F\./, '').split('.');
        // 屏蔽对define与module模块方法的重写
        if (keys[0] === 'define' || keys[0] === 'module') return this;
        // 遍历字符串数组,赋值到当前对象内
        let obj = this;
        for (let i = 0; i < keys.length; i++) {
            // 防止后续模块覆盖以前定义的模块
            if (typeof obj[keys[i]] === 'undefined') {
                obj[keys[i]] = {};
                if (i === keys.length - 1) {
                    obj[keys[i]] = fn && fn();
                }
            }
            obj = obj[keys[i]];
        }
        // 当前对象
        return this;
    };
    
    /**
     * 模块调用方法
     * @param  {...any} args 需要最后的参数为function
     */
    F.module = function (...args) {
        // 依赖模块列表
        let modules = [];
        // 判断最后一个参数是否为函数
        let fn = typeof args[args.length - 1] === 'function' ? args.pop() : null;
        // 将参数转换为数组
        let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
        // 遍历参数
        let i = 0;
        while (i < arr.length) {
            // 判断参数是否是字符串(通过define创建的)
            if (typeof arr[i] === 'string') {
                let keys = arr[i].replace(/^F\./, '').split('.');
                // 获取当前F对象
                let mod = this;
                for (let j = 0; j < keys.length; j++) {
                    // 替换当前的mod,查询到最里层对象
                    mod = mod[keys[j]];
                }
                modules.push(mod);
            } else {
                modules.push(arr[i])
            }
            i++;
        }
        fn && fn.apply(this, modules);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    // 创建导航模块
    F.define('nav.create', function () {
        return function (dom, data) {
            for (let i = 0; i < data.length; i++) {
                dom.append(`
  • ${data[i].label}
  • `
    ) } } }); // 添加导航引导模块 F.define('nav.create.addGuide', function () { return function (dom, data) { let li = $('li', dom); for (let i = 0; i < data.length; i++) { if (data[i].isGuide) { $(li[i]).append('[hot]'); } } } }); // 添加导航事件模块 F.define('nav.create.hover', function () { return function (dom) { let li = $('li', dom); li.on('mouseover', function () { $('#panel').css({ left: this.offsetLeft + 'px', display: 'block' }); }).on('mouseout', function () { $('#panel').css({ display: 'none' }); }); } }); // 导航数据请求模块 F.define('nav.request', function () { return function (cb) { setTimeout(function () { let data = [ { label: '首页', isGuide: false }, { label: '推荐', isGuide: true }, { label: '我的', isGuide: false } ]; cb($('#nav'), data); }, 2000); } }); console.log(F);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    F.module(['nav.request', 'nav.create', 'nav.create.addGuide', 'nav.create.hover'], function (request, create, addGuide, hover) {
        request(function (dom, data) {
            create(dom, data);
            addGuide(dom, data);
            hover(dom);
        });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    特点

    模块化开发即是以分而治之的思想,实现对复杂系统的分解,使系统随着其功能的增加而变得可控、可拓展、可维护。

    模块化开发是一种对系统的分解,但使用时又像是以组合模式对模块的组合。

    因此这也使得系统中的问题一般出现在局部,使得开发人员处理相应模块即可,而不用顾虑整个系统。

    因此相对于整个复杂的系统,对于局部模块的改造、优化甚至替换所需成本要小得多。

    组合的灵活性也使得我们可以实现更复杂、多样化的功能。

    大心脏-异步模块模式

    异步模块模式——AMD(Asynchronous Module Definition)

    模块化:将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块的复用率

    请求发出后,继续其他业务逻辑,直到模块加载完成执行后续的逻辑,实现模块开发中对模块加载完成后的引用

    异步加载文件中的模块

    在这里插入图片描述

    • index.js
      /**
       * 同步模块模式-SMD
       */
      
      // 向闭包中传入模块管理器对象F(~屏蔽压缩文件时,前面漏写;报错)
      ~(function (F) {
          /**
           * 定义模块管理器
           * @param {string} str 模块路由
           * @param {Function} fn 模块方法
           * @returns F模块管理器对象
           */
          F.define = function (str, fn) {
              // 过滤掉第一个元素为F的字符串数组
              let keys = str.replace(/^F\./, '').split('.');
              // 屏蔽对define与module模块方法的重写
              if (keys[0] === 'define' || keys[0] === 'module') return this;
              // 遍历字符串数组,赋值到当前对象内
              let obj = this;
              for (let i = 0; i < keys.length; i++) {
                  // 防止后续模块覆盖以前定义的模块
                  if (typeof obj[keys[i]] === 'undefined') {
                      obj[keys[i]] = {};
                      if (i === keys.length - 1) {
                          obj[keys[i]] = fn && fn();
                      }
                  }
                  obj = obj[keys[i]];
              }
              // 当前对象
              return this;
          };
      
          /**
           * 模块调用方法
           * @param  {...any} args 需要最后的参数为function
           */
          F.module = function (...args) {
              // 依赖模块列表
              let modules = [];
              // 判断最后一个参数是否为函数
              let fn = typeof args[args.length - 1] === 'function' ? args.pop() : null;
              // 将参数转换为数组
              let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
              // 遍历参数
              let i = 0;
              while (i < arr.length) {
                  // 判断参数是否是字符串(通过define创建的)
                  if (typeof arr[i] === 'string') {
                      let keys = arr[i].replace(/^F\./, '').split('.');
                      // 获取当前F对象
                      let mod = this;
                      for (let j = 0; j < keys.length; j++) {
                          // 替换当前的mod,查询到最里层对象
                          mod = mod[keys[j]];
                      }
                      modules.push(mod);
                  } else {
                      modules.push(arr[i])
                  }
                  i++;
              }
              fn && fn.apply(this, modules);
          };
      }((function () {
          return window.F = {};
      }())));
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
    • demo.js
      F.define('demo.say', function () {
          return function (name) {
              console.log(`Hello ${name}`);
          }
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • index.html
      <script src="./index.js">script>
      <script>
          /**
           * 加载脚本文件
           * @param {string} src 脚本路径
           */
          function loadScript(src) {
              let _script = document.createElement('script');                 // 创建脚本元素
              _script.type = 'text/javascript';                               // 设置类型
              _script.charset = 'UTF-8';                                      // 确认编码
              _script.async = true;                                           // 异步加载
              _script.src = src;                                              // 设置加载路径
              document.getElementsByTagName('head')[0].appendChild(_script);  // 将元素插入到页面中
          }
          // 加载demo.js脚本
          loadScript('./demo.js');
      
          // 调用demo.say模块方法 (Error ---> Uncaught TypeError: Cannot read properties of undefined (reading 'say'))
          F.module('demo.say', function (say) {
              say('Lee');
          });
      
          setTimeout(() => {
              // 调用demo.say模块方法 (Success ---> Hello Lee)
              F.module('demo.say', function (say) {
                  say('Lee');
              });
          }, 2000);
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29

    报错原因:

    由于浏览器中的文件是异步加载的,虽然现在开始加载demo.js文件,不过在文件没有加载完之前你可以继续做其他的事情,并且你写的方法,对于文件什么时候加载完成,你是无法获知的。

    同步模块模式会立即引用该模块,此时文件加载尚未加载完成,因此你是引用不到该模块的。

    当延迟了2s后,demo.js文件已经加载完毕,所以能得到demo.say模块,所以访问无问题。

    异步模块(使用require.js

    在这里插入图片描述

    https://requirejs.org/docs/release/2.3.6/comments/require.js

    <script src="https://requirejs.org/docs/release/2.3.6/comments/require.js">script>
    <script>
        /**
         * 异步模块(使用require.js)
         */
        require(['./lib/a', './lib/b'], function (a, b) {
            console.log(a, b);
            a('Lee'); // Hello Lee
            b.demo.getName(); // demo
        });
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // a.js
    define([], function () {
        return function (name) {
            console.log(`Hello ${name}`);
        }
    });
    
    // b.js 依赖于demo.js
    define(['./demo'], function (demo) {
        return {
            name: 'b',
            demo
        };
    });
    
    // demo.js
    define([], function () {
        return {
            name: 'demo',
            getName(){
                console.log(this.name);
            }
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    仿require.js的实现

    核心思想:每开始进行加载一个js脚本,计数器加1;每加载完成一个脚本,计数器减1;当全部脚本加载完成,计数器为0,此时执行回调函数,告诉控制台脚本已经加载完成

    在这里插入图片描述

    在这里插入图片描述

    // index.js
    
    /**
     * 异步模块 - 创建与调度模块
     */
    
    // 向闭包中传入模块管理器对象R(~屏蔽压缩文件时,前面漏写;报错)
    ~(function (R) {
    
        // 缓存所有加载的模块
        const cache = {};
    
        // 最终的回调
        let cb = function () { };
    
        // 脚本加载数量(每添加一个js脚本累加1;加载完成累减1;全部完成变为0)
        let depCount = 0;
    
        /**
         * 获取脚本路径
         * @param {string} name 路径
         * @returns 路径
         */
        function getScriptSrc(name) {
            // 拼接完整的文件路径字符串,如'lib/ajax' => 'lib/ajax.js'
            return String(name).replace(/\.js$/g, '') + '.js';
        }
    
        /**
         * 加载脚本文件
         * @param {string} src 路径
         */
        function loadScript(src) {
            let _script = document.createElement('script');
            _script.type = 'text/javascript';                               // 文件类型
            _script.charset = 'UTF-8';                                      // 确认编码
            _script.async = true;                                           // 异步加载
            _script.src = src;                                              // 文件路径
            document.getElementsByTagName('head')[0].appendChild(_script);  // 插入页面中
        }
    
        /**
         * 定义模块(当引入的js加载完成,就会自动执行此方法)
         * @param {string} name 模块名称
         * @param {Array} deps 依赖项 ([{name: 模块名称, path: 模块路径}, ...])
         * @param {Function} callback 回调
         */
        R.define = function (name, deps, callback) {
    
            depCount--;
    
            // 添加依赖脚本
            deps.forEach(dep => {
                depCount++;
                loadScript(getScriptSrc(dep.path));
            });
    
            // 是否存在此模块,避免重复赋值
            if (!cache[name]) {
                cache[name] = {
                    depNames: deps.length ? deps.map(dep => dep.name) : [], // 脚本的依赖脚本模块名称(作为缓存对象的key,用于最终整理)
                    hasDep: !!deps.length,                                  // 是否存在脚本的依赖脚本
                    export: deps.length ? callback : callback(),            // 最终导出的数据(存在依赖脚本则返回函数,用于后续判断调用;不存在依赖脚本,返回当前函数的执行)
                };
            }
    
            // 全部加载完成
            if (depCount === 0) {
                Object.values(cache).forEach(item => {
                    if (item.hasDep) {
                        let params = item.depNames.map(key => cache[key]['export']);
                        item.export = item.export.apply(null, params);
                    }
                });
    
                let obj = {};
                Object.keys(cache).forEach(key => {
                    obj[key] = cache[key].export;
                });
    
                cb.call(null, obj);
            }
    
        };
    
        /**
         * 执行各个脚本js模块
         * @param {Array} deps 当前页面的依赖项,js脚本路径集合
         * @param {Function} callback 回调
         */
        R.module = function (deps, callback) {
    
            // 保存当前的回调,用于脚本完全加载完成后调用
            cb = callback;
    
            // 添加依赖脚本
            deps.forEach(dep => {
                depCount++;
                loadScript(getScriptSrc(dep));
            });
    
        };
    
    }((function () {
        return window.R = {};
    }())));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • html.html
      <script src="./index.js">script>
      <script>
          console.log(R);
          /**
           * 异步模块
           */
          R.module(['./lib/a', './lib/b'], function (mod) {
              /**
               * {
               *      a: ƒ (name),
               *      b: {
               *          name: 'b', 
               *          demo1: {name: 'demo1', getName: ƒ}, 
               *          demo2: {name: 'demo2', getName: ƒ},
               *      },
               *      demo1: {name: 'demo1', getName: ƒ},
               *      demo2: {name: 'demo2', getName: ƒ},
               * }
               */
              console.log(mod);
      
              mod.a('Lee'); // Hello Lee
      
              mod.b.demo1.getName(); // demo1
              mod.b.demo2.getName(); // demo2
      
              mod.demo1.getName(); // demo1
              mod.demo2.getName(); // demo2
          });
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
    • lib/a.js
      // a.js
      R.define('a', [], function () {
          return function (name) {
              console.log(`Hello ${name}`);
          }
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • lib/b.js
      // b.js 依赖于 demo1.js demo2.js
      R.define('b', [{ name: 'demo1', path: './lib/demo1' }, { name: 'demo2', path: './lib/demo2' }], function (demo1, demo2) {
          console.log(demo1, demo2);
          return {
              name: 'b',
              demo1, 
              demo2
          };
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • lib/demo1.js
      // demo1.js
      R.define('demo1', [], function () {
          return {
              name: 'demo1',
              getName(){
                  console.log(this.name);
              }
          }
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • lib/demo2.js
      // demo2.js
      R.define('demo2', [], function () {
          return {
              name: 'demo2',
              getName(){
                  console.log(this.name);
              }
          }
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    特点

    模块化开发不仅解决了系统的复杂性问题,而且减少了多人开发中变量、方法名被覆盖的问题。通过其强大的命名空间管理,使模块的结构更合理。

    通过对模块的引用,提高了模块代码复用率。异步模块模式在此基础上增加了模块依赖,使开发者不必担心某些方法尚未加载或未加载完全造成的无法使用问题。

    异步加载部分功能也可将更多首屏不必要的功能剥离出去,减少首屏加载成本。

  • 相关阅读:
    08-Nginx缓存集成
    关于windows下tensorflow的配置与开发
    【LeetCode刷题】:仅仅反转字母(双指针+字符串)
    Linux shell编程学习笔记8:使用字符串
    NeurIPS 23 Spotlight丨3D-LLM:将3D世界注入大语言模型
    Android12上修改erofs为ext4
    centos8手动编译安装swoole过程
    曾仕强老师视频+音频+电子书合集百度网盘资源
    x86架构上构建arm64架构的docker镜像
    STM32物联网项目-高级定时器功能介绍
  • 原文地址:https://blog.csdn.net/weixin_43526371/article/details/127733309