ホームページ  >  記事  >  ウェブフロントエンド  >  angular4の実際のプロジェクト構築を詳しく解説

angular4の実際のプロジェクト構築を詳しく解説

小云云
小云云オリジナル
2018-01-05 10:36:541724ブラウズ

この記事では、angular4 の実際のプロジェクト構築の概要についての簡単な説明を主に紹介します。これが非常に優れていると思います。編集者をフォローして見てみましょう。皆さんのお役に立てれば幸いです。

前書き

私は PC バックエンド プロジェクトを受け取りました。私は 2 人の Android 同僚に参加して、このプロジェクトを一緒に学び、取り組むことになっているので、彼らを連れて行く必要があります。 ng1以降、他のバックエンドフレームワークを使った実際のプロジェクト経験はあまりなく、この期間に使用したモバイルフレームワークはvue、angularシリーズ、es6を含むreact、webpackなどではなく、慣れていません。 会社の他のバックエンド プロジェクトの 1 つで Vue を使用することがありますが、全体的にはシステムに詳しくありません。 私は常に Angular を好みます。おそらく、ng1 フレームワークの構築経験があり (以前にブログを書いたこともあります)、ng2 と ng4 の公式デモを研究したためです。一般的に、それらをコピーすることは意味がありません。プロジェクトは実際に使用することができます。 私は今は絶対に ng1 を使いたくないのですが、自分の好みと初期の基礎を踏まえて、もう一度新しいフレームワークを構築したいと思っています。たまたま、typescript の構文も同様です。 Android の同僚に対しては比較的友好的です。 リーダーに許可を求めたところ、許可が得られました。

私が最初にピットに入ったとき、プロジェクトを作成するために公式の angular-cli を使用しました。 cli の機能を改善し、途中のフレームワーク全体を理解するのはかなりの労力を要します。 一般に、今回はフレームワークを整理し、環境のデプロイメント、ルーティングのネスト、メイン ページのレイアウト (サイドバー、ナビゲーション バー、コンテンツ、ボトム)、パブリック サービス (読み込みなど) のいくつかの基本的な機能と依存関係を構成するのに 2 ~ 3 日かかりました。 、http リクエストのカプセル化、グローバル サービス タイトル、ユーザー情報アクセス)。 それから私は同僚と直接それを開発しました。 最初は、フレームワークの構築にどのくらい時間がかかるかわかりませんでした。もちろん、私が共有したいくつかのリソースと時間には非常に満足しています。プロセス中にオンラインのピアになります。 積極的なサポートをしてくれた 2 人の同僚にとても感謝しており、彼らは非常に早く学習しています。

問題の詳細

scss

angular-cli.json styleExtをscssに設定した後、コンポーネントにStylesを記述すると、scssがCSSにコンパイルされません。独立したscssで記述し、styleUrlsを通じて導入する必要があります。

ルート パス

ルート パスの前に / を追加することはできません。そうしないとエラーが報告されます

const routes: Routes = [
  { path: '', redirectTo: '/main', pathMatch: 'full' },
  { 
   path: 'main', 
   component: MainComponent
  },
]

は、ルーティングを通じてコン​​ポーネントにアクセスするときに、コンポーネントのセレクターは を見つけます。@Component によって定義されたセレクターはラベルを直接生成し、それを にロードします。名前が設定されていない場合は、デフォルトの ng-component になります。

RouterModule

  1. forRootは、すべてのディレクティブ、指定されたルート、ルーターサービス自体を含むモジュールを作成します。

  2. forChildは、すべてのディレクティブと指定されたルートを含むモジュールを作成しますが、ルーター サービス。

一般に、forRoot は第 1 レベルのルートを定義し、forChild は第 2 レベルのルートまたはサブルートを定義します。たとえば、ルート モジュールであるブート モジュール (通常は app.module.ts という名前) は、登録されたルートが /main であることを前提としており、forRoot を使用する必要があります。 forRoot を介してルート モジュールによって登録されたルート /main は、/main/home にネストする必要があります。次に、ホーム モジュールは forChild を使用してルートを登録する必要があります。

routes

ng4 のloadChildren 属性は、ルートの遅延読み込みを提供します。次のように、第 1 レベルのルート /main があり、/main には home コンポーネントに対応する第 2 レベルのルート /home が定義できます。次のメソッドを通じて子モジュールのルーティングとコンポーネントを作成しますが、この書き方ではルーティングを一緒に記述する必要があり、ホーム コンポーネントのルーティングがホーム フォルダーに存在する別個の home.routes.ts ファイルであることを望みます。 , 同様の親モジュールに子を導入することで、ホームルートをモジュールとして登録します

