模块化概述——「模块化」只是思想
最主流的代码整合方式,安装功能不同划分不同模块。
- 模块化演变过程
- 模块化规范
- 常用的模块化打包工具
- 基于模块化工具构建现代WEB应用
- 打包工具的优化技巧
模块化的演进过程
Stage 1 - 文件划分方式
完全依靠约定
├── index.html
├── module-01.js
└── module-02.js
- 通过scriptsrc属性引入模块
- 污染全局作用域,容易产生命名冲突
- 模块成员可以随意修改
- 无法管理模块依赖关系
Stage 2 - 命名空间方式
依靠约定,模块暴露全局对象, 所有成员都暴露在该对象下面,解决了污染全局作用域问题
var moduleA = {
name: 'module-a'
method1: function() {
console.log(this.name + 'method1')
}
method2: function() {
console.log(this.name + 'method2')
}
}
- 通过scriptsrc属性引入模块
- 模块成员可以随意修改
- 无法管理模块依赖关系
Stage 3 - IIFE
立即执行函数方式,为模块提供私有空间。实现了私有成员的概念。通过自执行函数传参的方式可以实现简单的模块化依赖处理
;
(function() {
var name = 'module-a'
function method1() {
console.log(name + 'method1')
}
function method2() {
console.log(name + 'method2')
}
window.moduleA = {
method1: method1,
method2: method2
}
})(jQuery)
- 通过scriptsrc属性引入模块
- 无法管理模块依赖关系
模块化规范的出现
模块化标准+模块化加载器
- 解决通过scriptsrc属性引入模块不受控制
- 解决无法管理模块依赖关系
CommonJs规范
以同步的模式加载模块,在node环境中不会存在问题,但是在浏览器环境会导致效率低下
- 一个文件就是一个模块
- 每个模块都有单独的作用域
- 通过module.exports 导出成员
- 通过require函数载入模块
AMD规范 + Require.js
异步模式加载模块
- 使用相对复杂
- 模块js文件请求频繁
- 基本三方模块都支持amd规范
define 定义模块
// 参数1 模块名字
// 参数2 声明依赖项
// 参数3 callback函数 函数形参中接收定义依赖项
// 返回函数值为导出模块
define('module1', ['jquery', './module2'], function($, module2) {
return {
start: function() {
$('body').animate({
margin: '200px'
})
module2()
}
}
})
require 加载模块
require(['./module2'], function(module1) {
module1.start()
})
模块化标准规范——模块化的最佳实践
-
node
遵循CommonJs规范
-
web环境
遵循ES modules规范
ES modules
基本特性
- 自动采用严格模式,忽略 ‘use strict’
- 每个module 都运行在私用作用域当中
- 通过CORS方式请求外部JavaScript模块,不支持文件访问
- 标签会延迟加载执行脚本
导入导出
直接导出
export const foo = 'es modules'
export function hello() {
console.log('hello')
}
export class Person {}
文件末尾导出
const foo = 'es modules'
function hello() {
console.log('hello')
}
class Person {}
export {
foo,
hello,
Person
}
别名导出
const foo = 'es modules'
export {
foo as myFoo
}
导出默认成员
const foo = 'es modules'
export default foo
常规引入
import {
foo,
hello,
Person
} from './module.js'
console.log(foo)
console.log(hello)
console.log(Person)
别名处理
import {
foo as myFoo
} from './module.js'
console.log(myFoo)
引入默认成员
import foo from './module.js'
导入导出注意事项
- 末尾导出非对象字面量导出, 而是固定的语法
- 导出的为引用关系不是复制一个新的对象
- 模块引入是并不是对象解构,而是固定的语法
- 模块引入的模块是只读的存在不能被修改
导入用法(原生)
- 不能省略文件后缀名
- 不能省略index
后续通过打包工具打包模块时可以省略
- 不能省略 ./ 相对路径,否则视为三方依赖包
- 可以使用项目的绝对路径
- 可以使用网络资源
导出一个模块里的所有方法
import * as mod from './module.js'
console.log(mod)
- import 只能存在最顶层不能嵌套函数或if语句
- import 的 from 后面不能是变量
动态导入
import('./module.js').then(module => {
console.log(module)
})
默认成员与命名成员同时导入
import name, {
age
} from './module.js'
导出导入成员
// 常用于index 文件,做导出零散文件使用
export {
foo,
bar
}from './module.js'