详解为生产环境编译Angular2应用的方法


Posted in Javascript onDecember 10, 2018

Angular 2 已经发布了 2.1.2 版本, 相信很多人已经在使用(试用)了, 相比 AngularJS 1.x , Angular 2 在性能上有了长足的进步, 同时 Angular 2 也变得非常的庞大, 动辄几兆的脚本, 如何部署到生产环境? 接下来就介绍如何为生产环境编译 Angular 2 应用, 在本文中, 我们将 Angular 2 官方文档中的 Hello Angular 应用编译到 50K 以下, 以用于生产环境。

未经优化的应用

根据 Angular2 官方的 QuickStart 快速创建一个 Hello Angular 应用, 在没有任何优化的情况下, 运行情况如下图所示:

详解为生产环境编译Angular2应用的方法

从上图可以看出, 仅仅一个 Hello 应用, 就产生了 40 个请求, 加载了 1.8M 的脚本, 这个在生产环境下(特别是移动端)是无法接受的。

要看这一步的完整源代码, 请移步 GitHub 。

打包与压缩

传统的方式无非就是进行打包和压缩, 我使用 browserify 和 uglifyjs 来进行打包与压缩, 首先是安装这两个工具类库:

npm i -D browserify uglifyjs

在 package.json 文件中添加这两个 npm 命令:

{
 "scripts": {
  "bundle": "browserify -s main app/main.js > dist/bundle.js",
  "minify": "uglifyjs dist/bundle.js --screw-ie8 --compress --mangle --output dist/bundle.min.js"
 }
}

现在运行这两个命令, 看看会怎么样:

npm run bundle && npm run minify

经过一大堆 WARN 之后, 没有出现 ERROR , 也没有出现 npm-debug.log 文件, 证明没有错误, 现在来分析一下大小:

ls -hl dist
-rw-r--r--  1 zhang staff  1.4M Nov 14 14:08 bundle.js
-rw-r--r--  1 zhang staff  528K Nov 14 14:10 bundle.min.js

bundle.js 有 1.4M , 不过 bundle.min.js 被压缩到了 528K , 看起来还不错, 还可以再优化一下, 那就是 gzip 压缩, 通常在服务器上都会启用:

gzip dist/bundle.min.js -c > dist/bundle.min.js.gz && ls -hl dist
-rw-r--r--  1 zhang staff  1.4M Nov 14 14:08 bundle.js
-rw-r--r--  1 zhang staff  528K Nov 14 14:10 bundle.min.js
-rw-r--r--  1 zhang staff  129K Nov 14 14:15 bundle.min.js.gz

经过 gzip 之后, bundle.min.js.gz 有 129K , 似乎应该可以了吧? 但是我觉得还有优化的空间。

要看这一步的完整源代码, 请移步 GitHub 。

AOT 以及 Tree Shaking

ES2016 (ES6) 有一个很重要的特性, 那就是 Tree Shaking , 可以移除项目中不需要的部分, 接下来我们使用 rollup 进行 Tree Shaking 。

为了能够使用 Tree Shaking , 我们需要将项目中的 TypeScript 编译成 ES2015 脚本, 需要修改 TypeScript 配置, 新建一个 tsconfig-es2015.json 的配置文件, 内容如下:

{
 "compilerOptions": {
  "target": "es2015",
  "module": "es2015",
  "moduleResolution": "node",
  "declaration": false,
  "removeComments": true,
  "noLib": false,
  "emitDecoratorMetadata": true,
  "experimentalDecorators": true,
  "lib": ["es6", "es2015", "dom"],
  "sourceMap": true,
  "pretty": true,
  "allowUnreachableCode": false,
  "allowUnusedLabels": false,
  "noImplicitAny": true,
  "noImplicitReturns": true,
  "noImplicitUseStrict": false,
  "noFallthroughCasesInSwitch": true,
  "typeRoots": [
   "./node_modules/@types",
   "./node_modules"
  ],
  "types": [
  ]
 },
 "files": [
  "app/main-aot.ts"
 ]
}

