【JS基础】一文看懂前端模块化规范

栏目: JavaScript · 发布时间: 5年前

内容简介:前端的模块化之路经历了漫长的过程,这里根据大佬们写的文章,将模块化规范部分做了汇总和整理,想详细了解的小伙伴可以看浪里行舟大神写的Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。CommonJS规范加载模块是

前端的模块化之路经历了漫长的过程,这里根据大佬们写的文章,将模块化规范部分做了汇总和整理,想详细了解的小伙伴可以看浪里行舟大神写的 前端模块化详解(完整版) ,希望读完的小伙伴能有些收获,也希望觉得有用的小伙伴可以点个赞,笔芯。

什么是模块

  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
  • 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信

CommonJS

Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。 在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。

CommonJS规范加载模块是 同步 的,也就是说,只有加载完成,才能执行后面的操作。

基本语法:

  • 暴露模块: module.exports = valueexports.xxx = value
  • 引入模块: require(xxx) ,如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径

但是,CommonJs有一个重大的局限使得它 不适用于浏览器环境 ,那就是 require 操作是同步的。这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。

因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous),这就是AMD规范诞生的背景。

AMD

特点:非同步加载模块,允许指定回调函数, 浏览器端一般采用AMD规范

代表作:require.js

用法:

//定义没有依赖的模块
    define(function(){
       return 模块
    })

    //定义有依赖的模块
    define(['module1', 'module2'], function(m1, m2){
       return 模块
    })
    
    //引入使用模块
    require(['module1', 'module2'], function(m1, m2){
       //使用m1/m2
    })
复制代码

CMD

特点:专门用于浏览器端,模块的 加载是异步的 ,模块使用时才会加载执行

代表作:Sea.js

用法:

//定义没有依赖的模块
    define(function(require, exports, module){
      exports.xxx = value
      module.exports = value
    })
    
    //定义有依赖的模块
    define(function(require, exports, module){
      //引入依赖模块(同步)
      var module2 = require('./module2')
        //引入依赖模块(异步)
        require.async('./module3', function (m3) {
        })
      //暴露模块
      exports.xxx = value
    })
    
    //引入使用模块
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
复制代码

CMD与AMD区别

AMD和CMD最大的区别是 对依赖模块的执行时机处理不同 ,而 不是加载的时机或者方式不同 ,二者皆为 异步加载 模块。

AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;

而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

一句话总结: 两者都是异步加载,只是执行时机不一样。AMD是依赖前置,提前执行,CMD是依赖就近,延迟执行。

UMD

UMD是AMD和CommonJS的糅合:

AMD模块以浏览器第一的原则发展,异步加载模块。

CommonJS模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。

这迫使人们又想出另一个更通用的模式UMD (Universal Module Definition),希望解决跨平台的解决方案。

UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用 Node.js 模块模式。

在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

(function (window, factory) {
        if (typeof exports === 'object') {
         
            module.exports = factory();
        } else if (typeof define === 'function' && define.amd) {
         
            define(factory);
        } else {
         
            window.eventUtil = factory();
        }
    })(this, function () {
        //module ...
    });
复制代码

ES6模块化

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

ES6 Module默认目前还没有被浏览器支持,需要使用babel,在日常写demo的时候经常会显示这个错误:

【JS基础】一文看懂前端模块化规范

ES6模块使用 import 关键字导入模块, export 关键字导出模块:

/** 导出模块的方式 **/
    
    var a = 0;
    export { a }; //第一种
       
    export const b = 1; //第二种 
      
    let c = 2;
    export default { c }//第三种 
    
    let d = 2;
    export default { d as e }//第四种,别名
    
    /** 导入模块的方式 **/
    
    import { a } from './a.js' //针对export导出方式,.js后缀可省略
    
    import main from './c' //针对export default导出方式,使用时用 main.c
    
    import 'lodash' //仅仅执行lodash模块,但是不输入任何值
复制代码

命名式导出与默认导出

export {<变量>} 这种方式一般称为 命名式导出 或者 具名导出 ,导出的是一个 变量的引用

export default 这种方式称为 默认导出 或者 匿名导出 ,导出的是一个

举例:

// a.js
    let x = 10
    let y = 20
    setTimeout(()=>{
        x = 100
        y = 200
    },100)
    export { x }
    export default y

    // b.js
    import { x } from './a.js'
    import y from './a.js'
    setTimeout(()=>{
        console.log(x,y) // 100,20
    },100)
复制代码

ES6 模块与 CommonJS 模块的差异

① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

CommonJS 模块输出的是 值的拷贝 ,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。而且,CommonJS 模块无论加载多少次,都 只会在第一次加载时运行一次 ,以后再加载,返回的都是第一次运行结果的缓存,除非手动清除系统缓存。

ES6 模块的运行机制与 CommonJS 不一样,JS 引擎对脚本静态分析的时候,遇到模块加载命令 import ,就会生成一个 只读引用 ,等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的 import 有点像 Unix 系统的“符号连接”,原始值变了, import 加载的值也会跟着变。因此,ES6 模块是 动态引用 ,并且 不会缓存值 ,模块里面的变量绑定其所在的模块。

② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“ 运行时加载 ”。

例如:

// CommonJS模块
    let { stat, exists, readFile } = require('fs');
    
    // 等同于
    let _fs = require('fs');
    let stat = _fs.stat;
    let exists = _fs.exists;
    let readfile = _fs.readfile;
复制代码

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。通过 export 命令显式指定输出的代码, import 时采用静态命令的形式。即在 import 时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“ 编译时加载 ”或者“ 静态加载 ”。

// ES6模块
    import { stat, exists, readFile } from 'fs';
复制代码

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它 不是对象

由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。

除了静态加载带来的各种好处,ES6 模块还有以下好处:

navigator
Math

以上所述就是小编给大家介绍的《【JS基础】一文看懂前端模块化规范》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

我看电商2(双色)

我看电商2(双色)

黄若 / 电子工业出版社 / 2016-6 / 39.00元

《我看电商2》是行业畅销书《我看电商》的续集。 《我看电商》自出版以来,连续印刷14 次,受到业界人士和广大读者的高度好评。《我看电商2》承续作者一贯的风格,以行业观察、经验分享为出发点,重点分析了过去一年中国电商界的最新动态与趋势,包括双11点评、京东关闭拍拍、上市公司私有化等。 电子商务是我国近年来发展最快的新兴行业之一,作者作为这个行业的长老级领军人物,善于思考,长于实操。《我看......一起来看看 《我看电商2(双色)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具