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 ...
随机推荐
- 使用win10远程控制ubuntu16.04
使用win10远程控制ubuntu16.04,网上很多需要安装xfce桌面的.今天介绍一下,不需要安装其他桌面,使用Ubuntu16.04自带桌面,漂亮美观. Ubuntu16.04端: 1.打开终端 ...
- FastClick 填坑及源码解析
最近产品妹子提出了一个体验issue —— 用 iOS 在手Q阅读书友交流区发表书评时,光标点击总是不好定位到正确的位置: 如上图,具体表现是较快点击时,光标总会跳到 textarea 内容的尾部.只 ...
- Storm介绍(二)
作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文是Storm系列之一,主要介绍Storm的架构设计,推荐读者在阅读 ...
- 安装keil MDK5
昨天买的stm32板子今天就到了,顺丰速度确实很快. 我这是刚开始整STM32,首先需要下载keil MDK,直接杀去keil官网下载,其实对于学习者,我建议大家下载软件能去官网就去官网,尽量少用二道 ...
- git 常见命令解析
转载自http://www.rainkong.net/post/git-image.html 之前用的都是svn ,git还是要了解的,万一哪天要用了呢
- 【Win 10 应用开发】Sqlite 数据库的简单用法
如果老周没记错的话,园子里曾经有朋友写过如何在 UWP 项目中使用 Sqlite数据库的文章.目前我们都是使用第三方封装的库,将来,SDK会加入对 Sqlite 的支持. 尽管目前 UWP-RT 库中 ...
- VS2010开发工具使用技巧<之简单讲解>
俗语云:工欲善其事必先利其器! 1.代码放大 效果:放大前----------------------------------------------------------------->放大 ...
- VS2010编写WebService与在IIS的发布<之简单讲解>
工具VS2010,window环境win7 一:Webservice的创建与方法查看调用 1.新建空web应用程序项目 2.新建web服务 3.自动生成 4.直接跑起来,可以看到有2个方法 5.点击H ...
- 【译】Unity3D Shader 新手教程(1/6)
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D ...
- 多线程同步工具——volatile变量
关于volatile,找了一堆资料看,看完后想找一个方法去做测试,测了很久,感觉跟没有一样. 这本书<深入理解Java内存模型>,对volatile描述中有这样一个比喻的说法,如下代码所示 ...