使用 typescript 和 gulp 快速开始 Angular 2 开发

阅读 400
收藏 23
2017-04-01
原文链接:github.com

Angular2出来了一段时间,通过更简单和更简洁的概念,如基于组件的架构,新的依赖注入或内置模块化,新版本的框架要学习得简单得多。在这个分步教程中,您将了解如何使用TypeScript和Gulp开始使用Angular2。源代码在Github上可用。

本文是翻译codeleak.pl上的一篇文章。(英语很挫,错误在所难免)

原文地址:blog.codeleak.pl/2016/03/qui…

[toc]

通用说明

更新日志

15/09/2016升级到2.0.0重新angular2文章,添加新的代码库中的代码来反映。添加快速入门指南。

  • 06/09/2016 - 更新至 Angular2 to 2.0.0-rc.6
  • 05/06/2016 - typings updated to 1.0.4. Section related to that part changed too.
  • 05/05/2016 - Upgraded Angular2 to 2.0.0-rc.1
  • 22/04/2016 - Upgraded Angular2 to 2.0.0-beta.15
  • 26/03/2016 - Upgraded Angular2 to 2.0.0-beta.12
  • 21/03/2016 - Upgraded Angular2 to 2.0.0-beta.11 (the post itself as well as the source code)

快速入门

如果你不感兴趣的一步一步的教程,只需按照以下步骤迅速启动。

1、环境要求

Nodejs 必须安装在您的系统和下面的全局节点包必须安装:

gulp
npm i -g gulp

gulp-cli
npm i -g gulp-cli

typings
npm i -g typings@1.3.3

typescript
npm i -g typescript@2.0.2

ts-node
npm i -g ts-node@1.3.0

2、克隆仓库

git clone https://github.com/kolorobot/angular2-typescript-gulp.git

3、定位至angular2-typescript-gulp目录

cd angular2-typescript-gulp

4、安装依赖

npm install

5、构建工程

npm run clean & npm run build
// 构建后会产生Build目录

6、启动应用

    npm start

介绍

至今,Angular 1.X可能仍然是最流行的前端框架,毫无疑问Angular 1.X是一个伟大的框架。然而,这是非常难以掌握。复杂的API和许多概念推出以来,使人了解框架和有效地使用它确实很难。

angular2,另一方面,是一个新的开放。新版本的框架要简单得多,学习更简单和更简洁的概念,如基于组件的体系结构,新的依赖注入或内置模块化。

如果你想找一个比 angular.io 更好地 练习和开始学习Angular 2的地方,如果正在寻找用gulp构建来使用Angular 2的方式,那么这篇文档正适合你!

Note: 文章所使用的代码资源在github
github.com/kolorobot/a….

工程预览

初始化的工程是基于Angular2 Quickstart:
angular.io/docs/ts/lat… 做了一些改变。
最重要的变化是将源文件与生成文件分离:SRC目录包含所有源文件并生成包含所有已编译和处理的文件。
服务器使用build目录作为基本目录来资源文件。

angular2-typescript-gulp
|   .gitignore
|   bs-config.json  -> BrowserSync configuration
|   gulpfile.ts     -> Gulp in TypeScript
|   package.json    -> npm configuration
|   tsconfig.json   -> TypeScript configuration
|   typings.json    -> TypeScript typings definitions
|   tslint.json     -> tslint configuration
|
\---src
│   │   index.html                 -> Starting point for the application
│   │   systemjs.config.js         -> SystemJS configuration
│   │
│   \---app                       -> Application modules
│       │   app.component.ts          -> Main application component
│       │   app.html              -> Main application template 
│       │   app.module.ts         -> Application module definition       
│       │   app.routing.ts        -> Routing configuration      
│       │   main.ts               -> Application bootstrap   
│       │
│       \---about 
│       │   └───components
│       │           about.components.ts
│       │           about.html
│       │
│       \---todo
│           ├───components
│           │       task-list.component.ts
│           │       task-list.css
│           │       task-list.html
│           │       task.component.ts
│           │       task.html
│           │
│           \---models
│           │       task.ts
│           │
│           \---services
│                   task-service.ts

NPM全局依赖

假设Node和NPM已经安装,你可以通过调用下面的命令安装全局依赖:

npm i -g <dependency>

为了运行工程必须安装的全局依赖:

gulp and gulp-cli
npm i -g gulp 
npm i -g gulp-cli

typings
npm i -g typings@1.3.3

typescript
npm i -g typescript@2.0.2

ts-node
npm i -g ts-node@1.3.0

// Note: To check global dependencies use the following command:

npm -g –depth 0 ls

创建工程目录和文件

创建如上文所述的目录和文件结构

构建配置

