JS-模块化

1.概述

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。

commonJS

// a.js
module.exports = {
    a: 1
}
// or
exports.a = 1

// b.js
var module = require('./a.js')
module.a // -> log 1


复制代码

AMD

不咋常用,至少我是没见过。

// AMD
define(['./a', './b'], function(a, b) {
    a.do()
    b.do()
})
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    var b = require('./b')
    b.doSomething()
})
复制代码

ES6模块化

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

在有Babel的情况下,可以直接使用ES6模块化。ES6模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能,可以在编译时就完成模块加载。

(Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在老版本的浏览器执行。这意味着,你可以用 ES6 的方式编写程序,又不用担心现有环境是否支持。)

ES6模块化

ES6模块化功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

// file a.js
export function a() {}
export function b() {}
// file b.js
export default function() {}

import {a, b} from './a.js'
import XXX from './b.js'
复制代码

export和export default的区别

  • export定义对外接口是有名称的,import时必须要同名;
  • export default定义,import可以使用任意名字;
  • export default用于指定模块的默认输出,一个模块只能有一个默认输出。

commonJS VS ES6模块化

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

// CommonJS模块
let { stat, exists, readfile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

复制代码

ES6模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

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

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

区别总结:

  • 前者支持动态导入,也就是 require(${path}/xx.js),后者目前不支持,但是已有提案

  • 前者是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而后者是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响

  • 前者整体加载模块,按需使用。后者按需加载,效率要高于前者。

  • 前者在导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以如果想更新值,必须重新导入一次。但是后者采用实时绑定的方式,导入导出的值都指向同一个内存地址,所以导入值会跟随导出值变化

  • 后者会编译成 require/exports 来执行的

个人觉得只需要会用ES6模块化就可以了,但还是需要能看懂commonJS和AMD,方便维护其他人的代码。

参考文章:

ECMAScript 6 入门 -阮一峰

前端面试之道

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享