adodo0829/blog

工程化(一)之模块化规范

Opened this issue · 1 comments

传统模式下的模块

  • 函数即模块
<script>
  function module() {
    // doSomething
  }
</script>
<!-- 利用全局变量, 即模块依赖不清晰 -->
  • 对象即模块
<script>
  var module = {
    id: 0,
    say: function () {}
  }
</script>
<!-- 也是全局变量对象, 且成员属性会被修改 -->
  • 自执行函数(函数作用域实现私有成员)
<script>
  var module = (function() {
    var id = 0; // 私有变量
    var hello = function () {};
    var world = function () {};
    return {
      hello: hello,
      world: world
    }
  })();
</script>
<!-- 全局变量, 但实现了部分成员私有, 其他成员共享 -->
<script>
module.hello()
module.world()
</script>

CommonJS 规范

node 环境下每个文件都有自己的模块作用域, 变量都属于模块

  • 导出模块
// 1.整体内容导出
// module.exports 初始值为一个空对象 {}, 由 Module 系统创建
module.exports = {
  id: 1,
  say: () => {}
}

// 2.exports导出
// exports 也是一个模块下的变量, 指向module.exports的引用, 指向同一块内存
exports.id = 1
exports.say = () => {}

// 模块导出的始终是 module.exports 对象
exports = module.exports = { id: 2, say: () => {} }
  • 导入模块
// node 下全局方法 require
var module1 = require('./module1.js') // 导入本地文件模块, 相对路径查找
var path = require('path')            // Node自带的模块, 得出相对路径后查找
var lodash = require('lodash')        // node_modules目录下的模块, 通过文件名逐级往上查找
  • 模块加载特点
1.所有代码都运行在模块作用域,不会污染全局作用域

2.模块是同步加载的,即只有加载完成,才能执行后面的操作

3.模块在首次执行后就会缓存,再次加载只返回缓存结果,如果想要再次执行,可清除缓存

4.模块加载的是值的拷贝, 同时 require('./moudle.js') 多次都只是一个对象的拷贝;
CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值

AMD 规范(require.js)

异步加载
浏览器环境下使用
定义模块: define(fn())
导入: require(['模块名',...], cb())
存在缓存

ESModule 规范

js 语言环境下使用, 语言层面的规范;
需要 babel工具 进行语法编译

export: 规定模块对外接口

默认导出:export default Person(当导入时可指定模块任意名称,无需知晓内部真实名称)
单独导出:export const name = "huhua"
按需导出:export { age, name, sex }(推荐)
改名导出:export { name as newName }

import:导入模块内部功能

输入的模块变量是不可重新赋值的,它只是个可读引用,可以改写属性

默认导入:import Person from "person"
整体导入:import * as Person from "person" // Module对象
按需导入:import { age, name, sex } from "person"
改名导入:import { name as newName } from "person"
自执导入:import "./person"
复合导入:import Person, { name } from "person"

复合模式

export命令和import命令结合在一起写成一行,变量实质没有被导入当前模块,相当于对外转发接口,导致当前模块无法直接使用其导入变量

默认导入导出:export { default } from "person"
整体导入导出:export * from "person"
按需导入导出:export { age, name, sex } from "person"
改名导入导出:export { name as newName } from "person"
具名改默认导入导出:export { name as default } from "person"
默认改具名导入导出:export { default as name } from "person"

CommonJS与ESModule规范的区别

  • CommonJS模块是运行时加载,ES6 Modules是编译时输出接口
    CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。
    而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成

  • CommonJS输出是值的拷贝; ES6 Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变

  • CommonJs导入的模块路径可以是一个表达式,因为它使用的是require()方法;而ES6 Modules只能是字符串

  • CommonJS this指向当前模块,ES6 Modules this指向undefined
    ES6 Modules中没有这些顶层变量:arguments、require、module、exports、__filename、__dirname

webpack打包工具

为了让同一套代码在各个运行环境规范下都能适用

  • 模块解析
将各个模块维护到一个list中
/* module code */ 样板代码解析 replace
  • 模块加载
同步加载: 直接加载文件
异步加载: jsonp + promise

update: module