1、typescript配置 - tsconfig.ts

编译文件会被保存至build/app;请注意gulpfile.ts要排除编译。

{
  "compilerOptions": {
    "outDir": "build/app",
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "gulpfile.ts",
    "node_modules"
  ]
}

说明:如果你想导入你的工程到IDE(如IntelliJ),让这个IDE也使用这个文件。

2、Typings - typings.json

To get started we need some definitions to be installed. Run the following commands:
为了能够正常运行开始,我们需要的一些定义被安装。运行一下命令:

typings install –global –save dt~core-js 
typings install –global –save dt~node

这些会被添加至typings.json

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "node": "registry:dt/node#6.0.0+20160909174046",
  }
}

Typings will be download to typings directory and they will be downloaded on npm install.

Typings 会被下载到typings目录并且他们会通过npm instal的方式被下载下来。

3、npm包和脚本配置 - package.json

关于脚本命令的一些命令:

  • clean - 清除build目录
  • compile - TypeScript compilation (with sourcemaps)
  • build - 打包构建工程
  • start - 运行使用来自bs-config.json配置的lite服务器,它还使用监视任务同步源目录的任何更改
{
  "name": "angular2-typescript-gulp",
  "version": "1.0.0",
  "description": "Angular2 with TypeScript and Gulp QuickStart",
  "scripts": {
    "clean": "gulp clean",
    "compile": "gulp compile",
    "build": "gulp build",
    "start": "concurrent --kill-others \"gulp watch\" \"lite-server\"",
    "postinstall": "typings install"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/kolorobot/angular2-typescript-gulp.git"
  },
  "author": "Rafał Borowiec",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/kolorobot/angular2-typescript-gulp/issues"
  },
  "dependencies": {
    "@angular/common": "2.0.0",
    "@angular/compiler": "2.0.0",
    "@angular/core": "2.0.0",
    "@angular/forms": "2.0.0",
    "@angular/http": "2.0.0",
    "@angular/platform-browser": "2.0.0",
    "@angular/platform-browser-dynamic": "2.0.0",
    "@angular/router": "3.0.0",
    "@angular/upgrade": "2.0.0",

    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.12",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.23"
  },
  "devDependencies": {
    "concurrently": "^2.2.0",
    "del": "^2.2.0",
    "gulp": "^3.9.1",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-tslint": "^6.1.1 ",
    "gulp-typescript": "^2.13.6",
    "lite-server": "^2.2.2",
    "tslint": "^3.5.0",
    "typescript": "^2.0.2",
    "typings": "^1.3.3",
    "ts-node": "^1.3.0"
  }
}

4、BrowserSync configuration - lite-server

默认情况下,内容从当前目录提供,因此需要更改;而且,由于Lite Server使用BrowserSync,足以提供配置服务器的bs-config.json来从构建目录提供内容。

{
  "port": 8000,
  "files": [
    "build/**/*.{html,htm,css,js}"
  ],
  "server": {
    "baseDir": "build"
  }
}

5、tslint(typescript语法检测配置) - tslint.json

TSLint检查TypeScript代码是否可读性,可维护性和功能错误,而Gulp可以与gulp-tslint插件一起使用。

tslint.json用于配置哪些规则可以运行。只需将文件添加到项目的根目录。您应该根据需要调整规则。您可以在这里找到有关规则的更多信息:palantir.github.io/tslint/usag…

{
  "rules": {
    "class-name": true,
    "curly": true,
    "eofline": false,
    "forin": true,
    "indent": [
      true,
      4
    ],
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [
      true,
      140
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-eval": true,
    "no-string-literal": false,
    "no-trailing-whitespace": true,
    "no-unused-variable": false,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "radix": true,
    "semicolon": true,
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator"
    ]
  }
}

6、Gul构建配置- gulpfile.ts

要开始,我们需要一个编译TypeScript文件,将资产和依赖关系复制到构建目录的任务。为了实现这些任务需要几个任务。

注意:Gulp文件是以TypeScript而不是JavaScript创建的。它需要ts-node执行,如本教程开头所述。

"use strict";

const gulp = require("gulp");
const del = require("del");
const tsc = require("gulp-typescript");
const sourcemaps = require('gulp-sourcemaps');
const tsProject = tsc.createProject("tsconfig.json");
const tslint = require('gulp-tslint');

/**
 * Remove build directory.
 */
gulp.task('clean', (cb) => {
    return del(["build"], cb);
});

/**
 * Lint all custom TypeScript files.
 */
gulp.task('tslint', () => {
    return gulp.src("src/**/*.ts")
        .pipe(tslint({
            formatter: 'prose'
        }))
        .pipe(tslint.report());
});