{
 path: 'main',
 compontent: MainComponent,
 children: [
   {
    path: 'home',
    component: HomeComponent,
   }
  ] 
}

そのため、loadChildrenを使用してサブモジュールとサブルートを登録する必要があります コードは次のとおりです。 loadChildren は、# で区切られ、その後にエクスポートのモジュール名が続くパス ファイル app/home/home.module を見つけます。 次の HomeModule は、ホームのルーティングとコンポーネントの関係を定義します。これはセカンダリ ルートであるため、ここでは RouterModule.forChild が使用されます。最終的に、フレームワークの処理を通じて、上記のコードの Children 属性の効果が実現されます。 ps: 本来は/main/homeにアクセスするとmainコンポーネントとhomeコンポーネントが読み込まれるのですが、/homeというパスに直接アクセスするとhomeコンポーネントも直接読み込めることがわかり、ルートドメイン名も登録されているようです。 loadChildrenを使用した後、HomeModuleモジュールがメインモジュールの下に登録されていることがわかり、AppModuleのimportでHomeModuleを再度登録したところ、インジェクションが繰り返され、親モジュールと同じレベルに登録されました。これには注意してください。

{
 path: 'main',
 compontent: MainComponet,//注意这里加载的是Main
 loadChildren: 'app/home/home.module#HomeModule',
}

//HomeModule
const routes: Routes = [
  { path: '', component: HomeCompontent}
]
@NgModule({
declarations: [
  HomeCompontent
],
imports: [
  RouterModule.forChild(routes)
],
providers: []
})
export class HomeModule { }

上記のルーター関連コンテンツを要約します:

假设一个场景,根模块注册两个路径,一个是/login,一个是/main。/login这个路由访问就是单纯的一个登陆页面,/main下面的路由都将是对应核心页面和业务,因为在main组件里包含了公用的侧边栏、导航栏、内容容器和底部栏,所以 /main路由加载的main组件的内容容器里需要嵌套子模块。 举个实例,当我访问/main/home的时候main组件会加载home组件到content容器中,当我访问/main/manager,manager组件又会替换content中的home。这样我们的公共页面部分就是不变的main组件,根据子路由的变化,去加载不同的组件到content里。

以下是main组件的html大致代码和实际页面截图:

这里也有一个知识点是标签的嵌套,上面的代码中

下面有一个标签,home等二级组件会被载入到父组件main的标签下。而main组件的父组件是引导组件AppCompontent,所以main组件是通过一级路由被载入到AppCompontent的html模板的下方,以下是AppCompontent组件部分代码:

那么打开调试器,我们就能从dom节点上看清楚,router-outlet的嵌套关系:

目录结构

再看下src的目录结构,component文件夹是存放一些公共的组件,login和main组件是注册的一级路由,home和另一个马赛克组件是注册为main的二级路由,实际后面会注册很多组件到main下,但是他们的文件夹划分都是同级。

使用hash路由

RouterModule.forRoot(routes, { useHash: true });使用hash路由,后端不用修改配置,这样比较方便,省去很多麻烦

title

引入了platform-browser的Title,使用它的setTitle方法改标题

APP_BASE_HREF

在非hash路由情况下,有时候环境的原因布置静态资源路径的时候可能不是根域名,同时还要删除index.html的标签,否则会有问题

import { APP_BASE_HREF } from '@angular/common';

在app.module里注册providers: [{provide: APP_BASE_HREF, useValue: environment.publicBase}],

http

使用http相关API,需要注入HttpModule,否则会报错: No provider for Http

import { HttpModule } from '@angular/http';

引入了三方JS,三方JS定义的全局变量,在引入到代码里,编译会报错:没有定义。需要在前面加个申明declare let thirdVar:any;

规范

文件命名service,component,route,module,主要类型的文件种类不多,每次新建文件命名太长,引入的时候也麻烦,所以除了根目录命名保持xx.component.ts这种格式,其他文件统一为xx.c.ts,xx.s.ts。

xx.s.ts == xx.service.ts | export class xxS
xx.r.ts == xx.routes.ts | export class xxR
xx.m.ts == xx.module.ts | export class xxM
xx.c.ts == xx.component.ts | export class xxC

