如何使用Uglify2.js分析函數中的依賴項

大家都知道,require.js遵循AMD規範,而國產的sea.js遵循自創的CMD規範。最近,又有大牛提出了更符合面向對象寫法,以殺死AMD與CMD規範爲己任,以JS工程化終極解決方案爲目標的KMD規範

作者有一篇介紹其內部的內核依賴的實現原理的文章。

從作者的博客裏我們可以知道,像是下面的函數裏的依賴關係中:

function test() {
    var user = new User();
}

function test() {
    var vp = new Array();
    var el = document.getElementById("xx");
}

第一個函數裏依賴了User,而第二個函數裏則沒有依賴。我們的目標就是找到像是User這樣的依賴,從而可以去單獨加載該模塊的js文件,從而減少不必要的依賴加載,提升模塊依賴加載性能。

作者用到了用於js代碼解析、壓縮、優化等的Uglify2.js庫(用nodeJS實現壓縮等)。在看到了其實現原理後,大概瞭解了其實現。但是作者實現的依賴裏只有new一個類形式以及調用某對象的方法形式的依賴,我添加了單純函數調用形式的依賴。代碼如下:

<script src="uglify2.js"></script>
<script>
var U2 = UglifyJS;
// test it

function test() {
    if (foo) throw bar;
    if (moo /* foo */) {
      throw "foo";
    }
    throw "bar";
	u.a();
	uu();
	new User();
	return ;
}

function test1() {
    var vp = new Array();
    var el = document.getElementById("xx");
}
function getRef(fn) {
    var U2 = UglifyJS;
    var ast = U2.parse(fn.toString());         
    ast.figure_out_scope();
    var result = [];
	console.log(U2.AST_Dot,U2.AST_New);
	// for(var p in U2){console.log(p);}
    ast.walk(new U2.TreeWalker(function (node) {
        if (node instanceof U2.AST_New || node instanceof U2.AST_Dot || node instanceof U2.AST_Call) {
            var ex = node.expression;
            var name = ex.name;
            if (ex.scope && name && !isInScopeChainVariables(ex.scope, name)) {
                result.push(name);
            }
        }
    }));
    return result;
}

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;
}

console.info(getRef(test),getRef(test1));// ["u", "uu", "User"] ["Array", "document"] 
</script>

結果已經很明顯。當然,對於Array,document這些js自帶的存在於全局變量中的屬性我們可以過濾掉。

這個操作在代碼分析、模塊加載分析(比如define一個模塊的時候寫依賴了一堆模塊,但並沒有全部使用的情形)是很有用的,記錄在此。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章