跨端开发,混合应用,小程序等
工程化
- 编码规范
- 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 | <picture> |
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 | npm eslint --init |
安装后自动运行cli提示,选择所需的运行环境(node.js vs browser),模块化风格(es import/export vs CommonJS),是否使用typescript等
之后会向package.json添加并安装@typescript-eslint/eslint-plugin eslint-plugin-react @typescript-eslint/parser eslint等
配置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:环境参数
Arrow function expected no return value. (consistent-return)
141:7 error React Hook useEffect has a missing dependency: ‘render’. Either include it or remove the dependency array react-hooks/exhaustive-deps
render 方法中包含state属性 应改为useCallback 加入依赖state属性, 然后把render加入报错的副作用的依赖中
Expected ‘this’ to be used by class method ‘getValBetweenFms’. (class-methods-use-this)
改为static方法
Expected to return a value at the end of method ‘getValBetweenFms’. (consistent-return)
函数在循环体中某条件达成时返回 运行时必定返回 但静态类型检查不通过 应在函数末尾return undefined
Kubernetes 也称为 K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统。
一句话概括:
Docker 负责把应用打包并运行成容器,Kubernetes 负责管理大规模容器集群,K3s 是 Kubernetes 的轻量版发行版。
这三个概念不是同一层面的东西:
Linux 很早就有容器相关能力,例如:
但这些底层能力对普通开发者并不友好。Docker 的重要贡献是把容器打包、分发和运行流程标准化:
1 | Dockerfile |
常见命令:
1 | docker build |
Docker 主要解决的问题是:
如何把一个应用及其依赖打包成一个可移植、可运行的容器。
例如:
1 | docker run nginx |
就可以快速启动一个 Nginx 容器。
当容器数量较少时,Docker 可以很好地完成本地开发和单机运行。但如果有很多机器和大量容器,例如:
1 | 100 台服务器 |
就会出现新的问题:
Docker 本身更擅长管理一台机器上的容器,不负责整个集群的调度和编排。
Kubernetes 解决的是:
如何管理一个集群中大量容器化应用的部署、调度、扩缩容、自愈和服务发现。
Kubernetes,简称 K8s,由 Google 基于内部 Borg 系统的经验开源而来。
可以这样理解:
1 | Kubernetes 负责决策和调度 |
Kubernetes 会根据用户声明的目标状态做出调度决策,例如:
一个形象的比喻:
港口不制造集装箱,但它负责决定集装箱放到哪里、从哪里运走、哪个仓库可用、哪条路线更合适。
Kubernetes 直接管理的最小调度单位不是 Container,而是 Pod。
一个 Pod 可以包含一个或多个容器:
1 | Pod |
因此层级关系可以理解为:
1 | Kubernetes |
实际开发中,最常接触的 Kubernetes 对象包括:
早期 Kubernetes 可以通过 Docker Engine 运行容器:
1 | Kubernetes |
后来 Kubernetes 去掉了对 Docker Engine 作为容器运行时的直接支持。这个变化容易被误解为“Kubernetes 废弃了 Docker”。
更准确的说法是:
Kubernetes 不再直接把 Docker Engine 当作容器运行时,但 Docker 仍然是常用的镜像构建和本地开发工具。
Kubernetes 真正需要的是符合 CRI 标准的容器运行时,例如 containerd。Docker Engine 内部本来也会调用 containerd,因此现在 Kubernetes 通常直接使用 containerd:
1 | Kubernetes |
这样少了一层 Docker Engine,结构更清晰,也更适合生产集群。
Docker 仍然非常重要,尤其是在开发阶段:
1 | 写代码 |
现代云原生流程通常是:
1 | 开发者 |
所以 Docker 与 Kubernetes 更多是互补关系:
Docker Compose 适合在一台机器上管理多个容器:
1 | docker compose up |
例如在本地同时启动:
但 Docker Compose 不擅长:
因此 Docker Compose 常用于本地开发和小规模单机部署,而 Kubernetes 面向更复杂的集群化生产环境。
K3s 是 Rancher 推出的轻量级 Kubernetes 发行版。它仍然是 Kubernetes,核心概念和 API 大部分保持一致,只是对安装方式、依赖和默认组件做了简化。
K3s 的特点:
安装示例:
1 | curl -sfL https://get.k3s.io | sh - |
K8s 与 K3s 对比:
| 项目 | K8s | K3s |
|---|---|---|
| 功能完整度 | 完整 | 保留大多数核心能力 |
| 安装复杂度 | 较高 | 较低 |
| 资源占用 | 较高 | 较低 |
| 默认数据库 | etcd | SQLite,或高可用场景下使用 etcd |
| 适合场景 | 大型生产集群 | 中小规模、边缘设备、开发测试 |
学习 K3s 时掌握的 Pod、Deployment、Service、Ingress、ConfigMap、Secret 等概念,绝大多数都可以迁移到标准 Kubernetes。
如果目标是机器人软件架构、数字孪生、WebGL 仿真、AI Agent 系统或云原生方向,可以按下面顺序学习:
对于机器人平台或 AI Native 系统,常见部署方式是:
工作区配置文件就是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'); |