翼度科技»论坛 编程开发 JavaScript 查看内容

23年,我又学习了一次amd模块化,模块化思想

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
  1.     项目目录
  2.       src
  3.         view1
  4.           index.html
  5.           main.js
  6.         view2
  7.       plugins
  8.         module.js
  9.         jquery.js
  10.         ......
  11.       modules // amd模块文件
  12.         a1.js
  13.         b1.js
  14.         c.js
  15.         b2.js
  16.         b21.js
复制代码
src/view1/index.html
  1. <!DOCTYPE html>
  2.     <html lang="en">
  3.     <head>
  4.         <meta charset="UTF-8">
  5.         <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6.         <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7.         <title>Document</title>
  8.     </head>
  9.     <body>
  10.         
  11.               
  12.         
  13.     </body>
  14.     </html>
复制代码
src/view1/main.js
  1. module
  2. .configs({
  3.     baseUrl: '../modules'
  4. })
  5. .require(['a1','a2'], function(a2) {
  6.     console.log('a2: ', a2)
  7. })
复制代码
plugins/module.js
  1.     // 使用IIFE
  2.     (function(win){
  3.         function module(){
  4.             this.context = {
  5.                 baseUrl: './',
  6.                 alias: {},
  7.                 entry: 'main.js',
  8.                 suffix: '.js',
  9.             }
  10.             this.deps = {};
  11.             this.srcs = [];
  12.             this.customEvt = new module.CustomEvt();
  13.             this.ready();
  14.         }
  15.         var utils = module.utils = {
  16.             each(arr, callback){
  17.                 var keys = Object.keys(arr);
  18.                 for(var i = 0; i < keys.length; i++) {
  19.                     if(callback.call(arr, arr[i], i) === false) {
  20.                         break;
  21.                     }
  22.                 }
  23.             },
  24.             // 只保留指定的keys
  25.             inKeys(target, keys) {
  26.                 return Object.keys(target).reduce((newData, key) => {
  27.                     if (keys.includes(key)) {
  28.                         newData[key] = target[key];
  29.                     }
  30.                     return newData;
  31.                 }, {});
  32.             },
  33.             // 路径拼接
  34.             pathJoin(...paths){
  35.                 let path = '';
  36.                 for(let i = paths.length - 1; i > -1; i--) {
  37.                     let pathItem = paths[i].replace(/\/+$/, '');
  38.                     // 如果是绝对路径
  39.                     if(/^\//.test(pathItem)) {
  40.                         path = pathItem + '/'+ path;
  41.                         break;
  42.                     }
  43.                     // 如果是相对路径
  44.                     if(/^\.\//.test(pathItem)) {
  45.                         pathItem = pathItem.slice(2)
  46.                     }
  47.                     // 如果是: ../../a/b/c
  48.                     if(/^(\.{2}\/)+\S+/.test(pathItem) && paths[i-1]) {
  49.                         let matches = null;
  50.                         while(matches = /^\.{2}\/(\S+)/.exec(pathItem)) {
  51.                             pathItem = matches[1];
  52.                             let prevPath = paths[i-1].replace(/\/+$/, '');
  53.                             if(prevPath) {
  54.                                 paths[i-1] = prevPath.slice(0, prevPath.lastIndexOf('/'))
  55.                             }
  56.                         }
  57.                     }
  58.                     path = pathItem + '/'+ path;
  59.                 }
  60.                 return path.replace(/\/+$/, '');
  61.             },
  62.             createScript(src) {
  63.                 var node = document.createElement('script');
  64.                 node.type = 'text/javascript';
  65.                 node.async = true;
  66.                 node.src = src;
  67.                 return node;
  68.             },
  69.             // 判断所有依赖是否加载完成
  70.             isDepsDone(options) {
  71.                 var { depsTree, entry = depsTree.entry, deps } = options;
  72.                 var deps = deps || depsTree[entry].deps;
  73.                 var done = true;
  74.                 utils.each(deps, function(moduleName) {
  75.                     var dep = depsTree[moduleName];
  76.                     if(dep) {
  77.                         if(dep.deps.length > 0) {
  78.                             done = utils.isDepsDone({
  79.                                 depsTree,
  80.                                 entry,
  81.                                 deps: dep.deps
  82.                             });
  83.                             if(!done) {
  84.                                 return false;
  85.                             }
  86.                         }
  87.                     }else {
  88.                         done = false;
  89.                         return false;
  90.                     }
  91.                 });
  92.                 return done;
  93.             }
  94.         }
  95.         var CustomEvt = function(){
  96.             this.subEvents = {};
  97.         };
  98.         CustomEvt.prototype = {
  99.             constructor:CustomEvt
  100.             ,$emit:function(event,data){
  101.                 event = this.subEvents[event];
  102.                 if(event){
  103.                     for(let i = 0; i < event.length; i++){
  104.                         event[i].apply(this, data);
  105.                     }
  106.                 }
  107.                 return this;
  108.             }
  109.             ,$on:function(event,handle){
  110.                 let subEvents = this.subEvents;
  111.                 !(event in subEvents) && (subEvents[event] = [])
  112.                 subEvents[event].push(handle);
  113.                 return this;
  114.             }
  115.             ,$off:function(event,handle){
  116.                 let events = this.subEvents[event];
  117.                 if(!handle){
  118.                     events && Reflect.deleteProperty(this.subEvents, event);
  119.                 }else{
  120.                     if(typeof handle == 'function' && events){
  121.                         for(let i = 0,len = events.length; i < len; i++){
  122.                             let event = events[i];
  123.                             if(event == handle){
  124.                                 return events.splice(i,1);
  125.                             }
  126.                         }
  127.                     }
  128.                 }
  129.                 return this;
  130.             }
  131.         }
  132.         module.CustomEvt = CustomEvt;
  133.         // // 主题对象
  134.         // function Subject() {
  135.         //     this.observers = [];
  136.         // }
  137.         // // 添加观察者
  138.         // Subject.prototype.addObserver = function(observer) {
  139.         //    !this.observers.includes(observer) && this.addObserver.push(observer);
  140.         // }
  141.         // Subject.prototype.notify = function(message) {
  142.         //     this.observers.forEach(observer => {
  143.         //         observer.update(message);//观察者接收消息的方法
  144.         //     })
  145.         // }
  146.         // function Observer() {}
  147.         // Observer.prototype.update = function(message) {
  148.         // }
  149.         Object.assign( module.prototype, {
  150.             // 入口函数
  151.             ready() {
  152.                 this.setCtxProxy();
  153.                 // 这里默认加载同级目录下的main.js,请确保main.js的存在
  154.                 var script = utils.createScript(this.getUrl('./', this.entry));
  155.                 document.body.appendChild(script);
  156.             },
  157.             // 设置context对象的属性,通过代理访问
  158.             setCtxProxy(){
  159.                 var _self = this;
  160.                 var ctx = this.context;
  161.                 utils.each(Object.keys(ctx), function(key) {
  162.                     Object.defineProperty(_self, key, {
  163.                         get: function() {
  164.                             return ctx[key];
  165.                         },
  166.                         set: function(value) {
  167.                             if(ctx[key] !== value) {
  168.                                 ctx[key] = value;
  169.                                 return true;
  170.                             }
  171.                         }
  172.                     })
  173.                 });
  174.             },
  175.             configs(options) {
  176.                 Object.assign(this.context, utils.inKeys(options, ['baseUrl', 'alias', 'entry']))
  177.                 return this;
  178.             },
  179.             getUrl(baseUrl, moduleName) {
  180.                 var path = utils.pathJoin(baseUrl, moduleName);
  181.                 return new RegExp('\\'+this.suffix+'$').test(path) ? path : path + this.suffix;
  182.             },
  183.             // 核心方法:define,收集依赖
  184.             define(moduleName, deps, factory) {
  185.                 typeof deps == 'function' && (
  186.                     factory = deps,
  187.                     deps = []
  188.                 )
  189.                 if(!this.deps[moduleName]) {
  190.                     this.deps[moduleName] = {
  191.                         moduleName,
  192.                         deps,
  193.                         factory
  194.                     };
  195.                 }
  196.             },
  197.             // 核心方法
  198.             // 迭代收集每一个入口依赖
  199.             traverseDeps(deps, callback){
  200.                 var _self = this;
  201.                 function buildDepsRelation(moduleName, depsTree) {
  202.                     var oldDepsTree = depsTree;
  203.                     var module = _self.deps[moduleName];
  204.                     depsTree = depsTree || { entry: moduleName };
  205.                     depsTree[moduleName] = module;
  206.                     if(module.deps.length > 0) {
  207.                         traverseScript(module.deps, depsTree);
  208.                     }else {
  209.                         // 所有依赖收集完,才返回
  210.                         if(utils.isDepsDone({ depsTree })) {
  211.                             typeof callback == 'function' && callback(depsTree);
  212.                         }
  213.                     }
  214.                     // 表示是第一层的递归,只重置第一层的递归
  215.                     if(!oldDepsTree) {
  216.                         depsTree = null;
  217.                     }
  218.                 }
  219.                 function traverseScript(deps, depsTree) {
  220.                     utils.each(deps, function(moduleName, i) {
  221.                         var curSrc = _self.getUrl(_self.baseUrl, moduleName);
  222.                         var isExistSrc = _self.srcs.includes(curSrc);
  223.                         // 判断相同的依赖是否已经加载过
  224.                         if(!isExistSrc) {
  225.                             _self.srcs.push(curSrc);
  226.                             var script = utils.createScript(curSrc);
  227.                             script.onload = function() {
  228.                                 _self.customEvt.$emit('scriptLoaded', [moduleName])
  229.                                 buildDepsRelation(moduleName, depsTree);
  230.                             }
  231.                             document.body.appendChild(script);
  232.                         }else {
  233.                             let curModuleName = moduleName;
  234.                             // 1,依赖已加载完成
  235.                             if(_self.deps[curModuleName]) {
  236.                                 buildDepsRelation(moduleName, depsTree);
  237.                             }else {
  238.                                 // 2,scriptLoad加载完成后,this.deps才有值
  239.                                 _self.customEvt.$on('scriptLoaded', function(moduleName) {
  240.                                     if(moduleName == curModuleName) {
  241.                                         buildDepsRelation(moduleName, depsTree);
  242.                                     }
  243.                                 });
  244.                             }
  245.                         }
  246.                     })
  247.                 }
  248.                 traverseScript(deps)
  249.             },
  250.             // 一次收集全部依赖树的依赖
  251.             getAllDeps(initDeps, callback) {
  252.                 var _self = this;
  253.                 function traverseDeps(deps) {
  254.                     utils.each(deps, function(moduleName) {
  255.                         var curSrc = _self.getUrl(_self.baseUrl, moduleName);
  256.                         var isExistSrc = _self.srcs.includes(curSrc);
  257.                         // 判断相同的依赖是否已经加载过
  258.                         if(!isExistSrc) {
  259.                             _self.srcs.push(curSrc);
  260.                             var script = utils.createScript(curSrc);
  261.                             script.onload = function() {
  262.                                 var module = _self.deps[moduleName];
  263.                                 if(module.deps.length > 0) {
  264.                                     traverseDeps(module.deps);
  265.                                 }else {
  266.                                     // 所有依赖收集完,才返回
  267.                                     var isDone = initDeps
  268.                                     .map(entryDep => utils.isDepsDone({ depsTree: _self.deps, entry: entryDep }))
  269.                                     .every(isDone => isDone === true);
  270.                                     if(isDone){
  271.                                         typeof callback == 'function' && callback(_self.deps);
  272.                                     }
  273.                                 }
  274.                             }
  275.                             document.body.appendChild(script);
  276.                         }else {
  277.                             // 所有依赖收集完,才返回
  278.                             var isDone = initDeps
  279.                             .map(entryDep => utils.isDepsDone({ depsTree: _self.deps, entry: entryDep }))
  280.                             .every(isDone => isDone === true);
  281.                             if(isDone){
  282.                                 typeof callback == 'function' && callback(_self.deps);
  283.                             }
  284.                         }
  285.                     })
  286.                 }
  287.                 traverseDeps(initDeps)
  288.             },
  289.             require(deps, factory) {
  290.                 typeof deps == 'string' && (deps = [deps]);
  291.                 // 迭代收集每一个入口依赖
  292.                 this.traverseDeps(deps, function(depsTree) {
  293.                     console.log(depsTree);
  294.                 });
  295.                 // 一次性收集所有依赖
  296.                 // this.getAllDeps(deps, function(alldeps) {
  297.                 //     console.log(alldeps)
  298.                 // })
  299.             }
  300.         });
  301.         win.module = new module();
  302.     })(this);
复制代码
源码链接: https://gitee.com/littleboyck/front/tree/master/front-module

联系方式:QQ: 1187253007

来源:https://www.cnblogs.com/littleboyck/p/amd.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具