一、模块化发展历程
背景:早期的Javascript程序很小,它们大多被用来执行独立的脚本任务,在你的 web 页面需要的地方提供一定交互,所以一般不需要多大的脚本。过了几年,我们现在有了运行大量 Javascript 脚本的复杂程序,还有一些被用在其他环境(例如 Node.js)。
因此,近年来,有必要开始考虑提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node.js 已经提供这个能力很长时间了,还有很多的 Javascript 库和框架 已经开始了模块的使用(例如, CommonJS 和基于 AMD 的其他模块系统 如 RequireJS, 以及最新的 Webpack 和 Babel)。
- 使用自执行函数来编写模块化,特点:在一个单独的函数作用域中执行代码,避免变量冲突。
(function(){
return {
data:[]
}
})()
复制代码
- 最古老的模块系统之一,最初由 require.js 库实现。
- 特点:依赖必须提前声明好。
define('./index.js',function(code){
// code 就是index.js 返回的内容
})
复制代码
- 使用seaJS 来编写模块化,特点:支持动态引入依赖文件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
});
复制代码
- CommonJS
- 为 Node.js 服务器创建的模块系统。
var fs = require('fs');
复制代码
- UMD
- 兼容AMD,CommonJS 模块化语法。
- Webpack/Babel
- ES Moduls
- ES6 引入的模块化,支持import 来引入另一个 js 。
二、模块的核心概念
这本书里写的很详细,具体参见:模块简介,下面做些总结:
- 一个模块就是一个文件。浏览器需要使用
<script type="module">
以使import/export
可以工作。模块相较于常规脚本有几点差别:- 默认是延迟解析的(deferred)
<script type="module"> alert(typeof button); // object:脚本可以“看见”下面的 button // 因为模块是被延迟的(deferred,所以模块脚本会在整个页面加载完成后才运行 </script> <script> alert(typeof button); // button 为 undefined,脚本看不到下面的元素 // 常规脚本会立即运行,常规脚本的运行是在在处理页面的其余部分之前进行的 </script> <button id="button">Button</button> //请注意:上面的第二个脚本实际上要先于前一个脚本运行!所以我们会先看到 undefined,然后才是 object。 复制代码
- Async 可用于内联脚本。
<!-- 所有依赖都获取完成(analytics.js)然后脚本开始运行 --> <!-- 不会等待 HTML 文档或者其他 <script> 标签 --> <script async type="module"> import {counter} from './analytics.js'; counter.count(); </script> 复制代码
- 要从另一个源(域/协议/端口)加载外部脚本,需要 CORS header。
<!-- another-site.com 必须提供 Access-Control-Allow-Origin --> <!-- 否则,脚本将无法执行 --> <script type="module" src="http://another-site.com/their.js"></script> 复制代码
- 重复的外部脚本会被忽略
- 模块具有自己的本地顶级作用域,并可以通过
import/export
交换功能。 - 模块始终使用
use strict
。 - 模块代码只执行一次。导出仅创建一次,然后会在导入之间共享。
三、ES6模块-导出和导入
这本书里写的很详细,具体参见:导出和导入,下面做些总结:
- 命名的导出 与 默认的导出
请记住,import
命名的导出时需要花括号,而 import
默认的导出时不需要花括号。建议团队尽可能的用命名导出。
- 自查表
-
在实际开发中,导入通常位于文件的开头,但是这只是为了更加方便。把
import/export
语句放在脚本的顶部或底部,都没关系。 -
请注意在
{...}
中的import/export
语句无效。
// 像这样的有条件的导入是无效的:
if (something) {
import {sayHi} from "./say.js"; // Error: import must be at top level
}
复制代码
四、ES6模块-动态导入
这本书里写的很详细,具体参见:动态导入,下面做些总结:
import(module)
表达式加载模块并返回一个promise
- 如果在异步函数中,我们可以使用
let module = await import(modulePath)
- 有默认的导出,可以使用模块对象的
default
属性:
// ? say.js
export default function() {
alert("Module loaded (export default)!");
}
// 访问
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
say();
复制代码
长文参见
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END