猫窝私语 — Makumo's Blog

玛酷猫的温馨小窝,记录生活点点滴滴。

@玛酷猫3月前

01/10
14:39
Ionic(Angular)

Ionic 3 升级 Ionic 4 迁移踩坑系列(三)

接下来就是页面的迁移,先用命令创建原来项目对应的页面,例如登录页面

ionic g page pages/signIn

这是会在src/app/pages目录下面生成sign-in文件夹,里面包含5个文件,相对于Ionic3,除了文件名命名有些许区别外,同时还多了一个测试文件,对比下Ionic3的sign-in.ts和4的sign-in.page.ts文件的主体结构

# ionic3
import { Component, OnInit } from '@angular/core';
@IonicPage({
  segment: 'sign-in'
})
@Component({
  selector: 'page-sign-in',
  templateUrl: 'sign-in.html',
})
export class SignInPage implements OnInit {
  ngOnInit() {}
}

# inoic4
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
  selector: 'app-sign-in',
  templateUrl: './sign-in.page.html',
  styleUrls: ['./sign-in.page.scss'],
  encapsulation: ViewEncapsulation.None
})
export class SignInPage implements OnInit {
  ngOnInit() {}
}

整体上差别不大,主要区别在@Component部分,选择器的命名,模板文件和样式文件的引用,3中的@IonicPage部分在4种移除,部分功能被放在Angular的路由中。此外特别提下encapsulation: ViewEncapsulation.None的问题,这部分我也没完全弄明白,大概理解是Ionic4使用了标准的Web Components,可以在页面上看到大量的shadow-dom,默认情况下,由于Angular提供的样式包装机制来封装组件,使得组件的样式不受外部影响,就是说无法修改组件的样式,所以这里要将封装模式调整成none,以便于修改组件的样式,详细说明请参考官方文档,传送门。此外由于使用标准Web Components,样式也要大量的使用CSS4 Variables的写法。

页面生命周期

在Ionic3中,常用的页面生命周期函数有下面6种

  • ionViewDidLoad 当页面加载的时候触发,仅在页面创建的时候触发一次,如果被缓存了,那么下次再打开这个页面则不会触发
  • ionViewWillEnter 当将要进入页面时触发
  • ionViewDidEnter 当进入页面时触发
  • ionViewWillLeave 当将要从页面离开时触发
  • ionViewDidLeave 离开页面时触发
  • ionViewWillUnload 当页面将要销毁同时页面上元素移除时触发

在Ionic4中,使用Angular的生命周期函数,具体ionViewDidLoad被ngOnInit替代,ionViewCanEnter和ionViewCanLeave被路由守卫替代,其他的暂时可以正常使用,不过可能在以后的版本中被其他用法做代替。

组件的标准化

由于使用Web Components标准,很多组件和属性的写法也发生变化,譬如

// ionic 3
<button type="button" ion-button clear small icon-start (click)="findPassword()" class="option-password">忘记密码</button>

<ion-list radio-group no-lines [(ngModel)]="schoolCode">
  <ion-item *ngFor="let s of schools">
    <ion-avatar item-start><img src="{{s.logo}}"></ion-avatar>
    <ion-label>{{s.name}}</ion-label>
    <ion-radio value="{{s.code}}"></ion-radio>
  </ion-item>
</ion-list>

// ionic 4
<ion-button type="button" fill="clear" size="small" (click)="findPassword()" class="option-password">忘记密码</ion-button>

<ion-list lines="none">
  <ion-radio-group [(ngModel)]="schoolCode">
    <ion-item *ngFor="let s of schools">
      <ion-avatar solt="start"><img [src]="s.logo" alt=""></ion-avatar>
      <ion-label>{{s.name}}</ion-label>
      <ion-radio [value]="s.code"></ion-radio>
    </ion-item>
  </ion-radio-group>
</ion-list>

除了button标签替换成ion-button以外,很多属性也发生变化,譬如button的所有属性都进行了更换,clear更换成fill=“clear”等,其他标签的属性,譬如item-start更换为solt=“start”,此外类似radio-group的用法也发生大变化。由于不少用法发生变化,具体还是对应着官方文档进行迁移,传送门

Ionic 3 升级 Ionic 4 迁移踩坑系列(三)

@玛酷猫4月前

01/4
10:59
Ionic(Angular)

Ionic 3 升级 Ionic 4 迁移踩坑系列(二)

首先是更新NodeJS版本,Angular6要求版本不低于8.X,由于之前安装高版本的nodeJS一直有着不可描述的问题,导致node-sass无法安装,无奈只能安装6.X的版本使用本地文件来强行安装node-sass,不过现在貌似没这个问题了。更新也比较方便,卸掉重新安装就好了。
开启一个新的tab项目,详细的安装可以参考官方文档

ionic start myApp tabs --type=angular

provider迁移
项目建好后,先将原来项目中的provider复制到新项目中,这时候会看到满篇的错误提示,一个一个来调整。
1、storage在Ionic4中不再是默认自带,需要单独安装

