Jak zautomatyzować minifikację JavaScript w aplikacjach internetowych Java?


122

Interesuje mnie, jak wolisz zautomatyzować minifikację JavaScript w swoich aplikacjach internetowych Java. Oto kilka aspektów, które szczególnie mnie interesują:

  • Jak to się integruje? Czy jest to część twojego narzędzia do budowania, filtr serwletów, samodzielny program przetwarzający dalej plik WAR, czy coś innego?
  • Czy łatwo jest włączać i wyłączać ? Próba debugowania zminimalizowanego skryptu jest bardzo niezręczna, ale programista może również sprawdzić, czy minifikacja niczego nie psuje.
  • Czy działa przejrzyście , czy też ma jakieś skutki uboczne (poza tymi nieodłącznymi od minifikacji), które muszę brać pod uwagę w codziennej pracy?
  • Którego minifier używa?
  • Czy brakuje mu jakichś funkcji , o których możesz pomyśleć?
  • Co w tym lubisz?
  • Co ci się w nim nie podoba?

Będzie to głównie służyć jako punkt odniesienia dla moich przyszłych projektów (i mam nadzieję, że inni SOer również uznają to za przydatne), więc wszelkiego rodzaju narzędzia są interesujące.

(Zwróć uwagę, że to nie jest kwestia tego, który minifier jest najlepszy . Mamy już ich wielu).


wygląda to naprawdę interesująco, nie słyszałem o tym. Wszystkie narzędzia, które znalazłem podczas szybkiego wyszukiwania, to narzędzia ręczne, które działają raz. Byłoby miło, gdyby była wtyczka dla mrówki lub mavena. Mam nadzieję, że ktoś ma dobrą odpowiedź.
Jay

I wydaje się, że ktoś to zrobił - sprawdź odpowiedź
dfa

Odpowiedzi:


65

13

Używamy zadania Ant, aby zminimalizować pliki js za pomocą YUICompressor podczas kompilacji produkcyjnej i umieścić wynik w oddzielnym folderze. Następnie przesyłamy te pliki na serwer WWW. Na tym blogu możesz znaleźć kilka dobrych przykładów integracji YUI + Ant .

Oto przykład:

<target name="js.minify" depends="js.preprocess">
    <apply executable="java" parallel="false">
        <fileset dir="." includes="foo.js, bar.js"/>
        <arg line="-jar"/>
        <arg path="yuicompressor.jar"/>
        <srcfile/>
        <arg line="-o"/>
        <mapper type="glob" from="*.js" to="*-min.js"/>
        <targetfile/>
    </apply>
</target>

2
Skrawek; miły. Czy retargetujesz swoje script srckompilacje deweloperskie, czy po prostu kopiujesz niezminifikowane pliki do katalogu skompresowanego / js?
gustafc

Tylko do produkcji prześlij skompresowane pliki na oryginalne w public_html / js. Dobrze, że nie ma kodowania ani zmian ścieżki między lokalnym a produkcyjnym, złe jest to, że musisz ręcznie przesyłać i nadpisywać (jestem pewien, że można to zautomatyzować, ale dla nas nie jest to tego warte, przesyłanie kilka plików js raz na jakiś czas nie jest zbyt wielkim problemem).
serg

Użyłem twojego kodu, ale tworzy on zminimalizowany plik w katalogu głównym mojego projektu, ustawiłem, <fileset dir="${generatedScriptsDir}" includes="**/*.js"/>ale to nie działa. Jak mogę zrobić, aby wygenerować plik w ${generatedScriptsDir}?
Vadorequest

spróbuj dodać atrybut „dir” do tagu „Apply”. upewnij się, że „$ {generatedScriptsDir}” zostało utworzone jako „właściwość” z zaprojektowanym miejscem docelowym
Rajasri.J

12

Myślę, że jednym z najlepszych i odpowiednich narzędzi do tego zadania jest wro4j Sprawdź https://github.com/wro4j/wro4j

Robi wszystko, czego potrzebujesz:

  • Dbaj o dobrą organizację zasobów internetowych projektu (js i css)
  • Scalaj i zmniejszaj je w czasie wykonywania (za pomocą prostego filtra) lub w czasie kompilacji (za pomocą wtyczki maven)
  • Wolne i otwarte: Wydane na licencji Apache 2.0
  • kilka narzędzi do minifikacji obsługiwanych przez wro4j: JsMin, kompresor Google Closure, YUI itp
  • Bardzo łatwy w użyciu. Obsługuje filtr serwletów, zwykłą konfigurację Java lub Spring
  • Obsługa Javascript i CSS Meta Frameworks: CoffeeScript, Less, Sass itp
  • Walidacja: JSLint, CSSLint itp

