js模块化
# 一、可以暴露函数的方式:
1、 全局函数模式: 将不同的功能封装成不同的全局函数 问题: Global被污染了, 很容易引起命名冲突,比如两个js文件暴露的函数对象命名是相同的,在HTML同时引入时,会导致命名产生冲突。
2、 namespace模式: 简单对象封装,减少了全局变量。(用对象来暴露功能) 问题: 不安全(数据不是私有的, 外部可以直接修改)
3、 IIFE模式: 匿名函数自调用(闭包),immediately-invoked function expression(立即调用函数表达式) 作用: 数据是私有的, 外部只能通过暴露的方法操作
(function (window) {
//数据
let data = 'test'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window)
4、IIFE模式增强 : 引入依赖,是现代模块实现的基石
(function (window, $) {
//数据
let data = 'test'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window, jQuery)
# 问题: 当HTML使用多个js文件时:
- 一个页面需要引入多个js文件
- 问题: 1). 请求过多,需要多个js请求 2). 依赖模糊,模块间可能会相互依赖,要求引入的顺序不能出错。 3). 难以维护
# 二、js模块化
js模块化是将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起,块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
组成: 1、 数据--->内部的属性 2、操作数据的行为--->内部的函数 模块化: 编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
1、CommonJS: 暴露方式: module.exports = value 向外暴露一个对象 module.exports = value 向外暴露一个函数 exports.xxx = value 向外暴露一个对象,这种方式可以在一个js文件中暴露多个对象
引入方式: var module = require(模块名或模块路径);
//commonJS-test1.js
module.exports = {
name:'tom',
age:18,
say:function(){
console.log('this is tom say')
}
}
module.exports.otherAttr = 'other'
exports.foo = function () {
console.log('foo() ....')
}
exports.bar = function () {
console.log('bar() .....')
}
//commonJs-test2.js
let a = require('./commonJS-test1');
console.log(a,'this is test1')
//结果:{ name: 'tom', age: 18, say: [Function: say], otherAttr: 'other' } bar和foo不在其中
//commonJS-test1.js
// module.exports = {
// name:'tom',
// age:18,
// say:function(){
// console.log('this is tom say')
// }
// }
module.exports.otherAttr = 'other'
exports.foo = function () {
console.log('foo() ....')
}
exports.bar = function () {
console.log('bar() .....')
}
//commonJs-test2.js
let a = require('./commonJS-test1');
console.log(a,'this is test1')
//结果:{ otherAttr: 'other', foo: [Function (anonymous)], bar: [Function (anonymous)]}
Node.js模块化,服务器端。运行时, 动态同步引入
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.json
{
"name": "commonJS-node",
"version": "1.0.0"
}
Browserify模块化,浏览器端 也称为js的打包工具,在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了), 运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块
|-js
|-dist //打包生成文件的目录
|-src //源码所在的目录
|-module1.js
|-module2.js
|-module3.js
|-app.js //应用主源文件
|-index.html
|-package.json
{
"name": "browserify-test",
"version": "1.0.0"
}
1、module1.js
module.exports = {
foo() {
console.log('moudle1 foo()')
}
}
2、module2.js
module.exports = function () {
console.log('module2()')
}
3、module3.js
exports.foo = function () {
console.log('module3 foo()')
}
exports.bar = function () {
console.log('module3 bar()')
}
4、app.js
let module1 = require('./module1')
let module2 = require('./module2')
let module3 = require('./module3')
let uniq = require('uniq')
//使用模块
module1.foo()
module2()
module3.foo()
module3.bar()
2、AMD-RequireJS 模块化(浏览器端) 定义暴露模块: define([依赖模块名], function(){return 模块对象}) 引入模块: require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象}) 配置:
require.config({
//基本路径
baseUrl : 'js/',
//标识名称与路径的映射
paths : {
'模块1' : 'modules/模块1',
'模块2' : 'modules/模块2',
'angular' : 'libs/angular',
'angular-messages' : 'libs/angular-messages'
},
//非AMD的模块
shim : {
'angular' : {
exports : 'angular'
},
'angular-messages' : {
exports : 'angular-messages',
deps : ['angular']
}
}
})
3、CMD-SeaJs模块化(浏览器端) 定义暴露模块:
define(function(require, module, exports){
通过require引入依赖模块
通过module/exports来暴露模块
exports.xxx = value
})
使用模块:seajs.use(['模块1', '模块2'])
4、ES6模块化,ES6内置了模块化 定义暴露模块 : export
暴露一个对象:
export default 对象
暴露多个:
export var xxx = value1
export let yyy = value2
或者
var xxx = value1
let yyy = value2
export {xxx, yyy}
引入使用模块 : import
default模块:
import xxx from '模块路径/模块名'
其它模块
import {xxx, yyy} from '模块路径/模块名'
import * as module1 from '模块路径/模块名'
问题: 所有浏览器还不能直接识别ES6模块化的语法
解决:
1、 使用Babel将ES6--->ES5(使用了CommonJS) ----浏览器还不能直接支行
2、使用Browserify--->打包处理----浏览器可以运行