kmdjs集成uglifyjs2打造极致的编程体验
回顾
上篇文章大概展示了kmdjs0.1.x时期的编程范式:
如下面所示,可以直接依赖注入到function里,
kmdjs.define('main',['util.bom','app.Ball','util.dom.test'], function(bom,Ball,test) {
    var ball = new Ball(0, 0, 28, 1, -2, 'kmdjs');
    var vp = bom.getViewport();
});
也可以直接在代码里把full namespace加上来调用,如:
kmdjs.define('main',['util.bom','app.Ball','util.dom.test'], function() {
    var ball = new app.Ball(0, 0, 28, 1, -2, 'kmdjs');
    var vp = util.bom.getViewport();
});
而且,在循环依赖的场景,因为执行顺序的问题,会导致第一种方式注入undefined,所以循环依赖的情况下只能用full namespace的方式来调用。
这种编程体验虽然已经足够好,但是可以更好。怎样才算更好?
- 不用依赖注入function
- 不用写full namespace,自动匹配依赖
如下所示:
kmdjs.define('main',['util.bom','app.Ball','util.dom.test'], function() {
    var ball = new Ball(0, 0, 28, 1, -2, 'kmdjs');
    var vp = bom.getViewport();
});
这就要借助uglifyjs能力,把function的字符串替换成带有namespace就可以实现上面的效果。
uglifyjs依赖分析和代码重构
function fixDeps(fn,deps) {
    var U2 = UglifyJS;
    //uglify2不支持匿名转ast
    var code = fn.toString().replace('function','function ___kmdjs_temp');
    var ast = U2.parse(code);
    ast.figure_out_scope();
    var nodes = [];
    ast.walk(new U2.TreeWalker(function (node) {
        if (node instanceof U2.AST_New) {
            var ex = node.expression;
            var name = ex.name;
            isInWindow(name) || isInArray(nodes, node) || isInScopeChainVariables(ex.scope, name) || nodes.push({name:name,node:node});
        }
        if (node instanceof U2.AST_Dot) {
            var ex = node.expression;
            var name = ex.name;
            var scope = ex.scope;
            if (scope) {
                isInWindow(name) || isInArray(nodes, node) || isInScopeChainVariables(ex.scope, name) || nodes.push({name:name,node:node});
            }
        }
        if (node instanceof U2.AST_SymbolRef) {
            var name = node.name;
            isInWindow(name) || isInArray(nodes, node) || isInScopeChainVariables(node.scope, name) || nodes.push({name:name,node:node});
        }
    }));
    var cloneNodes = [].concat(nodes);
    //过滤new nodes 中的symbo nodes
    for (var i = 0, len = nodes.length; i < len; i++) {
        var nodeA = nodes[i].node;
        for (var j = 0, cLen = cloneNodes.length; j < cLen; j++) {
            var nodeB = cloneNodes[j].node;
            if (nodeB.expression === nodeA) {
                nodes.splice(i, 1);
                i--;
                len--;
            }
        }
    }
    for (var i = nodes.length; --i >= 0;) {
        var item = nodes[i],
            node=item.node,
            name=item.name;
        var fullName=getFullName(deps,name);
        var replacement;
        if (node instanceof  U2.AST_New) {
            replacement = new U2.AST_New({
                expression: new U2.AST_SymbolRef({
                    name:fullName
                }),
                args: node.args
            });
        } else if (node instanceof  U2.AST_Dot) {
            replacement = new U2.AST_Dot({
                expression: new U2.AST_SymbolRef({
                    name: fullName
                }),
                property: node.property
            });
        }else if(node instanceof U2.AST_SymbolRef){
            replacement = new U2.AST_SymbolRef({
                    name: fullName
            });
        }
        var start_pos = node.start.pos;
        var end_pos = node.end.endpos;
        code = splice_string(code, start_pos, end_pos, replacement.print_to_string({
            beautify: true
        }));
    }
    return code.replace('function ___kmdjs_temp','function');
}
function getFullName(deps,name){
    var i= 0,
        len=deps.length,
            matchCount= 0,
            result=[];
    for(;i<len;i++) {
        var fullName = deps[i];
        if (fullName.split('.').pop() === name) {
            matchCount++;
            if (!isInArray(result, fullName))  result.push(fullName);
        }
    }
    if(matchCount>1){
        throw "the same name conflict: "+result.join(" and ");
    } else if(matchCount===1){
        return result[0];
    }else{
        throw ' can not find module ['+name+']';
    }
}
function splice_string(str, begin, end, replacement) {
    return str.substr(0, begin) + replacement + str.substr(end);
}
function isInScopeChainVariables(scope, name) {
    var vars = scope.variables._values;
    if (Object.prototype.hasOwnProperty.call(vars, "$" + name)) {
        return true;
    }
    if (scope.parent_scope) {
        return isInScopeChainVariables(scope.parent_scope, name);
    }
    return false;
}
function isInArray(arr,name){
    var i= 0,len=arr.length;
    for(;i<len;i++){
        if(arr[i]===name){
            return true;
        }
    }
    return false;
}
function isInWindow(name){
    if(name==='this')return true;
    return name in window;
}
通过上面的fixDeps,可以对代码就行变换。如:
 console.log(fixDeps(function (A) {
        var eee = m;
        var b = new A();
        var b = new B();
        var c = new C();
        var d = G.a;
    },['c.B','AAA.G','SFSF.C','AAAA.m'] ))
