模块化就是把系统分离成独立功能的方法,这样我们需要什么功能,就加载什么功能。

    主要解决的问题

    1. 命名冲突
    2. 文件依赖

    image.png

    image.png

    image.png

    image.pngimage.png

    image.png
    代码学习:

    1. define(function(require, exports, module) {
    2. var age = 30;
    3. var b = require('b.js');
    4. console.log(b.sex);
    5. exports.age = age;
    6. });
    1. define(function(require, exports, module){
    2. var sex = '男';
    3. return {
    4. sex: sex
    5. };
    6. });
    1. <script src="startUp.js"></script>
    2. <script>
    3. startUp.use(['a.js', 'b.js'], function(a, b) {
    4. console.log(a);
    5. console.log(b);
    6. console.log('startUp...');
    7. });
    8. </script>
    1. (function(global) {
    2. startUp = global.startUp = {
    3. version: '1.0.1'
    4. };
    5. var data = {};
    6. var cache = {};
    7. var anonymousMeta = {};
    8. var status = {
    9. FETCHED: 1, // 正在获取当前模块的uri
    10. SAVED: 2, // 在缓存中存储模块信息
    11. LOADING: 3, // 正在加载当前模块的依赖项
    12. LOADED: 4, // 准备执行当前模块
    13. EXECUTING: 5, // 正在执行单签模块
    14. EXECUTED: 6 // 执行完毕已经获取到接口对象
    15. };
    16. var isArray = function(obj){
    17. return toString.call(obj) === '[object Array]';
    18. };
    19. var isFunction = function(obj){
    20. return toString.call(obj) === '[object Function]';
    21. };
    22. var isString = function(obj) {
    23. return typeof obj === 'string';
    24. };
    25. // 是否使用了别名
    26. function parseAlias(id) {
    27. var alias = data.alias; //别名配置
    28. return alias && isString(alias[id]) ? alias[id] : id;
    29. }
    30. //不能以 "/" 或":"开头,结尾必须是一个“/”后面跟随任意字符至少一个
    31. var PATHS_RE = /^([^\/:]+)(\/.+)$/;
    32. // 是否使用了路径短名称
    33. function parsePaths(id) {
    34. var paths = data.paths;
    35. if(paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
    36. id = paths[m[1]] + m[2];
    37. }
    38. return id;
    39. }
    40. // 检测是否添加后缀
    41. function normalize(path) {
    42. var last = path.length - 1;
    43. var lastC = path.charAt(last);
    44. return (lastC === '/' || path.substring(last-2) === '.js') ? path : path + '.js';
    45. }
    46. // 添加根目录
    47. function addBase(id, uri) {
    48. var result;
    49. if (id.charAt(0) === '.') {
    50. result = relapath((uri ? uri.match(/[^?]*\//)[0] : data.cwd) + id);
    51. }
    52. else {
    53. result = data.cwd + id;
    54. }
    55. return result;
    56. }
    57. var DOT_RE = /\/.\//g; // 规范路径 "/./" => "/"
    58. // 相对路径的处理
    59. function relapath(path) {
    60. path = path.replace(DOT_RE, '/');
    61. return path;
    62. }
    63. // 生成绝对路径
    64. startUp.resolve = function(child, parent){
    65. if (!child) {
    66. return '';
    67. }
    68. child = parseAlias(child); // 处理别名
    69. child = parsePaths(child); // 处理路径别名,路径短名称
    70. child = normalize(child); // 是否添加后缀
    71. return addBase(child, parent); // 添加根目录
    72. };
    73. //加载依赖项
    74. startUp.request = function(url, callback){
    75. var node = document.createElement('script');
    76. node.src = url;
    77. document.body.appendChild(node);
    78. node.onload = function() {
    79. // 移除script元素,不留加载痕迹
    80. //node.onload = null;
    81. //document.body.removeChild(node);
    82. callback();
    83. };
    84. };
    85. // Module 构造函数,模块初始化数据
    86. function Module(uri, deps){
    87. this.uri = uri;
    88. this.deps = deps || [];
    89. this.exports = null;
    90. this.status = 0;
    91. this._waitings = {};
    92. this._remain = 0;
    93. }
    94. //分析主干上的依赖项
    95. Module.prototype.load = function() {
    96. var module = this;
    97. module.status = status.LOADING;
    98. var uris = module.resolve();
    99. var len = module._remain = uris.length;
    100. // 加载主干上的依赖项
    101. var m;
    102. for (var i = 0; i < len; i++) {
    103. //创建缓存信息
    104. m = Module.get(uris[i]);
    105. if (m.status < status.LOADED) {
    106. m._waitings[module.uri] = m._waitings[module.uri] || 1;
    107. }
    108. else {
    109. module._remain--;
    110. }
    111. }
    112. // 如果依赖项列表全部加载完毕
    113. if (module._remain == 0) {
    114. module.onload();
    115. }
    116. //准备执行根目录下的依赖项列表中的模块
    117. var requestCache = {};
    118. for(var i = 0; i < len; i++) {
    119. // 获取依赖模块
    120. m = Module.get(uris[i]);
    121. if (m.status < status.FETCHED) {
    122. m.fetch(requestCache);
    123. }
    124. }
    125. for(uri in requestCache) {
    126. requestCache[uri]();
    127. }
    128. };
    129. // 加载依赖列表中的模块
    130. Module.prototype.fetch = function(requestCache){
    131. var module = this;
    132. module.status = status.FETCHED;
    133. var uri = module.uri;
    134. requestCache[uri] = sendRequest;
    135. function sendRequest() {
    136. startUp.request(uri, onRequest);
    137. }
    138. function onRequest() {
    139. if(anonymousMeta) {
    140. module.save(uri, anonymousMeta);
    141. }
    142. // 递归,模块加载策略
    143. module.load();
    144. }
    145. };
    146. Module.prototype.onload = function() {
    147. var mod = this;
    148. mod.status = status.LOADED;
    149. if (mod.callback) {
    150. mod.callback();
    151. }
    152. _waitings = mod._waitings;
    153. var uri, m;
    154. for (uri in _waitings) {
    155. //根目录对应的Module实例对象
    156. m = cache[uri];
    157. m._remain -= _waitings[uri];
    158. if(m._remain == 0) {
    159. m.onload();
    160. }
    161. }
    162. };
    163. // 更改初始化数据
    164. Module.prototype.save = function(uri, meta) {
    165. var module = Module.get(uri);
    166. module.id = uri;
    167. module.deps = meta.deps || [];
    168. module.factory = meta.factory;
    169. module.status = status.SAVED;
    170. };
    171. // 获取模块对外接口对象
    172. Module.prototype.exec = function(){
    173. var module = this;
    174. //防止重复执行
    175. if(module.status >= status.EXECUTING) {
    176. return module.exports;
    177. }
    178. module.status = status.EXECUTING;
    179. var uri = module.uri;
    180. function require(id) {
    181. // 更新过后的数据
    182. console.log(require.resolve(id));
    183. // 获取接口对象
    184. return Module.get(require.resolve(id)).exec();
    185. }
    186. require.resolve = function(id){
    187. return startUp.resolve(id, uri);
    188. };
    189. var factory = module.factory;
    190. var exports = isFunction(factory) ? factory(require, module.exports = {}, module) : factory;
    191. if(exports === undefined) {
    192. exports = module.exports;
    193. }
    194. module.exports = exports;
    195. module.status = status.EXECUTED;
    196. return exports;
    197. };
    198. //资源定位, 解析依赖项生成绝对路径
    199. Module.prototype.resolve = function(){
    200. var mod = this;
    201. var ids = mod.deps; // ['a.js','b.js'] => ['./a','./b']
    202. var uris = [];
    203. for(let i = 0; i < ids.length; i++) {
    204. uris[i] = startUp.resolve(ids[i], mod.uri);
    205. }
    206. return uris;
    207. };
    208. //定义一个模块
    209. Module.define = function(factory){
    210. var deps;
    211. if(isFunction(factory)) {
    212. // 正则解析依赖项
    213. deps = parseDependencies(factory.toString());
    214. }
    215. // 存储当前模块信息
    216. var meta = {
    217. id: "",
    218. uri: "",
    219. deps: deps,
    220. factory: factory
    221. };
    222. anonymousMeta = meta;
    223. }
    224. // 检测缓存对象上是否有当前模块的信息
    225. Module.get = function(uri, deps){
    226. return cache[uri] || (cache[uri] = new Module(uri, deps));
    227. };
    228. Module.use = function(deps, callback, uri){
    229. //获取模块
    230. var module = Module.get(uri, isArray(deps) ? deps : [deps]);
    231. // 所有模块都加载完毕
    232. module.callback = function(){
    233. // 所有依赖项模块的接口对象
    234. var exports = [];
    235. var uris = module.resolve();
    236. for (var i = 0; i < uris.length; i++) {
    237. exports[i] = cache[uris[i]].exec();
    238. }
    239. if (callback) {
    240. callback.apply(global, exports);
    241. }
    242. };
    243. module.load();
    244. };
    245. var _cid = 0;
    246. function cid(){
    247. return _cid++;
    248. }
    249. data.preload = [];
    250. //获取当前项目文档的uri
    251. data.cwd = document.URL.match(/[^?]*\//)[0];
    252. Module.preload = function(callback) {
    253. var length = data.preload.length;
    254. if (!length) {
    255. callback();
    256. }
    257. //length !== 0, 先加载预先设定模块
    258. };
    259. startUp.use = function(list, callback) {
    260. // 检测有没有预先加载的模块
    261. Module.preload(function() {
    262. Module.use(list, callback, data.cwd + '_use_' + cid()); //虚拟根目录
    263. });
    264. };
    265. var REQUIRE_RE = /\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g;
    266. function parseDependencies(code) {
    267. var ret = [];
    268. code.replace(REQUIRE_RE, function(m, m1, m2){
    269. if(m2){
    270. ret.push(m2);
    271. }
    272. });
    273. return ret;
    274. }
    275. global.define = Module.define;
    276. })(this);