/**
 * Compile TypeScript sources and create sourcemaps in build directory.
 */
gulp.task("compile", ["tslint"], () => {
    let tsResult = gulp.src("src/**/*.ts")
        .pipe(sourcemaps.init())
        .pipe(tsc(tsProject));
    return tsResult.js
        .pipe(sourcemaps.write(".", {sourceRoot: '/src'}))
        .pipe(gulp.dest("build"));
});

/**
 * Copy all resources that are not TypeScript files into build directory.
 */
gulp.task("resources", () => {
    return gulp.src(["src/**/*", "!**/*.ts"])
        .pipe(gulp.dest("build"));
});

/**
 * Copy all required libraries into build directory.
 */
gulp.task("libs", () => {
    return gulp.src([
            'core-js/client/shim.min.js',
            'systemjs/dist/system-polyfills.js',
            'systemjs/dist/system.src.js',
            'reflect-metadata/Reflect.js',
            'rxjs/**',
            'zone.js/dist/**',
            '@angular/**'
        ], {cwd: "node_modules/**"}) /* Glob required here. */
        .pipe(gulp.dest("build/lib"));
});

/**
 * Watch for changes in TypeScript, HTML and CSS files.
 */
gulp.task('watch', function () {
    gulp.watch(["src/**/*.ts"], ['compile']).on('change', function (e) {
        console.log('TypeScript file ' + e.path + ' has been changed. Compiling.');
    });
    gulp.watch(["src/**/*.html", "src/**/*.css"], ['resources']).on('change', function (e) {
        console.log('Resource file ' + e.path + ' has been changed. Updating.');
    });
});

/**
 * Build the project.
 */
gulp.task("build", ['compile', 'resources', 'libs'], () => {
    console.log("Building the project ...");
});

7、安装依赖关系并检查构建

现在是安装所有依赖关系的时候了。运行:

npm install

应该在安装期间创建node_modules和typing目录。

构建工程

npm run clean & npm run build

build目录应在构建期间创建

注意:如果在编译期间看到以下内容,请确保至少有ts-node 1.3.0:

[00:49:42] Failed to load external module ts-node/register
[00:49:42] Failed to load external module typescript-node/register
[00:49:42] Failed to load external module typescript-register
[00:49:42] Failed to load external module typescript-require

工程配置

1. Index - src/index.html

库是在构建任务期间创建的lib目录的引用

<html>
<head>
    <title>Angular 2 TypeScript Gulp QuickStart</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- 1. Load libraries -->
    <!-- Polyfill(s) for older browsers -->
    <script src="lib/core-js/client/shim.min.js"></script>

    <script src="lib/zone.js/dist/zone.js"></script>
    <script src="lib/reflect-metadata/Reflect.js"></script>
    <script src="lib/systemjs/dist/system.src.js"></script>

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app')
                .then(null, console.error.bind(console));
    </script>

</head>

<!-- 3. Display the application -->
<body>
<app>Loading...</app>
</body>

</html>

2. SystemJS 配置 - src/systemjs.config.js

function (global) {
    System.config({
        paths: {
            // paths serve as alias
            'npm:': 'lib/'
        },
        // 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'
        },
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            app: {
                main: './main.js',
                defaultExtension: 'js'
            },
            rxjs: {
                defaultExtension: 'js'
            }
        }
    });
})(this);

3、应用组件

1. 主入口组件 - src/app/app.component.ts

import {Component, OnInit} from "@angular/core";

@Component({
    selector: "app",
    templateUrl: "./app/app.html"
})
export class AppComponent implements OnInit {
    ngOnInit() {
        console.log("Application component initialized ...");
    }
}

他的模板 (src/app/app.html):

<p>Angular 2 is running ... </p>

2. About 模块

关于模块由两个基本构件 - 组件及其模板组成。

src/app/about/components/about.component.ts

import {Component} from "@angular/core";
import {OnInit} from "@angular/core";

@Component({
    templateUrl: './app/about/components/about.html'
})
export class AboutComponent implements OnInit {

    ngOnInit() {

    }
}

src/app/about/components/about.html:

<h1>About</h1>
<p>这是about</p>

3. Todo 模块

Todo模块有点复杂 - 它包含组件,模型和服务。

src/app/todo/components/task.component.ts

import {Component} from "@angular/core";
import {Input} from "@angular/core";

import {Task} from "../models/task";
import {Output} from "@angular/core";
import {EventEmitter} from "@angular/core";

@Component({
    selector: 'task',
    templateUrl: './app/todo/components/task.html'
})
export class TaskComponent {
    @Input() task:Task;
    @Output() statusChanged:any = new EventEmitter<any>();

    toggleDone() {
        this.task.toggleDone();
        this.statusChanged.emit(null);
    }
}

