使用UMD捆绑包捆绑Angular 2应用程序(不构建供应商捆绑包)

时间:2016-07-01 21:06:11

标签: angular webpack bundle systemjs

我目前正在将我的Angular 2应用与WebPack捆绑在一起。我们仍然在进行快速循环,因此我们希望包含很少变化的Angular 2 UMD CDN准备捆绑包,而不是为我们的构建和应用程序加载过程添加延迟,例如:

<script src="https://npmcdn.com/@angular/core@2.0.0-rc.3/bundles/core.umd.min.js"></script>
<script src="https://npmcdn.com/@angular/common@2.0.0-rc.3/bundles/common.umd.min.js"></script>
<script src="https://npmcdn.com/@angular/compiler@2.0.0-rc.3/bundles/compiler.umd.min.js"></script>
  • 当我让WebPack做它的事情时,程序包运行正常但只有几MB,因为它没有利用预先构建的bundle或分离出Angular 2&#34; vendor&#34;代码。
  • 当我使用Angular 2 WebPack Recommendations时,例如:plugins: [ new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.bundle.js") ],我的应用包很小,但我手动构建了一个独立的1MB包,其中包含大部分Angular 2框架,每个版本。这个文件会根据我的应用程序稍微改变每个版本,并且在我的应用程序版本或各种应用程序之间不可移植,并且没有&#34; CDN&#34;的好处。当然,我必须包含此文件才能运行我的应用程序。
  • 当我使用IgnorePlugin过滤@angular|rxjs时,例如plugins: [ new webpack.IgnorePlugin(/\@angular|rxjs/) ],它排除了供应商文件,但在应用程序包的顶部插入了硬编码的异常/抛出错误。
  • 当我使用WebPack externals时,例如externals: ['@angular/core', ...,我的应用包中输出function(module, exports) { module.exports = @angular/core; },,这显然不起作用。 WebPack文档并非即将发布,但我想我可以指定一个libraryTarget或引用的resolve函数,它将指示WebPack在模块加载时进行编译。
  • 当我完全抛弃WebPack并使用TypeScript编译器捆绑器(根据this guide使用UMD捆绑包)时,我得到System.register()个调用,指的是我期望的NPM命名空间,例如: System.register("myapp/boot", ['@angular/core', ...,但我仍在使用SystemJS配置来调用UMD。另外,相对于WebPack正在生成的文件,此文件的大小为25%。
  • 如果我在前面的项目中使用SystemJS,我希望这个编译在构建期间或作为并行过程发生,而不是作为文件保存的一部分。我想SystemJS-Builder(参见相关问题herehere)会是这样做的吗?也许这也会产生较小的文件大小,这些文件大小与Typescript集成的&#34; bundler&#34;。
  • 相同

如何构建一个不依赖于唯一重新打包的Angular 2捆绑包的应用程序包?

我目前正在对抗RC3。我的进程目前是WebPack,如上所述,但如果能让它变得更容易,我会转移到另一个工具集。

做一些更多的研究,我想我已被WebPack&#34; Loader&#34;术语。我必须使用模块加载器,并且它看起来不像WebPack有一个适用于此的。

要分配UMD捆绑模块名称空间(以及连接依赖项),它们无法加载到脚本标记中。相反,它们必须使用给定的this上下文进行评估,以充当模块引用。这意味着即使我希望它们全部同步加载,我仍然需要配置其他类似SystemJS的东西来加载它们,因此它们的上下文被控制/包装。

Angular 2 plunker接近我正在寻找的东西。它使用Angular 2 UMD捆绑包,但不使用RxJs捆绑包,尽管如果我想要整个RxJs库,这看起来很容易改变。

2 个答案:

答案 0 :(得分:2)

我的问题中不止一种方法可行。有些人不会,有些人目前不会因Angular 2的缺陷而出现。这是我目前正在使用的方法:

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()
    ]
};

我只告诉它externals是什么,以及伪标准机制将在运行时通过libraryTarget(AMD / RequireJS,CommonJs / node,UMD)加载它们。我的设置只会导致外部库引用包含在define()

请注意,我不会对WebPack中的路径执行任何操作。在我的软件中,node_modules文件夹中的任何内容在我的软件和第三方模块中都有类似的参考机制。我的代码和第三方库都期望在rxjs找到RxJS(例如,而不是../rxjs或&#39; node_modules / rxjs`)。在运行时,两者都需要映射,但由于我们不允许WebPack进入第三方模块(我们使用的是预先构建的UMD&#39;),因此WebPack不是这个地方做那个映射。它只会映射我的代码。相反,我们应该在运行时加载器中执行此操作:

的index.htm

<script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
<script src="https://npmcdn.com/zone.js@0.6.12/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>

此外,因为如果您使用的是WebPack和UMD,您可能会关心生成的文件大小和时间。这个子应用程序的Angular 2构建过程从大约24秒到1秒。它的发布更改大小从超过2MB到大约10万。

以下是缓存的依赖项的有线加载大小以供参考。奇怪的是,它们在UMD版本中目前只有几个KB 较小,而不是集成的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

显然,更新后我的公共网站加载时间大幅减少(从大约10-20秒减少到1秒),但这些数字根据连接变化很大。

答案 1 :(得分:1)

到目前为止,我一直试图取得相同的结果而没有成功。

作为第一次尝试,我设法让webpack使用resolve配置加载预构建的UMD包,如下例所示:

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/]
  },

};

此处的主要配置为noParseresolve/alias。我告诉webpack在发现需要时加载UMD预构建的包,例如@angular/common等等。

此配置确实使webpack包含UMD文件,而不是处理每个angular和rxjs文件。我的目标是改善构建时间。但是,就我而言,我的应用程序在运行时失败了。我想我正在使用一个需要在其src文件夹上存在角度文件的库,这使它失败了。我尝试使用此设置来检查它是否适用于您的方案。

作为第二次尝试,我尝试了http://engineering.invisionapp.com/post/optimizing-webpack/所述的名为Webpack DLL的功能。它创建了一个可以跨构建重用的bundle,从而避免每次重新绑定angular2。不幸的是,我的应用程序在此方案中失败了依赖注入异常。

我确实设法使用SystemJS而不是Webpack加载UMD捆绑包,具有以下设置

(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);

这是非常接近官方Angular2文档提出的设置,增加了path部分。它曾经能够将RxJS作为单个UMD捆绑包加载,但是最近引入了路由器3.0,此设置失败,路由器代码例外。我不得不将RxJS作为个别请求再次恢复并加载。