Może działać zarówno w trybie debugowania, jak i produkcyjnym. Po prostu określ wszystkie pliki, które ma obsługiwać / wstępnie przetwarzać, a on zajmie się resztą.

Możesz po prostu dołączyć scalone, zminimalizowane i skompresowane zasoby w następujący sposób:

<script type="text/javascript" src="wro/all.js"></script>

2
Wydaje się naprawdę sprytne narzędzie. Dzięki za aktualizację!
gustafc

Czy dodaje wersjonowanie do plików zasobów, aby wymusić odświeżanie po stronie klienta? Nie mogłem znaleźć żadnej dokumentacji na temat tej funkcji.
Qiang,

Jedyne, czego naprawdę brakuje mi w wro4j, to prefiks css.
inafalcao

Czy można obsługiwać zawartość statyczną (generowaną przez wroserwer aplikacji) z apacheserwera WWW?
HybrisHelp

8

Napisałem makra Ant dla kompilatora Google Closure i kompresora Yahoo i dołączam ten plik do różnych projektów internetowych.

<?xml version="1.0" encoding="UTF-8"?>
<!-- CSS and JS minifier. -->
<!DOCTYPE project>
<project name="minifier" basedir=".">

  <property name="gc" value="compiler-r1592.jar" />
  <property name="yc" value="yuicompressor-2.4.6.jar" />

  <!-- Compress single js with Google Closure compiler -->
  <macrodef name="gc-js">
    <attribute name="dir" />
    <attribute name="src" />
    <sequential>
      <java jar="${gc}" fork="true">
        <!--
        - - compilation_level WHITESPACE_ONLY | SIMPLE_OPTIMIZATIONS | ADVANCED_OPTIMIZATIONS
        Specifies the compilation level to use. Default: SIMPLE_OPTIMIZATIONS
        - - warning_level QUIET | DEFAULT | VERBOSE
        Specifies the warning level to use.
        -->
        <arg line="--js=@{dir}/@{src}.js" />
        <arg line="--js_output_file=@{dir}/@{src}-min-gc.js" />
      </java>
    </sequential>
  </macrodef>

  <!-- Compress single js with Yahoo compressor -->
  <macrodef name="yc-js">
    <attribute name="dir" />
    <attribute name="src" />
    <sequential>
      <java jar="${yc}" fork="true">
        <arg value="@{dir}/@{src}.js" />
        <arg line="-o" />
        <arg value="@{dir}/@{src}-min-yc.js" />
      </java>
    </sequential>
  </macrodef>

  <!-- Compress all js in directory with Yahoo compressor -->
  <macrodef name="yc-js-all">
    <attribute name="dir" />
    <sequential>
      <apply executable="java" parallel="false">
        <fileset dir="@{dir}" includes="*.js" excludes="*-min*.js" />
        <arg line="-jar" />
        <arg path="${yc}" />
        <srcfile />
        <arg line="-o" />
        <mapper type="glob" from="*.js" to="@{dir}/*-min-yc.js" />
        <targetfile />
      </apply>
    </sequential>
  </macrodef>

  <!-- Compress all css in directory with Yahoo compressor -->
  <macrodef name="yc-css-all">
    <attribute name="dir" default="${build.css.dir}" />
    <sequential>
      <apply executable="java" parallel="false">
        <fileset dir="@{dir}" includes="*.css" excludes="*-min*.css" />
        <arg line="-jar" />
        <arg path="${yc}" />
        <arg line="-v --line-break 0" />
        <srcfile />
        <arg line="-o" />
        <mapper type="glob" from="*.css" to="@{dir}/*-min.css" />
        <targetfile />
      </apply>
    </sequential>
  </macrodef>
</project>
  • Integracja: <import file="build-minifier.xml" />w pliku build.xml, a następnie jak zwykle wywołaj zadania Ant:<gc-js dir="${build.js.dir}" src="prototype" /> <yc-js-all dir="${build.js.dir}" />

  • Do wyboru dwa minifier: kompilator Google Closure i kompresor Yahoo, należy je pobrać ręcznie i umieścić w pobliżu pliku xml

  • Minifier pomijają już skompresowane pliki (kończące się na -min*)

  • Zwykle tworzę trzy wersje skryptu: nieskompresowaną (np. prototype.js) Do debugowania, skompresowaną za pomocą kompilatora zamknięcia ( prototype-min-gc.js) dla serwera produkcyjnego, skompresowaną za pomocą Yahoo ( prototype-min-yc.js) do rozwiązywania problemów, ponieważ kompilator zamknięcia używa ryzykownych optymalizacji i czasami tworzy nieprawidłowy skompresowany plik, a kompresor Yahoo jest bezpieczniejszy

  • Kompresor Yahoo może zminimalizować wszystkie pliki w katalogu za pomocą jednego makra, kompilator Closure nie może


