標籤(空格分隔): Angularjavascript
ng build --prod --build-optimizer --base-href=/
來發布base-href
能夠設置服務器上的某個子路徑,使用 ng build --base-href=/my/path/
.angular-cli.json
配置文件apps
屬性下增長deployUrl
,等同於webpack的publicPath
。如遇刷新找不到頁面(404)的狀況,須要在服務器配置重定向到index.html。以nginx爲例,能夠在location添加try_files $uri $uri/ /index.html?$query_string;
來重定向到index.html。php
若是碰到 *ngIf
*ngFor
用不了得狀況,好比拋出 Property binding ngForOf not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations".的錯誤,一般是由於沒有importCommonModule
,並且這個import必須在組件被引用的module
中。好比我把routes
和modules
分離,這樣組件將會在xx-routing.module.ts
中被import,那麼這個CommonModule
就得在xx-routing.module.ts
中被import,在xx.module.ts
引用是不行的。css
ng serve
拋出錯誤:Node Sass could not find a binding for your current environment。此時須要執行npm rebuild node-sass
來解決。參見stackoverflow。html
APP_INITIALIZER
實現。app.module.tsvue
export function loadToken(tokenService: InitDataService) { return () => tokenService.tokenAndTime(); } providers: [ ... { provide: APP_INITIALIZER, useFactory: loadToken, deps: [InitDataService], multi: true }, ... ],
querySelector()
選擇器,默認是返回Element
,這時候就不能在其後用.style
了。須要將選擇到的Element轉爲HTMLElement(參見):java
let overlay = <HTMLElement>document.querySelector(`#${this.ID}`); overlay.style.display = 'none';
private init() { this.url = apiData.ServiceUrl + this.path; const datas: Datas = { ClientType: apiData.ClientType, Token: this.tokenDatasService.token }; Object.assign(datas, this._datas); // 將參數對象序列化爲 [key1]=[value1]&[key2]=[value2]的字符串 let params = new HttpParams(); if (!this.isGet) { datas.Timespan = this.tokenDatasService.timespanFormat; } for (let key in datas) { params = params.set(key, datas[key]); } if (this.isGet) { this.datas = { params: params }; } else { this.datas = params; } }
參見node
@Component({ encapsulation: ViewEncapsulation.None, ... })
這時樣式將再也不侷限於當前組件。webpack
const ROUTES: Routes = [ ... { path: '**', component: NotFoundComponent} ];
polyfills.ts
以前沒有取消註釋這個文件中的引用,在IE下打開發現報錯,取消註釋第一塊引用後,發現全部瀏覽器都出現自定義DI拋出錯誤Uncaught Error: Can't resolve all parameters for ApiService: (?). at syntaxError (compiler.es5.js:1694) ...。css3
google了半天都是說沒寫@Injectable()
或者少@或者(),然而檢查了半天並非。最後在GitHub的一個Issues中找到了答案,須要取消註釋import 'core-js/es7/reflect';
便可解決。緣由暫且未去探究。nginx
使用@ViewChild('[name]') canvasRef: ElementRef
來選擇canvas畫布。
background: url("/assets/img/shared/logo.png") no-repeat center/100%;
父級提供服務支持(providers),父級在constructor方法中訂閱(subscribe),子路由在ngOnInit方法或者其餘自定義事件中賦值(next)。
能夠經過setTimeout([callback], 0)異步處理結果實現參見GitHub Issues :
this.accountService.titles$.subscribe(titles => setTimeout(() => { this.title = titles.title; this.titleLink = titles.titleLink.link; this.titleLinkName = titles.titleLink.name; }, 0));
以nginx爲例:
location / { root C:\Web\Site; index index.html; ry_files $uri $uri/ /index.html?$query_string; }
v-if
和v-else
,ng中也有這樣的模板語法:注意必須使用ng-template
。
<h2 class="nick-name" *ngIf="isLogin; else notLogin">{{ userInfo.Name }}</h2> <ng-template #notLogin> <a href="javascript: void(0);" class="nick-name">當即登陸</a> </ng-template>
Subject
實現組件之間的通訊ionic中有一個Events
服務,能夠經過publish
發佈事件,在其餘組件中subscribe
事件。
在Angular項目中,咱們能夠經過Subject
來建立一個服務實現相似的效果。類同本文(# 10)所述。
import {Injectable} from '@angular/core'; import {Datas} from '../models/datas.model'; import {Subject} from 'rxjs/Subject'; import {Observable} from 'rxjs/Observable'; import {Subscriber} from 'rxjs/Subscriber'; @Injectable() export class EventsService { private events: Datas = {}; public eventsName = []; constructor() { } /** * 發佈 * @param {string} topic 事件名稱 * @param {Datas} params 參數(對象) */ public publish(topic: string, params: Datas = {}) { const event = this.getEvent(topic); Object.assign(params, { EVENT_TOPIC_NAME: topic }); event.next(params); } /** * 訂閱事件 * @param {string} topic 事件名稱 * @return {Observable} */ public subscribe(topic: string) { return this.getEvent(topic).asObservable(); } /** * 取消訂閱事件 * @param {Subscriber} subscriber 訂閱事件對象 */ public unsubscribe(subscriber: Subscriber<any>) { subscriber.unsubscribe(); } private getEvent(topic: string) { this.eventsName.push(topic); this.eventsName = Array.from(new Set(this.eventsName)); let _event; for (const i in this.events) { // 判斷是否已有事件 if (this.events.hasOwnProperty(i) && i === topic) { _event = this.events[i]; break; } } if (!_event) { // 沒有事件 建立一個 _event = new Subject<Datas>(); const eventObj = { [topic]: _event }; Object.assign(this.events, eventObj); } return _event; } }
... constructor(private eventsService: EventsService) { } ngOnInit() { const a = this.eventsService.subscribe('setHeader').subscribe(v => { console.log(v); // 取消訂閱 this.eventsService.unsubscribe(a); }); } ...
... export class IndexComponent implements OnInit { constructor(private eventsService: EventsService) { } ngOnInit() { // 第一次觸發 this.eventsService.publish('setHeader', { a: 1, b: 2 }); setTimeout(() => { // 第二次觸發 this.eventsService.publish('setHeader', { c: 3 }); }, 5000); } }
在控制檯,咱們能夠看到:
第二次觸發並無被打印。是由於調用了取消訂閱事件。將取消訂閱事件註釋掉,能夠看到第二次觸發打印:
常常會用到路由跳轉後執行一些操做。經過Route
來進行操做。
import {NavigationEnd, Router} from '@angular/router'; ... constructor(private router: Router) { } ... // 導航 navWatch() { this.router.events.subscribe(e => { if (e instanceof NavigationEnd) { // TODO 路由跳轉完畢 } }); } ...
使用@Output() [eventName] = new EventEmitter<T>();
,而後在組件內部經過this[eventName].emit([params])
來觸發事件、傳遞參數。組件外部經過圓括號<my-component (eventName)="watchEvent($event)"></my-component>
。其中$event
就是傳遞過來的參數。
能夠經過宿主監聽器@HostListener([event]: string, [args]: string[])
來操做。
好比監聽window滾動事件:
... @HostListener('window:scroll', []) onWindowScroll() { // TODO 滾動事件 // this.scrollEvent().subscribe(obj => { // this.scrollStyle(obj.offset, obj.direction); // }); } ...
如何實現兩次輸入密碼一致(兩個輸入框值相等)的自定義驗證器。
data-*
等屬性直接使用方括號你會發現拋出錯誤。這時候能夠加個attr
來解決:
<img [attr.data-src]="value">
rem
的使用咱們習慣使用 html { font-size: 62.5%; }
來做爲根大小(10px),可是Chrome並不支持12px如下的大小,這將致使Chrome與其餘瀏覽器顯示不一樣。
搜索解決方案。
body { font-size: 1.4em; }
,經試驗不起做用(至少在個人項目中)。-webkit-transform: scale(.8, .8);
,不是很滿意。html { font-size: 625%; }
,至關於100px。我更偏向於第三種。
若是想要手動配置webpack來打包項目:(非必要)
使用ng new my-app
初始化的項目並不包含webpack配置文件,須要ng eject
命令來加入webpack.config.js
配置文件。
注意此時不能再用 ng build 之類的命令了,開發環境是npm start
,打包命令是npm run build
。
這時候webpack缺乏一些原來的配置。
uglifyjs-webpack-plugin
js壓縮插件將js文件壓縮,減少打包後文件的體積。
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); ... new UglifyJsPlugin({ "test": /\.js$/i, "extractComments": false, "sourceMap": true, "cache": false, "parallel": false, "uglifyOptions": { "output": { "ascii_only": true, "comments": false }, "ecma": 5, "warnings": false, "ie8": false, "mangle": { properties: { regex: /^my_[^_]{1}/, reserved: ["$", "_"] } }, "compress": {} } })
compression-webpack-plugin
生成gzip文件插件進一步減少打包文件體積。
const CompressionWebpackPlugin = require('compression-webpack-plugin'); ... new CompressionWebpackPlugin()
這個須要服務器開啓gzip on;
,以nginx爲例,須要爲服務器進行如下配置:
conf/nginx.conf:
http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; # 開啓gzip gzip on; gzip_static on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_comp_level 2; gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; gzip_vary on; gzip_disable "MSIE [1-6]\."; server { listen 8088; server_name localhost; location / { root website/angular; index index.html; } } }
clean-webpack-plugin
清除打包文件工具每次npm run build
後都會生成新的打包文件(文件名添加hash),這個插件能夠在打包後刪除以前舊的文件。
const CleanWebpackPlugin = require('clean-webpack-plugin'); ... new CleanWebpackPlugin(['dist'], { root: projectRoot, verbose: true, dry: false })
src/assets/
文件夾下的靜態資源以及favicon.ico
文件也須要打包,這時須要修改一下自動生成的配置代碼:
new CopyWebpackPlugin([ { "context": "src", "to": "assets/", "from": "assets" }, { "context": "src", "to": "", "from": { "glob": "favicon.ico", "dot": true } } ], { "ignore": [ ".gitkeep", "**/.DS_Store", "**/Thumbs.db" ], "debug": "warning" }),
若是須要分離css單獨打包,可使用 extract-text-webpack-plugin
。
可能會有解決方案,暫時不作深刻探究。仍是推薦直接使用ng-cli。
注意,分離css後,angular的特殊選擇器將失效,好比:host {}
選擇器,使用正常的css方法實現來替代。
注意,樣式的引用就須要經過import './xx.scss';
的方式來引用樣式文件,不然會拋出Expected 'styles' to be an array of strings.
的錯誤。
也有經過"use": ['to-string-loader'].concat(ExtractTextPlugin.extract(<options>))
的方法來實現。
由於不經過@Component({ styleUrls: '' })
的方式,樣式的scope做用將消失。
webpack.config.js
:
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const extractCSS = new ExtractTextPlugin('[name].[contenthash:8].css'); const extractSCSS = new ExtractTextPlugin('[name].[contenthash:8].css'); module.exports = { ... "entry": { ... "styles": [ "./src/app.scss" ] }, "module": { "rules": [ { "test": /\.css$/, "use": extractCSS.extract({ "fallback": "style-loader", "use": [ { "loader": "css-loader", "options": { "sourceMap": false, "import": false } }, { "loader": "postcss-loader", "options": { "ident": "postcss", "plugins": postcssPlugins, "sourceMap": false } }] }) }, { "test": /\.scss$|\.sass$/, "use": extractSCSS.extract({ "fallback": "style-loader", "use": [ { "loader": "css-loader", "options": { "sourceMap": false, "import": false } }, { "loader": "postcss-loader", "options": { "ident": "postcss", "plugins": postcssPlugins, "sourceMap": false } }, { "loader": "sass-loader", "options": { "sourceMap": false, "precision": 8, "includePaths": [] } }] }) }, ], "plugins": [ ... extractCSS, extractSCSS ] } ... }
app.component.ts
import './app.component.scss'; @Component({ selector: 'app-root', templateUrl: './app.component.html' })
publicPath
屬性,能夠設置資源引用路徑,須要寫在output
屬性下:module.exports = { ... "output": { "publicPath": '/', "path": path.join(process.cwd(), "dist"), "filename": "[name].bundle.[chunkhash:8].js", "chunkFilename": "[id].chunk.[chunkhash:8].js", "crossOriginLoading": false }, ... }
若是使用ng-cli,能夠在apps
屬性下設置deployUrl
,等同於publicPath。
個人環境
Angular CLI: 1.6.7 (e)
Node: 8.11.1
OS: win32 x64
Angular: 5.2.3
***
demo源碼
參考文章: angular-cli issues | style-loader issues | stackoverflow | copy-webpack-plugin拷貝資源插件等
The end... Last updated by: Jehorn, Sep 17, 2018, 04:29 PM