详解为生产环境编译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 相关文章推荐
JavaScript之appendChild、insertBefore和insertAfter使用说明
Dec 30 Javascript
jQuery的初始化与对象构建之浅析
Apr 12 Javascript
简单谈谈javascript中的变量、作用域和内存问题
Aug 30 Javascript
利用select实现年月日三级联动的日期选择效果【推荐】
Dec 13 Javascript
详解如何较好的使用js
Dec 16 Javascript
Angularjs在360兼容模式下取数据缓存问题的解决办法
Jun 22 Javascript
Angularjs上传图片实例详解
Aug 06 Javascript
AngularJS 事件发布机制
Aug 28 Javascript
Vue.js实现可排序的表格组件功能示例
Feb 19 Javascript
vue项目中全局引入1个.scss文件的问题解决
Aug 01 Javascript
vue中可编辑树状表格的实现代码
Oct 31 Javascript
vue祖孙组件之间的数据传递案例
Dec 07 Vue.js
深入理解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实现读取手机客户端浏览器的类
2015/01/09 PHP
php计算函数执行时间的方法
2015/03/20 PHP
PHP的邮件群发系统phplist配置方法详细总结
2016/03/30 PHP
php封装的mongodb操作类代码
2017/08/06 PHP
PHP实现二维数组中的查找算法小结
2018/06/09 PHP
PHP中使用CURL发送get/post请求上传图片批处理功能
2018/10/15 PHP
jQuery中Dom的基本操作小结
2014/01/23 Javascript
javascript创建createXmlHttpRequest对象示例代码
2014/02/10 Javascript
jQuery实现的动态伸缩导航菜单实例
2015/05/07 Javascript
基于JavaScript的操作系统你听说过吗?
2016/01/28 Javascript
基于JavaScript Array数组方法(新手必看篇)
2016/08/20 Javascript
用jquery获取自定义的标签属性的值简单实例
2016/09/17 Javascript
完美实现js焦点轮播效果(二)(图片可滚动)
2017/03/07 Javascript
详解angular ui-grid之过滤器设置
2017/06/07 Javascript
详解Vue 2.0封装axios笔记
2017/06/22 Javascript
微信小程序 生成携带参数的二维码
2019/10/23 Javascript
element日历calendar组件上月、今天、下月、日历块点击事件及模板源码
2020/07/27 Javascript
[02:03]《现实生活中的DOTA2》—林书豪&DOTA2职业选手出演短片
2015/08/18 DOTA
深入浅析Python中的yield关键字
2018/01/24 Python
python如何重载模块实例解析
2018/01/25 Python
python中numpy的矩阵、多维数组的用法
2018/02/05 Python
教你使用python实现微信每天给女朋友说晚安
2018/03/23 Python
使用Python将Mysql的查询数据导出到文件的方法
2019/02/25 Python
Flask和pyecharts实现动态数据可视化
2020/02/26 Python
纯CSS3实现Material Design效果
2017/03/09 HTML / CSS
CSS3动画效果回调处理详解
2014/12/10 HTML / CSS
英国领先的男士美容护发用品公司:Mankind
2016/08/31 全球购物
JBL美国官方商店:扬声器、耳机等
2019/12/01 全球购物
人力资源管理专业毕业生推荐信
2013/11/07 职场文书
高中考试作弊检讨书
2014/01/14 职场文书
毕业寄语大全
2014/04/09 职场文书
法定代表人身份证明书
2014/09/10 职场文书
关于幸福的感言
2015/08/03 职场文书
学校2016年全国助残日活动总结
2016/04/01 职场文书
使用CSS实现小三角边框原理解析
2021/11/07 HTML / CSS
pd.DataFrame中的几种索引变换的实现
2022/06/16 Python