angular2版本的cnode[更新]
发布于 6 年前 作者 p412726700 3995 次浏览 来自 分享

github地址 github 项目demo ng2-cnode 上张图 ng2-cnode

angular2框架图 angualr2 architecture 充分的模块化开发,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表现会更好。

写的不好的话还请各位斧正、海涵。

4 回复

不错不错收藏先

ng2是什么?

更新下

回到顶部