Javascript模块化

365 阅读3分钟

CommonJS NodeJs

ES6 Module

基本规则或特点

  1. 每个模块只加载一次,每一个JS只执行一次,如果下次再去加载同目录下同文件,直接从内存中读取。一个模块就是一个单例,或者说就是一个对象;
  2. 每一个模块内声明的变量都是局部变量,不会污染全局作用域;
  3. 模块内部的变量或函数可以通过export导出;
  4. 一个模块可以导入别的模块。

import 和 export 命令

举例

a.js

var x = 1
import {xx, yy } from './b.js'
import {fun} from './b.js'
var y = 2
console.log(x,y)
console.log(xx,yy)
fun()
export {x,y}

b.js

export var xx = 11
import {x,y} from './a.js'
export var yy = 22
console.log(xx,yy)
console.log(x,y)
export function fun(){
    console.log('this is b module fun method')
}

输出结果

11 22
b.js:6 undefined undefined
a.js:4 1 2
a.js:5 11 22
b.js:8 this is b module fun method
  • 整个流程是:预编译a.js -> 发现关键词import -> 预编译b.js -> 执行b.js -> 执行a.js
  • import命令具有提升效果,会提升到整个模块的头部,首先执行(是在编译阶段执行的) -import {fun} from './b.js'' 这行代码被Js引擎编译时,将b模块的属性fun指向对应模块的export function fun()方法上,这里只是做了指针指向,并不是执行fs模块。当执行readFile()时,就会去找指针指向的代码并执行。 将a.js文件中的xx换成xxx,会报语法错误
SyntaxError: The requested module './b.js' does not provide an export named 'xxx'
  • 代码运行前会有一个编译阶段

import()函数

import 和 export命令只能在模块的顶层,不能在代码块之中。否则会出现语法错误。这样的设计,可以提高编译器效率,但是没有办法实现运行时加载。

因为require()是运行时加载,所以 import命令没有办法代替require的动态加载功能。所以,es6模块方案引入了import()函数。完成动态加载。

webpack Module

原理

  1. 使用一个缓存对象__webpack_module_cache__缓存已经加载的模块
  2. 使用webpack自定义方法__webpack_require__加载模块
  3. 先判断缓存对象__webpack_module_cache__中是否已缓存需要加载的模块
  4. 已缓存,则直接返回exports对象;
  5. 没有缓存,则new一个module,并放入缓存
  6. 缓存的key是路径标识符;value是一个对象,里面有一个exports属性

es6 import和export被webpack打包后的样式

  • 最外层是一个立即执行函数
(()=>{
})();
  • 第一行代码是 "use strict"
  • 内部代码简化后如下:
// a.js文件转化为函数moduleA
function moduleA(__unused_webpack_module, __webpack_exports__, __webpack_require__){  
    var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/pages/b.js");
    var x = 1
    var y = 2;
    var z = 3 
    __webpack_exports__[Symbol.toStringTag] = 'Module'
    __webpack_exports__.__esModule = true
    __webpack_exports__.default = {x,y,z}
    return __webpack_exports__;
}

function moduleB(__unused_webpack_module, __webpack_exports__, __webpack_require__){
    var x = 11
    __webpack_exports__[Symbol.toStringTag] = 'Module'
    __webpack_exports__.__esModule = true
    __webpack_exports__.default = {x}
    return __webpack_exports__;
}

var __webpack_modules__ = { 
    './src/pages/a.js': moduleA,
    './src/pages/b.js':moduleB 
}
var __webpack_module_cache__ = {};

function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
        return cachedModule.exports;
    }
    var module = __webpack_module_cache__[moduleId] = {
        exports: {}
    }
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    return  module.exports
}
var __webpack_exports__ = __webpack_require__('./src/pages/a.js')

  1. 一个__webpack_modules__对象;后面简称为modules,该modules是一个对象;长度为2。
    1. 第一个key是"./src/pages/a.js"
    2. 第二个key是"./src/pages/b.js"
  2. 一个__webpack_module_cache__对象;
  3. 一个__webpack_require__方法;用于加载模块
  4. 使用Object.defineProperty的get获取模块里面的属性和方法,不能修改模块内的变量和方法