写点什么

聊聊什么是 CommonJs 和 Es Module 及它们的区别

用户头像
蛙人
关注
发布于: 2021 年 03 月 12 日
聊聊什么是CommonJs和Es Module及它们的区别

前言


初衷: 将我整理的笔记分享给大家,希望本篇文章能给你带来不一样的认知,不喜勿喷。


适合人群: 前端初级开发,大佬绕道。


内容结构: 为什么有模块化 -> 基本语法 -> 两者区别。


为什么会有 CommonJs 和 Es Module 呢


我们都知道在早期JavaScript模块这一概念,都是通过script标签引入js文件代码。当然这写基本简单需求没有什么问题,但当我们的项目越来越庞大时,我们引入的js文件就会越多,这时就会出现以下问题:


  • js 文件作用域都是顶层,这会造成变量污染

  • js 文件多,变得不好维护

  • js 文件依赖问题,稍微不注意顺序引入错,代码全报错


为了解决以上问题JavaScript社区出现了CommonJsCommonJs是一种模块化的规范,包括现在的NodeJs里面也采用了部分CommonJs语法在里面。那么在后来Es6版本正式加入了Es Module模块,这两种都是解决上面问题,那么都是解决什么问题呢。


  • 解决变量污染问题,每个文件都是独立的作用域,所以不存在变量污染

  • 解决代码维护问题,一个文件里代码非常清晰

  • 解决文件依赖问题,一个文件里可以清楚的看到依赖了那些其它文件


那么我们下面来一一了解它们的语法及弊端吧


CommonJs 基本语法


导出


CommonJs中使用module.exports导出变量及函数,也可以导出任意类型的值,看如下案例。


// 导出一个对象module.exports = {    name: "蛙人",    age: 24,    sex: "male"}
// 导出任意值module.exports.name = "蛙人"module.exports.sex = nullmodule.exports.age = undefined
复制代码


直接导出


导出也可以省略module关键字,直接写 exports 导出也可以,看如下案例。


exports.name = "蛙人"exports.sex = "male"
复制代码


注意:如果使用 exports 导出单个值之后,就不能在导出一个对象值,这会修改当前导出的引用,然而之前的导出就会被覆盖。


exports.name = "蛙人"exports.sex = "male"exports = {    name: "蛙人"}
复制代码


上面 example 中,这种情况会改变对象的引用值,所以最后导出的只是一个对象。


混合导出


混合导出,exportsmodule.exports可以同时使用,不会存在问题。


exports.name = "蛙人"module.exports.age = 24
复制代码


导入


CommonJs中使用require语法可以导入,如果想要单个的值,可以通过解构对象来获取。


// index.jsmodule.exports.name = "蛙人"module.exports.age = 24
let data = require("./index.js")console.log(data) // { name: "蛙人", age: 24 }
复制代码


重复导入


不管是CommonJs还是Es Module都不会重复导入,就是只要该文件内加载过一次这个文件了,我再次导入一次是不会生效的。


let data = require("./index.js")let data = require("./index.js") // 不会在执行了
复制代码


动态导入


CommonJs支持动态导入,什么意思呢,就是可以在语句中,使用require语法,来看如下案例。


