2013-05-02 10 views
6

Używam uglifyjs do minifikacji połączonego zestawu plików, który działa dobrze, ale nie jest wystarczająco dobry. Wbudowany lib używa nazw, więc klasy, funkcje i stałe są przechowywane w zmiennej przestrzeni nazw root:Mangle zagnieżdżone klasy i zmienne z uglifyjs

(function() { 
    var root = { api:{}, core:{}, names:{} }; 

    /* util.js file */ 
    root.names.SOME_LONG_NAMED_CONST='Angel'; 

    /* Person.js file */ 
    root.core.Person = function(name) { this.name = name }; 

    /* API.js with the functions we want to expose */ 
    root.api.perform = function(param_for_api) { /* do something */ } 

    window.lib_name.perform = root.api.perform; 

})(); 

który minified do niezbyt minimalna wersja

(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})(); 

Rozumiem zeszpecić prawdopodobnie myśli ten root var jest strukturą danych, która musi być przechowywana tak jak jest i nie może być zmieniona. Czy istnieje sposób na pozwolenie na pomylenie zagnieżdżonych nazw w głównej przestrzeni nazw?

Odpowiedz

8

Gdy zminimalizujesz JavaScript, możesz zmienić tylko nazwy zmiennych, api, rdzeń i nazwy nie są zmiennymi, ale właściwościami obiektu. Jeśli zostały one zmienione przez minimizer, potencjalnie uzyskasz nieoczekiwane wyniki. Co zrobić, jeśli w kodzie nazwałbyś

root["api"].perform = function()... 

lub nawet coś podobnego

function doIt(section, method, argument) { 
    root[section][method](argument); 
} 
doIt('api','perform', 101); 

All JS całkowicie legalne, ale Minimizer nigdy nie mógł zrozumieć, co się dzieje.

+2

Tak, rozumiem wasze argumenty. Czy mogę coś jeszcze zrobić? – whadar

+0

Bardzo ładna odpowiedź. – SoonDead

+0

@Jan Developer może to zdecydować, a nie uglifyjs. Mogę skonfigurować w tym celu polecenie uglifyjs: "nie modyfikuj ciągów" lub "nie dotykaj zmiennych, które ci powiedziałem". Używałem obfuscatora i wszystko to bez problemu. –

4

Oprócz punktu @JanMisker (który jest całkowicie poprawny), przepisanie właściwości jest niebezpieczne, ponieważ mogą być narażone na kod wykraczający poza zakres zminimalizowania.

Chociaż funkcja wykonywania samo ma zakres, a jeśli kod jest tylko

(function() { 
    var root = { api:{}, core:{}, names:{} }; 
    root.names.SOME_LONG_NAMED_CONST='Angel'; 
    alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something 
})(); 

Prawdą jest, że poza funkcją, nie ma sposobu, aby uzyskać dostęp do obiektu głównego, więc przepisywanie nazwy własności jest bezpieczny, a następujący kod spowoduje taki sam:

(function() { 
    var a = { b:{}, c:{}, d:{} }; 
    a.d.e='Angel'; 
    alert(a.d.e); 
})(); 

Ale nawet jeśli jesteś wewnątrz zakresu prywatnej można uzyskać dostęp, a co ważniejsze przypisać do zmiennych z zakresu zewnętrznego! Wyobraź sobie:

(function() { 
    var root = { api:{}, core:{}, names:{} }; 
    root.api.perform = function(param_for_api) { /* do something */ } 
    window.lib_name = root.api; 
})(); 

Wystawiasz nie tylko funkcję, ale obiekt z funkcją na niej. Funkcja będzie widoczna z dowolnego miejsca, w którym okno jest widoczne.

Tak więc, na przykład pisząc następujące w konsoli javascript by dawać różne wyniki i bez minifikacji:

window.lib_name.perform(asdf); 

Z minifikacji trzeba by napisać:

window.lib_name.f(asdf); 

lub coś podobnego.

Pamiętaj, że zawsze możesz znaleźć kod poza swoją minifikacją.

To nie jest najważniejsze, aby mieć absolutnie minimalną JS, ale jeśli Z jakiegoś powodu jest to kluczowe (na przykład: kosmici uprowadzili pasierbicę, a jedynym sposobem, aby ją odzyskać jest zminimalizowanie tego poniżej 100 znaków lub więc), możesz ręcznie zmienić niepożądaną długą nazwę właściwości na krótszą, po prostu upewnij się, że nie będzie ona nigdzie widoczna i nie będzie dostępna przez skojarzeniową notację tablicową (root['api']).

+1

Chociaż pasierbica nie została uprowadzona :) potrzeba zagnieżdżenia się jest kluczowa dla zminimalizowania. Myślę, że rozmiar kodu można zmniejszyć o ponad 20%. pamiętać, że celowo stosowana window.lib_name.perform = root.api.perform więc minifikacji będzie nadal działać – whadar

+0

