美文网首页
ESM和CJS区别

ESM和CJS区别

作者: 乐宝呗 | 来源:发表于2021-10-13 13:12 被阅读0次

    ES6 Module和CommonJS区别

    区别一

    前者属于编译时加载,即静态加载,在编译时就能够确定模块的依赖关系,以及输入和输出的变量;后者属于运行时加载,都只有在代码运行时才能确定这些东西。ESM形式的好处是可以做到tree shaking。

    区别二

    前者可以加载模块的部分内容,后者需要加载模块整个对象,再取到内容。

    区别一和二可以用tree shaking来解释一下是大概是怎样一个流程。

    按照webpack官网的解释,tree shaking通常用于描述移除JavaScript上下文中的未引用代码(dead-code)。它依赖于ESM的静态分析能力,例如import和export。用大白话解释就是,如果是使用模块化开发的话,就可以删除那些引入某个模块中用不到的函数。

    为什么叫tree shaking, 我个人觉得这个名次叫的很形象呀。你可以将应用程序想象成一棵树。绿色表示实际用到的源码和 library,是树上活的树叶。灰色表示无用的代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

    1.index.js的作用是 创建一个DOM元素,然后引入math里面的cube函数来使用,只用了cube函数。

    import { cube } from './math'

    function component() {

    var element = document.createElement('div')

        element.innerHTML = [

    'hello webpack!',

    '5 cubed is equal to' + {cube(5)

        ].join('\n\n')

    return element

    }

    document.body.appendChild(component())

    而在math.js里,export了两个工具函数。实际上我们要看的就是,只有cube在index里面使用了,但是看下square在不同的模块系统下是否被引入。

    export function square (x) {

    return x * x

    }

    export function cube (x) {

    return x * x * x

    }

     最后我们运行npm run build后,会在dist目录下生成app.bunble.js到文件,这个文件是被压缩过的,我这边格式化下代码。

    根据结果我们看到,index.js里面只使用了cube函数,在最终的结果bundle.js中,也只把math中的cube函数引入了进来,而没有处理square函数。因为它没有在实际过程中使用到。这就通过ESM的import和export的方式实现了tree shaking。也就是我们在区别一、二中说到的,ESM可以在编译时静态分析模块之间的依赖,并且只加载模块中被使用的内容。

    2.ok看到这里,我们已经实现了tree shaking。但是我们要琢磨下,不同的import和export方式或者使用CommonJS的情况下,能否实现tree shaking。

    我们把index.js里面的引入方式改成如下

    import * as math from './math'

    // 使用的方式改成math.cube(5)

    math.js的export方式不变,但是我们改变了index.js的引入方式。在运行下npm run build后,我们发现bundle.js也只是输出了cube函数,square函数就被tree shaking掉了。实际上跟上面那种的结果是一样的。

    改变index.js中引入math的方式

    3. 按照ESM的语法规定,我们可以混用export和export default,那么我们在math.js再加上export default ,index.js引入math的方式可以使用两种。

    // math.js

    export ..

    export ..

    export default {

    square,

    cube

    }

    // index.js

    import { cube } from './math'

    // 或者 import math from './math'

    运行npm run build后,格式化bunble.js文件,我们会发现当我们只引入cube函数时,可以做到tree shaking,但是当我们直接import math来后,就把没有用到的square函数也引入了。

    import { cube } from './math'的方式

    import math from './math'的方式

    仔细想想,这也就说明了ESM要实现加载模块的部分内容,export某个模块是ok的,但是export default整个模块出来,就无法做tree shaking了。

    4. 最后看看CommonJS的结果是怎样的。我们知道,CommonJS属于运行时加载,它会在代码运行到那一行时将整个模块加载进来。我们将index.js的引入方式改成如下,而math还是按照原始状态,单独export 两个 工具函数。

    // index.js

    const { cube } = require('./math')

    // 写法二:const math = require('./math')

    // math.js 

    // 注:webpack支持ESM和CommonJS模块的相互转换

    export function square (x) {

        return x * x

    }

    export function cube (x) {

        return x * x * x

    }

    运行npm run build后,我们发现不管index.js里面的写法有什么不同,只要是CommonJS的语法,他们的结果都是一样的,即无法实现tree shaking,将无关的square函数引入到bunble.js中。

    index.js中两种写法都是一样的结果

    ok,以上三种引用方式,进一步说明了ESM和CommonJS的区别。即区别一二指出的

    另外,实现tree shaking的好处就不言而喻了,可以极大减少build后js的大小。

    tree shaking的实现依赖于ESM的静态分析能力,import和export可以实现tree shaking,但是直接export default 整个对象或者使用CommonJS的语法是无法实现的。

    区别三

    前者输出的是值的引用,后者输出的是值的拷贝。这个其实在阮一峰老师关于Module的加载实现里面有谈过这个。这边我觉得这种模块之间的引入,一般很少会说去改变模块引入进来的内容。所以具体的例子可以参考阮一峰老师的。传送门:Module 的加载实现

    区别四

    由于前者属于编译时加载,无法像后者一般,做到运行时加载。所以,有一个提案,引入import()函数,完成运行时加载,或者叫动态加载。import()和require()相同点都是运行时加载;区别在于,import()属于异步加载,require()属于同步加载。

    运行时加载的好处:按需加载;条件加载;动态的模块路径。

    什么叫运行时加载呢?就是说 你可以在一些比如条件判断语句里引入模块。而编译时加载就不行。如果使用import或者export时会报错,会告诉你这两个关键字的使用必须是在顶层的作用域才行。

    if(type===1){

        constmath=require('./math')

    }// 报错,错误类似于// 'import' and 'export' may only appear at the top levelif(type===1){importmathfrom'./math'}

    为了解决ESM中无法实现运行时加载的问题,ESM提出了import()函数的概念,来完成运行时加载。而且从例子中,我们也可以明显的看出,import函数是异步加载的,而CommonJS是同步加载的。

    if(type===1){

        import('./math.js').then(res=>{

            // do something

        }).catch(err=>{

            // oops!

        })

     }

    if(type===1){

        const math=require('./math')

        // do something

    }

    文章摘抄自 https://zhuanlan.zhihu.com/p/71098263

    相关文章

      网友评论

          本文标题:ESM和CJS区别

          本文链接:https://www.haomeiwen.com/subject/edwloftx.html