8

Próbowałem na dwa sposoby:

  1. używając filtru serwletu. W trybie produkcyjnym filtr jest aktywowany i kompresuje wszelkie dane powiązane z adresem URL, takie jak * .css lub * .js
  2. używając maven i yuicompressor-maven-plugin ; kompresja jest wykonywana non-tantum (podczas montażu wojny produkcyjnej )

Oczywiście to drugie rozwiązanie jest lepsze, ponieważ nie zużywa zasobów w czasie wykonywania (moja aplikacja internetowa korzysta z silnika aplikacji Google) i nie komplikuje kodu aplikacji. Więc przyjmijmy ten drugi przypadek w następujących odpowiedziach:

Jak to się integruje? Czy jest to część twojego narzędzia do budowania, filtr serwletów, samodzielny program przetwarzający dalej plik WAR, czy coś innego?

za pomocą maven

Czy łatwo jest włączać i wyłączać? Próba debugowania zminimalizowanego skryptu jest bardzo niezręczna, ale programista może również sprawdzić, czy minifikacja niczego nie psuje.

aktywujesz go tylko podczas kończenia wojny; w trybie programowania widzisz nieskompresowaną wersję swoich zasobów

Czy działa przejrzyście, czy też ma jakieś skutki uboczne (poza tymi nieodłącznymi od minifikacji), które muszę brać pod uwagę w codziennej pracy?

absolutnie

Którego minifier używa?

Sprężarka YUI

Czy brakuje mu jakichś funkcji, o których możesz pomyśleć?

nie, jest bardzo kompletny i łatwy w użyciu

Co w tym lubisz?

jest zintegrowany z moim ulubionym narzędziem (maven), a wtyczka znajduje się w centralnym repozytorium (dobry obywatel-maven)


Wtyczka Maven - fajnie. Szkoda, że ​​wszystkie moje obecne projekty używają ant :)
gustafc

możesz stworzyć cel, który zbuduje "plik wojenny produkcji", używając zadania YUI Ant
dfa

4

Myślę, że potrzebujesz biblioteki kompresji, na przykład znacznika Granule.

http://code.google.com/p/granule/

Wykonuje gzip i łączy javascripts opakowane tagiem g: compress przy użyciu różnych metod, ma również zadanie Ant

przykładowy kod to:

<g: compress>
  <script type = "text / javascript" src = "common.js" />
  <script type = "text / javascript" src = "closure / goog / base.js" />
  <script>
       goog.require ('goog.dom');
       goog.require ('goog.date');
       goog.require ('goog.ui.DatePicker');
  </script>
  <script type = "text / javascript">
      var dp = new goog.ui.DatePicker ();
      dp.render (document.getElementById ('datepicker'));
  </script>
</ g: compress>
...


Tak, wygląda całkiem fajnie.
gustafc

3

Jestem naprawdę zaskoczony, że nikt nie wspomniał o JAWR - https://jawr.github.io

Jest dość dojrzały i obsługuje wszystkie standardowe funkcje, których można się spodziewać, i trochę więcej. Oto, jak to się ma do doskonałych kryteriów PO.

Jak to się integruje? Czy jest to część twojego narzędzia do budowania, filtr serwletów, samodzielny program przetwarzający dalej plik WAR, czy coś innego?

Początkowo przetwarzanie / podnoszenie ciężarów podczas uruchamiania aplikacji było oparte na serwlecie . Począwszy od 3.x dodali obsługę integracji w czasie kompilacji .

Obsługa JSP i Facelets jest zapewniana przez niestandardową bibliotekę znaczników JSP do importowania przetworzonych zasobów. Oprócz tego zaimplementowano moduł ładujący zasoby JS, który obsługuje ładowanie zasobów ze statycznych stron HTML .

Czy łatwo jest włączać i wyłączać? Próba debugowania zminimalizowanego skryptu jest bardzo niezręczna, ale programista może również sprawdzić, czy minifikacja niczego nie psuje.