bootstrap4

考虑引入boostrap4来作为css库布局。

关于rem,我们一般用rem作为单位的时候,是更希望利用它的特性改变font-size达到自适应效果,会先定义一个font-size的范围和对应的屏幕宽度范围,根据设计稿的宽度得到一个基数,再用设计稿中元素的实际像素去除以基数得到rem,最后根据屏幕宽度动态设置font-size的相应值达到自适应效果。bootstrap4以浏览器字体默认大小16px,直接定义了元素的rem值,它的源码里没有任何计算,我想他们是参照16px来设置的元素大小,然后求出的rem值,当页面根font-size的值是16px的时候,所有的bootstrap4的元素大小就是标准大小,如果我们想让页面的元素整体放大或者缩小,我们只需要去改变font-size的大小,font-size设置为多少,需要我们自己计算和定义规则。因为是三方库,所以这块的实现方法和我们自己实际项目使用rem的时候,会有些反差。

如果项目中引入了它,我们给自定义元素直接设置px值的话,就会出现问题,如果我们需要改变font-size的大小,就必须统一使用rem,否则font-size改变的时候,自定义的px元素并不会改变。那么自定义元素需要设置为rem值。

NG-ZORRO

想了想,需要快速开发,还是需要一个UI插件库,自己去造轮子开发成本太高,经撸哥介绍,知道了蚂蚁金服的ng库ng-zorro,支持ng4,https://ng.ant.design。 看了下很全,还提供了栅格布局和按钮样式,转眼一想,如果用bootstrap4,相互之间可能有冲突,比如boostrap的reset相关的,而且用boot的按钮样式和蚂蚁的样式可能看起来不搭调。所以我在引入ng-zorro之后,先注释了对bootstrap4的引用,一些公用样式,后面可以考虑自己写。

部署打包

src目录下有个environments文件夹,这里的文件是配置环境的,.angular-cli.json文件有配置两个默认环境,一个是开发一个是发布环境,在我们开发的时候,默认选择的是dev环境

在src下的main.ts里有这么一段代码,这里的意思是切换到生产模式时禁用开发环境下特有的检查(双重变更检测周期)来让应用运行得更快。

我们在开发项目的时候,也必然需要配置开发,测试和生产环境,不同的环境的接口或者其他设置肯定是不一样的。 我需要配置一个apiBase变量,代表不同环境的接口域名,在开发的时候ng4会运行ng serve在本地运行一个服务,域名是localhost,那么后端部署的接口肯定不在我们这个开发域名下,所以这个 apiBase 就是我们后端接口的域名 apiBase='http://www.xxxx.com' (需要后端支持跨域)。 当我们把打包好的代码部署到QA或者生产环境后,访问前端页面的url和后端接口都在同一域名,所以 apiBase='/' 。 那么dev和prod的environment代码分别如下:

//dev
export const environment = {
 production: false,
 apiBase: 'http://www.xx.com/'
};
//prod
export const environment = {
 production: true,
 apiBase: '/'
};

angular-cli 创建的 environment.ts 里有一段注释,如下图。 意思是如果我们用 ng build 命令打包的时候,加上--env=prod(如果是自定义environment文件,必须是ng build --environment=xxx命令),将会把 environment.ts 替代为 environment.prod.ts ,那么 main.ts 里的代码 import { environment } from './environments/environment'; 实际变成了import { environment } from './environments/environment.prod'; 可以通过在 main.ts 打印日志查询当前环境变量是否是我们需要的

因此,我们就只需要把相应的环境变量配置好,如下API接口的代码和 main.ts 文件一样,我引入了 environment,在开发或者打包的时候,angular 配置的打包工具会自动载入相应的环境变量

结语

因为业务需求原因,太久没真正学习搭建新框架了,心里也一直不踏实,感觉再不学点都跟不上时代了, 所以这次项目的机会也算是了却自己一个心愿。 对比ng4和ng1,开发模式有了很大的变化,给我的感觉就是ng4的模块划分更干净,写法更舒服。 也可能是因为有一些angualr系列的基础, 能力也应该比前年学习ng1的时候更强,这次入门很快。 es6和typescript语法有时候分不清谁是谁,落后的知识趁着这次尽快补起来。 因为新鲜感的原因,写代码变得更有乐趣了,在页面细节和动画上,稍微多搞了一点东西进去(后台项目没有设计师,自由发挥)。转眼3个多月没写博客了,这之间学的新东西不多,但是回头补了一下基础的一些知识,也算是有很多收获和新的理解。 设计模式的博客写了一部分,后面会抽时间一步步完成,是一篇希望大家都能看懂的博客,尽请期待!

