本文从以时间为轴从以下几个方面进行总结JS模块化。从无模块化 => IIFE => CJS => AMD => CMD => ES6 => webpack这几个阶段进行分析。
此处写法文件拆分是最基础的模块化(第一步)
script标签的参数:async & defer
兼容性:> IE9
问题可能被引导到 => 1. 浏览器的渲染原理 2.同步异步原理 3.模块化加载原理
出现的问题
let count = 0;
const increase = () => ++count;
const reset = () => {count = 0;
}
利用函数的块级作用域
(() => {let count = 0;...
})
//最基础的部分
const iifeModule = (() => {let count = 0;const increase = () => ++count;const reset = () => {count = 0;}console.log(count);increase();
})();
优化1:依赖其他模块的传参型
const iifeModule = ((dependencyModule1,dependencyModule2) => {let count = 0;const increase = () => ++count;const reset = () => {count = 0;}console.log(count);increase();...//可以处理依赖中的方法
})(dependencyModule1,dependencyModule2)
参考 前端进阶面试题详细解答
将本身的方法暴露出去
const iifeModule = ((dependencyModule1,dependencyModule2) => {let count = 0;const increase = () => ++count;const reset = () => {count = 0;}console.log(count);increase();...//可以处理依赖中的方法return increase,reset}
})(dependencyModule1,dependencyModule2)
iifeModule.increase()
=> 揭示模式 revealing => 上层无需了解底层实现,仅关注抽象 => 框架
node.js指定
特征:
main.js
const dependencyModule1 = require('./dependencyModule1')
const dependencyModule2 = require('./dependencyModule2')let count = 0;
const increase = () => ++count;
const reset = () => {count = 0;
}
console.log(count);
increase();exports.increase = increase;
exports.reset = reset;module.exports = {increase, reset
}
exe
const {increase, reset} = require(./main.js)
复合使用
(function(this.value,exports,require,module){const dependencyModule1 = require('./dependencyModule1')const dependencyModule2 = require('./dependencyModule2')
}).call(this.value,exports,require,module)
(function(window,$,undefined){const _show = function(){$("#app").val("hi zhuawa")}window.webShow = _show;
})(window,jQuery)
阻断思路
一. window
(function(c){})(window) // window会被优化成c
//window在里面所有别的执行所有的变化都会随着执行完毕都会跟着c一起被销毁
二. jquery
三. undefined
防止改写:在执行内部这段代码的时候保证undefined是正确的,不会被改写,如在外部定义一个undefined =1
undefined对jquery本身是一个很重要的一个存在
=> 异步依赖的处理
通过异步执行 + 允许指定回调函数
经典实现框架:require.js
新增定义方式:
//define来定义模块
define(id, [depends], callback)
//require来进行加载
reuqire([module],callback)
模块定义的地方
define('amdModule',[dependencyModule1,dependencyModule2],(dependencyModule1,dependencyModule2) => {//业务逻辑let count = 0;const increase = () => ++count;module.exports = {increase}
})
引入的地方
require(['amdModule'],amdModule => {amdModule.increase()
})
面试题:如果在AMDModule中想兼容已有代码,怎么办?
define('amdModule',[],require => {const dependencyModule1 = require('./dependencyModule1')const dependencyModule2 = require('./dependencyModule2')//业务逻辑let count = 0;const increase = () => ++count;module.exports = {increase}
})
面试题:手写兼容CJS&AMD
//判断的关键:1. object还是function2. exports ?3. define(define('AMDModule'),[],(require,export,module) => {const dependencyModule1 = require('./dependencyModule1')const dependencyModule2 = require('./dependencyModule2')let count = 0;const increase = () => ++count;const reset = () => {count = 0;}console.log(count);export.increase = increase();
})(//目标:一次性区分CJS还是AMDtypeof module === 'object' && module.exports && typeof define !== function ? //CJSfactory => module.exports = factory(require,exports,module): //AMDdefine
)
按需加载
主要应用框架:sea.js
define('module',(require,exports,module) => {let $ = require('jquery')let dependencyModule1 = require('./dependencyModule1')
})
新增定义:
面试:
// ES11原生解决方案
import('./esMModule.js').then(dynamicModule => {dynamicModule.increase();
})
优点:
通过一种统一各端的形态,整合了js模块化的方案
缺点:本质上还是运行时分析
根本问题:运行时进行依赖分析
解决方案:线下执行
define('a', () => {let b = require('b')let c = require('c')
})
完全体:webpack为核心的前端工程化 + mvvm框架的组件化 + 设计模式