let lists = ["./index.js", "./config.js"]lists.forEach((url) => require(url)) // 动态导入
if (lists.length) { require(lists[0]) // 动态导入}
复制代码


导入值的变化


CommonJs导入的值是拷贝的,所以可以修改拷贝值,但这会引起变量污染,一不小心就重名。


// index.jslet num = 0;module.exports = {    num,    add() {       ++ num     }}
let { num, add } = require("./index.js")console.log(num) // 0add()console.log(num) // 0num = 10
复制代码


上面 example 中,可以看到exports导出的值是值的拷贝,更改完++ num值没有发生变化,并且导入的num的值我们也可以进行修改


总结


CommonJs解决了变量污染,文件依赖等问题,上面我们也介绍了它的基本语法,它可以动态导入(代码发生在运行时),不可以重复导入。


Es Module 基本语法


导出


Es Module中导出分为两种,单个导出(export)、默认导出(export default),单个导出在导入时不像CommonJs一样直接把值全部导入进来了,Es Module中可以导入我想要的值。那么默认导出就是全部直接导入进来,当然Es Module中也可以导出任意类型的值。


// 导出变量export const name = "蛙人"export const age = 24
// 导出函数也可以export function fn() {}export const test = () => {}
// 另一种形式导出const sex = "male"export sex
// 如果有多个的话const name = "蛙人"const sex = "male"export { name, sex }
复制代码


混合导出


可以使用exportexport default同时使用并且互不影响,只需要在导入时地方注意,如果文件里有混合导入,则必须先导入默认导出的,在导入单个导入的值。


export const name = "蛙人"export const age = 24
export default = { fn() {}, msg: "hello 蛙人"}
复制代码


导入


Es Module使用的是import语法进行导入。如果要单个导入则必须使用花括号{}注意:这里的花括号跟解构不一样


// index,jsexport const name = "蛙人"export const age = 24
import { name, age } from './index.js'console.log(name, age) // "蛙人" 24
// 如果里面全是单个导出,我们就想全部直接导入则可以这样写import * as all from './index.js'console.log(all) // {name: "蛙人", age: 24}
复制代码


混合导入


混合导入,则该文件内用到混合导入,import语句必须先是默认导出,后面再是单个导出,顺序一定要正确否则报错。


// index,jsexport const name = "蛙人"export const age = 24export default = {    msg: "蛙人"}
import msg, { name, age } from './index.js'console.log(msg) // { msg: "蛙人" }
复制代码


上面 example 中,如果导入的名称不想跟原本地名称一样,则可以起别名。


// index,jsexport const name = "蛙人"export const age = 24export default = {    msg: "蛙人"}
import { default as all, name, age } from './index.js'console.log(all) // { msg: "蛙人" }
复制代码


导入值的变化


export导出的值是值的引用,并且内部有映射关系,这是export关键字的作用。而且导入的值,不能进行修改也就是只读状态。


// index.jsexport let num = 0;export function add() {    ++ num}
import { num, add } from "./index.js"console.log(num) // 0add()console.log(num) // 1num = 10 // 抛出错误
复制代码


Es Module 是静态


就是Es Module语句`import只能声明在该文件的最顶部,不能动态加载语句,Es Module语句运行在代码编译时。


if (true) {	import xxx from 'XXX' // 报错}
复制代码


总结


Es Module也是解决了变量污染问题,依赖顺序问题,Es Module语法也是更加灵活,导出值也都是导出的引用,导出变量是可读状态,这加强了代码可读性。


CommonJs 和 Es Module 的区别


CommonJs


  • CommonJs 可以动态加载语句,代码发生在运行时

  • CommonJs 混合导出,还是一种语法,只不过不用声明前面对象而已,当我导出引用对象时之前的导出就被覆盖了

  • CommonJs 导出值是拷贝,可以修改导出的值,这在代码出错时,不好排查引起变量污染


Es Module


  • Es Module 是静态的,不可以动态加载语句,只能声明在该文件的最顶部,代码发生在编译时


  • Es Module 混合导出,单个导出,默认导出,完全互不影响

  • Es Module 导出是引用值之前都存在映射关系,并且值都是可读的,不能修改


感谢

谢谢各位在百忙之中点开这篇文章,希望对你们能有所帮助,如有问题欢迎各位大佬指正。


我是蛙人,如果觉得写得可以的话,请点个赞吧。


感兴趣的小伙伴可以加入 [[ 前端娱乐圈交流群 ]](http://chuantu.xyz/t6/741/1615129069x1700339730.png) 欢迎大家一起来交流讨论


往期好文


《带你轻松理解数据结构之Map》


《这些工作中用到的JavaScript小技巧你都知道吗?》


《理解数据结构之Set,只要5分钟!》


《【建议收藏】分享一些工作中常用的Git命令及特殊问题场景怎么解决》


《解构:使数据访问更便捷!》


《你真的了解ES6中的函数特性么?》


《一看就懂的var、let、const三者区别》


发布于: 2021 年 03 月 12 日阅读数: 10
用户头像

蛙人

关注

勇于跨越,追求卓越 2020.09.08 加入

前端白菜一枚 @公众号:前端娱乐圈

评论

发布
暂无评论
聊聊什么是CommonJs和Es Module及它们的区别