最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 几种常用的模块化规范总结

    正文概述 掘金(DennisWu)   2021-04-05   638

    几种常用的模块化规范总结

    一、CommonJS规范

    在CommonJS目录下创建三个文件:

    • CommonJS/moduleB.js
    module.exports = new Date().getTime();
    
    • CommonJS/moduleA.js
    const timestamp = require('./moduleB');
    console.log('moduleA:::', timestamp);
    
    • CommonJS/index.js
    require('./moduleA');
    const timestamp = require('./moduleB');
    console.log('index:::', timestamp);
    

    其中,CommonJS/index.js引用了moduleA,而moduleA中引用了moduleB,另外,CommonJS/index.js还直接引用了moduleB,可见moduleB是被引用了两次。在这样的ModuleB被两次依赖的情况下,ModuleB是否会被执行两次呢?

    通过执行node CommonJS/index.js我们来看看输出结果:

    moduleA::: 1617595616025
    index::: 1617595616025
    

    可以看出,虽然ModuleB被依赖了两次,但是输出的时间戳的值是一样的,可见,ModuleB并没有被执行两次。

    总结一下,关于CommonJS规范:

    1、JavaScript原生并不支持该规范,在没有经过打包工具打包的情况下,它是只可以在Node.js环境中运作的(在浏览器环境下不支持该规范,会报错);

    2、CommonJS模块被多次引用时,会保持模块是一个单例,而不会被重复执行多次;

    3、用module.exports导出模块,用require引入模块。

    二、AMD规范

    受到CommonJS模块化规范的启发,浏览器端逐步发展起来了AMD规范。AMD全称为Asynchronous module definition,意为异步的模块定义。正如其名,所有模块默认都是异步加载的,这也是当时为了满足Web开发的需要,因为如果在Web端也像CommonJS规范那样同步加载的话,那么页面在解析脚本文件的过程中可能由于耗时过长,而使得页面暂停响应。

    在AMD文件夹下新建4个文件:

    • AMD/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>test AMD</title>
    </head>
    <body>
      <h2>test AMD</h2>
      <script src="https://requirejs.org/docs/release/2.1.16/minified/require.js"></script>
      <script src="./index.js"></script>
    </body>
    </html>
    
    • AMD/index.js
    require([
      './moduleA.js',
      './moduleB.js'
    ], function(moduleA, moduleB) {
      console.log('index:::', moduleB);
    });
    
    • AMD/moduleA.js
    define(function (require) {
      const timestamp = require('./moduleB.js');
      console.log('moduleA:::', timestamp);
    });
    
    • AMD/moduleB.js
    define(function (require) {
      return new Date().getTime();
    });
    

    然后执行npm install -g anywhere安装静态服务启动工具anywhere,并执行

    cd AMD
    anywhere -p 80
    

    在80端口上启动静态资源服务器。

    然后通过 http://localhost 访问该服务,在Chrome Devtools的console面板上可看到如下输出:

    moduleA::: 1617597603392
    index::: 1617597603392
    

    这表明同CommonJS规范一样,AMD规范中,模块被多次引用时,也是单例的,不会被执行多次。

    接下来,我们把AMD/index.js文件的内容改成如下,即去除AMD/index.js对moduleB的依赖:

    require([
      './moduleA.js',
    ], function(moduleA) {
    });
    

    然后,我们打开Chrome Devtools的Network面板,把网络模式Slow 3G(如下图)。

    几种常用的模块化规范总结

    然后刷新一下页面,查看到各JS文件的加载顺序如下图:

    几种常用的模块化规范总结

    可见,ModuleA和ModuleB这两个AMD模块确实是异步加载的,且因为ModuleA所依赖的ModuleB,也是在ModuleA加载完之后才去异步地加载ModueB的。

    AMD异步加载的核心原理是使用了JSONP来加载模块。

    总结一下:

    1、AMD规范是运行在浏览器端的,不能运行在Node.js环境中。

    2、AMD默认是支持模块异步加载的。因为如果在Web端也像CommonJS规范那样同步加载的话,那么页面在解析脚本文件的过程中可能由于耗时过长,而使得页面暂停响应。

    3、AMD模块被多次引用时,会保持模块是一个单例,而不会被重复执行多次;

    4、用define(function (require) {})(匿名模块,以文件名为模块名) 或者 define('模块id', ['依赖数组'], function([依赖数组]){});(具名模块)定义模块,用require(['./xxx.js'], function(xxx) {})引入模块,其中第二个参数是回调。

    三、UMD规范

    UMD全称是Universal Module Definition(通用模块规范),它是由社区想出来的一种整合了CommonJS和AMD两个模块定义规范的方法。所以,严格意义上说,它并不是一个独立的模块化规范。其基本原理是用一个工厂函数来统一不同的模块定义规范。

    下面基于UMD规范写一个简单的例子(模块内容只是纯演示,该内容不是一个值得封装成模块的东西):

    • UMD/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>test 111</title>
    </head>
    <body>
      <button id="btn">按钮</button>
      <div id="container"></div>
      <script src="./jquery.js"></script>
      <script src="./moduleA.js"></script>
      <script src="./index.js"></script>
    </body>
    </html>
    
    • UMD/index.js
    window.moduleA.init();
    
    • UMD/jquery.js

    略去,从网上下载一个jQuery.js文件放在这里即可。

    • UMD/moduleA.js
    ((global, factory) => {
      //如果 当前的上下文有define函数,并且AMD  说明处于AMD 环境下
      if (typeof define === 'function' && define.amd) {
        // 检查AMD是否可用
        define(['./jquery'], factory);
      } else if (typeof exports === 'object') {
        // 检查CommonJS是否可用
        modules.exports = factory(require('./jquery'));
      } else {
        // 两种都不能用,把模块添加到JavaScript的全局命名空间中
        global.moduleA = factory(global.jQuery);
      }
    })(this, ($) => {
      //本模块的定义
      function init() {
        var $container = $('#container');
        var count = 0;
        var text = ['关', '开'];
        document.getElementById('btn').addEventListener('click', function() {
          count += 1;
          $container.html(text[count % 2]);
        });
      }
    
      return {
        init: init
      }
    })
    

    四、ESModule

    不管是CommonJS还是AMD,都是由语言上层的运行环境中实现的模块化规范,模块化规范由环境自己定义。但是ESModule是在语言层面实现的。

    几种常用的模块化规范总结

    先创建如下5个文件:

    • ESModule/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>test ESModule</title>
    </head>
    <body>
      <h2>test ESModule</h2>
      <script src="./dist/index.bundle.js"></script>
    </body>
    </html>
    
    • ESModule/index.js
    import './moduleA.js';
    import timestamp from './moduleB.js';
    
    console.log('index:::', timestamp);
    
    • ESModule/moduleA.js
    import timestamp from './moduleB.js';
    
    console.log('moduleA:::', timestamp);
    
    • ESModule/moduleB.js
    export default new Date().getTime();
    

    然后安装依赖:

    yarn add webpack webpack-cli @babel/core babel-loader @babel/preset-env
    

    并修改package.json中的scripts为:

    "scripts": {
        "build": "webpack"
    },
    

    执行npm run build进行打包。然后执行anywhere -p 80启动静态服务器。

    然后通过 http://localhost 访问该服务,在Chrome Devtools的console面板上可看到如下输出:

    moduleA::: 1617601623808
    index::: 1617601623808
    

    不过,由于兼容性的问题,ESModule目前在浏览器环境下还不能直接使用,需要进行打包编译。

    Babel会将ES6中的importexport编译成CommonJS规范的requireexports。如下图所致,左边是编译前的代码,右边是编译后的代码(可以到www.babeljs.cn/中体验测试):

    几种常用的模块化规范总结

    但是我们知道,CommonJS是无法运行在浏览器端的,怎么让它能够变成能运行在浏览器端的代码呢?这就是打包要做的事情了。

    那么Node.js中是如何做到能运行CommonJS模块的呢?

    例如,对于这样一段CommonJS代码:

    require('./moduleA');
    const timestamp = require('./moduleB');
    console.log('index:::', timestamp);
    

    它是这样处理的,通过fs模块读到模块的代码字符串,然后在其头部拼接上function(require, module, exports) {,在其尾部拼接上},最后将拼接得到的字符串放入vm.runInNewContextAPI中执行:

    const str = 'require('./moduleA');const timestamp = require('./moduleB');console.log('index:::', timestamp);'
    
    const functionWrapper = [
        'function(require, module, exports) {',
        '}'
    ];
    
    const result = functionWrapper[0] + str + functionWrapper[1];
    const vm = require('vm');
    
    vm.runInNewContext(result);
    

    vm是Node.js中的一个内置模块,其runInNewContext方法的作用相当于new Function(codeStr)() (参见这里)或者 eval(codeStr)(参见这里),关于runInNewContext方法的用法可参见Node.js的官方文档。

    此外,还有人可能用过CMD规范,但因为它毕竟当前应用已经不那么多了,这里就不介绍了,感兴趣的可以自行了解下。


    起源地下载网 » 几种常用的模块化规范总结

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元