2016-07-01 25 views
6

Aktualnie rozpakowuję moją aplikację Angular 2 za pomocą pakietu WebPack. Ciągle kręci szybkich cykli, więc zamiast dodawać opóźnień do naszego procesu ładowania budować i aplikacji, chcemy obejmować rzadko zmieniających kątowa 2 UMD CDN przygotowane zestawy, np:Aplikacja Bundle Angular 2 przy użyciu pakietów UMD (nie buduje pakietu dostawców)

<script src="https://npmcdn.com/@angular/[email protected]/bundles/core.umd.min.js"></script> 
<script src="https://npmcdn.com/@angular/[email protected]/bundles/common.umd.min.js"></script> 
<script src="https://npmcdn.com/@angular/[email protected]/bundles/compiler.umd.min.js"></script> 
  • Kiedy tylko pozwolić WebPack robi to, pakiet działa dobrze, ale ma kilka MB, ponieważ nie wykorzystuje wstępnie przygotowanych pakietów ani nie rozdziela kodu Angular 2 "dostawcy".
  • Kiedy używam Angular 2 WebPack Recommendations, np .: plugins: [ new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.bundle.js") ], mój pakiet aplikacji jest mały, ale ręcznie buduję osobny, unikalny pakiet 1MB zawierający większość struktury Angular 2 w każdej kompilacji. Ten plik zmienia nieznacznie każdą kompilację w zależności od mojej aplikacji i nie jest przenośny między wersjami moich aplikacji lub różnych aplikacji i nie ma zalet "CDN". Oczywiście muszę dołączyć ten plik do uruchomienia mojej aplikacji.
  • Gdy używam IgnorePlugin do filtrowania @angular|rxjs, np. plugins: [ new webpack.IgnorePlugin(/\@angular|rxjs/) ], wyklucza pliki dostawców, ale wstawia zakodowane na sztywno wyjątki/zgłasza błędy na górze mojego pakietu aplikacji.
  • Kiedy używam WebPack externals, np. externals: ['@angular/core', ..., Otrzymuję dane wyjściowe function(module, exports) { module.exports = @angular/core; }, w pakiecie aplikacji, co oczywiście nie działa. Dokumentacja WebPack nie jest strasznie nieprzewidywalna, ale myślę, że być może będę w stanie podać albo libraryTarget lub cytowaną funkcję rozstrzygania, która zaleciłaby WebPackowi kompilację podczas ładowania modułu.
  • Gdy całkowicie zrezygnuję z pakietu WebPack i używam pakowacza kompilatorów języka TypeScript (zgodnie z this guide, który używa pakietów UMD), otrzymuję wywołania System.register() odnoszące się do przestrzeni nazw NPM, których się spodziewałem, np. System.register("myapp/boot", ['@angular/core', ..., ale nadal pracuję nad konfiguracją SystemJS, aby wywołać UMD. Na marginesie ten plik ma dodatkowe 25% rozmiaru w porównaniu do tego, co generuje WebPack.
  • Jeśli używam SystemJS jak w poprzednim elemencie, chcę, aby kompilacja ta występowała podczas kompilacji lub jako proces równoległy, a nie jako część składowania plików. Chyba SystemJS-Builder (zobacz powiązane pytania here i here) byłby sposób to zrobić? Być może spowoduje to również zmniejszenie rozmiarów plików zintegrowanych ze słowem kluczowym "bundler".

Jak mogę zbudować pakiet aplikacji, który nie jest zależny od unikalnie przepakowanego pakietu Angular 2?

Obecnie buduję na bazie RC3. Mój proces jest obecnie WebPack, jak wspomniano powyżej, ale chciałbym przejść do innego zestawu narzędzi, jeśli to ułatwia.

Przeprowadzając więcej badań, myślę, że wprowadził mnie w błąd termin "ładowarka" WebPack. Muszę użyć modułu ładującego i nie wygląda na to, że WebPack ma taki, który będzie na to pracował.

Aby przypisać przestrzenie nazw modułów pakunkowych UMD (i powiązać zależności), nie można ich załadować w znacznikach skryptu. Zamiast tego muszą one zostać poddane ewaluacji z danym kontekstem this, aby działać jako odniesienie do modułu. Oznacza to, że nawet jeśli chcę, aby wszystkie były ładowane synchronicznie, nadal muszę skonfigurować coś innego, jak SystemJS, aby załadować je przez przewód, więc ich kontekst jest kontrolowany/pakowany.

Ten produkt jest w pobliżu tego, czego szukam. Korzysta z pakietów Angular 2 UMD, ale nie używa pakietu RxJs, chociaż wygląda na to, że łatwo go zmienić, jeśli chcę mieć całą bibliotekę RxJs.

Odpowiedz

2

Więcej niż jedno z podejść w moim pytaniu zadziała. Niektóre nie, niektóre nie obecnie z powodu wad Kątowymi 2. Oto podejście obecnie używam:


WebPACK + require.js

angular2-WebPACK-config.js

var config = { 
    entry: { 
     app: inputFile 
    }, 
    externals: [ 
     /^@angular\//, 
     /^rxjs\// 
    ], 
    output: { 
     libraryTarget: "amd", 
     path: __dirname, 
     filename: './' + outputName 
    }, 
    plugins: [ 
     new require('webpack').optimize.UglifyJsPlugin() 
    ] 
}; 

powiedzieć tylko to, co jest i co externals pseudo-standardowy mechanizm będzie załadować je w czasie wykonywania przez libraryTarget (AMD/RequireJS, Komisja onJs/node, UMD). Moje ustawienie powoduje, że odwołania do zewnętrznej biblioteki są zawijane w define().

Należy zauważyć, że nie robię nic ze ścieżkami w pakiecie WebPack. W moim oprogramowaniu wszystko w folderze node_modules ma podobny mechanizm referencyjny zarówno w moim oprogramowaniu, jak iw modułach stron trzecich, wewnętrznie. Zarówno mój kod, jak i biblioteki innych firm spodziewają się znaleźć RxJS pod numerem rxjs (np. Zamiast ../rxjs lub "node_modules/rxjs"). W czasie wykonywania oba muszą być odwzorowane, ale ponieważ nie pozwalamy aplikacji WebPack sięgać do modułów stron trzecich (używamy gotowych UMD), WebPack nie jest miejscem, w którym można wykonać to mapowanie. Mapowałoby to tylko mój kod. Zamiast tego powinniśmy to zrobić w naszym programie ładującym środowisko wykonawcze:

indeks.htm

<script src="https://npmcdn.com/core-js/client/shim.min.js"></script> 
<script src="https://npmcdn.com/[email protected]/dist/zone.min.js"></script> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/reflect-metadata/0.1.3/Reflect.min.js"></script> 
<script> 
    var require = (function(){ 
     var versions = { 
      'router-deprecated': '@@2.0.0-rc.2', 
      'forms': '@@0.1.1', 
      'angular': '@@2.0.0-rc.4', 
      'rxjs': '@@5.0.0-beta.10' 
     } 

     var paths = { 
      'rxjs': "https://npmcdn.com/rxjs" + versions.rxjs + "/bundles/Rx.umd.min" 
     }; 
     [ 
      'core', 
      'http', 
      'common', 
      'compiler', 
      'platform-browser', 
      'router-deprecated', 
      'platform-browser-dynamic' 
     ].forEach(function (submodule) { 
      var module = '@@angular/' + submodule 
      paths[module] = 'https://npmcdn.com/' + module + (versions[submodule] || versions.angular) + '/bundles/' + submodule + '.umd.min'; 
     }); 

     var rxmap = {}; 
     [ 
      'Rx', 
      'Observable', 
      'Subject', 
      'observable/PromiseObservable', 
      'operator/toPromise' 
     ].forEach(function (submodule) { 
      rxmap['rxjs/' + submodule] = 'rxjs'; 
     }) 

     return { 
      paths: paths, 
      map: { 
       '*': rxmap 
      } 
     }; 
    })(); 
</script> 
<script data-main="../assets/compiled/a2.webpack.js" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js"></script> 

Ponadto, ponieważ jeśli używasz WebPACK i UMDs pewnie obchodzi wynikające rozmiary i czasy plików. Proces tworzenia Angula 2 w tej aplikacji przebiegał od około 24 sekund do 1 sekundy. Jego publikacja zmieniła rozmiar z ponad 2 MB na około 100 tysięcy.

Oto rozmiary obciążenia drutu dla buforowanych zależności dla odniesienia. O dziwo, są one obecnie nieco mniejsze od KB mniejszej niż w wersji UMD niż wzrost rozmiaru kabla w zintegrowanym, przycinanym pakiecie WebPack.

KB 
27.5 shim 
6.8 zone 
8.0 require 
3.3 platform-browser-dynamic 
36.8 http 
8.7 core 
20.8 common 
16.5 router 
98.5 compiler 
27.9 platform-browser 
39.0 Rx 

Oczywiście moja strona publiczna czas ładowania po aktualizacji są drastycznie zmniejszona (do 1 sekundy od około 10-20 sekund), ale te numery są dość zmienne oparte na połączeniu.

1

Próbowałem osiągnąć ten sam wynik, ale bez powodzenia.

W pierwszej próbie udało mi się mieć WebPACK ładowanego wstępnie zbudowane UMD wiązek za pomocą resolve konfiguracje, tak jak w poniższym przykładzie:

var webpack = require('webpack'); 

module.exports = { 
    entry: './src/main', 
    output: { 
    filename: 'bundle.js', 
    path: 'bundles/app', 
    }, 

    resolve: { 
    extensions: ['', '.ts', '.js'], 
    alias: { 
     '@angular/common' : '@angular/common/common.umd.js', 
     '@angular/compiler' : '@angular/compiler/compiler.umd.js', 
     '@angular/core' : '@angular/core/core.umd.js', 
     '@angular/http' : '@angular/http/http.umd.js', 
     '@angular/platform-browser' : '@angular/platform-browser/platform-browser.umd.js', 
     '@angular/platform-browser-dynamic' : '@angular/platform-browser-dynamic/platform-browser-dynamic.umd.js', 
     '@angular/router' : '@angular/router/router.umd.js', 
     '@angular/router-deprecated' : '@angular/router-deprecated/router-deprecated.umd.js', 
     '@angular/upgrade' : '@angular/upgrade/upgrade.umd.js', 
     'rxjs/Observable' : 'rxjs/bundles/Rx.umd.min.js', 
     'rxjs/add/operator/map' : 'rxjs/bundles/Rx.umd.min.js', 
     'rxjs/add/observable/fromEvent' : 'rxjs/bundles/Rx.umd.min.js', 
     rxjs : 'rxjs/bundles/Rx.umd.min.js' 
    } 
    }, 

    module: { 
    loaders: [ 
     {test: /\.ts$/, exclude: /node_modules/, loader: 'ts'}, 
    ], 
    noParse: [/@angular/, /rxjs/] 
    }, 

}; 

Kluczowe konfiguracje Oto noParse i resolve/alias. Mówię webpacowi, aby załadował gotowe pakiety UMD, gdy tylko znajdzie wymagania, takie jak @angular/common i inne.

Ta konfiguracja powoduje, że pakiet sieciowy zawiera pliki UMD zamiast przetwarzania każdego pliku rastru i rxjs. Moim celem było poprawienie czasu budowy. Jednak w moim przypadku moja aplikacja nie powiodła się w czasie wykonywania. Domyślam się, że korzystałem z biblioteki, która wymaga plików kątowych na swoich folderach src i sprawiło, że się nie udała. Dałbym tej konfiguracji spróbować sprawdzić, czy działa na twoim scenariuszu.

Podczas drugiej próby wypróbowałem funkcję o nazwie Webpack DLL zgodnie z opisem podanym pod numerem http://engineering.invisionapp.com/post/optimizing-webpack/. Stworzyła pakiet, który może być ponownie użyty w różnych kompilacjach, dzięki czemu unika się ponownego łączenia kątowego 2 za każdym razem. Niestety, w tym scenariuszu moja aplikacja kończy się niepowodzeniem z wyjątkami dotyczącymi wtyczki zależności.

I udało się załadować UMD wiązek za pomocą SystemJS zamiast Webpack o następującej konfiguracji

(function(global) { 

    var paths = { 
    'jquery' : '../node_modules/jquery/dist/jquery.js', 
    'bootstrap' : '../node_modules/bootstrap/dist/js/bootstrap.js', 
    //'rxjs/*' : '../node_modules/rxjs/bundles/Rx.umd.min.js', //FAILS with router 3.0 
    } 

    var map = { 
    'app':      'app', // 'dist', 
    'angular2-in-memory-web-api': '../node_modules/angular2-in-memory-web-api', 
    '@angular':     '../node_modules/@angular', 
    'lodash':      '../node_modules/lodash', 
    'rxjs':      '../node_modules/rxjs'  
    }; 

    var packages = { 
    'app':      { main: 'main.js', defaultExtension: 'js' }, 
    'angular2-in-memory-web-api': { defaultExtension: 'js' }, 
    'lodash':      { main: 'lodash.js', defaultExtension: 'js' }, 
    'rxjs':      { defaultExtension: 'js' }, 
    }; 

    var ngPackageNames = [ 
    'common', 
    'compiler', 
    'core', 
    'http', 
    'platform-browser', 
    'platform-browser-dynamic', 
    'router', 
    'router-deprecated', 
    'upgrade', 
    ]; 
    ngPackageNames.forEach(function(pkgName) { 
    packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' }; 
    }); 

    var config = { 
    map: map, 
    packages: packages, 
    paths : paths, 
    }; 

    // filterSystemConfig - index.html's chance to modify config before we register it. 
    if (global.filterSystemConfig) { global.filterSystemConfig(config); } 

    System.config(config); 

})(this); 

Jest to dość mych konfiguracja proponowany przez oficjalnych docs Angular2 z dodatkiem path sekcji. Kiedyś był w stanie załadować RxJS jako pojedynczy pakiet UMD, ale przy niedawnym wprowadzeniu routera 3.0 konfiguracja ta zakończyła się niepowodzeniem z wyjątkami dotyczącymi kodu routera. Musiałem ponownie zapisać i załadować RxJS jako indywidualne żądania.

+0

Tak, mam to teraz działa z pakietami SystemJS, chociaż nadal używam 'router-przestarzałe', więc nie ma jeszcze komentarzy na ten temat, używając SystemJS-Builder. Czy tak to robisz? Wciąż jednak musisz zużywać pakiety UMD, kod SystemJS, który pokazałeś, tylko je konfiguruje. Możesz również zmapować je na globale i ręcznie odwzorować na globale w konfiguracji "Externals", która jest podobna do tej, którą aktualnie próbuję (używając require.js tylko dlatego, że jest mniejsza). – shannon

+0

Chcesz porozmawiać na ten temat? – shannon

+0

Pamiętaj, że regularnie aktualizuję moje odkrycia w powyższym pytaniu. Oczywiście udzielę odpowiedzi, jeśli znajdę odpowiednie rozwiązanie przed opublikowaniem. – shannon