debug=onOpcja jest dostępna do wykorzystania przed uruchomieniem aplikacji, a zwyczaj GETparametr może być określony w poszczególnych żądań w produkcji do przełączania trybu debugowania selektywnie w czasie wykonywania do wspomnianego wniosku.

Którego minifier używa?

W przypadku JS obsługuje kompresor YUI i JSMin, w przypadku CSS nie jestem pewien.

Czy brakuje mu jakichś funkcji, o których możesz pomyśleć?

SASSprzychodzi na myśl wsparcie. To powiedziawszy, obsługuje LESS.


2

Nasz projekt poradził sobie z tym na wiele sposobów, ale nadal używaliśmy kompresora YUI przez różne iteracje.

Początkowo mieliśmy serwlet obsługujący kompresję dla JavaScript przy pierwszym dostępie do tego konkretnego pliku; był następnie buforowany. Mieliśmy już system do obsługi plików niestandardowych właściwości, więc po prostu zaktualizowaliśmy nasze pliki konfiguracyjne, aby obsługiwały włączanie lub wyłączanie kompresora w zależności od środowiska, w którym pracowaliśmy.

Obecnie środowiska programistyczne nigdy nie używają skompresowanego kodu JavaScript do celów debugowania. Zamiast tego zajmujemy się kompresją w naszym procesie kompilacji podczas eksportowania naszej aplikacji do pliku WAR.

Nasz klient nigdy nie zgłaszał obaw dotyczących kompresji, a programiści nie zauważają tego, dopóki nie zdecydują się debugować JavaScript. Więc powiedziałbym, że jest raczej przezroczysty z minimalnymi, jeśli w ogóle, efektami bocznymi.


Jak używasz kompresora YUI w procesie tworzenia? Wtyczka Maven czy coś innego?
gustafc

1
Przepraszamy, obecnie używamy Ant. Oto przydatny link do zadania Ant: blog.gomilko.com/2007/11/29/yui-compression-tool-as-ant-task
doomspork

1

To zadziałało dla mnie: https://bitbucket.org/m6_russell_francis/yui-compressor-ant-task/wiki/Home

<!-- minimize all static *.css & *.js content -->
<target name="static-content-minify">

    <taskdef name="yuicompressor"
             classname="com.metrosix.yuicompressor.anttask.YuiCompressorTask">
        <classpath>
            <pathelement location="${jar.yui.compressor}"/>
            <pathelement location="${jar.yui.anttask.compressor}" />
        </classpath>
    </taskdef>

    <yuicompressor todir="${build.static.content.min}" charset="utf-8" 
        preserveallsemicolons="true" munge="true" >
        <fileset dir="${src.static.content}">
            <include name="**/*.css"/>
            <include name="**/*.js"/>
        </fileset>
    </yuicompressor>
</target>

Otrzymałem $ {jar.yui.compressor} z search.maven.org: search.maven.org/…
Reverse Tarzan

1

Piszę framework do zarządzania zasobami internetowymi, zwany humpty . Ma być prostszy i nowocześniejszy niż jawr czy wro4j przy użyciu WebJars i ServiceLoaders.

Jak to się integruje? Czy jest to część twojego narzędzia do budowania, filtr serwletów, samodzielny program przetwarzający dalej plik WAR, czy coś innego?

W trakcie programowania serwlet przetwarza zasoby w razie potrzeby. Zasoby byłyby następnie wstępnie kompilowane przed rozpoczęciem produkcji i umieszczane w folderze publicznym, tak aby jedyną używaną częścią było generowanie poprawnych dołączeń w HTML.

Czy łatwo jest włączać i wyłączać? Próba debugowania zminimalizowanego skryptu jest bardzo niezręczna, ale programista może również sprawdzić, czy minifikacja niczego nie psuje.

Można to zrobić, przełączając się między trybami rozwoju i produkcji.

Czy działa przejrzyście, czy też ma jakieś skutki uboczne (poza tymi nieodłącznymi od minifikacji), które muszę brać pod uwagę w codziennej pracy?

Uważam, że jest przejrzysty, ale zdecydowanie sprzyja korzystaniu z WebJars.

Którego minifier używa?

Niezależnie od tego, którego używa wtyczka, którą umieścisz w ścieżce klas. Obecnie rozważam napisanie wtyczki do Google Closure Compiler.

Czy brakuje mu jakichś funkcji, o których możesz pomyśleć?

Wciąż przedpremierowy, chociaż używam go w produkcji. Wtyczka maven nadal wymaga dużo pracy.

Co w tym lubisz?

Prostota dodania zależności w celu skonfigurowania struktury

Co ci się w nim nie podoba?

