Angular 学习手册第二版(五)
原文:
zh.annas-archive.org/md5/6C06861E49CB1AD699C8CFF7BAC7E048译者:飞龙
第十四章:SystemJS
SystemJS 是一个模块加载器,可以在以下 GitHub 链接中找到github.com/SystemJS/SystemJS。
它是建立在原始的 ES6 模块加载器 polyfill 之上的。它旨在解决在浏览器中加载模块的基本问题,目前除非浏览器得到一些来自库的帮助,否则是行不通的,比如 SystemJS。
在这个附录中,我们将涵盖:
-
SystemJS 本身
-
一个实用的 SystemJS 示例,使用 Angular 的快速启动存储库
SystemJS 介绍
SystemJS 从上到下加载文件,然后从下到上实例化。不过,这意味着什么呢?这意味着如果你有一个名为Module1的文件需要加载,它依赖于Module2,那么Module1将首先被加载。加载完毕后,我们有了执行代码的部分,它采取相反的方向。在这种情况下,它将执行Module2,以获得它的一个实例并将其传递给Module1。
SystemJS 的典型用法如下:
System.import('./file.js').then( file => // do something )
SystemJS 在处理脚本时会进行不同的步骤:
-
规范化文件路径:路径可以是相对的、绝对的和别名的,SystemJS 将所有这些转换为一个格式
-
XHR 或提供它:当一个模块被要求时,可能会发生两种情况;如果它已经在之前被预加载过,它将从内部注册表中加载,或者会为它发出 XHR 请求
-
准备好使用:在最后一步中,模块将被执行,添加到注册表中,并解析其承诺
使用快速启动存储库快速入门
要开始使用quickstart存储库,你需要使用以下命令获取该项目的副本:
git clone https://github.com/angular/quickstart.git quickstart
这将从 GitHub 下载所有所需的文件,并将它们放在一个名为quickstart的目录中。现在,进入该目录:
cd quickstart
该项目将指定一堆它依赖的库。你需要安装这些库。这可以通过输入以下命令来完成:
npm install
最后,我们需要提供应用程序,也就是在浏览器中显示它。这可以通过输入以下命令来完成:
npm start
值得一提的是,该存储库使用 SystemJS 作为模块加载器和引导我们的 Angular 应用程序。
理解各个部分
获得 GitHub 存储库或使用脚手架工具是很好的。您可以快速开始,并且几乎立即感到高效。不过,这里有一个但。如果出了问题,我们该如何解决?为了能够做到这一点,我们需要更好地了解底层发生了什么。
使用 SystemJS 设置任何 Angular 项目的基本概念
这些概念构成了您的应用程序的核心。它们将出现在每个项目中:
-
起始网页
-
Node 包管理器(npm)
-
SystemJS
-
TypeScript 设置和 TypeScript 定义文件
-
Linting
让我们讨论这些概念,以介绍设置。
所有 Web 项目都需要一个起始网页。
Node.js 是服务器端的 JavaScript。在Angular 构建的上下文中,Node.js 被用来引入许多库(来自 npm)来帮助处理诸如打包、测试和最小化等任务。至少要对如何使用 Node.js 及其生态系统有一定了解是至关重要的。关于这一点的更详细描述将在接下来的小节中进行。
至于 SystemJS,它是一个模块打包工具。JavaScript 项目不再只是写在一个文件中;有时候,它们是由成千上万个文件组成的。这些文件之间的关系是通过使用模块系统来实现的,而 SystemJS 是众多模块打包工具之一。Angular 团队选择了 TypeScript 作为编写 Angular 应用程序的通用语言,这意味着我们需要正确设置 TypeScript 来编译它,并确保 TypeScript 知道如何使用 ES5 编写的依赖库。
最后,linting 是确保我们在编写代码时遵循最佳实践的过程,既为了一致性,也为了避免错误。
现在,让我们详细讨论这些概念。
起始网页 - index.html
这个文件的目的是呈现给 Web 服务器,最终将其渲染成一个应用程序。它将包含一些标记,但更重要的是我们应用程序运行所需的script标签。
index.html也包含了许多script标签。这些script标签是项目运行所需的。
核心文件 - Angular 依赖的文件
许多浏览器缺乏 ES2015 带来的一些功能。为了解决这个问题,我们可以通过添加一些称为 polyfill 的东西来增强我们的浏览器,以弥补这些缺失的功能。除了利用现代 JavaScript 的 polyfill 之外,Angular 还使用了一种全新的方式来检测应用程序中的变化,这是通过使用zone.js库来实现的。最后,Angular 团队决定使用 Rxjs 来处理 HTTP 请求。他们甚至进一步将其集成到许多其他方面,比如处理表单和路由。这三个东西构成了我们需要导入的核心功能,以使我们的应用程序正常工作。
core-js
这个文件将 ES2015 的功能带给了 ES5 浏览器。由于您将使用相当多的 ES2015 构造,这对一切都是必要的:
<script scr="node_modules/core-js/client/shim.min.js"></script>
zone.js
这个文件被 Angular 用来处理变化检测和数据绑定,没有这个库什么都不会工作:
<script scr="node_modules/zone.js/dist/zone.js"></script>
rxjs
RxJS 是 Angular 大量使用的异步库,用于处理从 HTTP 请求到表单和路由的所有内容。
SystemJS - 我们的模块加载器
SystemJS 是您用来处理模块加载的库,由两个链接组成:
-
SystemJS 核心文件
-
SystemJS 配置文件
前者是 SystemJS 运行所需的,后者是您指示 SystemJS 加载哪些文件以及找到您的应用程序和相关资产的位置。
这指出了核心 SystemJS 文件:
<script src="node_modules/SystemJS/dist/system.scr.js"></script>
这指出了如何配置 SystemJS。您需要调用这个文件SystemJS.config.js:
<script src="SystemJS.config.js"></script>
查看SystemJS.config.js显示了以下配置调用:
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'SystemJS-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
看起来相当长而令人生畏,但让我们分解不同的部分,如下所示:
paths:系统文件的别名位置。值得注意的是,我们通过输入以下内容来创建对node_modules的别名:
path: { 'npm:': 'node_modules/'}
这将在以后为我们服务,当我们需要提及应用程序需要的所有库时。
map:这是我们需要告诉 SystemJS 它可以在哪里找到所有部分的地方。
以下代码片段显示了以下内容:
-
- 找到我们的应用程序的位置,名为 app 的键
-
找到 Angular 文件的位置,名为
@angular/... -
找到支持库的位置,这些库包括 angular 库(框架分成许多较小的库)以及上一节中提到的核心库
map : {
app : app, // instruct that our app can be found in the app directory
'@angular/core': 'npm:@angular/core/bundles/core.umd.js'
// supporting libraries omitted for brevity
}
在这里,我们可以看到我们在引用@angular/core时使用了别名npm,这意味着以下内容:
'npm: @angular/core/bundles/core.umd.js'
使用以下完整路径:
'node_modules/@angular/core/bundles/core.umd.js'
packages: 这是配置文件的最后部分。它指示应该首先加载应用程序文件夹中的哪些文件,也提供了defaultExtension。
Node.js 设置 - package.json
package.json是 Node.js 项目的描述文件。它包括元数据信息,如name,author和description,但它还包含一个script属性,允许我们运行执行工作的脚本,例如:
-
创建一个 bundle
-
运行测试
-
执行 linting
要运行script标签中的命令之一,您需要输入:
npm run <command>
您的应用程序将依赖于许多库来构建和运行。列在dependencies或devDependencies中的库将通过您输入npm install来下载。
在dependencies和devDependencies中列出哪些库应该有一个语义上的区别。最终将帮助应用程序运行的任何内容都将最终出现在dependencies中,包括 Angular 库以及支持库。devDependencies有些不同;这里放置的内容更多是支持性质的。例如 TypeScript,Linter,测试库以及用于处理 CSS 和创建 bundle 本身的不同工具。
至于dependencies中的 angular 部分,这些都是纯 Angular 依赖项,用@angular表示:
-
@angular/common -
@angular/compiler -
@angular/core -
@angular/forms -
@angular/http -
@angular/platform-browser -
@angular/platform-browser-dynamic -
@angular/router
其余的依赖项是我们在本节中提到的Angular 依赖的核心文件列表:
-
core-js -
reflect-metadata.js -
rxjs -
system.js -
zone.js
TypeScript 设置
tsconfig.json是 TypeScript 编译器将处理并确定编译应该如何发生的文件。
以下是基本设置:
target: 'es5',
module : 'commonjs',
emitDecoratorMetadata : true, // needed for compilation to work
experimentalDecorators : true // needed for compilation to work
如前面的代码注释中所述,emitDecoratorMetadata和experimentalDecorators需要设置为true,因为 Angular 大量使用这些功能。
摘要
本附录介绍了 SystemJS,并描述了它如何处理文件以及以何种顺序处理文件,因为它是一个模块加载器。随后,介绍了官方的快速启动存储库。然后,我们看了 SystemJS 需要的不同部分或者它需要解决的问题。在这一点上,我们已经准备好深入了解如何使用 SystemJS 来设置 Angular 应用程序。我们还看了 Angular 框架需要 SystemJS 加载的核心部分以及顺序。离开这个附录,我们现在对 SystemJS 解决的问题有了更清晰的理解,以及如何使用它来设置 Angular 应用程序。值得注意的是,大多数 Angular 应用程序都在使用 Angular CLI 或 webpack,但这绝对是一个将在一段时间内得到支持的好选择。
第十五章:与 Angular 一起使用 webpack
Webpack 是一个模块捆绑器。它能够捆绑不同的资产,如 JavaScript、CSS 和 HTML。webpack 非常受欢迎,正在成为设置应用程序的首选方式。然而,在前端世界中,事物变化很快。这使得重要的是理解需要解决的问题,而不是特定捆绑工具的技术细节。
在本附录中,您将:
-
了解 webpack 中的重要概念
-
学习如何在简单的 Web 项目中使用 webpack
-
利用 webpack 设置 Angular 项目
核心概念
基本上,webpack 尝试通过爬取文件中的所有导入语句来创建依赖关系图。想象一下,您有以下代码片段:
//main.js
import { Lib } from './lib';
Lib.doStuff)() // lib.js
//lib.js
import { OtherLib } from './otherlib'
OtherLib.doStuff()
在这种情况下,它会推断main.js依赖于lib.js,而lib.js又依赖于otherlib.js,从而创建了一系列依赖关系。
爬取所有导入语句并找出所有依赖关系的最终结果是生成一个捆绑包,您可以将其作为index.html的一部分并呈现给浏览器进行渲染。
加载程序
webpack 需要一个加载程序来理解特定的文件扩展名并对其进行操作。我们所说的扩展名是.ts、.js、.html等。我们为什么关心呢?在设置时,我们需要确保已设置了适当的加载程序,以便处理我们关心的特定文件扩展名。在 webpack 中,当您想要处理扩展名时,您设置规则。规则可以如下所示:
rules: [{
test: /\.blaha$/,
use: 'blaha-loader'
}]
test属性是一个正则表达式,您可以在其中指定要查找的文件扩展名。
loader属性是您指定加载程序名称的地方。webpack 内置了许多加载程序,但如果需要,也可以下载它。
插件
插件可以在构建过程的不同步骤触发。这意味着您可以在某个步骤执行额外的工作。要使用插件,您需要在plugins属性中指定它,如下所示:
plugins: [new MyAwesomePlugin()]
在我们进入 Angular webpack 设置之前,让我们首先确定我们到目前为止学到了什么。webpack 能够处理 JavaScript、CSS、TypeScript 等,并创建我们可以包含在起始 HTML 文件中的捆绑文件,通常称为index.html。此外,如果通过config文件进行配置,我们可以设置一些规则。每个规则由一个正则表达式组成,该正则表达式将捕获特定文件结束的所有文件,并将指向一个处理捕获文件的加载器。还有一些称为插件的东西,它们能够在特定的生命周期步骤给我们提供进一步的功能。然而,如果我们将这些知识付诸实践,那将是很好的,所以让我们在下一节中这样做。
Webpack - 第一个项目
为了正确地为设置 Angular 项目做准备,让我们首先通过一个简单的项目来展示我们将用来设置 Angular 的所有常见场景。
首先,我们需要安装 webpack。通过运行以下命令来完成:
npm install webpack -g
安装成功后,是时候试一试了。首先,让我们创建几个文件,内容如下:
//index.html
<html></html>
//app.js
var math = require('./mathAdd');
console.log('expect 1 and 2 to equal 3, actual =', math(1,2));
//mathAdd.js
module.exports = function(first, second){
return first + second;
}
运行以下命令:
webpack ./app.js bundle.js
这将从app.js开始爬取所有依赖项,并从中创建一个bundle.js文件。要使用所述的bundle.js,请在index.html中添加一个脚本标签,使其看起来如下:
<html>
<script src="bundle.js"></script>
</html>
要在浏览器中查看您的应用程序,您需要一个可以托管您的文件的 Web 服务器。有许多小型、轻量级的 Web 服务器;例如,Python 自带一个。我要推荐一个叫做http-server的服务器。可以通过在终端中输入以下内容轻松安装:
npm install http-server -g
安装完成后,将自己放在与index.html文件相同的目录中,并输入以下内容来调用 Web 服务器:
http-server -p 5000
在浏览器中导航到http://localhost:5000,并打开devtools;应该显示如下内容:
expect 1 and 2 to equal 3, actual = 3
恭喜,您已成功创建了您的第一个 webpack 捆绑文件,并且您有一个可工作的应用程序。
改进我们的项目-使用配置文件
能够轻松创建一个捆绑文件是很好的,但这并不真实。大多数 webpack 项目将使用config文件而不是在命令行上调用 webpack。所以让我们这样做:让我们创建一个名为Webpack.config.js的config文件,并将以下代码添加到其中:
//webpack.config.js
module.exports =
{
entry: "./app.js",
output: { filename : "bundle.js" }
}
这本质上重新创建了我们在命令行上写的内容,即从app.js开始,并确保生成的捆绑文件名为bundle.js。
现在在命令行中键入webpack。
再次启动您的应用程序,并确保一切仍然正常。成功!我们已经从命令行转移到了配置文件。
但是,我们不希望一直在终端中输入webpack。我们希望在更改时重新构建捆绑包,因此让我们添加该功能:
module.exports = {
entry: "./app.js",
output: { filename : "bundle.js" },
watch: true
}
注意额外的属性watch。
在终端中输入webpack,现在 webpack 进程不会像以前那样退出,而是继续运行并等待我们进行更改。
例如,将app.js的操作更改为以下内容:
var math = require('./mathAdd');
console.log('expect 1 and 2 to equal 3, actual =', math(1,2));
保存文件并注意捆绑包在终端中的重新构建。这很棒,但我们可以做得更好。我们可以添加一个 Web 服务器,它会在更改时自动启动和重新启动我们的应用程序。我在谈论一种叫做热重载的东西。基本上,对代码进行更改,重新创建捆绑包,浏览器反映更改。为此,我们需要做两件事:
-
安装一个与 webpack 兼容的 HTTP 服务器实用程序
-
在
config文件中启用热重载
要安装 webpack HTTP 服务器实用程序,我们输入以下内容:
npm install webpack-dev-server -g
现在让我们将config文件更新为以下内容:
var webpack = require('webpack');
module.export = {
entry: './app.js',
output: { filename : 'bundle.js' },
watch: true,
plugins: [new Webpack.HotModuleReplacementPlugin()]
}
已添加两个功能。这是第一个:
var webpack = require('Webpack');
这是第二个:
plugins: [new Webpack.HotModuleReplacementPlugin()]
我们已添加了一个热重载插件。使用以下命令启动应用程序:
webpack-dev-server
现在,Web 服务器会监听更改;如果发生更改,它将重新构建捆绑包,并在 Web 浏览器中显示更改。
为我们的项目添加更多功能
在现代 Web 应用程序项目中,我们可以做更多有趣的事情。其中之一是能够使用所有最新的 ES2015 功能,以及能够将我们的捆绑包拆分成更多专用的捆绑包,比如一个用于应用程序,一个用于第三方库。webpack 可以轻松支持这两个功能。
创建多个捆绑包
有多个原因可以解释为什么您希望为应用程序创建多个捆绑包。可能是您有多个页面,您不希望每个页面加载一个沉重的捆绑包,而只需要它所需的 JavaScript。您可能还希望将第三方库与应用程序本身分开。让我们尝试看看如何创建多个捆绑包。
我们的理想情况是,我们希望有三个不同的文件,app.js,init.js和vendor.js:
-
app.js:这是我们的应用程序所在的位置 -
init.js:这应该包含捆绑包共有的内容,也就是我们的 webpack 运行时 -
vendor.js:这是我们依赖的第三方库所在的地方,比如query和lodash
为了实现这一点,我们需要更改配置文件,以便如下所示:
module.exports = {
entry : {
app: "./app.js",
vendor: ["angular"]
},
output: { filename : "[name].js" },
watch: true,
plugins: [
new Webpack.HotModuleReplacementPlugin(),
new Webpack.optimize.CommonsChunkPlugin("init")
]
}
让我们来分解一下:
entry: {
app: "./app.js",
vendor: ["angular"]
}
我们过去在这里有一个指向app.js的入口。现在我们想要有两个入口,但是用于不同的事情。Vendor 指向一个库数组。这意味着当 webpack 看到a:require('angular')时,它知道要将node_modules/angular库放在vendor.js中,它将创建。
第二个感兴趣的部分是:
plugins: [ new Webpack.optimize.CommonsChunkPlugin('init') ]
在这里,我们说要将我们共有的一切(在这种情况下是 webpack 运行时)放在init.js中。
使用 webpack 设置 Angular
掌握了 webpack 的核心概念以及如何添加额外功能的知识后,我们现在应该准备好启动 Angular 项目了。首先,创建以下文件:
-
webpack:在设置 webpack 时,通常最好将配置设置为以下三个文件: -
webpack.common.js:这是大部分配置将发生的地方 -
webpack.dev.js:这是dev环境特定的配置 -
webpack.prod.js:这是prod环境特定的配置 -
package.json:此文件将列出我们依赖的库,以便正确引导 Angular。这些列在devDependencies和dependencies中。我们还将在script中列出一些命令,以便启动应用程序,以便在 web 服务器上运行。此外,我们还将创建用于测试的命令和用于创建生产捆绑包的命令。 -
tsconfig.json:这个文件是为 TypeScript 编译器准备的。值得注意的是,我们希望启用某些功能,使应用程序能够正常工作,比如emitDecoratorMetadata和experimentalDecorators。
通用配置
这个文件的简要概述如下:
-
Entry,应用程序的入口点 -
Module.rules,一个指定如何加载某些文件以及使用什么加载器的对象 -
插件,一个在 webpack 生命周期中为我们提供额外功能的插件数组
entry部分指定将有三个不同的捆绑:polyfills,vendor和app。你可能会问为什么是这三个捆绑?嗯,为polyfills有一个单独的捆绑是有道理的,因为它是与其他不同的概念。polyfills捆绑确保我们选择的浏览器具有来自 ES2015 的所有最新功能。vendor捆绑是我们放置所有被认为是我们应用程序的辅助程序的库,但并不是应用程序本身。app捆绑真正是我们应用程序的所在;它包含我们的业务代码。
以下代码片段显示了创建前面提到的三个捆绑所需的配置应该是什么样子的:
entry : {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
}
module部分定义了一系列规则。提醒一下,规则是关于处理特定文件扩展名的。每个规则都包括一个test属性,定义要查找的文件扩展名。它还包括一个loader属性,指向能够处理该文件扩展名的加载程序。例如,如果文件扩展名是.sass,加载程序能够将 Sass 编译成 CSS 文件。
以下代码片段举例说明了如何设置规则来处理 HTML 文件:
module : {
rules : [
{
test: /\.HTML$/,
loader: 'HTML-loader'
}
// other rules emitted for brevity
]
}
我们可以看到一个正则表达式测试.html扩展名,并让HTML-loader处理它。我们项目的完整规则列表应该设置规则来处理 TypeScript、资源(图像)、CSS 和 HTML。如果我们都有了,就可以开始了。
我们还需要通过设置一些插件来增强构建过程,即:
-
ContextReplacementPlugin -
CommonChunksPlugin -
HTMLWebpackPlugin
ContextReplacementPlugin的工作是用另一个上下文替换一个上下文。但这到底是什么意思呢?最常见的用例是使用动态的require语句,就像这样:
require('directory/' + name + '.js')
在编译时,webpack 无法确定要包含哪些文件。为了确保它在运行时能够正常工作,它会包含该目录中的所有内容。一个常见情况是处理翻译文件。您可能在这样的目录中有数百个文件,包含所有这些文件会使捆绑文件变得不必要地庞大。因此,您可以使用该插件,并给它一个过滤参数,缩小文件数量,就像这样:
new Webpack.ContextReplacementPlugin(
/directory\//, //when trying to resolve a file from this directory
/(sv-SE|se).js // narrow down the search by only including files
that match this
)
当您尝试创建多个捆绑文件时,将使用CommonChunksPlugin,就像这样:
entry : {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
}
为了避免每个捆绑包都包含 webpack 运行时和其他常见部分;可以使用上述插件来提取常见部分。有许多调用这个插件的方法;这里是一个:
plugins: [ new Webpack.optimize.CommonsChunkPlugin('init') ]
这将创建一个init.js文件。
webpack 生成了许多文件,如 HTML 和 JavaScript 文件。你可以在index.html中链接到所有这些文件,但这变得相当麻烦。更好的处理方法是使用HTMLWebpackPlugin,它将为你注入这些link和script标签。
没有这个插件,你的index.html会看起来像这样:
<link href="app.css"></link>
<script src="app.bundle.js"></script>
<script src="page1.bundle.js"></script>
<script src="page2.bundle.js"></script>
<script src="common.bundle.js"></script>
你明白了,使用这个插件几乎是必须的,至少如果你想确保将index.html与你的解决方案同步,并避免不必要的输入,需要添加/更改脚本标签。
我们需要做的是使这个插件工作,指向需要注入script和link标签的位置,如下所示:
new HtmlWebpackPlugin({
template: 'src/index.HTML'
})
到目前为止,我们已经涵盖了创建的捆绑包,需要设置处理所有不同文件扩展名的规则,以及需要的插件。这是 webpack 设置的核心。然而,配置需要根据我们处理的是开发环境还是生产环境有所不同。
开发配置
webpack 在开发模式和生产模式下以不同的方式处理你的文件。首先,你的 JavaScript 文件都是在内存中的,也就是说,没有文件实际写入输出目录,如下所示:
output: {
path: helpers.root('dist')
// other config is omitted
}
在开发环境中,我们关心设置源映射。源映射记住了在所有东西被合并成一个或多个捆绑包之前文件结构是什么样子的。当文件在 IDE 中与项目结构相似时,调试变得更容易。设置源映射的一种方法是输入以下内容:
devtool: 'cheap-module-eval-source-map'
生产配置
在生产配置中,通过使用UglifyJS插件进行最小化设置是很重要的。这很重要,因为我们希望我们的应用尽可能小,这样它加载起来会很快。我们的用户中可能有更多的人在 3G 连接上,所以我们需要迎合所有类型的用户:
new Webpack.optimize.UglifyJsPlugin({
mangle: { keep_fnames : true } // keep file names
})
测试
任何值得一提的开发人员都应该关心编写测试。测试的设置并不难。
我们需要以下文件来使测试工作:
karma.conf.js:我们正在使用 karma 作为测试运行器。这需要一个config文件,设置测试的位置,是否在无头浏览器或真实浏览器中运行我们的测试,以及许多其他内容。
这个文件中需要注意的config是:
preprocessors: {
'./karma-test-shim.js': ['Webpack', 'sourcemap']
}
预处理步骤是必需的,以便将我们的 TypeScript 文件编译成 ES5 JavaScript。它还将设置适当的源映射,并指出从 Angular 框架中需要哪些文件才能使我们的测试正常运行。
另一个值得一提的属性是:
var WebpackConfig = require('./webpack.test');
module.exports = function(config) {
var _config = {
Webpack : WebpackConfig
}
// other config omitted
config.set(_config);
}
这指向了Webpack.test.js文件中指定的配置。
-
webpack.test.js:这只是Webpack.common.js的副本,正常的配置。然而,通过将其制作成一个单独的文件,我们有能力稍后覆盖某些配置。 -
karma-test-shim.js:如前所述,这个文件负责导入运行所需的 Angular 框架的所有部分,框架的核心部分,以及与测试相关的专用部分。完整的文件如下:
Error.stackTraceLimit = Infinity;
require('core-js/es6');
require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
var appContext = require.context('./src', true, /\.spec\.ts/);
appContext.keys().forEach(appContext);
var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(
browser.BrowserDynamicTestingModule,
browser.platformBrowserDynamicTesting()
);
值得注意的是以下一行:
var appContext = require.context('./scr, true, /\.spec\.ts/');
这定义了在尝试定位要运行的测试时要查找的内容。因此,让我们创建一个匹配这种模式的测试,test.spec.ts,在src目录下:
describe('should return true', () => {
it('true is true', () => expect(true).toBe(true) );
});
所有这些都设置正确后,你应该能够输入:
npm run test
这应该启动 Chrome 浏览器。你应该看到以下内容:
按下调试按钮将显示以下屏幕,清楚地指示正在运行我们的测试和结果,即通过测试。
总结
本附录描述了 webpack 与 Angular 的配合工作方式。此外,我们已经探讨了与设置 Angular 应用程序相关的部分,甚至如何设置单元测试,这是强烈建议尽早适应的。希望你通过这个附录感到有所启发,并且觉得设置并不那么复杂。通常情况下,项目的设置只需要一次,你只需要在项目开始时进行一次设置,之后几乎不再碰。为了简洁起见,我们没有展示很多配置,而是讨论了不同配置文件如何一起工作来使我们的设置生效。然而,如果你想详细研究配置,可以在以下 GitHub 存储库中找到:github.com/softchris/angular4-Webpack。