在 Angular2 应用中, 包含了一个即时编辑器 (JIT) , 在预编译好的应用中不是必需的, 使用 Angular2 的 AOT 编译可以移除即时编译器 (JIT) , 因此需要先安装 Angular 的编译器:

npm i -D @angular/compiler-cli

为了使用 aot 编译出来的文件, main.ts 文件也要做相应的修改, 将 main.ts 文件另存为 main-aot.ts , 修改内容如下:

import { enableProdMode } from '@angular/core';
import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from './app.module.ngfactory';
enableProdMode();
const platform = platformBrowser();
platform.bootstrapModuleFactory(AppModuleNgFactory);

不再使用 platform-browser-dynamic , 改为使用 platform-browser

同时 index.html 也另存为 index-aot.html , 也做相应的修改, 不在加载 system.js , 改为直接使用最终的 aot 脚本:

<!--
<script src="node_modules/systemjs/dist/system.src.js"></script>
-->
<!-- 2. Configure SystemJS -->
<!--
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
-->
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
<script src="dist/bundle-aot.min.js"></script>
</body>

接下来的整体的思路是:

1、使用 ngc 进行 aot 编译;

npm run ngc -- -p tsconfig-es2015.json

这一步将会生成一系列 *.ngfactory.ts *.module.metadata.json 临时文件, 可以更新 .gitignore 来忽略这些文件, 避免对代码库造成污染;

2、将 typescript 文件编译成 es2015 (es6) 脚本;

npm run tsc -- -p tsconfig-es2015.json

3、使用 rollup 进行 tree shaking , 移除项目不使用的功能;

rollup -f iife -c rollup.config.js -o dist/bundle-aot-es2015.js

4、再次使用 typescript 将 tree shaking 之后的脚本编译成 es5 脚本;

tsc --target es5 --allowJs dist/bundle-aot-es2015.js -out dist/bundle-aot.js

5、使用 uglifyjs 再次压缩上一部生成的 es5 脚本;

uglifyjs dist/bundle-aot.js --screw-ie8 --compress --mangle --output dist/bundle-aot.min.js

这几个命令对应的 npm 脚本如下:

{
 "scripts": {
  "ngc": "ngc",
  "rollup": "rollup -f iife -c rollup.config.js -o dist/bundle-aot-es2015.js",
  "es5": "tsc --target es5 --allowJs dist/bundle-aot-es2015.js -out dist/bundle-aot.js",
  "minify-aot": "uglifyjs dist/bundle-aot.js --screw-ie8 --compress --mangle --output dist/bundle-aot.min.js",
  "prod-aot": "npm run ngc -- -p tsconfig-es2015.json && npm run tsc -- -p tsconfig-es2015.json && rollup && npm run es5 && npm run minify-aot"
 }
}

最终只要运行一个命令即可:

npm run prod-aot

最后来看一下最终的文件大小:

ls -hl
-rw-r--r-- 1 zhang staff  595K Nov 14 15:59 bundle-aot-es2015.js
-rw-r--r-- 1 zhang staff  669K Nov 14 16:01 bundle-aot.js
-rw-r--r-- 1 zhang staff  194K Nov 14 16:01 bundle-aot.min.js
-rw-r--r-- 1 zhang staff  46K Nov 14 16:02 bundle-aot.min.js.gz
-rw-r--r-- 1 zhang staff  1.4M Nov 14 15:54 bundle.js
-rw-r--r-- 1 zhang staff  528K Nov 14 15:54 bundle.min.js
-rw-r--r-- 1 zhang staff  129K Nov 14 16:02 bundle.min.js.gz

最终生成的 bundle-aot.min.js.gz 只有 46K , 比没有使用 aot 编译的最终文件 bundle.min.js.gz 少了将近 2/3 , 可以说 aot + tree shaking 效果非常的显著。

要看这一步的完整源代码, 请移步 GitHub 。

经过这样的终极编译优化编译之后, 应该可以放心的部署到生产环境了。

参考资料:

Angular Quick Start
AoT Compilation
Building an Angular 2 Application for Production

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
让iframe框架网页在任何浏览器下自动伸缩
Aug 18 Javascript
利用浏览器全屏api实现js全屏
Jan 16 Javascript
jQuery表格排序组件-tablesorter使用示例
May 26 Javascript
JavaScript实现向OL列表内动态添加LI元素的方法
Mar 21 Javascript
JavaScript搜索字符串并将搜索结果返回到字符串的方法
Apr 06 Javascript
基于jQuery倾斜打开侧边栏菜单特效代码
Sep 15 Javascript
JS中mouseover和mouseout多次触发问题如何解决
Jun 06 Javascript
AngularJS页面带参跳转及参数解析操作示例
Jun 28 Javascript
理解Koa2中的async&amp;await的用法
Feb 05 Javascript
解决Vue中引入swiper,在数据渲染的时候,发生不滑动的问题
Sep 27 Javascript
Vue优化:常见会导致内存泄漏问题及优化详解
Aug 04 Javascript
vue实现下拉菜单树
Oct 22 Javascript
深入理解Vue.js轻量高效的前端组件化方案
Dec 10 #Javascript
es6基础学习之解构赋值
Dec 10 #Javascript
vue中将html字符串转换成html后遇到的问题小结
Dec 10 #Javascript
vue的.vue文件是怎么run起来的(vue-loader)
Dec 10 #Javascript
Bootstrap 实现表格样式、表单布局的实例代码
Dec 09 #Javascript
Bootstrap 按钮样式与使用代码详解
Dec 09 #Javascript
Vue源码中要const _toStr = Object.prototype.toString的原因分析
Dec 09 #Javascript
You might like
php操作sqlserver关于时间日期读取的小小见解
2009/11/29 PHP
php启用zlib压缩文件的配置方法
2013/06/12 PHP
PHP设置图片文件上传大小的具体实现方法
2013/10/11 PHP
destoon实现会员商铺中指定会员或会员组投放广告的方法
2014/08/21 PHP
解决PHP Opcache 缓存刷新、代码重载出现无法更新代码的问题
2020/08/24 PHP
JavaScript中的字符串操作详解
2013/11/12 Javascript
Jquery ajaxStart()与ajaxStop()方法(实例讲解)
2013/12/18 Javascript
Jquery遍历Json数据的方法
2015/04/20 Javascript
JavaScript识别网页关键字并进行描红的方法
2015/11/09 Javascript
JS弹出窗口插件zDialog简单用法示例
2016/06/12 Javascript
微信小程序入门教程
2016/11/18 Javascript
Vue.JS入门教程之自定义指令
2016/12/08 Javascript
bootstrapValidator表单验证插件学习
2016/12/30 Javascript
Vue.js学习笔记之修饰符详解
2017/07/25 Javascript
AngularJS select设置默认值的实现方法
2017/08/25 Javascript
使用Angular CLI生成路由的方法
2018/03/24 Javascript
如何获取TypeScript的声明文件.d.ts
2018/05/01 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
2018/06/07 Javascript
快速解决bootstrap下拉菜单无法隐藏的问题
2018/08/10 Javascript
Vue computed 计算属性代码实例
2020/04/22 Javascript
python多进程共享变量
2016/04/06 Python
Python进度条实时显示处理进度的示例代码
2018/01/30 Python
使用numpy和PIL进行简单的图像处理方法
2018/07/02 Python
Python将文字转成语音并读出来的实例详解
2019/07/15 Python
Python将string转换到float的实例方法
2019/07/29 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
浅谈Python xlwings 读取Excel文件的正确姿势
2021/02/26 Python
美国大型的健身社区和补充商店:Bodybuilding.com
2016/09/06 全球购物
世界各地的当地人的食物体验:Eatwith
2019/07/26 全球购物
我们的节日清明节活动方案
2014/03/05 职场文书
学生自我鉴定格式及范文
2014/09/16 职场文书
2015年手术室工作总结
2015/05/11 职场文书
幼儿园教师个人工作总结2015
2015/05/12 职场文书
《秋天的雨》教学反思
2016/02/19 职场文书
MATLAB 如何求取离散点的曲率最大值
2021/04/16 Python
MySQL Innodb索引机制详细介绍
2021/11/23 MySQL