PS:这篇博客不定期更新,更新的写到下面吧。

  1. [ngClass]="item.fromAccount == webimS.userId ? 'me':'other'" ,ngClass可以这样写,官网没有这种示例

  2. 按照缩写规则,指令directive我应该写成xx.d.ts的,但是d.ts这个格式的文件似乎会被框架其他程序处理,就会报错,所以指令的命名我就没用d.ts这样的缩写了

  3. 指令在父模块declarations之后,发现在子模块里使用指令,根本没有反应。折腾了很久,发现declarations申明的只在当前模块才能使用,而我的懒加载的子模块无效,所以为指令定义一个独立的module,在需要使用指令的地方import这个module,如下图

在指令directive中要拿到当前使用指令的dom,需要使用ViewContainerRef

import { Directive, EventEmitter, ViewContainerRef, AfterViewInit, OnDestroy} from '@angular/core';
constructor(public viewContainerRef: ViewContainerRef) { }

如果我们需要拿到当前控制器下某个dom节点,需要使用@ViewChild

//compontent
@ViewChild('xxx')
 xxx: ElementRef;

getXXX(){
  console.log(this.xxx)
}

//html
<p #xxx></p>

表单验证指令封装

ng提供表单验证 FormGroup 可以定义每个表单的验证条件,定义好之后,需要在表单下面写很多的ngIf dom来判断和展示当前表单的错误填写提醒,这样很不好的一点是提醒的文字是需要占位置的,在处理页面的时候需要兼容这些提醒文案,给他们做兼容布局(如果表单全部是独立的一排一排的还好,如果一行里有很多表单,每个表单的宽度可能也不一样,这时提醒文案就不好放了),而且每次写那么多条件和dom真的很麻烦。

写了一个指令组件,提醒文案作为弹出层展示出来,把当前表单的formControl对象传入指令,把所有的条件统一文案,比如说required 文案为‘'必填‘'。那需要做4件事情,1:动态为指令里加入组件(参考了官网核心知识->模板与绑定->动态组件),2:让组件绝对定位到表单右上角,需要用一个p包裹一下表单,并获取表单的宽度,把宽度传给组件,组件给提示框设置绝对定位。3:传入formControl对象,指令组件需要判断显示隐藏,4:统一文案,条件满足后给显示框展示对应文案,因为formControl的errors是一个对象,所以需要配置一个管道pipe来把errors转换为对应文案。

一个报错:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'false'.

我写的每一个指令都会有这个报错,一般报错在数据变化后触发,网上查了一下,说是没有使用 enableProdMode();方法就会触发这个报错,在main.ts中判断了环境,如果是开发环境的话就不使用enableProdMode();方法,所以目前开发环境会报错,但是也不影响逻辑,所以这个报错暂时忽略

formreset:

一个页面可能会有一个弹窗来填写表单,填写的时候有两种状态,编辑和新增,但是都是用的同一个弹窗对象,表单做了验证、错误条件达到并且dirty属性为true的时候,就会展示错误提醒。在新增和编辑切换的时候如果直接修改表单的值,dirty就会一直是true,就可能一直有错误提示。所以需要在一定情况下使用 formGroup 的 reset来重置表单,dirty就会是false了。每个表单自己也有reset方法。当使用formGroup重置表单的时候,textarea有可能并不会被重置,如果没有被重置,需要单独处理下textarea,给textarea的formControl对象单独reset一下。

正式环境打包的检查:

使用ng build --prod命令时,打包的检查比较严格。开发时候使用的private定义可以在模板里使用对象,在开发环境就会报错。一些模板里绑定的对象数据,是需要后端返回了数据才会传值给对象,在打包的时候就会检测到当前对象属性不存在就无法通过,这时就不能用{{xxx.atrr}}这种形式输出数据,得用{{xxx['attr']}}这种方式,才能跳过检查。

某些情况使用this编译会报错:

直接上图,图一会报错编译无法通过,代码逻辑是正确的。图二能编译通过

相关推荐:

angular4实现tab栏切换的方法分享

Angular4绑定html内容出现警告如何解决

Angular4表单响应功能示例分析

以上がangular4の実際のプロジェクト構築を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。