src/app/todo/components/task.html

<form role="form">
    <input title="Name" type="text" [(ngModel)]="task.name" name="name" [disabled]="task.done" />
    <input title="Done" type="checkbox" (click)="toggleDone()" [checked]="task.done" />
</form>

src/app/todo/components/task-list.component.ts

import {Component} from "@angular/core";
import {Task} from "../models/task";
import {OnInit} from "@angular/core";
import {TaskService} from "../services/task-service";
import {TaskComponent} from "./task.component";

@Component({
    selector: 'task-list',
    templateUrl: './app/todo/components/task-list.html',
    styleUrls: ['./app/todo/components/task-list.css'],
    providers: [TaskService]
})
export class TaskListComponent implements OnInit {

    todoCount:number;
    selectedTask:Task;
    tasks:Array<Task>;

    constructor(private _taskService:TaskService) {
        this.tasks = _taskService.getTasks();
        this.calculateTodoCount();
    }

    ngOnInit() {
        console.log("Todo component initialized with " + this.tasks.length + " tasks.");
    }

    calculateTodoCount() {
        this.todoCount = this.tasks.filter(t => !t.done).length;
    }

    select(task:Task) {
        this.selectedTask = task;
    }
}

src/app/todo/components/task-list.css

li.selected {
    background-color: #8a8a8a;
}

src/app/todo/components/task-list.html

<h1>Todo tasks ({{todoCount}})</h1>
<ul>
    <li *ngFor="let task of tasks" [class.selected]="task == selectedTask">
        <a href="javascript:void(0)" (click)="select(task)">
            {{task.name}}
            <span [ngSwitch]="task.done">
                <template [ngSwitchCase]="true">[Done]</template>
                <template [ngSwitchCase]="false">[Todo]</template>
            </span>
        </a>
    </li>
</ul>

<task *ngIf="selectedTask" [task]="selectedTask" (statusChanged)="calculateTodoCount()"></task>

src/app/todo/models/task.ts

export class Task {

    constructor(public name:string, public done:boolean) {
    }

    toggleDone() {
        this.done = !this.done;
    }
}

src/app/todo/services/task-service.ts

import {Injectable} from "@angular/core";
import {Task} from "../models/task";

@Injectable()
export class TaskService {

    private tasks:Array<Task> = [
        new Task("Task 1", false),
        new Task("Task 2", false),
        new Task("Task 3", false),
        new Task("Task 4", false),
        new Task("Task 5", false)
    ];

    getTasks():Array<Task> {
        return this.tasks;
    }

    addTask(name:string) {
        this.tasks.push(new Task(name, false));
    }

}

4. Angular2 路由 - src/app/app.routing.ts

import {Routes, RouterModule} from '@angular/router';
import {TaskListComponent} from "./todo/components/task-list.component";
import {AboutComponent} from "./about/components/about.component";
import {ModuleWithProviders} from "@angular/core";

const appRoutes: Routes = [
    {path: 'tasks', component: TaskListComponent, data: {title: 'TaskList'}},
    {path: 'about', component: AboutComponent, data: {title: 'About'}}
];

export const appRoutingProviders: any[] = [];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });

5. 模块定义 - src/app/app.module.ts

Angular2模块有助于将应用程序整合到一个功能性的集合中。

import {NgModule}      from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';

import {AppComponent} from "./app.component";
import {TaskListComponent} from "./todo/components/task-list.component";
import {AboutComponent} from "./about/components/about.components";
import {TaskComponent} from "./todo/components/task.component";

import {routing, appRoutingProviders} from './app.routing';
import {FormsModule} from "@angular/forms";

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        routing
    ],
    declarations: [
        AppComponent,
        TaskComponent,
        TaskListComponent,
        AboutComponent
    ],
    providers: [
        appRoutingProviders
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

6. Application bootstrap - src/app/main.ts

//<reference path="../../typings/index.d.ts"/>

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);

构建运行

npm run clean & npm run build 
npm start

您应该看到应用程序在浏览器中运行

关于IntelliJ的注意事项

IntelliJ处理建议的设置没有问题。如果IntelliJ中使用TypeScript编译器,则应使用该项目的tsconfig.json。在这种情况下,ts文件的更改将立即反映在构建目录中。

代码资源

github.com/kolorobot/a…

下一步做什么?

需要更多的实际使用这个项目。因此,很快就会添加更多功能。如果您有任何建议,评论或添加Github问题。

angular2-seed

如果您需要更成熟的启动器,请查看angular2-seed项目

github.com/mgechev/ang…
angular2-seed以更先进的方式使用gulp,并且已经支持生产和开发构建,单元和集成测试等等。

评论