Problemem jest to, że dynamiczne typowanie iw zasadzie wszystko o obiektach w JavaScript jest tak wyrozumiały, że uglifyjs po prostu nie może być 100% pewności, co eksponujesz, patrząc na scenariusz. Również @JanMisker podniósł punkt o ciągach. Mogą być budowane dynamicznie, a następnie wykorzystywane jako indeksator dla tablicy asocjacyjnej. Nie ma po prostu sposobu, aby uglifyjs bezpiecznie wdrożyć do tego celu minifikację. Będziesz musiał to zrobić ręcznie. – SoonDead

+0

Zamknięcie może to zrobić (ADVANCED_OPTIMISATIONS). – miracle2k

1

jak @ Jan-Misker wyjaśnił w swojej odpowiedzi, że wymazanie nazwy własności NIE jest dobrym pomysłem, ponieważ mogłoby to potencjalnie złamać twój kod.

Można jednak obejść go przez określenie nazwy właściwości jako zmienne lokalne i modyfikować wszystkie .Properties do [Klawisze], aby mniejszy rozmiar pliku:

(function() { 
    var API = 'api'; 
    var CORE = 'core'; 
    var NAMES = 'names'; 
    var SLNC = 'SOME_LONG_NAMED_CONST'; 

    var root = {}; 
    root[API]={}; 
    root[CORE]={}; 
    root[NAMES]={}; 

    /* util.js file */ 
    root[NAMES][SLNC] ='Angel'; 

    /* Person.js file */ 
    root[CORE].Person = function(name) { this.name = name }; 

    /* API.js with the functions we want to expose */ 
    root[API].perform = function(param_for_api) { /* do something */ } 

    window.lib_name.perform = root[API].perform; 

})(); 

Bo teraz wszystkie właściwości stał się lokalnym zmienna, zeszpecić js będzie magiel/skrócić nazwy zmiennych i jako konsekwencja ty ogólny rozmiar pliku zredukowany:

!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}(); 

jednak zmniejszony rozmiar pliku nie oznacza dostaniesz krótszy czas pobierania na prawdziwym serwerze, ponieważ zwykle nasz http transport jest gzipowany, większość re petycje będą kompresowane przez twój serwer http i będzie lepiej niż człowiek.

1

Najnowsze wydanie uglify (dzisiaj) ma wymazanie własności obiektu, patrz v2.4.18. Obsługuje również pliki zastrzeżone, aby wykluczyć zarówno właściwości obiektów, jak i zmienne, których nie chcesz zmanipulować. Sprawdź to.

Użyj opcji --mangle-props i --reserved-file filename1.json filename2.json itp ....

+0

Myślę, że łatwiej jest powiedzieć brzydkie, aby "zmapować tylko te" zamiast "magować z wyjątkiem tego", ponieważ w tym drugim każde nowe słowo kluczowe, które utworzysz, może zostać przypadkowo zniekształcone (zapomnij wstawić na liście wykluczeń). –

4

I zostały próbuje użyć --mangle-props z UglifyJS2 i może powiedzieć: „czyni bałagan”.

Jak ktoś zauważył: „Twórca powinien zdecydować, jakie właściwości do modyfikacji, nie uglifyjs

jestem zbliża ten problem używając tych opcji:

--mangle-props 
--mangle-regexp="/_$/" 

regex dopasowuje dowolny obiekt z podkreślenie na końcu.

Poprosiłeś o łączenie zagnieżdżonych nazw w głównej przestrzeni nazw. Tak, Twój kod:

(function() { 
    var root = { api:{}, core:{}, names:{} }; 

    root.names.SOME_LONG_NAMED_CONST_='Angel'; 

    root.core.Person_ = function(name) { this.name = name }; 

    root.api.perform_ = function(param_for_api) { } 

    window.lib_name.perform = root.api.perform; 
})(); 

spowodowałoby to:

(function() { 
    var n = { 
     api: {}, 
     core: {}, 
     names: {} 
    }; 
    n.names.a = "Angel"; 
    n.core.b = function(n) { 
     this.name = n; 
    }; 
    n.api.c = function(n) {}; 
    window.lib_name.perform = n.api.c; 
})(); 

Command: uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js

Jeśli chcesz magiel pierwszy poziom korzenia nazw (api, core, names) wystarczy umieścić podkreślenia na nich (api_, core_, names_) masz kontrolę;)

Tylko jedna uwaga boczna: gdy manipulujesz właściwościami używanymi przez inne pliki js, powinieneś zmapować wszystkie pliki razem z tym samym poleceniem, więc ten sam identyfikator będzie używany dla wszystkich plików.

+0

Miło. Wolę jednak prowadzić podkreślenia wiodące (pozostałość z moich dni C++) i posortować z '/^_ /' – cmroanirgo

+1

, ale widziałem, ale robiłem to z końcowymi podkreśleniami, ponieważ łatwiej jest pisać (poprawa kodu działa lepiej). –