github地址 github 项目demo ng2-cnode 上张图
angular2框架图
充分的模块化开发,angular2定义了几种常见功能组件的命名,通过创建这些组件完成特定的业务功能,然后又把这些小组件组装到一个大模块中,比如说有一个登陆模块(login.module.ts)
其中就包含login.component.{ts|html|css} login.service.ts login.schema.ts login.pipe.ts
等,用的时候就把这个导入到根模块中即可,其中详细的定义和用法请参考angular2官网
本项目文件结构如下 . ├── README.md ├── config/ │ ├── helpers.js │ ├── webpack.common.js │ ├── webpack.dev.js │ └── webpack.prod.js ├── package.json ├── public/ │ ├── css/ │ │ ├── app.styl │ │ ├── fonts.styl │ │ ├── header.styl │ │ ├── style.styl │ │ ├── topics.styl │ │ └── variables.styl │ ├── fonts/ │ └── images/ ├── src/ │ ├── app/ │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.routing.ts │ │ ├── login.component.ts │ │ ├── topic.component.html │ │ ├── topic.component.ts │ │ ├── topics.component.html │ │ ├── topics.component.ts │ │ ├── user.component.html │ │ └── user.component.ts │ ├── index.html │ ├── main.ts │ ├── pipes/ │ │ └── tab.ts │ ├── polyfills.ts │ ├── schemas/ │ │ └── topics.ts │ ├── services/ │ │ └── topics.ts │ └── vendor.ts ├── tsconfig.json ├── tslint.json ├── typings/ │ ├── globals/ │ │ ├── angular-protractor/ │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── core-js/ │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── jasmine/ │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── markdown-it/ │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── node/ │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ └── selenium-webdriver/ │ │ ├── index.d.ts │ │ └── typings.json │ └── index.d.ts ├── typings.json └── webpack.config.js
config/
存放一些webpack文件编译、打包相关的配置
package.json
npm依赖及一些脚本命令
public/
资源文件,例如css、font、img等,官方用法是把同一个component的html、css、ts写到同一个目录下,这里由于我们要使用stylus的公共变量,并没有找到合理有效的引用公共变量方法,所以样式文件抽出来写在同一个目录下了。另外第三方样式会在style.styl中引入。
src/
主要包含了各种组件,根据我厂的实际使用情况,我们并没有完全照搬官方的用法,官方是把相互关联强的各种组件都打包到一个模块中,但是根据我们的实际情况有好多公用的组件,所以是按照组件类型划分,结构上略有不同。
app/
各个组件的ts、html文件。
index.html
入口html文件。
main.ts
入口ts文件。
pipes/
各种过滤器,类似angular1。
polyfill.ts
浏览器兼容文件。
schemas/
model文件,类似于mvc结构中的m文件。
services/
为各个component服务的文件,大部分的业务逻辑写在这里,轻component重service。
vendor.ts
js资源文件,包括angular和第三方的。
tsconfig.json
typescript编译选项文件,类似于babelrc。
tslint.json
typescript格式规范检查,类似于eslint。
typings
ts静态类型重要组成部分,当用一些第三方库的时候没有这些文件的话,编译过程或者编辑器会报错,例如,当用jquery,$(selector).append()时如果没有相应的typings文件,就会报错找不到定义。
typings.json
typings配置及依赖文件,类似package.json。
webpack.config.js
地球人都知道
项目功能
先期功能有列表展示页、切换列表页、下一页、详情页等
在package.json中定义好依赖后,直接npm install,然后依次创建上面的文件夹。 先在src中定义一个入口文件index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>NG2-Cnode</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
</body>
</html>
作用就是在浏览器输入地址打开的就是这个页面,我们使用webpack关联入口ts文件。
然后创建main.ts,入口ts文件
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
if (process.env.ENV === 'production') {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
这个文件通过webpack插件(html-webpack-plugin)与index.html文件关联起来,编译打包后就会注入到index.html底部。
创建app文件夹并创建app.module.ts app.module中包含了项目所需的各种ts组件,这个算是angular2项目的基石。
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app.component';
import {routing} from "./app.routing";
import {TopicsComponent} from "./topics.component";
import {HttpModule} from "@angular/http";
import {FormsModule} from "@angular/forms";
import {TabPipe} from "../pipes/tab";
import {TopicComponent} from "./topic.component";
import {TopicsService} from "../services/topics";
import {UserComponent} from "./user.component";
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
routing
],
declarations: [
AppComponent,
TopicsComponent,
TopicComponent,
UserComponent,
TabPipe
],
providers: [TopicsService],
bootstrap: [AppComponent]
})
export class AppModule {
}
然后创建app.module.html 这里面就是项目的layout了
<header>
<h4><a href="javascript:;" [routerLink]="['/']">NG2-Cnode</a></h4>
</header>
<main>
<router-outlet></router-outlet>
</main>
所有的组件都会映射到router-outlet中,就是说当点击某个链接变换的就是main中的元素
接下来,写一个topics.component.ts(列表组件)
import {Component, OnInit} from "@angular/core";
import {TopicsService} from '../services/topics';
import {Topics} from '../schemas/topics';
const timeago = require("timeago.js");
@Component({
templateUrl: 'topics.component.html'
})
export class TopicsComponent implements OnInit {
topics: Topics[];
tab: string = '';
page: number = 1;
loading: boolean = true;
timeagoInstance = new timeago();
constructor(private topicsService: TopicsService) {
}
ngOnInit() {
this.fetchData().then(data=>this.topics = data)
}
changeTab(tab: string) {
this.page = 1;
this.tab = tab;
this.fetchData().then(data=>this.topics = data)
}
fetchData() {
this.loading = true;
return this.topicsService.getTopics(this.page, this.tab)
.then(res=>res.json())
.then(res=> {
this.loading = false;
return res.data
})
}
onScroll() {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight && !this.loading) {
++this.page;
this.fetchData().then(data=>this.topics = this.topics.concat(data))
}
}
}
因为这是个页面组件,所以引入component,绝大多数时候我们都在与component打交道,oninit是页面刚打开时执行的函数,TopicsService是与后端的数据交换及大部分业务逻辑,Topics是定义的model文件。
创建topics.component.html
<div class="loader-inner-wrap" *ngIf="loading">
<div class="loader-inner ball-scale-multiple">
<div></div>
<div></div>
<div></div>
</div>
</div>
<div class="topic" *ngIf="!loading">
<h3 class="title">{{topic.title}}</h3>
<div class="info">
<span class="font-smaller"><i class="icon-user"></i>{{topic.author.loginname}}</span>
<span class="font-smaller"><i class="icon-calendar"></i>{{moment(topic.create_at).format('MM-DD HH:mm')}}</span>
</div>
<div class="divider"></div>
<div class="content" [innerHTML]="md.render(topic.content)">
</div>
<div class="divider"></div>
<div class="comments">
<div class="comment" *ngFor="let comment of topic.replies">
<img src="{{comment.author.avatar_url}}" alt="{{comment.author.loginname}}" class="comment-avatar">
<small><strong>{{comment.author.loginname}}</strong></small>
<small>{{moment(comment.create_at).format('MM-DD HH:mm')}}</small>
<div class="comment-content" [innerHtml]="md.render(comment.content)">
</div>
<div class="divider"></div>
</div>
</div>
</div>
这上面绑定了ts文件中定义的各种方法、变量。
当打开这个页面时,会执行ngOnInit函数,从而执行fetchData方法拉取数据,滚动页面时执行onScroll方法,判断滚到底部拉取时数据,并设置标志位防止连续拉取。
其中定义的topics变量类型为Topics[],这样当在使用这个变量时就能正确的获取到其属性,就像java、c#一样,如果不定义的话默认是any类型,虽然也可以用,但失去了用typescript的初衷。
创建 app.routing文件
import {ModuleWithProviders} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {TopicsComponent} from "./topics.component";
import {TopicComponent} from "./topic.component";
import {UserComponent} from "./user.component";
const appRoutes: Routes = [
{
path: '',
component: TopicsComponent
}, {
path: 'topic/:id',
component: TopicComponent
}, {
path: 'user/:id',
component: UserComponent
}
]
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
定义了路由匹配的component文件,angular2还提供了子路由机制,可以懒加载组件,不过这个项目中暂未用到。
项目中还有些其它文件,不过至此已经能跑起来了。
angular2总体使用下来感受很不错,很强的oop思想和组件分割,配合上ts的静态类型检查,特别适合大型项目的构建,上手难度上感觉要比react-redux简单不少,值得一试,ts更是由于angular2火到了一个新高度,antd2.0也是用ts开发了,angular2的各种ui组件也在如火如荼的进行中,在未来的技术选型上又多了一个很好的选择,或许在某些场景下angular2表现会更好。
写的不好的话还请各位斧正、海涵。