npm install --save @ionic/storage

2、换用新的http模块

//ionic3 部分代码
import { Http, Headers, RequestOptions } from '@angular/http';
//……中间其他代码……
const headers = new Headers();
let Token: string = '';
headers.append('Token', Token);
this.options = new RequestOptions({ headers: headers });

//ionic4 部分代码
import { HttpClient, HttpHeaders } from '@angular/common/http';
//……中间其他代码……
const headers = new HttpHeaders();
let Token: string = '';
headers.set('Token', Token);
this.options = { headers };

由于需要和数据服务器API通讯,用户的信息token是存放在请求的header中,在Ionic3中使用的Headers, RequestOptions来处理请求头部,这两个组件在3中也属于不推荐使用的,当时因为还能用,图省事就继续使用了。4种更换为HttpClient、HttpHeaders,和以前方法使用变化不大,调整下就好。

3、调整Components用法,这部分也是整个项目变动最多的地方,几乎所有的Controller都有参数变动和使用方法的变动,想像迁移文档说的那样直接复制进来就能用基本没法实现。举个例子,项目中有个全局的顶部消息提示控件

//ionic3
toast(message: string) {
  this.toastCtrl.create({
    message: message,
    duration: 1500,
    position: 'top'
  }).present();
}

//ionic4
async toast(message: string) {
  const toast = await this.toastCtrl.create({
    message: message,
    duration: 1500,
    position: 'top'
  });
  toast.present();
}

在Ionic3中可以直接创建后接present()来触发使用,而在4中,需要配合async/await来使用。

4、RxJS的调整,官方有个迁移文档,传送门,并且给出了一个迁移工具,不过试了下不是太好用,可能原本的项目写的就不是很规范,只能自动修复一小部分。还是老老实实手动来吧,顺便加深下印象。以下是一个登录的代码片段

//ionic3
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
//……中间其他代码……
PostSignIn(request: PostUserSignInRequest): Observable<any> {
  return this.http.post(this.coreService.baseUrl + '/tokens', request)
    .map(res => res.json());
}

//ionic4
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
//……中间其他代码……
PostSignIn(request: PostUserSignInRequest): Observable<any> {
  return this.http.post(this.coreService.baseUrl + '/tokens', request)
  .pipe(map(res => res));
}

首先是引入调整,这个全项目批量替换下就好,然后是原来的链式操作(dot-chained operators)要更换为管道操作(pipeable operators),简单点说就是将原先所有的.xxx操作全部放置到pipe()这个方法中,其他的用法项目中用到的少,可以去查看官方文档。

Ionic 3 升级 Ionic 4 迁移踩坑系列(二)

@玛酷猫4月前

12/28
16:31
Ionic(Angular)

Ionic 3 升级 Ionic 4 迁移踩坑系列(一)

Angular今年的发展真是快,去年年底5刚刚发布,年中就发布6,到年底7也来了,大版本的升级也导致它的很多组件也随之升级,慢慢的放弃了对低版本的支持,这对于使用基于Angular的Ionic开发的本人来说就是件很郁闷的事情。之前一直使用Ionic 3开发项目,还是基于Angular5的,年中的时候本地开发环境更新组件没注意,直接把Angular升级到6了,然后可想而知,项目直接跑不起来了。还原package.json后每次更新组件都检查下版本,避免错误升级,很是麻烦。随着Ionic4的测试版本不断的更新,Ionic cli动不动就提示更新Ionic4,稍不留神就傻逼,再加上越来越多的组件默认使用Angular 6,主要是其中RxJS的语法变动很大,导致没法向下兼容5,这也将Ionic升级到4的想法提上日程。毕竟ios和android还在不停的更新版本,每次更新或多或少都会出现兼容性问题,组件导致的兼容性问题主要还是靠组件作者更新,虽然可以自己fork一个分支处理,毕竟费时费力。

去Ionic官网翻了翻迁移指南,官方写的实在是太简单,全文如下:

  1. Generate a new project using the blank starter (see Starting an App)
  2. Copy any Angular services from src/providers to src/app/services
    • Services should include { providedIn: ‘root’ } in the @Injectable() decorator. For details, please see Angular provider docs.
  3. Copy the app’s other root level items (pipes, components, etc) keeping in mind that the directory structure changes from src/components to src/app/components, etc.
  4. Copy global Sass styling from src/app/app.scss to src/global.scss
  5. Copy the rest of the application, page by page or feature by feature, keeping the following items in mind:
    • Emulated Shadow DOM is turned on by default
    • Page/component Sass should no longer be wrapped in the page/component tag and should use Angular’s styleUrls option of the @Component decorator
    • RxJS has been updated from v5 to v6 (see RxJS Changes)
    • Certain lifecycle hooks should be replaced by Angular’s hooks (see Lifecycle Events)
    • Markup changes may be required (migration tool available, see Markup Changes)

