Skip to content

Latest commit

History

History
273 lines (237 loc) · 7.16 KB

File metadata and controls

273 lines (237 loc) · 7.16 KB

模块化相关知识点

历史模块化标准方案

AMD

异步模块定义,最古老模块标准,最初由 require.js、r.js、curl.js 库实现 特点:

  • 支持异步加载代码 优点:
  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块 缺点:
  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

实现:

  • RequireJS
  • r.js
  • curl

define 定义模块

define('模块名',['依赖模块1','依赖模块2',...],function(dep1,dep2,...){// 回调函数// dep1依赖引用// 输出return{..}})// 例子define('myModule',['jquery'],function($){// $ 是 jquery 模块的输出$('body').text('hello world');// 输出vara=123return{a}});

require加载模块

require(['依赖模块1','依赖模块2',...],function(dep1,dep2,...){// 使用});// 例子require(['math'],function(math){alert(math.add(1,1));});

CommonJS

为 Node.js 服务器创建的模块系统。 服务器端采用同步加载完成 浏览器端不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)

特点:

  • 所有代码都运行在模块作用域,不会污染全局作用域
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。

优点:

  • 服务器端模块便于重用
  • NPM 中已经有将近20万个可以使用模块包
  • 简单并容易使用 缺点:
  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

实现:

  • Node.js
  • Browserify,浏览器端的 CommonJS 实现

require 加载模块

// 声明 require(‘文件路径’)const变量名=require('加载文件路径')// 例子constexample=require('./example.js')

module对象

Node内部提供一个Module构建函数。所有模块都是Module的实例。 每个模块内部,都有一个module对象,代表当前模块。它有以下属性module对象

// 例子varjquery=require('jquery');exports.$=jquery;console.log(module);/* { id: '.', exports: { '$': [Function] }, parent: null, filename: '/path/to/example.js', loaded: false, children: [ { id: '/path/to/node_modules/jquery/dist/jquery.js', exports: [Function], parent: [Circular], filename: '/path/to/node_modules/jquery/dist/jquery.js', loaded: true, children: [], paths: [Object] } ], paths: [ '/home/user/deleted/node_modules', '/home/user/node_modules', '/home/node_modules', '/node_modules' ]} */

module.exports属性

module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。

// foo.jsmodule.exports=function(x){console.log(x);};// main.jsvarfoo=require("./foo");foo("Hi");

exports变量

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

varexports=module.exports;

exports对象添加方法

exports.area=function(r){returnMath.PI*r*r;};// 不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系exports=function(x){console.log(x)};

UMD

通用的模块系统,与 AMD 和 CommonJS 都兼容

CMD

Common Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。 优点:

  • 依赖就近,延迟执行
  • 可以很容易在 Node.js 中运行 缺点:
  • 依赖 SPM 打包,模块的加载逻辑偏重

实现:

  • Sea.js
  • coolie
define(function(require,exports,module){var$=require('jquery');varSpinning=require('./spinning');exports.doSomething= ...
module.exports= ...
})

ES6规范

ECMAScript6 标准增加了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

import"jquery";exportfunctiondoStuff(){}

优点:

  • 容易进行静态分析
  • 面向未来的 ECMAScript 标准 缺点:
  • 原生浏览器端还没有实现该标准
  • 全新的命令字,新版的 Node.js才支持

实现:

  • Babel

export 命令

  • 必须提供对外的接口,必须要通过定义一个接口,模块之间才可以传值,建立了一一对应的关系,接口可以是一个变量或对象
    // a.jsconsta=12exporta// 或export{b: 123}// main.jsimport{a,b}from'./a.js'
  • export语句输出的接口,与其对应的值是动态绑定关系,即是可以取到模块内部引用实时的值
  • export命令可以出现在模块的任何位置,只要处于模块顶层就可以,出现在块级作用域内就报错
    // 这会报错if(true){
    export {b: 123}}

export default 命令

  • 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出
    exportdefault{b: 123}
  • 使用export default时,对应的import语句不需要使用大括号
    // a.jsexportdefault{b: 123}// main.jsimportafrom'./a.js'
  • 一个模块只能有一个默认输出,因此export default命令只能使用一次
    // 这会报错exportdefault{a: 123}exportdefault{b: 456}

import命令

  • import命令是编译阶段执行的,先于所有代码
  • .js后缀可以省略
    import{lastName}from'./profile';
  • 大括号里面的变量名,必须与被导入模块对外接口的名称相同
    // a.jsexportconsta=1// 错误import{b}from'./a.js'
  • import命令输入的变量都是只读的,不允许在加载模块的脚本里面,改写接口,但是如果输入的变量是对象,更改属性是允许的
    // wrongimport{foo}from'./module.js'foo={a: 1}// Rightimport{bar}from'./module.js'bar.a=1
  • 模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块
    import{myMethod}from'util';// 需要配置 alias: util
  • import是静态执行,所以不能使用表达式和变量
    // 报错import{'f'+'oo'}from'my_module';// 报错letmodule='my_module';import{foo}frommodule;// 报错if(x===1){import{foo}from'module1';}else{import{foo}from'module2';}

as关键字

  • 将输入的变量重命名
    import{originMoudleasnewMoudleName}from'./module.js'