js模块化——commonJS

总结于知乎《前端科普系列-CommonJS:不是前端却革命了前端》,作者:无名之辈

What

为了解决模块化问题,而制定的一种模块化规范

规范核心部分:

  1. Node.js 应用由模块组成,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
// a.js
var name = 'morrain'
var age = 18

上面代码中,a.js 是 Node.js 应用中的一个模块,里面申明的变量 name 和 age 是 a.js 私有的,其它文件都访问不到。
2. 每个模块内部有两个变量可以使用,require 和 module。

require 用来加载某个模块

module 代表当前模块,是一个对象,保存了当前模块的信息。exports 是 module 上的一个属性,保存了当前模块要导出的接口或者变量,使用 require 加载的某个模块获取到的值就是那个模块使用 exports 导出的值

// a.js
var name = 'morrain'
var age = 18
module.exports.name = name
module.exports.getAge = function(){
    return age
}

//b.js
var a = require('a.js')
console.log(a.name) // 'morrain'
console.log(a.getAge())// 18

HOW

  1. 关于export
    为了方便,Node.js 在实现 CommonJS 规范时,为每个模块提供一个 exports的私有变量,指向 module.exports。你可以理解为 Node.js 在每个模块开始的地方,添加了如下这行代码。
    var exports = module.exports
    于是上面的代码也可以这样写:
// a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.getAge = function(){
    return age
}

如果一个模块的对外接口,就是一个单一的值,可以使用 module.exports 导出

// a.js
var name = 'morrain'
var age = 18
module.exports = name
  1. 关于require
    require 命令的基本功能是,读入并执行一个 js 文件,然后返回该模块的 exports 对象的拷贝。如果没有发现指定模块,会报错。
    2.1 NodeJs 会缓存一个module

    如图所示,第二次require的时候,并没有重新执行并加载module A,而是直接返回了第一次 require 时的结果,也就是模块A的 module.exports。
    2.2 require 的是被导出的值的拷贝。也就是说,一旦导出一个值,模块内部的变化就影响不到这个值 。
// a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.age = age
exports.setAge = function(a){
    age = a
}
// b.js
var a = require('a.js')
console.log(a.age) // 18
a.setAge(19)
console.log(a.age) // 18

CommonJS 的实现

有了commonjs的规范,我们可以将它在前端/后端进行实现:

commonjs在前端的实现:

Browserify 、Webpack就是将各模块代码进行打包,进而转化为浏览器能够运行的js代码。commonjs规范无非是三个东西,module、require和exports,webpack等打包工具将其实现后,就可以进行转化了:

// bundle.js
(function (modules) {
    // 模块管理的实现
})({
  'a.js': function (module, exports, require) {
    // a.js 文件内容
  },
  'b.js': function (module, exports, require) {
    // b.js 文件内容
  },
  'index.js': function (module, exports, require) {
    // index.js 文件内容
  }
})

** 但是前端有一个异步加载的问题,因为各个模块不一定同时下载完成 **
为了解决这个问题,后面发展起来了众多的前端模块化规范,包括 CommonJS 大致有如下几种:

现在大都使用的是ES6 Module了,内容也不用多说,就是import,export, export default, import()等

ES6 Module 和 CommonJS 的区别

  1. 执行时加载和编译时加载
    CommonJS 只能在运行时确定导出的接口,实际导出的就是一个对象。而 ES6 Module 的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及导入和导出的变量,也就是所谓的"编译时加载"。

正因为如此,import 命令具有提升效果,会提升到整个模块的头部,首先执行。下面的代码是合法的,因为 import 的执行早于 getAge 的调用。

// a.js
export const name = 'morrain'
const age = 18
export function getAge () {
    return age
}

// b.js
const age = getAge()
console.log(age) // 18
import { getAge } from 'a.js'
  1. 没有拷贝或者cache
    由于编译过后代码已经被拼接在一起,所以不会有对被require文件的缓存。所以对被引模块进行内部方法修改变量后,能够反映到后续程序执行中。
// a.js
var name = 'morrain'
var age = 18
const setAge = a => age = a
export {
    name,
    age,
    setAge
}

// b.js
import * as a from 'a.js'

console.log(a.age) // 18
a.setAge(19)
console.log(a.age) // 19
posted @ 2021-12-15 16:26  Bravo_Jack  阅读(112)  评论(0编辑  收藏  举报