0%

Azure SQL包括

  • Azure SQL DB
  • Azure MI (Managed Instance 与SQL Server功能一致的服务,用于云迁移方案)
  • SQL Server in Azure VM
    Azure SQL DB 和 Azure MI属于PaaS, SQL Server in Azure VM属于IaaS

    可用性功能

    可用性区域和可用性组。较低服务层级中的数据库使用“不同但等效的机制”通过存储提供冗余。 内置逻辑可帮助防范单个计算机发生故障。 使用活动异地复制功能可以在灾难损毁整个区域时提供保护。
    业务连续性和全局可伸缩性。

安装并初始化既有项目

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.json

1
"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:环境参数

Issues:

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

工作区配置文件就是angular.json

关于当前Project

1
2
3
4
5
6
7
8
"MyApp": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {}
},

编译/测试等工具

关于build target

architect/build 节会为 ng build 命令的选项配置默认值。它具有下列顶层属性:builder,option,configurations.
另外对应ng serve,ng test,ng lint命令,有architect/serve|test|lint命令

  • builder就是个编译器名字,默认是@angular-devkit/build-angular:browser,(ng test的是karma,ng lint的是tslint)
  • options提供构建时的选项及默认值,私以为这些option可以认为是builder工具链所需参数,因而在test和lint中也各有不同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    "options": {
    "aot": true,
    "progress": false,
    "extractCss": true,
    "outputPath": "dist",
    "index": "src/index.html",
    "main": "src/main.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "src/tsconfig.app.json",
    "assets": [
    "src/favicon.ico",
    "src/assets",
    {
    "glob": "**/*",
    "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
    "output": "/assets/"
    }
    ],
    "styles": [
    "src/theme.less",
    "src/styles.scss"
    ],
    "scripts": []
    }
  • configurations 脚手架生成项目会添加一个production的配置在这里,对编译进行部分优化以及打包限制,ng build 带—prod参数(注意—xxx是命令参数)使用该production配置,可以仿照production写其他(如 stage)配置,使用时形如 ng build —configuration stage
    另配置可以加载复数个,后者的项会覆盖前者:ng build —configuraion staging,fr
    “configurations”: {
    “production”: {
      "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"
      }
      ]
    
    }
    }
    ```

    样式预处理选项

    Angular Doc:Styles and scripts configuration

ngFor loop with async pipe

参数订阅可观察对象的值,从而保持最新,当数据更新时,循环渲染的视图会随之更新,见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\

ngIf else

1
2
3
4
5
6
<div *ngIf="isLoggedIn(); else notLoggedIn">
Hi, {%raw%}{{ user.name }}!{%endraw%}
</div>
<ng-template #notLoggedIn>
You're not logged in.
</ng-template>

事件‘委托’

需求是文件上传,往往隐藏input type=”file”而放一个好看的入口。
事件可以直接在HTML的native element上触发,在jQuery中

1
$("#fileInput").click();

原生js
1
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();
}
}

测试驱动开发(Test Driven Development, TDD)和行为驱动开发(Behavior Driven Development, BDD)
Jasmine 通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法, 行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的

被测系统(System under test, SUT)

单测不负责检查跨类或者跨系统的交互逻辑,那都是集成测试的范围

单测不能受到外界环境的影响, 依赖需要用本地实现注入,或者提供一个mock(桩对象)

单测需要能快速执行,有必要在每次修改代码时运行单测

单测应随编码进行,补单测是没有意义的

1
2
3
4
5
6
7
8
9
10
11
12
13
├───lib
│ └───jasmine-3.4.0
│ boot.js
│ jasmine-html.js
│ jasmine.css
│ jasmine.js
│ jasmine_favicon.png
├───spec
│ PlayerSpec.js
│ SpecHelper.js
└───src
Player.js
Song.js

spec + src文件夹是栗子

笔记目的导向:

  • spec.ts写的啥(Jasmine 单元测试的书写语法)
  • 组件声明周期中应该存在哪些用例(如实例化,输入/输出,方法,释放)
  • 依赖项的处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    describe("Player", function() {
    var player;
    var song;

    beforeEach(function() {
    player = new Player();
    song = new Song();
    });

    it("should be able to play a Song", function() {
    player.play(song);
    expect(player.currentlyPlayingSong).toEqual(song);

    //demonstrates use of custom matcher
    expect(player).toBePlaying(song);
    });

    describe("when song has been paused", function() {
    beforeEach(function() {
    player.play(song);
    player.pause();
    });

    it("should indicate that the song is currently paused", function() {
    expect(player.isPlaying).toBeFalsy();

    // demonstrates use of 'not' with a custom matcher
    expect(player).not.toBePlaying(song);
    });

    it("should be possible to resume", function() {
    player.resume();
    expect(player.isPlaying).toBeTruthy();
    expect(player.currentlyPlayingSong).toEqual(song);
    });
    });

    // demonstrates use of spies to intercept and test method calls
    it("tells the current song if the user has made it a favorite", function() {
    spyOn(song, 'persistFavoriteStatus');

    player.play(song);
    player.makeFavorite();

    expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
    });

    //demonstrates use of expected exceptions
    describe("#resume", function() {
    it("should throw an exception if song is already playing", function() {
    player.play(song);

    expect(function() {
    player.resume();
    }).toThrowError("song is already playing");
    });
    });
    });
    init with Node.js
    1
    2
    npm install --save-dev jasmine
    npx jasmine init

    describe

    以describe分组specs,它代表一组相似的测试用例,通常有 2 个参数:字符串和方法。字符串作为特定 Suite 的名字和标题。方法是包含实现的代码。
    1
    2
    3
    4
    5
    6
    describe ('HeroesService (with spies)', () => {
    ...
    });
    describe('HeroesService (with mocks)', () => {
    ...
    });

    waitForAsync TestBed

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
    describe('nz-table', () => {
    let injector: Injector;
    beforeEach(
    waitForAsync(() => { //
    injector = TestBed.configureTestingModule({
    imports: [BidiModule, NzTableModule],
    declarations: [NzTestTableBasicComponent, NzTestTableScrollComponent, NzTableSpecCrashComponent, NzTestTableRtlComponent]
    });
    TestBed.compileComponents();
    })
    );
    ...
    })

    it 和 specs

    specs即specification(规则),它们是一个个断言,可以是 true 或者 false。当每个 Spec 中的所有 expectations 都是 true,则通过测试。以it函数定义,与describe类似的,有 2 个参数:标题和方法。

    expect tobe

    1
    2
    expect(true).toBe(true);
    expect(false).not.toBe(true);

    断言

  • toBe 和 toEqual 前者相当于比较运算符=== 后者比较字面量的值(对于对象进行属性的比较)
  • toMatch
  • toBeDefined 和 toBeNull
  • toContain
  • toBeGreaterThan 和 toBeLessThan
  • toBeCloseTo
  • toThrow

    beforeEach和afterEach

    分别在每个it断言测试前/后调用

    spy

    存根(stub)和跟踪(track)任意函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    spyOn(foo, 'setBar');
    foo.setBar(123);
    foo.setBar(456, 'another param');
    });
    it("tracks that the spy was called", function() {
    expect(foo.setBar).toHaveBeenCalled();
    });
    it("tracks that the spy was called x times", function() {
    expect(foo.setBar).toHaveBeenCalledTimes(2);
    });
    it("tracks all the arguments of its calls", function() {
    expect(foo.setBar).toHaveBeenCalledWith(123);
    expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
    });
    spy一个foo对象上的setBar方法,分别断言该方法被调用、被调用若干次、被以某某参数调用
    很多时候用spy对应模拟对象

用例

Azure DevOps Engineer Expert

DevOps 是人员、流程和产品的集合体现,它可让我们向最终用户持续交付价值。 ———— Donovan Brown

Azure DevOps

Azure DevOps 是 Microsoft 提供的一种软件即服务 (SaaS) 平台,它能提供用于开发和部署软件的端到端 DevOps 工具链。
组成

  • Azure Repos 源代码管理
  • Azure Pipelines CI/CD服务
  • Azure Boards 类似TP的kanban工具以及Agile tools等
  • Azure Test Plans 测试工具,包括manual/exploratory testing 和 continuous testing
  • Azure Artifacts 大致上就是构建自己的库(allows teams to share packages such as Maven, npm, NuGet and more from public and private sources and integrate package sharing into your CI/CD pipelines)

生产DevOps的内容

  • 操作系统
  • 脚本
  • 容器
  • 其他

琐碎

staging vs deployment slot
它们是两个不同层面、维度的概念,但它们经常被结合使用,以实现高效的软件发布流程。

staging环境介于开发和生产之间的独立、隔离的运行环境。用于模拟生产环境以发现问题。deployment slot是云平台提供的 可运行同一个应用的多个版本(比如生产版和暂存版)
亦可做蓝绿部署和版本回退

对于branch

对于敏捷开发的团队,选择避免使用长期分支(long-lived branch),以致力于短期功能和 Bug 修复分支,任何工作付出的目标都是以生成pull request将工作合并回master

A long-lived branch is a Git branch that is never deleted. Some teams prefer to avoid them altogether in favor of short-lived feature and bug fix branches. For those teams, the goal of any effort is to produce a pull request that merges their work back into master.
对于web应用,往往不会支持或回退到起初的版本,适用于上述工作方式,但也有其他场景需要长期保留分支,如用于同时支持市场上的多个版本,release V1, release V2将持续维护

来源微软Docs:GitHub基于Release的工作流

master是稳定版分支,与线上版本保持绝对一致,release是预发布分支,从develop创建出来进行测试。

关于release

语义化版本标签
release notes(存目)

配置和管理虚拟网络占比30-35%
目标导向:

  • 学习创建虚拟网络
  • 创建虚拟VPN网关
  • 认识使用ExpressRouter

    虚拟网络

    功能:
  • 隔离和分割(Isolation & segmentation)
  • 网络通信、资源间通信、与本地网络(on-premises)通信
  • 连接虚拟网络
  • 路由|筛选|连接网络流量(route|filter network traffic)

VPN(virtual private networks):

  • Point-to-site
  • Site-to-site
  • Azure ExpressRoute

    Network Monitor

  • 监视vm与endpoint(可以是其他vm)之间的通信
  • 查看vnet中的资源及其关系
  • 诊断(Diagnose)出入vm的网络流量筛选问题
  • 诊断vm网络路由问题
  • 诊断vm出站连接(outbound connections)
  • 捕获出入vm的数据包
  • 诊断vnet网关与连接的问题
  • 检查区域与internet相对延迟
  • 查看安全规则

    概念

    IP地址空间:举个栗子地址空间192.168.1.0/24,子网掩码255.255.255.248。子网掩码用来指明某个IP地址哪些位是网络位,哪些是主机位,同网络位IP之间的通信不需要通过网关,主机位数值就是有多少主机。IP总共32位,‘/24’是指前24位都是网络位,主机坐在的网络,248即11111000,这个网络有可以有 25 即32个子网,每个子网可分配地址为 23 - 2(减去广播地址和网络地址),为6个

    虚拟机通过虚拟网络通信的实践

    创建虚拟网络,名为default
    1
    2
    $Subnet=New-AzVirtualNetworkSubnetConfig -Name default -AddressPrefix 10.0.0.0/24
    New-AzVirtualNetwork -Name myVnet -ResourceGroupName vm-networks -Location $Location -AddressPrefix 10.0.0.0/16 -Subnet $Subnet
    使用powershell创建两个Azure VM
    1
    2
    3
    4
    5
    6
    7
    New-AzVm `
    -ResourceGroupName "vm-networks" `
    -Name "testvm1" `
    -VirtualNetworkName "myVnet" `
    -SubnetName "default" `
    -image "Win2016Datacenter" `
    -Size "Standard_DS2_v2"
    *取消其中一台的公共IP
    1
    2
    3
    $nic = Get-AzNetworkInterface -Name testvm2 -ResourceGroup vm-networks
    $nic.IpConfigurations.publicipaddress.id = $null
    Set-AzNetworkInterface -NetworkInterface $nic
    使用PublicIP远程VM1,在VM1使用计算机名访问同一虚拟网络的VM2

VPN网关

Azure虚拟网关为‘从本地到Azure’的传入连接提供一个endpoint,VPN网关是一种虚拟网关类型,可以作为加密的endpoint,在Azure的实践中,VPN网关用以在不同区域之间安全地链接虚拟机和服务