NativeScript 是一款跟 ReactNative 對着懟的移動開發技術,其官方欽定了 Angular2 作為推薦的技術框架,那么如何讓在瀏覽器中運行的 Angular2 Web app 項目與 NativeScirpt 項目共享代碼呢?
安裝 git
git 是一個好東西,讓我們首先來安裝他,但與以往的安裝方式稍有不同。
以下內容 linuxer 可以忽略
進入 git 官網,下載最新的 git for windows 客戶端 https://git-scm.com/
安裝過程大多可以無腦下一步,但是在下面的頁面中,要注意一下,勾上Enable symbolic links
。
創建項目倉庫
使用 git init 初始化項目倉庫,然后編輯 .git/config 文件,將 symlinks=false
改成 symlinks=true
。
建立項目
使用 angular-cli 與 nativescript-cli 創建項目,建立項目沒有特別的地方,安裝cli 的說明建立即可,使目錄結構如圖所示:
│ .gitignore
│
├─mobile
│ │ package.json
│ │ references.d.ts
│ │ tsconfig.json
│ │
│ ├─app
│ │
│ ├─hooks
│ │
│ ├─node_modules
│ │
│ └─platforms
│
└─web
│ .editorconfig
│ angular-cli.json
│ karma.conf.js
│ package.json
│ protractor.conf.js
│ README.md
│ tslint.json
│
├─e2e
│
├─node_modules
│
└─src
建立共享代碼文件夾
在 web/src/app 中建立 x-shared 文件夾,表示 cross-platform-shared 的意思。
然后,cd 到 mobile/app 文件夾中,以管理員身份運行命令行(cmd)並輸入:
mklink /d x-shared ..\..\web\src\app\x-shared
這樣我們就使用軟鏈接建立一個共享文件夾
linuxer 請使用 ln 命令建立軟鏈接
然后在任意一個 x-shared 文件夾中建立需要跨平台共享的代碼文件,比如負責 http 請求的 service,這里我來做個示例:
// account.service.ts
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Injectable } from '@angular/core';
// rxjs
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { LoginResult } from '../models/login-result';
import { ErrorJson } from '../models/error';
import { Config } from '../config';
@Injectable()
export class AccountService {
private loginUrl = Config.baseUrl + 'api/account/login';
/**
* 登錄!
*/
constructor(private http: Http) {
}
/**
* 解析消息
*
* @private
* @param {Response} res
* @returns {LoginResult}
*
* @memberOf AccountService
*/
private extractData(res: Response): LoginResult {
let body = res.json();
return <LoginResult>body;
}
/**
* 錯誤處理,將錯誤消息返回,消息形式:錯誤代碼 - 錯誤詳情
*
* @private
* @param {(Response | any)} error
* @returns
*
* @memberOf AccountService
*/
private errorHandler(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = <ErrorJson>error.json();
errMsg = `${body.errorMsg}`;
} else {
errMsg = error.toString();
}
console.log('errMsg');
return Observable.throw(errMsg);
}
/**
* 用戶登錄
*
* @param {string} usr 用戶名
* @param {string} pwd 明文密碼
* @returns 登錄結果
*
* @memberOf AccountService
*/
login(usr: string, pwd: string) {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
let data = {
userName: usr,
password: pwd
};
return this.http.post(this.loginUrl, data, options)
.map(this.extractData)
.catch(this.errorHandler);
}
}
然后分別在兩個項目中引用這個 service。
調用共享的 Service
就像原先一樣來引用共享的 service
// web 版的登錄入口
import { Component, OnInit } from '@angular/core';
import { AccountService, LoginResult, ErrorJson } from 'x-shared'; // 就像原先一樣來引用共享的 service
import { LocalNoticeService, LocalNoticeType } from 'shared/notice.service';
@Component({
selector: 'nav-account',
templateUrl: './nav-account.component.html',
styleUrls: ['./nav-account.component.css'],
providers: [AccountService]
})
export class NavAccountComponent implements OnInit {
constructor(
private account: AccountService,
private localNoticeService: LocalNoticeService // 這是在網頁彈窗的消息服務
) {
}
login(usr: string, pwd: string) {
if (usr === '' || pwd === '') {
// 在網頁上顯示一個提醒
this.localNoticeService.showMsg('登錄失敗', '請輸入用戶名和密碼!', LocalNoticeType.error);
return;
}
// 跨平台的登錄功能
this.account.login(usr, pwd)
.subscribe(
(res: LoginResult) => {
this.localNoticeService.showMsg('登錄成功', '', LocalNoticeType.success);
},
(error: string) => {
this.localNoticeService.showMsg('登錄失敗', error, LocalNoticeType.error);
});
}
ngOnInit() { }
}
// 手機上面的登錄頁面
import { Component, OnInit } from '@angular/core';
import * as dialogs from 'ui/dialogs';
import { AccountService, LoginResult } from '../../x-shared'; // 就像原先一樣來引用共享的 service
@Component({
selector: 'login',
templateUrl: './pages/login/login.component.html',
providers: [AccountService]
})
export class LoginComponent implements OnInit {
constructor(private account: AccountService) { }
ngOnInit() { }
login(usr: string, pwd: string) {
if (usr === '' || pwd === '') {
// 調用原生的 API 彈窗提示
dialogs.alert({
message: '請填寫用戶名和密碼!',
okButtonText: '確定'
});
return;
}
// 跨平台的登錄功能
this.account.login(usr, pwd)
.subscribe(
(res: LoginResult) => {
let options: dialogs.AlertOptions = {
title: '登陸成功',
message: `${res.token.authToken}
${res.token.refreshToken}`,
okButtonText: '確定'
};
dialogs.alert(options).then(() => console.dir(res));
},
(err: string) => {
let options: dialogs.AlertOptions = {
title: '登陸失敗',
message: err,
okButtonText: '確定'
};
dialogs.alert(options);
});
}
}
效果!
web 網頁
手機移動端
至此,我們就實現了網頁版與手機客戶端共享一套代碼的功能,一旦 service 需要發生變動,只需要更改任意一個 x-shared 文件夾的代碼,更改就會同時作用到另一個項目上。
注意!
-
在 windows10 創造者更新之前,創建軟鏈接需要管理員權限,請確保通過使用帶有管理員權限的命令行來克隆倉庫
-
windows 下的軟鏈接只在 Vista 以上的 windows 系統中起作用