To moje kochanie, kocham to wszystko;)


1

Naprawdę spóźniłem się na imprezę tutaj, ale pomyślałem, że może to pomóc komuś, kto wciąż szuka innej odpowiedzi:

Po próbie użycia YUI Compressor byłem rozczarowany, że był on niekompatybilny z nowszymi wersjami jQuery i Prism (dwie główne biblioteki JS innych firm, których potrzebowałem do mojego projektu, które chciałem skompresować do jednego pliku). Zdecydowałem się więc na Terser , który jest rozwidleniem Uglify-JS obsługującym ES6 +. Nie byłem w stanie uruchomić go bezpośrednio przy użyciu <exec>zadania, ale użycie metody wiersza poleceń systemu Windows działa przynajmniej dla Win 10 (nie mówiąc, że nie może działać inaczej, ale było to bardzo łatwe obejście). Nie ma potrzeby dodawania niczego więcej do zmiennej systemowej Path (ponieważ Node.JS jest zwykle dodawany podczas instalacji). Najpierw używam <concat>zadania ANT, aby utworzyć duży, nieskompresowany plik. Użyj, <fileset>ponieważ zachowa porządek (jeśli to ważne, w każdym razie).

<concat destfile="${js-big-file}" encoding="UTF-8" outputencoding="UTF-8" fixlastline="true">
   <filelist refid="js-input-filelist"/>
</concat>

Następnie użyj tego <exec>zadania, aby uruchomić dowolny program NPM, taki jak Terser. Strona podręcznika Apache dotycząca tego zadania wskazała, że ​​jest to obejście systemu Windows do uruchamiania plików .bat, ale tak naprawdę pozwala na uruchomienie prawie każdej aplikacji wiersza poleceń (nawet tych, których w <exec>tajemniczy sposób nie można znaleźć inaczej).

<exec executable="cmd">
   <arg value="/c"/>
   <arg value="terser"/>
   <arg value="${js-big-file}" />
   <arg value="-o" />
   <arg value="${smaller-js-file}"/>  
</exec>

Zintegrować? Jest częścią skryptu budowania ANT (wtyczka DITA Open Toolkit obsługująca między innymi niestandardowy JavaScript - nie jest to aplikacja internetowa Java jako taka, ale używanie Javy do tworzenia wyjścia HTML5), więc integracja nie była czymś więcej niż dodaniem tych zadania do nowego celu (jest więcej kodu dotyczącego ustawiania wartości domyślnych i sprawdzania parametrów wejściowych!).

Łatwe do włączenia / wyłączenia? W moim przypadku mam parametr, który przekazuję do kompilacji ANT, aby uwzględnić kompilację i minifikację pliku JS. Więc tak, osiąga ten cel tylko wtedy, gdy ustawię parametr na „Tak”. To dość łatwa rzecz do skonfigurowania w kompilacji ANT.

Przezroczysty Jak dotąd wydaje się, że nie ma to wpływu na żaden z kilku plików JS, które dołączam. Niektóre z nich są moimi własnymi (i w żadnym wypadku nie jestem ekspertem od JS), a niektóre, jak wspomniałem, są popularnymi bibliotekami JS.

Minifier Terser, ale przy użyciu tej metody możesz użyć prawie dowolnego zminimalizowania z danymi wejściowymi wiersza poleceń.

Brakuje funkcji? Terser działa tylko z JavaScriptem. Jeśli chcę zrobić to samo z moimi plikami CSS (co robię), używam YUI Compressor.

Jak to jest obecnie aktywny projekt i ma dobre wsparcie. Ponadto obecna implementacja (wywołanie jej tylko przez <exec>cel ANT ) pozwala mi na wymianę minifier, jeśli będę musiał użyć czegoś innego w przyszłości.

Nie podoba mi się, że wymaga Node.JS. Nie ma nic przeciwko Node.JS, pamiętaj tylko, że ten konkretny projekt nie potrzebuje go inaczej. Wolałbym użyć do tego pliku Java .jar, takiego jak YUI Compressor (w razie potrzeby mogę go łatwo rozpowszechnić za pomocą wtyczki).


Spóźnialscy też są mile widziani! Zgadzam się, że jest to kłopotliwe, gdy projekt zależy od dwóch różnych środowisk programistycznych (Java + Node). Mimo to nie jest zaskakujące, że większość prac w Javascript odbywa się w społeczności Node, więc nie ma zbyt wiele do zrobienia, a Terser wydaje się obecnie mieć duży rozmach. Dziękuję za Twój wkład!
gustafc
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.