zoukankan      html  css  js  c++  java
  • 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的方式来调用。

    这种编程体验虽然已经足够好,但是可以更好。怎样才算更好?

    1. 不用依赖注入function
    2. 不用写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('
    ') +"
    
    ";
        each(kmdjs.factories, function (item) {
            topNsStr+=item[0]+' = ('+ fixDeps(item[2],item[1])+')();
    
    ' ;
        });
        if(kmdjs.buildEnd) kmdjs.buildEnd(topNsStr);
        return topNsStr;
    }
    

    build出来的包,当然全都加上了namespace。再也不用区分循环依赖和非循环依赖了~~~

    Github

    上面的所有代码可以Github上找到:
    https://github.com/kmdjs/kmdjs

  • 相关阅读:
    POJ 1251 Jungle Roads
    1111 Online Map (30 分)
    1122 Hamiltonian Cycle (25 分)
    POJ 2560 Freckles
    1087 All Roads Lead to Rome (30 分)
    1072 Gas Station (30 分)
    1018 Public Bike Management (30 分)
    1030 Travel Plan (30 分)
    22. bootstrap组件#巨幕和旋转图标
    3. Spring配置文件
  • 原文地址:https://www.cnblogs.com/iamzhanglei/p/5585226.html
Copyright © 2011-2022 走看看