In many cases, using the Ionic CLI to generate a new object and then copying the code also works very well.

大概意思就是用命令新建一个空的项目,将原来项目各个部分拷贝进来,注意4的路径有所调整。服务的声明有所调整、全局样式文件不一样、RxJS请自己看升级文档、页面生命周期时间沿用Angular。看来不麻烦呀,于是乎我就傻呵呵的按着文档操作一顿后,都不用试跑的,IDE上满屏幕的红色波浪线(各种错误)仿佛在嘲笑。看了看IDE上面提示的各种错误,连最基本的ionic、angular的引用方式都有所变化,我就不信有人能按照这个智障的迁移文档,把项目跑起来。尤其是看到文档最后一句话:“In many cases, using the Ionic CLI to generate a new object and then copying the code also works very well. ”,还信誓旦旦的说大部分情况下按照这个操作项目都能跑起来,我&……¥&*……&#,还是老老实实一个模块一个页面的迁移好了。

经历的大半个月的迁移工作,虽说还没有完全结束,迁移过程中踩的坑可真不少,准备做一个系列来写了。一方面记录下的这些问题,方便以后查看,另一方面也希望帮助其他的人,毕竟目前在国内,相对于React和Vue,Angular还太小众,各种文档和解决方案都很少,遇到问题都要爬google搜索,要不就去啃官方论坛和stackoverflow。

随带一提的是Ionic(Angular)的发展简直是坑神一样的存在,当年AngularJs升级Angular(ionic1到2)的时候,惊呼上当受骗,本以为是个版本升级,能迁移的时候菜发现这明明就是两种不同的东西,没办法相当于重新学了一遍,ionic2/3到4又相当于再重学一边,4为了更标准化web component,修正了以前版本各种不标准的用法和属性,而且可以说不在依赖框架,也就是说以前Ionic和Angular是捆绑在一起的,从4版本开始,也可以使用React和Vue来做开发,更加灵活。并且使用标准化的web component后,以后也不会出现类似1到2、2/3到4这种痛苦的迁移过程了,应该会很平滑的升级,版本迭代的速度应该会更快。

Ionic 3 升级 Ionic 4 迁移踩坑系列(一)

@玛酷猫1年前

11/3
10:46
Ionic(Angular)

Ionic3应用中添加Faye功能

公司rails项目中通过Faye来实现一个简单的好友聊天互动的功能,移动端需要开发简易的教师上课点名的小功能,想想正好可以利用Faye服务来实现,具体逻辑很简单,在课程页面,教师点击点名模块,设置密钥后进入点名页面,通过Faye发布该课程点名信息,学生接收到点名信息后在页面上显示签到按钮,学生点击签到按钮输入密钥,通过Faye服务传递到教师点名页面上,时时反馈签到情况,最后教师通过关闭按钮结束签到并通过Faye告知所有学生,学生页面隐藏签到按钮。

逻辑理顺了,页面搭建完毕,加入Faye犯难了,官方文档只是简单写了下浏览器端如何使用,引入Faye服务端js,然后可以使用subscribe和publish来发布和接受了。在Ionic3项目里面就傻眼了,第一步引入js就不知道咋搞了,网上翻资料,绝大部分都是使用ionic 1.x的资料,度娘里面居然还掺杂一大堆王菲的搜索页面。。。谷歌研究了半天,参考了一篇关于ionic3部署socket.io的文章,终于搞定。

首先还是安装Faye组件

npm install faye --save

新建一个服务provider,初始化同时把相关的推送接收用法写进去

import { Injectable } from '@angular/core';
import * as Faye from 'faye'

@Injectable()

export class FayeService {
  clientUrl: string;
  data: any;
  client: any;

  constructor() {
    this.clientUrl = 'https://your website/faye';
    this.data = null;
    this.client = new Faye.Client(this.clientUrl)
  }

  subscript(callback:any, channelName:string){
    this.client.subscribe('/' + channelName, function (data) {
      callback(data);
      // console.log('receiced data:'+data);
    })
  }

  publish(channelName:string, data:any){
    let publication = this.client.publish('/' + channelName, data);
    publication.then(function() {
    }, function(error) {
      console.log('There was a problem: ' + error.message);
    });
  }

}

最后在app.module.ts中引入这个provider并声明下就可以在项目中使用了。

此外还有一点,由于项目的browser版和接口都启用的ssl,直接使用http://your website:8080/faye 会报错,提示在ssl下访问非ssl存在安全问题balabala之类的,这个需要在nginx中设置端口转发。

在nginx的接口配置文件中添加faye的upstream

upstream faye-socket{
    server 127.0.0.1:8080;
}

接着在配置443的server下面添加转发规则

location /faye {
	proxy_pass http://faye-socket;

	proxy_http_version 1.1;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
	proxy_cache_bypass $http_upgrade; 
}

保存后重新加载nginx配置即可。

nginx -s reload

Ionic3应用中添加Faye功能