跨端开发,混合应用,小程序等
工程化
- 编码规范
- repository规范
- ci/cd
- 测试 单元测试 e2e
- 性能
- 数据上报 requestIdleCallback, restful api
- 用户行为收集 metadata, page/input/click… —> kafka Queue —> ElasticSearch
Tips
刷新/关闭页面前发请求
Navigator.sendBeacon(url, data) 在页面unload时也可以调用
Tips
刷新/关闭页面前发请求
Navigator.sendBeacon(url, data) 在页面unload时也可以调用
移动端‘像素点’密度往往高于桌面显示器,而横纵比例较小,使用移动设备打开页面,可能因为宽度不足产生挤压变形,也可能因为自动像素缩放导致layout变形
在document形成的整个视图中,从一个虚拟的窗口观察,视口较小时,需要借助横纵滚动条才能浏览页面1
<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1″>
视口宽度为设备宽度,初始缩放比例为1,最大缩放比例为1
水平滚动条是极差的用户体验,将视口宽度设置为设备宽度,将页面样式调整到方便垂直滚动浏览
1 | .col-1 {width: 8.33%;} |
1 | /* For width smaller than 400px: */ |
1 | import { Component, ContentChild, Input, TemplateRef } from "@angular/core"; |
position: relative和transform: translateZ(0)将‘窗口’定位,其子元素是宽度超过屏幕宽度的所有滑块的集合,这个超长的‘胶片’用translate3d控制移动,使其某一格可以暴露在窗口中
移动的动画效果为 transition: 0.6s ease-in-out
子组件(Carousel)中,为每个item套用其宿主定义的template,这里用到了NgContainer和TemplateRef,NgContainer使组件可以内嵌视图模板,内嵌的内容(ng-template)用ViewChild读取,此处声明为dataItemTemplateRef,使用NgTemplateOutlet指令将dataItemTemplateRef插入到ng-content位置,同时可以用ngTemplateOutletContext指令为内嵌视图附加一个上下文对象,显然就是为传递循环体的item打造的。
使用#id指定要插入到ng-container的template
官方示例:1
2
3
4
5
6
7
8
9
10<ng-container *ngTemplateOutlet="greet"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr>
<ng-template #greet><span>Hello</span></ng-template>
<ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
<ng-template #svk let-person="localSk"><span>Ahoj {{person}}!</span></ng-template>
其中 myContext = {$implicit: ‘World’, localSk: ‘Svet’};模板中用let-绑定上下文, $implicit(implicit 隐式的)指默认值,也就是例如let-keyXX=”parameterXX”的语法在缺省=”parameterXX”的表达下的值
npm install @angular/sdk
注意该package大版本跟随Angular版本
Angular CDK Layout1
2
3
4
5
6
7
8
9
10
11
12constructor(private breakpointObserver: BreakpointObserver) {
this.breakpointObserver.observe([Breakpoints.XSmall, Breakpoints.Small]).subscribe(result => {
if (result.matches) {
// max-width: 959px
this.windowWidth = { width: `${this.slickWidth}px` };
this.interval = 1;
} else {
this.windowWidth = { width: `${this.slickWidth * 3}px` };
this.interval = 3;
}
});
}
安装并初始化既有项目1
npx eslint --init
安装后自动运行cli提示,选择所需的运行环境(node.js vs browser),模块化风格(es import/export vs CommonJS),是否使用typescript等
配置cli(commandline interface)
package.json1
"lint": "eslint --ext .js src/"
Issue: ‘global’ is not defined
对于使用webpack打包的web app其编译环境是node.js的,因此可以配置
1
2
3
4 "env": {
"browser": true,
"node": true
},
ESLint:环境参数
Kubernetes 也称为 K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统。
工作区配置文件就是angular.json
1 | "MyApp": { |
architect/build 节会为 ng build 命令的选项配置默认值。它具有下列顶层属性:builder,option,configurations.
另外对应ng serve,ng test,ng lint命令,有architect/serve|test|lint命令
1 | "options": { |
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}参数订阅可观察对象的值,从而保持最新,当数据更新时,循环渲染的视图会随之更新,见StackOverflow:*ngFor loop with async pipe?
完整栗子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@Component({
selector: 'users-list',
template: `
<ul>
<li *ngFor="let user of users$ | async">
{{ user.username }}
</li>
</ul>
`
})
export class UsersListComponent {
users$;
constructor(private http: Http) { }
ngOnInit() {
this.users$ = this.http
.get('/api/users')
.map(res => res.json());
}
}
如果需要基于返回值的其他参数,有1
*ngFor="let user of users$ | async as users; index as i"
这里的users就是Array类型了
类似的ngIf也可以用异步管道,形如*ngIf=”user$ | async as user”,显然这里的user$应为Observable\
1 | <div *ngIf="isLoggedIn(); else notLoggedIn"> |
需求是文件上传,往往隐藏input type=”file”而放一个好看的入口。
事件可以直接在HTML的native element上触发,在jQuery中1
$("#fileInput").click();
原生js1
document.getElementById("fileInput").click();
Angular可以使用viewChild获取元素1
2
3
4
5
6
7
8@ViewChild('fileInput') fileInput:ElementRef;
constructor(private renderer:Renderer) {}
showImageBrowseDlg() {
let event = new MouseEvent('click', {bubbles: true});
this.renderer.invokeElementMethod(
this.fileInput.nativeElement, 'dispatchEvent', [event]);
}
MDN:dispatchEvent
通过dom结构定位元素1
2
3
4
5
6
7
8
9
10
11
12
13
14<div style="width: 128px; height: 128px; background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 2px;cursor: pointer;display: flex;justify-content: center;align-items: center;"
(click)="showImageBrowseDlg($event)"> +
<input type="file" style="display: none;" (change)="handleUpload($event)">
</div>
showImageBrowseDlg(event){
if(event.target.children[0]){
const fileinput:HTMLElement = event.target.children[0] as HTMLElement;
fileinput.click();
event.stopPropagation();
}
}
封装一个http的provider,参考ionic3+angular4 HttpClient封装优化
测试驱动开发(Test Driven Development, TDD)和行为驱动开发(Behavior Driven Development, BDD)
Jasmine 通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法, 行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的
被测系统(System under test, SUT)
单测不负责检查跨类或者跨系统的交互逻辑,那都是集成测试的范围
单测不能受到外界环境的影响, 依赖需要用本地实现注入,或者提供一个mock(桩对象)
单测需要能快速执行,有必要在每次修改代码时运行单测
单测应随编码进行,补单测是没有意义的
1 | ├───lib |
spec + src文件夹是栗子
笔记目的导向:
1 | describe("Player", function() { |
1 | npm install --save-dev jasmine |
1 | describe ('HeroesService (with spies)', () => { |
1 | import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; |
1 | expect(true).toBe(true); |
1 | spyOn(foo, 'setBar'); |