输出:
function (A) {
        var eee = AAAA.m;
        var b = new A();
        var b = new c.B();
        var c = new SFSF.C();
        var d = AAA.G.a;
}
这样,kmdjs在执行模块function的时候,只需要fixDeps加上full namespace就行:
function buildBundler(){
    var topNsStr = "";
    each(kmdjs.factories, function (item) {
        nsToCode(item[0]);
    });
    topNsStr+=  kmdjs.nsList.join('\n') +"\n\n";
    each(kmdjs.factories, function (item) {
        topNsStr+=item[0]+' = ('+ fixDeps(item[2],item[1])+')();\n\n' ;
    });
    if(kmdjs.buildEnd) kmdjs.buildEnd(topNsStr);
    return topNsStr;
}
build出来的包,当然全都加上了namespace。再也不用区分循环依赖和非循环依赖了~~~
Github
上面的所有代码可以Github上找到:
https://github.com/kmdjs/kmdjs
kmdjs集成uglifyjs2打造极致的编程体验的更多相关文章
- EDM数据营销之电商篇| 六大事务性邮件,环环相扣打造极致用户体验!
		“以用户为中心”的时代,电商们致力于打造极致的用户体验,想尽各式新颖营销办法,但难免还是会出现营销断层,以至于和用户间无法达到完整的交互. 本次Focussend以邮件营销为例,聚焦用户从浏览到支付等 ... 
- Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验
		很久之前开发了一个名为Dora.Interception的开源AOP框架(github地址:https://github.com/jiangjinnan/Dora,如果你觉得这个这框架还有那么一点价值 ... 
- observejs改善组件编程体验
		传送门 observejs:https://github.com/kmdjs/observejs 本文演示:http://kmdjs.github.io/observejs/list/ 本文代码:ht ... 
- 依赖注入[6]: .NET Core DI框架[编程体验]
		毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动时构建请求处理管道过程中,以及利用该管道处理每个请求过程中使用到的服务对象均来源于DI容器.该DI容器不仅为A ... 
- iOS-Socket编程体验
		CHENYILONG Blog Socket编程体验 Socket编程体验 技术博客http://www.cnblogs.com/ChenYilong/新浪微博http://weibo.com/lu ... 
- AOP框架Dora.Interception 3.0 [1]: 编程体验
		.NET Core正式发布之后,我为.NET Core度身定制的AOP框架Dora.Interception也升级到3.0.这个版本除了升级底层类库(.NET Standard 2.1)之外,我还对它 ... 
- 全新升级的AOP框架Dora.Interception[1]: 编程体验
		多年之前利用IL Emit写了一个名为Dora.Interception(github地址,觉得不错不妨给一颗星)的AOP框架.前几天利用Roslyn的Source Generator对自己为公司写的 ... 
- .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]
		原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ... 
- AMD and CMD are dead之KMDjs集成Blob一键下载全部build包
		更新 不zuo,[A/C]MD就不会死,所以kmdjs赢来来其伟大的版本0.0.6,该版本主要的更新有: 移除去了kmdjs.get(..).then的支持,只支持kmdjs.get(-,functi ... 
随机推荐
- DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记
			今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ... 
- git基本操作
			一.在Windows平台上安装Git,可以下载一个msysGit的安装包,点击exe即可安装运行.安装包下载地址:https://git-for-windows.github.io/备注:git命令行 ... 
- sqlyog导出json数据格式支持mysql数据转存mongodb
			<!-------------知识的力量是无限的(当然肯定还有更简单的方法)-----------!> 当我考虑将省市区三级联动数据从mysql转入mongodb时遇到了网上无直接插入mo ... 
- Xamarin技术文档------VS多平台开发
			此技术业余时间研究,仅供大家学习参考,不涉及深入研究,有一定开发基础的人员,应该都能较快上手. 一.简介 Xamarin始创于2011年,旨在使移动开发变得难以置信地迅捷和简单.Xamarin的产品简 ... 
- Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体
			问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ... 
- Spark2 ML 学习札记
			摘要: 1.pipeline 模式 1.1相关概念 1.2代码示例 2.特征提取,转换以及特征选择 2.1特征提取 2.2特征转换 2.3特征选择 3.模型选择与参数选择 3.1 交叉验证 3.2 训 ... 
- 自定义view(一)
			最近在学习自定义view 一遍看一别学顺便记录一下 1.View的测量-------->onMeasure() 首先,当我们要画一个图形的时候,必须知道三个数据:位置,长度,宽度 才能确定 ... 
- fir.im Weekly - APP 性能监测优化 二三事
			每一个成功的 App,都拥有强大的性能体验.本期 fir.im Weekly 整理了微信读书.美团外卖. 天猫.美团点评技术团队的关于性能监测优化方面策略和工具的分享,一起来看看. 微信读书 iOS ... 
- 【Win 10 应用开发】分析 URI 中的查询字符串
			分析URI中的字符有K种方法(K >= 2),如果查询字符串中的参数比较简单,可以通过子字符串查找的方式来处理:如果查询字符串相对复杂,你可以使用正则表达式来匹配 key1=value1 , ... 
- 《JS设计模式笔记》构造函数和工厂模式创建对象
			工厂模式 function createPerson (name,age,job) { var o=new Object(); o.name=name; o.age=age; o.job=job; o ... 
