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

项目目录
 src
 view1
 index.html
 main.js
 view2
 plugins
 module.js
 jquery.js
 ......
 modules // amd模块文件 
 a1.js
 b1.js
 c.js
 b2.js
 b21.js

src/view1/index.html

<!DOCTYPE html>
 <html lang="en">
 <head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
 </head>
 <body>
 <!-- 引入amd插件-->
 <!-- amd都有一个入口文件,我这里设计的方式是:自动引入同级目录下的main.js--> 
 <script src="/plugins/module.js"></script>
 </body>
 </html>

src/view1/main.js

module
.configs({
 baseUrl: '../modules'
})
.require(['a1','a2'], function(a2) {
 console.log('a2: ', a2)
})

plugins/module.js

// 使用IIFE
 (function(win){
 function module(){
 this.context = {
 baseUrl: './',
 alias: {},
 entry: 'main.js',
 suffix: '.js',
 }
 this.deps = {};
 this.srcs = [];
 this.customEvt = new module.CustomEvt();
 this.ready();
 }
 var utils = module.utils = {
 each(arr, callback){
 var keys = Object.keys(arr);
 for(var i = 0; i < keys.length; i++) { 
 if(callback.call(arr, arr[i], i) === false) {
 break;
 }
 }
 },
 // 只保留指定的keys
 inKeys(target, keys) {
 return Object.keys(target).reduce((newData, key) => {
 if (keys.includes(key)) {
 newData[key] = target[key];
 }
 return newData;
 }, {});
 },
 // 路径拼接
 pathJoin(...paths){
 let path = '';
 for(let i = paths.length - 1; i > -1; i--) {
 let pathItem = paths[i].replace(/\/+$/, '');
 // 如果是绝对路径
 if(/^\//.test(pathItem)) {
 path = pathItem + '/'+ path;
 break;
 }
 // 如果是相对路径
 if(/^\.\//.test(pathItem)) {
 pathItem = pathItem.slice(2)
 }
 // 如果是: ../../a/b/c
 if(/^(\.{2}\/)+\S+/.test(pathItem) && paths[i-1]) {
 let matches = null;
 while(matches = /^\.{2}\/(\S+)/.exec(pathItem)) {
 pathItem = matches[1];
 let prevPath = paths[i-1].replace(/\/+$/, '');
 if(prevPath) {
 paths[i-1] = prevPath.slice(0, prevPath.lastIndexOf('/'))
 }
 }
 }
 path = pathItem + '/'+ path;
 }
 return path.replace(/\/+$/, '');
 },
 createScript(src) {
 var node = document.createElement('script');
 node.type = 'text/javascript';
 node.async = true;
 node.src = src;
 return node;
 },
 // 判断所有依赖是否加载完成
 isDepsDone(options) {
 var { depsTree, entry = depsTree.entry, deps } = options;
 var deps = deps || depsTree[entry].deps;
 var done = true;
 utils.each(deps, function(moduleName) {
 var dep = depsTree[moduleName];
 if(dep) {
 if(dep.deps.length > 0) {
 done = utils.isDepsDone({
 depsTree,
 entry,
 deps: dep.deps
 });
 if(!done) {
 return false;
 }
 }
 }else {
 done = false;
 return false;
 }
 });
 return done;
 }
 }
 var CustomEvt = function(){
 this.subEvents = {};
 };
 CustomEvt.prototype = {
 constructor:CustomEvt
 ,$emit:function(event,data){
 event = this.subEvents[event];
 if(event){
 for(let i = 0; i < event.length; i++){
 event[i].apply(this, data);
 }
 }
 return this;
 }
 ,$on:function(event,handle){
 let subEvents = this.subEvents;
 !(event in subEvents) && (subEvents[event] = [])
 subEvents[event].push(handle);
 return this;
 }
 ,$off:function(event,handle){
 let events = this.subEvents[event];
 if(!handle){
 events && Reflect.deleteProperty(this.subEvents, event);
 }else{
 if(typeof handle == 'function' && events){
 for(let i = 0,len = events.length; i < len; i++){
 let event = events[i];
 if(event == handle){
 return events.splice(i,1);
 }
 } 
 }
 }
 return this;
 }
 }
 module.CustomEvt = CustomEvt;
 // // 主题对象
 // function Subject() {
 // this.observers = [];
 // }
 // // 添加观察者
 // Subject.prototype.addObserver = function(observer) {
 // !this.observers.includes(observer) && this.addObserver.push(observer);
 // }
 // Subject.prototype.notify = function(message) {
 // this.observers.forEach(observer => {
 // observer.update(message);//观察者接收消息的方法
 // })
 // }
 // function Observer() {}
 // Observer.prototype.update = function(message) {
 // }
 Object.assign( module.prototype, {
 // 入口函数
 ready() {
 this.setCtxProxy();
 // 这里默认加载同级目录下的main.js,请确保main.js的存在
 var script = utils.createScript(this.getUrl('./', this.entry));
 document.body.appendChild(script);
 },
 // 设置context对象的属性,通过代理访问
 setCtxProxy(){
 var _self = this;
 var ctx = this.context;
 utils.each(Object.keys(ctx), function(key) {
 Object.defineProperty(_self, key, {
 get: function() {
 return ctx[key];
 },
 set: function(value) {
 if(ctx[key] !== value) {
 ctx[key] = value;
 return true;
 }
 }
 })
 });
 },
 configs(options) {
 Object.assign(this.context, utils.inKeys(options, ['baseUrl', 'alias', 'entry']))
 return this;
 },
 getUrl(baseUrl, moduleName) {
 var path = utils.pathJoin(baseUrl, moduleName);
 return new RegExp('\\'+this.suffix+'$').test(path) ? path : path + this.suffix;
 },
 // 核心方法:define,收集依赖
 define(moduleName, deps, factory) {
 typeof deps == 'function' && (
 factory = deps,
 deps = []
 )
 if(!this.deps[moduleName]) {
 this.deps[moduleName] = {
 moduleName,
 deps,
 factory
 };
 }
 },
 // 核心方法
 // 迭代收集每一个入口依赖
 traverseDeps(deps, callback){
 var _self = this;
 function buildDepsRelation(moduleName, depsTree) {
 var oldDepsTree = depsTree;
 var module = _self.deps[moduleName];
 depsTree = depsTree || { entry: moduleName };
 depsTree[moduleName] = module;
 if(module.deps.length > 0) {
 traverseScript(module.deps, depsTree);
 }else {
 // 所有依赖收集完,才返回
 if(utils.isDepsDone({ depsTree })) {
 typeof callback == 'function' && callback(depsTree);
 }
 }
 // 表示是第一层的递归,只重置第一层的递归
 if(!oldDepsTree) {
 depsTree = null;
 }
 }
 function traverseScript(deps, depsTree) {
 utils.each(deps, function(moduleName, i) {
 var curSrc = _self.getUrl(_self.baseUrl, moduleName);
 var isExistSrc = _self.srcs.includes(curSrc);
 // 判断相同的依赖是否已经加载过
 if(!isExistSrc) {
 _self.srcs.push(curSrc);
 var script = utils.createScript(curSrc);
 script.onload = function() {
 _self.customEvt.$emit('scriptLoaded', [moduleName])
 buildDepsRelation(moduleName, depsTree);
 }
 document.body.appendChild(script);
 }else {
 let curModuleName = moduleName;
 // 1,依赖已加载完成
 if(_self.deps[curModuleName]) {
 buildDepsRelation(moduleName, depsTree);
 }else {
 // 2,scriptLoad加载完成后,this.deps才有值
 _self.customEvt.$on('scriptLoaded', function(moduleName) {
 if(moduleName == curModuleName) {
 buildDepsRelation(moduleName, depsTree);
 }
 });
 }
 }
 })
 }
 traverseScript(deps)
 },
 // 一次收集全部依赖树的依赖
 getAllDeps(initDeps, callback) {
 var _self = this;
 function traverseDeps(deps) {
 utils.each(deps, function(moduleName) {
 var curSrc = _self.getUrl(_self.baseUrl, moduleName);
 var isExistSrc = _self.srcs.includes(curSrc);
 // 判断相同的依赖是否已经加载过
 if(!isExistSrc) {
 _self.srcs.push(curSrc);
 var script = utils.createScript(curSrc);
 script.onload = function() {
 var module = _self.deps[moduleName];
 if(module.deps.length > 0) {
 traverseDeps(module.deps);
 }else {
 // 所有依赖收集完,才返回
 var isDone = initDeps
 .map(entryDep => utils.isDepsDone({ depsTree: _self.deps, entry: entryDep }))
 .every(isDone => isDone === true);
 if(isDone){
 typeof callback == 'function' && callback(_self.deps);
 }
 }
 }
 document.body.appendChild(script);
 }else {
 // 所有依赖收集完,才返回
 var isDone = initDeps
 .map(entryDep => utils.isDepsDone({ depsTree: _self.deps, entry: entryDep }))
 .every(isDone => isDone === true);
 if(isDone){
 typeof callback == 'function' && callback(_self.deps);
 }
 }
 })
 }
 traverseDeps(initDeps)
 },
 require(deps, factory) {
 typeof deps == 'string' && (deps = [deps]);
 // 迭代收集每一个入口依赖
 this.traverseDeps(deps, function(depsTree) {
 console.log(depsTree);
 });
 // 一次性收集所有依赖
 // this.getAllDeps(deps, function(alldeps) {
 // console.log(alldeps)
 // })
 }
 });
 win.module = new module();
 })(this);

源码链接: https://gitee.com/littleboyck/front/tree/master/front-module


联系方式:QQ: 1187253007

作者:littleboyck原文地址:https://www.cnblogs.com/littleboyck/p/amd.html

%s 个评论

要回复文章请先登录注册