-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
487 lines (287 loc) · 205 KB
/
atom.xml
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>向宇的博客 | Xiang Yu's Blog</title>
<subtitle>Hello World, Hello Programming!</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://kisky3.github.io/"/>
<updated>2022-05-16T14:40:09.449Z</updated>
<id>https://kisky3.github.io/</id>
<author>
<name>Xiang Yu</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>AWS Certified Solutions Architect – Associate Memo</title>
<link href="https://kisky3.github.io/2022/05/15/AWSSAAMEMO/"/>
<id>https://kisky3.github.io/2022/05/15/AWSSAAMEMO/</id>
<published>2022-05-15T14:14:37.000Z</published>
<updated>2022-05-16T14:40:09.449Z</updated>
<content type="html"><![CDATA[<p>AWS AWS 助理解决方案架构师认证 学习笔记汇总</p><a id="more"></a><p>to be continued..</p><h2 id="S3"><a href="#S3" class="headerlink" title="S3"></a>S3</h2><p>・デフォルト設定でパフォーマンスの最大化が可能<br>・CORS(Cross-Origin Resource Sharing)<br> 別サーバのドメインに対して共有できるようにすること。<br> →Ajax 通信を使用したアプリケーションの構築が可能<br>・特定の IP アドレスからのアクセスは IAM ロールで設定。SG での制御はできない!<br>・S3 は書き込み後の読み取りの強い整合性モデルを利用しているため、反映に誤差が生じることはありません。<br>・デフォルトで高可用性であり 1 つの AZ に依存していない<br>・S3 のクロスリージョンレプリケーションとは S3 のオブジェクトを別リージョンのバケットに自動複製するサービス。<br> オブジェクトの登録と同時に実行される。バージョニングが有効化されている必要あり。<br>・サーバーサイドの暗号化は SSE-S3(暗号化方式は AES-256)。<br> 暗号化、復号化は自動。暗号化キーの管理に手間がかからない。<br> バケットの暗号化によりログの暗号化も自動で実行される。<br>・Access Analyzer で不正アクセスの確認、アクセス権限の最小化構成のモニタリングが可能<br>・ストレージクラス分析で適切なデータをいつ適切なストレージクラスに移行するかを判断可能<br>・全てのデータに対するパブリックアクセスを拒否する場合、S3 パブリックアクセス設定機能でブロックを有効化する<br>・Amazon S3 のパフォーマンスを最大化することができる設定<br> 『日付ベースの順次命名を使用してオブジェクトを保存すること』でアクセス時のパフォーマンスを最大化。<br>・S3 イベント通知<br> SNS、SQS、Lambda<br>・マルチアップロード<br> CLI で aws s3 cp コマンド</p><h2 id="ストレージクラス"><a href="#ストレージクラス" class="headerlink" title="ストレージクラス"></a>ストレージクラス</h2><p>・S3 Standard<br> 標準。アクセス頻度の高いデータ向け<br>・S3 Intelligent-Tiering<br> アクセスパターンが不明または変化するデータに対して自動的にコストを削減する。<br> 低頻度アクセスのオブジェクトを自動的に低頻度アクセス層に移動できる</p><p>・S3 Standard-Infrequent Access (S3 Standard-IA)<br> Infrequency Access =まれなアクセス<br> 低頻度アクセス用のストリームタイプ<br> IA は標準と One-zone の2つがありますが、重要なデータは標準 IA を利用することになります。<br> 1Zone は永続性なし。</p><h2 id="S3-Glacier"><a href="#S3-Glacier" class="headerlink" title="S3 Glacier"></a>S3 Glacier</h2><p>アーカイブ最大容量 250MB</p><p>データ取得時間<br> 迅速 - 1 ~ 5 分<br> 標準 - 3 ~ 5 時間<br> 大容量 - 5 ~ 12 時間<br>※Glacier Deep Archive<br> 標準 - ~ 12 時間<br> 大容量 - ~ 48 時間<br> データ保持期間<br> 最低 - 90 日</p><h2 id="Amazon-Athena"><a href="#Amazon-Athena" class="headerlink" title="Amazon Athena"></a>Amazon Athena</h2><p>データ解析が最も容易にコスト最適に実現。サーバレス。<br>Amazon S3 から直接データに対して SQL クエリ処理が可能</p><h2 id="CloudFront"><a href="#CloudFront" class="headerlink" title="CloudFront"></a>CloudFront</h2><p>ウェブアプリケーションのコンテンツ配信処理を向上させる<br>ALIAS コード<br>*S3 との連携がポイント!HTTP ベース</p><p>Amazon S3 Transfer Acceleration でクライアントと S3 の間で長距離にわたるファイル転送を高速・簡単・安全に行う。<br>Transfer Acceleration は CroudFront のエッジロケーションを利用<br>CloudFront のディストリビューションに WAF で作成した WEB ACL(WAF の設定の塊)を関連づけることでアクセス制御が可能</p><p>× CroudFront における Referer 制限を実施する<br> ○ AWS WAF で Referer によって制御する。<br>オリジンアクセスアイデンティティ (OAI) と呼ばれる特別な CloudFront ユーザーを作成してアクセス制限が可能</p><p>・コストはトラフィックの分散地域、リクエスト数、データ転送アウトにかかる</p><p>・AWS Global Accelerator<br> 1 つ以上の AWS リージョンで実行されているアプリケーションにエッジでパケットをプロキシすることにより、<br>TCP または UDP を介した幅広いアプリケーションのパフォーマンスを向上させます。<br>アプリケーションをエッジロケーションで実行。CDN のアプリケーション版。</p><h2 id="Amazon-EKS"><a href="#Amazon-EKS" class="headerlink" title="Amazon EKS"></a>Amazon EKS</h2><p>Amazon Elastic Kubernetes Service (Amazon EKS) は、フルマネージド型の Kubernetes サービスです。<br>Amazon ECS は Kubernetes を利用していません</p><h2 id="Amazon-ECS"><a href="#Amazon-ECS" class="headerlink" title="Amazon ECS"></a>Amazon ECS</h2><p>Amazon Elastic Container Service (ECS) は、非常にスケーラブルで高性能なコンテナ管理サービスで、Docker コンテナのみ対応。Docker コンテナサービスです。</p><h2 id="AWS-Fargate"><a href="#AWS-Fargate" class="headerlink" title="AWS Fargate"></a>AWS Fargate</h2><p>Amazon ECS と Amazon EKS の両方で動作するフルマネージドサービス(サーバレス)。<br>管理は AWS 側となるため、ユーザーでは不要。</p><h2 id="Lambda"><a href="#Lambda" class="headerlink" title="Lambda"></a>Lambda</h2><p>最大一時実行ボリューム 512MB<br>デフォルトアイムアウト時間 3 秒。最大実行時間は 15 分<br>API ゲートウェイは URL(API エンドポイント)を用意できる<br>環境変数によって、機密情報を保存できる。</p><h2 id="SQS"><a href="#SQS" class="headerlink" title="SQS"></a>SQS</h2><p>・メッセージサイズ<br> 最大 - 256KB<br> ※拡張クライアントライブラリを利用すると最大 2GB まで<br>・メッセージ保存期間<br> デフォルト - 4 日<br> 最小 - 60 秒<br> 最大 - 14 日<br> 優先度の設定が可能<br> Auto Scaling トリガーを構成する場合は SQS のキューサイズを確認する<br>・メッセージ上限数<br> 無制限<br>・可視性タイムアウト<br> 重複を無くすことができる。</p><h2 id="EBS"><a href="#EBS" class="headerlink" title="EBS"></a>EBS</h2><p>・DLM(Data Lifecycle Manager)を使用すると定時バックアップが可能<br> EBS 自体には、SnapShot のライフサイクル機能、レプリケーションという機能はない<br>・バックアップは最初はフルバックアップでその後は増分バックアップ<br>・プロビジョンド IOPS のみ複数の EC2 インスタンスをアタッチできる(2020/02 ~)何百は無理。<br>・アクセス頻度の低いが大切なデータの保存のストレージには EBS のコールド HDD を選択<br>・レガシーアプリの独自のファイルシステムも対応可能。</p><h2 id="Amazon-KMS"><a href="#Amazon-KMS" class="headerlink" title="Amazon KMS"></a>Amazon KMS</h2><p>AWS Key Management Service (KMS) を使用することで、キーを簡単に作成・管理し、幅広い AWS のサービスやアプリケーションで暗号化の使用を制御できるようになります。EBS に対応しており、KMS により EBS に保存されたデータを暗号化することができます。</p><h2 id="S3-における暗号化方式"><a href="#S3-における暗号化方式" class="headerlink" title="S3 における暗号化方式"></a>S3 における暗号化方式</h2><p>SSE-S3・・・Amazon S3 がキーを管理。キーは定期的に更新される。(暗号化方式は AES-256)<br>SSE-KMS・・・ユーザが KMS でキーを生成して管理。<br>SSE-C・・・ユーザーがキーを準備し管理。</p><h2 id="AWS-CloudHSM"><a href="#AWS-CloudHSM" class="headerlink" title="AWS CloudHSM"></a>AWS CloudHSM</h2><p>EU などの各国の厳しいセキュリティ基準を満たす。</p><h2 id="RDS"><a href="#RDS" class="headerlink" title="RDS"></a>RDS</h2><p>・マルチ AZ 配置を有効化することで DB インスタンスの高可用性及びフェイルオーバーサポートを提供<br> ※Redshift はマルチ AZ 構成は不可<br>・自動的にバックアップを取得し S3 に保存<br>・スナップショットを元に DB インスタンスを作成してリストアが可能。データ復旧、災害対策、システム拡張に利用できる。<br>・フェールオーバーすると DB インスタンスの CNAME レコードを自動で切り替える<br>・RDS Proxy(プロキシ)がアプリ(lambda とか)と RDS を仲介する<br> Lambda を RDS と連携させたいときに利用。利用しないとデータセッションを効率的に継続できない<br>・RDS の拡張モニタリングを有効化すると CPU の使用率が CloudWatchLogs で表示できる<br>・シャーディングが可能<br>・データベースの暗号化はサーバサイド暗号化を有効にする。<br>・RDS の PostgreSQL は Aurora に比較すると性能が劣る(RDS PostgreSQL < Aurora)<br>・Storage Auto Scaling 可能。</p><h2 id="AWS-Secrets-Manager"><a href="#AWS-Secrets-Manager" class="headerlink" title="AWS Secrets Manager"></a>AWS Secrets Manager</h2><p>パスワード保存に使用。パスワードの自動ローテションも可能。</p><h2 id="Redshift"><a href="#Redshift" class="headerlink" title="Redshift"></a>Redshift</h2><p>・Redshift を暗号化する。<br> AWS KMS と HSM</p><p>・WLM(Work Load Management)<br> Redshift のクエリ処理に対して割り当てる Redshift のリソースを指定する機能。Redshift の SQS みたいなもの。</p><h2 id="DynamoDB"><a href="#DynamoDB" class="headerlink" title="DynamoDB"></a>DynamoDB</h2><p>・メタデータ・ユーザー設定・セッションデータや一連のストリームデータを蓄積することでビッグデータ解析に利用できる。シームレス。フルマネージドサービス。強力な整合性。集中管理。<br>・DynamoDB ストリームの有効化により、イベントによる Lambda 関数起動などが可能(※DynamoDB イベントという機能は無い)S3 内は解析不可。<br>・DynamoDB Accelerator(DAX)はコスト高い!リードレプリカを増設する際は DAX を有効化する必要がある<br>・Auto Scaling 可能<br>・グローバルセカンダリインデックス<br> テーブルとはパーティションキー または パーティション/ソートキーが異なるインデックス。<br>・ローカルセカンダリインデックス<br> テーブルとパーティションキーは同じですが、ソートキーが異なるインデックス</p><h2 id="CloudWatch"><a href="#CloudWatch" class="headerlink" title="CloudWatch"></a>CloudWatch</h2><p>CloudWatch エージェントのインストールにより EC2 とオンプレミスサーバーのログの詳細なログを収集し、CloudWatchLogs で取得したログを集約できる。<br>・カスタムメトリックス・・・メモリ使用率</p><h2 id="CloudFormation"><a href="#CloudFormation" class="headerlink" title="CloudFormation"></a>CloudFormation</h2><p>テンプレート<br>例)<br>NetworkACL<br>RuleNumber:100<br>RuleAction:allow<br>Egress:true ※アウトバウンドトラフィック設定。false だとインバウンド。<br>・・・<br>(以下略)<br>※port 番号<br>80/HTTP<br>20/FTP<br>22/SSH<br>3389/TCP&UDP リモートデスクトップ接続</p><p>・CloudFormation スタックセット<br> 複数の AWS アカウントやリージョンにリソースを展開<br>・CloudFormation ドリフト<br> リソースと CloudFormation テンプレートの内容に乖離があることをドリフトという。</p><h2 id="Amazon-EMR"><a href="#Amazon-EMR" class="headerlink" title="Amazon EMR"></a>Amazon EMR</h2><p>Amazon EMR は、オープンソースのフレームワークである Apache Spark と Hadoop を使用して、膨大な量のデータを迅速かつコスト効率よく処理して分析するサービス。<br>活用例:マシーンラーニング、データ抽出、変換、読み込み (ETL)、ストリーム分析、リアルタイムストリーミングデータ蓄積、<br> インタラクティブ分析(チャート表示など)、ゲノミクス(ゲノム、遺伝子研究のための分析)</p><h2 id="ElastiCache"><a href="#ElastiCache" class="headerlink" title="ElastiCache"></a>ElastiCache</h2><p>RDS などの DB(NoSQL 型)の読み込み処理を高速化する<br>可用性、性能は、Memcached < Redis<br>Redis は静的コンテンツはキャッシュできない。</p><h2 id="AWS-Elastic-Beanstalk"><a href="#AWS-Elastic-Beanstalk" class="headerlink" title="AWS Elastic Beanstalk"></a>AWS Elastic Beanstalk</h2><p>Docker の仕組みを利用して構成したアプリケーションを展開する<br>アプリケーションのバージョン管理や状態の監視の詳細を自動化する。運用ダッシュボードと Linux OS のバッチ配布も可能。<br>ウェブアプリケーションやワーカー環境の構築に利用される<br>RDS と長時間実行ワーカーに適している。</p><h2 id="AWS-STS"><a href="#AWS-STS" class="headerlink" title="AWS STS"></a>AWS STS</h2><p>高セキュリティ。AWS セキュリティトークンサービス(AWS STS)を使用して、信頼されたユーザーを作成し、AWS リソースへのアクセスを制御できる一時的なセキュリティ認証情報を提供することが可能。 一時的な認証情報は、「アクセスキー」と「シークレットアクセスキー」、「セッショントークン」の 3 つから成立。OpenID Connect 互換は、ウェブ ID フェデレーション(ID 連携)によるアクセス認証。</p><h2 id="Amazon-SES"><a href="#Amazon-SES" class="headerlink" title="Amazon SES"></a>Amazon SES</h2><p>・SNS でもメール通知は可能ですが、特定 IAM ユーザーへのメール通知に向いており、不特定多数の一般ユーザーへのメール通知には SES を利用する。</p><p>ウェブ ID フェデレーション(ID 連携)<br>必要に応じて一時的な AWS セキュリティ認証情報を動的に要求するようにアプリを構築します。提供される一時的な認証情報は、モバイルアプリケーションで必要とされるタスクの実行に必要なアクセス許可のみを持つ AWS ロールにマッピングされます。</p><h2 id="AWS-Storage-Gateway"><a href="#AWS-Storage-Gateway" class="headerlink" title="AWS Storage Gateway"></a>AWS Storage Gateway</h2><p>オンプレミスから実質無制限のクラウドストレージへのアクセスを提供するハイブリッドクラウドストレージサービス<br>iSCSI、SMB、NFS</p><p>・Amazon S3 ファイルゲートウェイ<br> NFS や SMB プロトコルを使用して、S3 でオブジェクトの保存と取得を実行。</p><p>・Amazon FSx ファイルゲートウェイ<br> SMB プロトコルを使用して Amazon FSx for Windows ファイルサーバーでファイルを保存および取得<br> AD 統合可能。</p><p>・ボリュームゲートウェイ<br> >キャッシュ型ボリュームゲートウェイ(iSCSI)<br> プライマリデータは S3 に格納。アクセス頻度の高いデータはローカルにキャッシュする。<br> >保管型ボリュームゲートウェイ(iSCSI)<br> プライマリデータはオンプレに格納。データ全体が低レイテンシーアクセス可能。</p>]]></content>
<summary type="html">
<p>AWS AWS 助理解决方案架构师认证 学习笔记汇总</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="AWS" scheme="https://kisky3.github.io/tags/AWS/"/>
<category term="Certified Solutions Architect" scheme="https://kisky3.github.io/tags/Certified-Solutions-Architect/"/>
</entry>
<entry>
<title>Atomic Design Memo</title>
<link href="https://kisky3.github.io/2022/04/29/atomicDesign/"/>
<id>https://kisky3.github.io/2022/04/29/atomicDesign/</id>
<published>2022-04-29T14:13:13.000Z</published>
<updated>2022-04-29T15:35:39.681Z</updated>
<content type="html"><![CDATA[<p>原子设计理念</p><a id="more"></a><h2 id="为什么要有原子设计理念?"><a href="#为什么要有原子设计理念?" class="headerlink" title="为什么要有原子设计理念?"></a>为什么要有原子设计理念?</h2><p>如果产品规模小只需要 1,2 个设计师,大概率是前期不需要大费周章搞一个需要开发配合的设计系统。可以只利用 Sketch 或者 Figma 做一个像贴纸一样的设计组件库。但是功能多了怎么办,要招人,当新设计师产出后就会发现与原设计师设计的风格不一样,慢慢发现整体设计风格不统一,只靠一个贴纸组件库很难能满足新功能需求,新功能贴纸上没有设计或者设计需要变通,又或者是新设计师忙着交工忘记检查自己新组件和已有组件的异同。</p><p>这时候就会需要一个设计师领头去重新整理设计系统,挑出来发现同一个正文所用十几个不同字号,又或者是十几个透明度不同的灰黑色字体,所有组件跟现有上线产品对比整理好后一并交给开发,开发再一一整理代码库。</p><hr><h2 id="原子设计理念好在哪里?"><a href="#原子设计理念好在哪里?" class="headerlink" title="原子设计理念好在哪里?"></a>原子设计理念好在哪里?</h2><p>从上面这个例子我们就可以看出首先贴纸组件库需要经常维护,同时也需要和开发人员及时沟通同步更新代码库,同时贴纸组件库也无法有效约束新设计师保证设计风格一致性。原子设计的原理可以最大程度保证任何一个设计组件的构成与开发构建组件一一对应。同时应用原子设计的设计软件(e.g., Sketch, Figma)可以给新设计师足够的自由满足需求变通,并且可以保证同一个设计组件的更新会覆盖到任何一个使用这个组件的设计中。</p><hr><h2 id="什么是-Atomic-Design-原子设计"><a href="#什么是-Atomic-Design-原子设计" class="headerlink" title="什么是 Atomic Design 原子设计?"></a>什么是 Atomic Design 原子设计?</h2><p>原子设计是创建设计系统的理论方法,分为 5 个层次:</p><ul><li>原子(Atoms)</li><li>分子(Molecules)</li><li>组织(Organisms)</li><li>模板(Templates)</li><li>页面(Pages)</li></ul><img src="./1.jpg" style="width: 500px"><p>原子是我们设计中最为基础元素,可以是一种字体,一个色板,一组图标或者一个矩形。<br>把几个原子组合构成一个分子,比如黑体字、矩形、16 号字、蓝色、白色、构成一个按钮。<br>几个分子构成一个组织,我们也可以称为组件,这样更符合日常习惯。</p><p>组件构成页面框架,最后生成页面。</p><p>这就是原子设计的基本概念,而当我们改变原子时,整个体系都会产生变化,同时我们也可以逐级调整,总之你的每次改变都会影响到这个系统,但是它又是保持一致性的。</p><p>当然我们并不一定是最后生成页面,也许是 Apps 或者其他出版物,甚至我们可以根据自己从事工作的属性去设定个性的结构。</p><p>但是无论如何构建自己的设计系统,原子设计为界面元素提供了应用规则和组织原理,这套理论对于规范化设计、团队协作、产品迭代意义重大。</p><h3 id="Atoms-原子层:单一可工作的原子组件"><a href="#Atoms-原子层:单一可工作的原子组件" class="headerlink" title="Atoms 原子层:单一可工作的原子组件"></a>Atoms 原子层:单一可工作的原子组件</h3><ul><li>Typographic styles 字体样式</li><li>Color swatches 颜色色板</li><li>Icons 图标</li><li>Radio buttons 单选按钮</li><li>Checkboxes 多选框</li><li>Sliders 滑杆</li><li>Toggles 切换按钮</li><li>Profile pictures placeholders 个人资料图片占位符</li><li>Product pictures placeholders 产品资料图片占位符</li><li>Buttons 按钮</li><li>Links 链接</li><li>Input fields (without labels) 无标识输入栏</li><li>Tabs 标签文字型按钮(常见导航)</li><li>Pills 药丸型按钮(常见热门,历史搜索关键字)</li><li>Badges 提醒小红点或数字(常见微信未读提示)</li><li>Tags 标签</li><li>Tooltips 信息提示框</li><li>Loading Bar/Circle 加载条/加载圈</li></ul><h3 id="Molecules-分子层:1-3-个不同的原子组成的简单-UI-功能组件"><a href="#Molecules-分子层:1-3-个不同的原子组成的简单-UI-功能组件" class="headerlink" title="Molecules 分子层:1-3 个不同的原子组成的简单 UI 功能组件"></a>Molecules 分子层:1-3 个不同的原子组成的简单 UI 功能组件</h3><ul><li>Dropdown menus 下拉菜单</li><li>Radio buttons inside regular buttons 普通按钮里嵌套单选按钮</li><li>Dropdown buttons 下拉按钮</li><li>Date pickers 日期选择器</li><li>Search components 搜索组件</li><li>Blockquotes 引用组件</li><li>Breadcrumbs 面包屑导航栏</li><li>Card/Tile components 卡片/长条卡片组件</li><li>Collapsible group items 可收缩群组</li><li>Input fields (with labels) 带标识输入栏</li><li>Media uploaders 上传组件(选文件按钮+标题)</li><li>Loading components 加载模块(进度条+X+名称)</li><li>Interactive Notifications/Pop-ups/Modal 1-2 个按钮的简单提示框/弹窗/状态窗 - (cookie 提示接受/拒绝)</li><li>Pagination 页面页数选择栏</li><li>Media objects 媒体组件(产品小图+标题+内容简介)</li></ul><h3 id="Organisms-有机体:由多个分子组件组成"><a href="#Organisms-有机体:由多个分子组件组成" class="headerlink" title="Organisms 有机体:由多个分子组件组成"></a>Organisms 有机体:由多个分子组件组成</h3><ul><li>Navigation/Tab bars 导航栏</li><li>Video/Music players 视频/音乐播放器</li><li>Media grids 媒体表单(卡片组成的媒体库表单)</li><li>Tables 信息表格(常见小长条卡片组成)</li><li>Carousels 轮播形式(常见卡片+选择箭头/…)</li><li>Forms 信息表单</li></ul><hr><h2 id="利用原子设计理念提升设计团队"><a href="#利用原子设计理念提升设计团队" class="headerlink" title="利用原子设计理念提升设计团队"></a>利用原子设计理念提升设计团队</h2><p>基于 Single Responsibility Principle (单一职责原则)的概念, 一个系统有不同的模块,每个模块只负责一个功能,模块和模块之间的连带性较小,所以这样的系统更强。</p><p>这可能会带来以下的优点:</p><ul><li>可以对各个 component 为单位进行单体测试</li><li>减少过耦合带来的 bug</li><li>易于维护</li><li>提高代码的复用率</li><li>易于多人数平行开发</li><li>能更灵活地应付需求改变</li></ul>]]></content>
<summary type="html">
<p>原子设计理念</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="AtomicDesign" scheme="https://kisky3.github.io/tags/AtomicDesign/"/>
</entry>
<entry>
<title>Web Security</title>
<link href="https://kisky3.github.io/2022/02/15/WebSecurity/"/>
<id>https://kisky3.github.io/2022/02/15/WebSecurity/</id>
<published>2022-02-15T04:31:39.000Z</published>
<updated>2022-03-15T10:26:16.559Z</updated>
<content type="html"><![CDATA[<p>网络安全</p><a id="more"></a><h2 id="SQL-インジェクション"><a href="#SQL-インジェクション" class="headerlink" title="SQL インジェクション"></a>SQL インジェクション</h2><ul><li>SQL 文の組み立ては全てプレースホルダで実装する。</li><li>SQL 文の組み立てを文字列連結により行う場合は、エスケープ処理等を行うデータ<br>ベースエンジンの API を用いて、SQL 文のリテラルを正しく構成する。</li><li>ウェブアプリケーションに渡されるパラメータに SQL 文を直接指定しない。</li><li>エラーメッセージをそのままブラウザに表示しない。</li><li>データベースアカウントに適切な権限を与える。</li></ul><h2 id="OS-コマンド・インジェクション"><a href="#OS-コマンド・インジェクション" class="headerlink" title="OS コマンド・インジェクション"></a>OS コマンド・インジェクション</h2><ul><li>シェルを起動できる言語機能の利用を避ける</li><li>シェルを起動できる言語機能を利用する場合は、その引数を構成する全ての変数に<br>対してチェックを行い、あらかじめ許可した処理のみを実行する。</li></ul><h2 id="パス名パラメータの未チェック/ディレクトリ・トラバーサル"><a href="#パス名パラメータの未チェック/ディレクトリ・トラバーサル" class="headerlink" title="パス名パラメータの未チェック/ディレクトリ・トラバーサル"></a>パス名パラメータの未チェック/ディレクトリ・トラバーサル</h2><ul><li>外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける。</li><li>ファイルを開く際は、固定のディレクトリを指定し、かつファイル名にディレクトリ名が含<br>まれないようにする。</li><li>ウェブサーバ内のファイルへのアクセス権限の設定を正しく管理する。</li><li>ファイル名のチェックを行う。</li></ul><h2 id="セッション管理の不備"><a href="#セッション管理の不備" class="headerlink" title="セッション管理の不備"></a>セッション管理の不備</h2><ul><li>セッション ID を推測が困難なものにする。</li><li>セッション ID を URL パラメータに格納しない。</li><li>HTTPS 通信で利用する Cookie には secure 属性を加える。</li><li>ログイン成功後に、新しくセッションを開始する。</li><li>ログイン成功後に、既存のセッション ID とは別に秘密情報を発行し、ページの遷移<br>ごとにその値を確認する。</li><li>セッション ID を固定値にしない。</li><li>セッション ID を Cookie にセットする場合、有効期限の設定に注意する。</li></ul><h2 id="クロスサイト・スクリプティング"><a href="#クロスサイト・スクリプティング" class="headerlink" title="クロスサイト・スクリプティング"></a>クロスサイト・スクリプティング</h2><ul><li><p>HTML テキストの入力を許可しない場合の対策</p><ul><li>ウェブページに出力する全ての要素に対して、エスケープ処理を施す。</li><li>URL を出力するときは、「http://」や 「https://」で始まる URL のみを許可する。</li><li><script>...</script> 要素の内容を動的に生成しない。</li><li>スタイルシートを任意のサイトから取り込めるようにしない。</li><li>入力値の内容チェックを行う</li></ul></li><li><p>HTML テキストの入力を許可する場合の対策</p><ul><li>入力された HTML テキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する</li><li></li></ul></li><li><p>全てのウェブアプリケーションに共通の対策</p><ul><li>HTTP レスポンスヘッダの Content-Type フィールドに文字コード(charset)を<br>指定する。</li><li>Cookie 情報の漏えい対策として、発行する Cookie に HttpOnly 属性を加え、<br>TRACE メソッドを無効化する。</li><li>クロスサイト・スクリプティングの潜在的な脆弱性対策として有効なブラウザの機能を有効にするレスポンスヘッダを返す。</li></ul></li></ul><h2 id="CSRF(クロスサイト・リクエスト・フォージェリ"><a href="#CSRF(クロスサイト・リクエスト・フォージェリ" class="headerlink" title="CSRF(クロスサイト・リクエスト・フォージェリ)"></a>CSRF(クロスサイト・リクエスト・フォージェリ)</h2><ul><li><p>☞ 処理を実行するページを POST メソッドでアクセスするようにし、その「hidden パラメータ」に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する。</p></li><li><p>処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する。</p></li><li><p>Referer が正しいリンク元かを確認し、正しい場合のみ処理を実行する。</p></li><li><p>重要な操作を行った際に、その旨を登録済みのメールアドレスに自動送信する。</p></li></ul><h2 id="HTTP-ヘッダ・インジェクション"><a href="#HTTP-ヘッダ・インジェクション" class="headerlink" title="HTTP ヘッダ・インジェクション"></a>HTTP ヘッダ・インジェクション</h2><ul><li>ヘッダの出力を直接行わず、ウェブアプリケーションの実行環境や言語に用意されているヘッダ出力用 API を使用する。</li><li>改行コードを適切に処理するヘッダ出力用 API を利用できない場合は、改行を許可しないよう、開発者自身で適切な処理を実装する。</li><li>外部からの入力の全てについて、改行コードを削除する。</li></ul>]]></content>
<summary type="html">
<p>网络安全</p>
</summary>
<category term="security" scheme="https://kisky3.github.io/tags/security/"/>
</entry>
<entry>
<title>Create Test Environment By Nginx, EC2 and S3</title>
<link href="https://kisky3.github.io/2022/01/28/createNginxTestEnvironment/"/>
<id>https://kisky3.github.io/2022/01/28/createNginxTestEnvironment/</id>
<published>2022-01-28T13:30:40.000Z</published>
<updated>2022-01-31T12:28:54.164Z</updated>
<content type="html"><![CDATA[<p>用Nginx, EC2 和 S3来造一个测试环境</p><a id="more"></a><p>最近因为测试环境不够用, 所以试造了一个测试环境。<br>使用了Nginx, EC2 和 S3.</p><p>下面记录一下步骤:</p><h2 id="EC2"><a href="#EC2" class="headerlink" title="EC2:"></a>EC2:</h2><p>1.首先要有两个EC2的服务器, 一个用于存放cakephp源代码, 一个用于nginx设定的ngin反向代理服务器。</p><ul><li>test-web-redhat-test (用于存放cakephp源代码)</li><li>test.kou (nginx反向代理服务器)</li></ul><p>平时一般是启动两个EC2, 在<code>test-web-redhat-test</code>寻找到源代码路径,<br>git checkout branch & git pull 来进行代码获取。</p><p>连接多用ssh。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh -i ~/.ssh/<your.pem> -oStrictHostKeyChecking=no ec2-user@<your redhat server <span class="keyword">public</span> ip></your></span><br></pre></td></tr></table></figure><hr><h2 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h2><p>然后同样方式连接到test.kou (nginx反向代理服务器)。<br>找到nginx.conf, 在里面进行redirect设定。指向S3的bucket地址。<br>例:<br>server_settings/nginx_settings/nginx/conf/nginx.conf</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">location /test/ {</span><br><span class="line"> # if ($geo = "disable") { return 404; break; }</span><br><span class="line"></span><br><span class="line"> proxy_set_header host forms-test.s3-website-ap-northeast-1.amazonaws.com;</span><br><span class="line"> proxy_pass http://forms-test.s3-website-ap-northeast-1.amazonaws.com/;</span><br><span class="line"> break;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p> nginx.conf修改之后要重新启动服务器,<br><code>sudo service nginx configtest</code>进行测试没啥问题的话,<br>使用<code>sudo service nginx restart</code>来重启服务器。</p><p>这样当你连接<your_domain>/test的时候就会被nginx反向代理到S3上的index.html了.</your_domain></p><hr><h2 id="S3"><a href="#S3" class="headerlink" title="S3"></a>S3</h2><p>新建一个bucket。并设置成能够公开静态页面的状态。在此取得proxy_pass</p><img src="./1.png" style="width:500px"><img src="./2.png" style="width:500px"><p>有必要的话进行bucket policy设置来进行ip block.</p><img src="./3.png" style="width:500px"><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "Version": "2012-10-17",</span><br><span class="line"> "Statement": [</span><br><span class="line"> {</span><br><span class="line"> "Sid": "PublicReadGetObject",</span><br><span class="line"> "Effect": "Allow",</span><br><span class="line"> "Principal": "*",</span><br><span class="line"> "Action": "s3:GetObject",</span><br><span class="line"> "Resource": "arn:aws:s3:::test-s3-bucket-20200509-1140/*"</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>用Nginx, EC2 和 S3来造一个测试环境</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="nginx" scheme="https://kisky3.github.io/tags/nginx/"/>
<category term="EC2" scheme="https://kisky3.github.io/tags/EC2/"/>
<category term="S3" scheme="https://kisky3.github.io/tags/S3/"/>
</entry>
<entry>
<title>Automatic Download Function Through Javascript</title>
<link href="https://kisky3.github.io/2021/11/28/AutomaticDownloadFunction/"/>
<id>https://kisky3.github.io/2021/11/28/AutomaticDownloadFunction/</id>
<published>2021-11-28T13:31:24.000Z</published>
<updated>2022-01-28T13:43:09.650Z</updated>
<content type="html"><![CDATA[<p>js实现点击浏览器自动下载</p><a id="more"></a><p>记录一下用纯js实现各种浏览器自动下载的功能。<br>兼容IE。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">downloadDocument(<span class="string">"YOUR DOCUMENT"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">downloadDocument</span>(<span class="params">pdfType</span>) </span>{</span><br><span class="line"> <span class="comment">// your defination of file url and file name</span></span><br><span class="line"> <span class="keyword">let</span> fileUrl = documentInfo[pdfType][<span class="string">"url"</span>];</span><br><span class="line"> <span class="keyword">let</span> fileName = <span class="string">`<span class="subst">${documentInfo[pdfType][<span class="string">"name"</span>]}</span>.pdf`</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> oReq = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"> oReq.open(<span class="string">"GET"</span>, fileUrl, <span class="literal">true</span>);</span><br><span class="line"> oReq.responseType = <span class="string">"blob"</span>;</span><br><span class="line"> oReq.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> fileBlob = <span class="keyword">new</span> Blob([oReq.response], {</span><br><span class="line"> <span class="comment">// or other type</span></span><br><span class="line"> type: <span class="string">"application/pdf"</span>,</span><br><span class="line"> });</span><br><span class="line"> saveAs(fileBlob, fileName);</span><br><span class="line"> };</span><br><span class="line"> oReq.send();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">saveAs</span>(<span class="params">blob, filename</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> URL = <span class="built_in">window</span>.URL || <span class="built_in">window</span>.webkitURL</span><br><span class="line"> <span class="keyword">const</span> type = blob.type</span><br><span class="line"> <span class="keyword">const</span> force_saveable_type = <span class="string">'application/octet-stream'</span></span><br><span class="line"> <span class="keyword">const</span> slice = blob.slice || blob.webkitSlice || blob.mozSlice</span><br><span class="line"> blob = slice.call(blob, <span class="number">0</span>, blob.size, force_saveable_type)</span><br><span class="line"> <span class="keyword">const</span> url = URL.createObjectURL(blob)</span><br><span class="line"> <span class="keyword">const</span> save_link = <span class="built_in">document</span>.createElementNS(</span><br><span class="line"> <span class="string">"http://www.w3.org/1999/xhtml"</span>,</span><br><span class="line"> <span class="string">"a"</span></span><br><span class="line"> );</span><br><span class="line"> save_link.href = url;</span><br><span class="line"> save_link.download = filename;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> event = <span class="keyword">new</span> MouseEvent(<span class="string">"click"</span>, {</span><br><span class="line"> bubbles: <span class="literal">true</span>,</span><br><span class="line"> cancelable: <span class="literal">true</span>,</span><br><span class="line"> view: <span class="built_in">window</span>,</span><br><span class="line"> });</span><br><span class="line"> save_link.dispatchEvent(event);</span><br><span class="line"> URL.revokeObjectURL(url);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>js实现点击浏览器自动下载</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="js" scheme="https://kisky3.github.io/tags/js/"/>
<category term="download" scheme="https://kisky3.github.io/tags/download/"/>
</entry>
<entry>
<title>Cakephp Tips</title>
<link href="https://kisky3.github.io/2021/10/30/cakephptip/"/>
<id>https://kisky3.github.io/2021/10/30/cakephptip/</id>
<published>2021-10-30T14:44:21.000Z</published>
<updated>2022-01-28T13:43:23.682Z</updated>
<content type="html"><![CDATA[<p>Cakephp 小窍门</p><a id="more"></a><p>最近在捣鼓cakephp(非自愿),踩过的一些小坑在这里记录一下。</p><h3 id="1-关于Validation的设置"><a href="#1-关于Validation的设置" class="headerlink" title="1.关于Validation的设置"></a>1.关于Validation的设置</h3><p>Cakephp的Validation设置和验证都可以写在相应的Model Table文件里。<br>当 CakePHP 调用 <code>save()</code> 时,validationDefault() 方法将指示如何验证数据。在下面的代码例子中, 我们规定 <code>title</code> 和 <code>body</code> 不可以为空,而且必须要达到一定的长度。</p><p>例:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/Model/Table/ArticlesTable.php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// add this use statement right below the namespace declaration to import</span></span><br><span class="line"><span class="comment">// the Validator class</span></span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Validation</span>\<span class="title">Validator</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Add the following method.</span></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">validationDefault</span><span class="params">(Validator $validator)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $validator</span><br><span class="line"> ->notEmpty(<span class="string">'title'</span>)</span><br><span class="line"> ->minLength(<span class="string">'title'</span>, <span class="number">10</span>)</span><br><span class="line"> ->maxLength(<span class="string">'title'</span>, <span class="number">255</span>)</span><br><span class="line"></span><br><span class="line"> ->notEmpty(<span class="string">'body'</span>)</span><br><span class="line"> ->minLength(<span class="string">'body'</span>, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> $validator;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当然你也可以自己定义Validation文件来满足你的开发需要。<br>比如可以在<code>Model</code>文件夹下建立一个<code>Validation</code>文件夹,然后在里面继承Validation方法并进行自定义。</p><p>例:<br>Path: Model/Validation/ClientValidation</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Airticle</span>\<span class="title">Model</span>\<span class="title">Validation</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">ORM</span>\<span class="title">TableRegistry</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Validation</span>\<span class="title">Validation</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Validation</span>\<span class="title">Validator</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ClientValidation</span> <span class="keyword">extends</span> <span class="title">Validator</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">airticleValidation</span><span class="params">($data)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">isset</span>($data[<span class="string">'AIRTICLE'</span>])) {</span><br><span class="line"> <span class="keyword">$this</span>->clientValidationAirticle();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">clientValidationAirticle</span><span class="params">($value, $data)</span> </span>{</span><br><span class="line"> <span class="comment">// 这里的两个参数一个为改项目的值, 另一个为controller相关的所有date</span></span><br><span class="line"> <span class="keyword">$this</span></span><br><span class="line"> ->notEmpty(<span class="string">'AIRTICLE'</span>, <span class="string">'请输入文章名字'</span>)</span><br><span class="line"> ->add(<span class="string">'AIRTICLE'</span>, <span class="string">'length'</span>, [<span class="string">'rule'</span> => [<span class="string">'maxLength'</span>, <span class="string">'25'</span>], <span class="string">'message'</span> => <span class="string">'文章名字太长了!'</span>])</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当我们想要使用自定义的Validation的时候可以在相应的Controller里进行呼出。</p><p>然后在Controller里的方法里进行使用:</p><p>例:<br>Path: Controller/AirticleController.php</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先需要在Controller里先申明`use`</span></span><br><span class="line"><span class="keyword">use</span> <span class="title">Airticle</span>\<span class="title">Model</span>\<span class="title">Validation</span>\<span class="title">ClientValidation</span>;</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">// 进行Validation方法的自定义</span></span><br><span class="line"><span class="keyword">$this</span>->dataValid();</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在这里具体呼出Model里定义好的Validation, 该方法为private.</span></span><br><span class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">dataValid</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 先new一下</span></span><br><span class="line"> $validator = <span class="keyword">new</span> ClientValidation();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义一个用于储存error结果的数列</span></span><br><span class="line"> $errors = [];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 这里是将文章的数据传给Model里的Validation进行判断,并将返回的error结果代入储存error结果的数列。</span></span><br><span class="line"> $errors[] = $validator->airticleValidation(<span class="keyword">$this</span>->request->data(<span class="string">'data.airticle_data'</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样我们就可以完成Validation的自定义了,当我们在不同的功能页面里需要使用同样的Validation判断的时候可以考虑采用这样的方法。</p><p>当然Cakephp还提供了很多智能的Validation方法,比如邮件地址的判断, 电话号码的判断等,我们可以直接使用他们或者也可以自定义相关项目的Validation.</p><p>官方文档参考:<br><a href="https://book.cakephp.org/3/en/core-libraries/validation.html" target="_blank" rel="noopener">Validation</a></p><hr><h3 id="2-关于设置一个自动送信api"><a href="#2-关于设置一个自动送信api" class="headerlink" title="2.关于设置一个自动送信api"></a>2.关于设置一个自动送信api</h3><p>我们经常用cakephp来生成api,那么简单做一个自动送信的api的步骤是怎样呢。</p><h4 id="1-首先在路由里设置api路径"><a href="#1-首先在路由里设置api路径" class="headerlink" title="1.首先在路由里设置api路径"></a>1.首先在路由里设置api路径</h4><p>config/routes.php</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$routes->connect(<span class="string">'/sendmail'</span>, [</span><br><span class="line"> <span class="string">'controller'</span> => <span class="string">'SendMail'</span>,</span><br><span class="line"> <span class="string">'action'</span> => <span class="string">'postMail'</span>,</span><br><span class="line"> <span class="string">'api'</span> => <span class="keyword">true</span>,</span><br><span class="line"> <span class="string">'[method]'</span> => <span class="string">'POST'</span></span><br><span class="line">]);</span><br></pre></td></tr></table></figure><p>这样前端部分就可以向endpoint<code>「http://yourdomain/sendmail」</code>发送请求的时候,<br>CakePHP就会去自动执行<code>SendMailController</code>里的<code>postMail method</code>里的操作。</p><h4 id="2-准备相应的Controller"><a href="#2-准备相应的Controller" class="headerlink" title="2.准备相应的Controller"></a>2.准备相应的Controller</h4><p>src/Controller下生成<code>SendMailController.php</code>.</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Api</span>\<span class="title">Controller</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Mailer</span>\<span class="title">Email</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Oikura</span>\<span class="title">Consts</span>\<span class="title">ErrorMail</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Oikura</span>\<span class="title">Consts</span>\<span class="title">MailConsts</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Error</span>\<span class="title">Debugger</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Oikura</span>\<span class="title">Consts</span>\<span class="title">PropsConsts</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Core</span>\<span class="title">Configure</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">Cake</span>\<span class="title">Event</span>\<span class="title">Event</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SendMailController</span> <span class="keyword">extends</span> <span class="title">AppController</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">beforeFilter</span><span class="params">(Event $event)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">parent</span>::beforeFilter($event);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">$this</span>->autoRender = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">$this</span>->response->type(<span class="string">'application/json'</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">postMail</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="comment">// get date from client form</span></span><br><span class="line"> $response = <span class="keyword">$this</span>->response;</span><br><span class="line"> $data = <span class="keyword">$this</span>->request->data;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// definate subject and mail template</span></span><br><span class="line"> $customerSubject = MailConsts::MAIL_SUBJECT_TO_CUSTOMER;</span><br><span class="line"> $customerTemplate = MailConsts::MAIL_TEMPLATE_TO_CUSTOMER;</span><br><span class="line"> $mailToCustomer = <span class="keyword">$this</span>->sendMail($userEmail, $customerTemplate, $customerSubject, $data);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return response to client</span></span><br><span class="line"> <span class="keyword">if</span> ($mailToCustomer) {</span><br><span class="line"> <span class="keyword">$this</span>->response->statusCode(<span class="number">200</span>);</span><br><span class="line"> $response[<span class="string">'message'</span>] = <span class="string">'Mail Success'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">$this</span>->response->statusCode(<span class="number">500</span>);</span><br><span class="line"> $response[<span class="string">'message'</span>] = <span class="string">'Mail Fail'</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">sendMail</span><span class="params">($giver, $template, $subject, $data)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> $email = <span class="keyword">new</span> Email();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> $email->subject($subject)</span><br><span class="line"> ->template($template)</span><br><span class="line"> ->from(MailConsts::fromService, PropsConsts::CONFIRM_FROM_NAME)</span><br><span class="line"> ->to($giver)</span><br><span class="line"> ->emailFormat(<span class="string">'html'</span>)</span><br><span class="line"> ->viewVars($data);</span><br><span class="line"></span><br><span class="line"> $email->send();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (\<span class="keyword">Exception</span> $e) {</span><br><span class="line"> Debugger::log($e);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>前端部分: 给api发送请求和数据</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> endpoint: <span class="string">`your domain/api/sendmail`</span>,</span><br><span class="line">...</span><br><span class="line"><span class="keyword">async</span> submitForm() {</span><br><span class="line"> <span class="keyword">await</span> post(<span class="keyword">this</span>.endpoint, <YOUR_FORM_DATA>)</span><br><span class="line"> .then(<span class="function">(<span class="params">result</span>) =></span> {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> })</span><br><span class="line"> .catch(<span class="function"><span class="params">error</span> =></span> {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> })</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>大功告成!</p><hr><h3 id="3-关于Cakephp的常用配置约定"><a href="#3-关于Cakephp的常用配置约定" class="headerlink" title="3. 关于Cakephp的常用配置约定"></a>3. 关于Cakephp的常用配置约定</h3><p>Cakephp是MVC框架。它的命名遵循一定的配置约定。大致的代码都保存在<code>src</code>目录下。</p><h4 id="控制器-Controller-约定"><a href="#控制器-Controller-约定" class="headerlink" title="控制器(Controller)约定"></a>控制器(Controller)约定</h4><p>控制器的类名是复数,首字母大写, 并且以 Controller 结尾。<br>UsersController 和 ArticleCategoriesController 都是约定的控制器类名的例子。<br>控制器中 public 的方法通常被暴露为可通过web浏览器访问的“操作”。</p><p>例:<br> <code>/users/view</code> 映射到<code>UsersController</code>中的 view() 方法。</p><p><code>/article-categories/view-all</code> 是访问 ArticleCategoriesController::viewAll() 方法。</p><h4 id="模型-Model-约定"><a href="#模型-Model-约定" class="headerlink" title="模型(Model)约定"></a>模型(Model)约定</h4><p>CakePHP 的模型是由 <code>Table</code> and <code>Entity</code> 两种对象组成。<br>Table 为是一个特定的数据库表 的抽象。他们储存在 <code>src/Model/Table</code> 目录中。首先我们建立文件 <code>src/Model/Table/ArticlesTable.php</code>。</p><p><code>Table</code> 类名是复数、首字母大写、以 Table 结尾的。UsersTable、ArticleCategoriesTable、UserFavoritePagesTable 分别是对应 users、article_categories、user_favorite_pages 表的 table 类名。</p><p><code>Entity</code> 类名是单数、首字母大写、无后缀的。User、ArticleCategory、UserFavoritePage 分别是对应 users、article_categories、user_favorite_pages 表的 entity 类名。</p><h4 id="视图-View-约定"><a href="#视图-View-约定" class="headerlink" title="视图(View)约定"></a>视图(View)约定</h4><p>视图模板文件使用它对应的控制器方法的名字以下划线形式命名。<br>模版文件都储存在 <code>src/Template</code> 目录中的一个文件夹中。此文件夹以其对应的控制器命名。<br><code>ArticlesController</code>类的 viewAll() 防范将会对应视图模板文件 <code>src/Template/Articles/view_all.ctp</code>。</p><p>总结一下就是下面这样的,遵行约定可以减少我们进行配置的时间,从而提高开发效率。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">数据库表:articles</span><br><span class="line"></span><br><span class="line">Table 类:ArticlesTable,在文件 src/Model/Table/ArticlesTable.php 中</span><br><span class="line"></span><br><span class="line">Entity 类:Article,在文件 src/Model/Entity/Article.php 中</span><br><span class="line"></span><br><span class="line">控制器类:ArticlesController,在文件 src/Controller/ArticlesController.php 中</span><br><span class="line"></span><br><span class="line">视图模板,在文件 src/Template/Articles/index.ctp 中</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>Cakephp 小窍门</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="cakephp" scheme="https://kisky3.github.io/tags/cakephp/"/>
</entry>
<entry>
<title>Using Vue Proxy To Solve Axios CORS Problem</title>
<link href="https://kisky3.github.io/2021/10/14/VuedevServerProxy/"/>
<id>https://kisky3.github.io/2021/10/14/VuedevServerProxy/</id>
<published>2021-10-14T12:49:46.000Z</published>
<updated>2022-01-28T06:52:51.372Z</updated>
<content type="html"><![CDATA[<p>利用 Vue 的反向代理来解决 Axios 的 CORS 问题</p><a id="more"></a><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>当在开发环境开发的时候,使用 axios 来向后端的 API 发送求情,<br>但是会被 Policy 绊住, 就是常见的 CORS 问题.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Access to XMLHttpRequest at 'http://localhost:3000/api/test' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource</span><br></pre></td></tr></table></figure><p>因为 Origin 不同所以浏览器会将请求拒绝.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(example)Origin不同</span><br><span class="line">Vue.js:http://localhost:8080</span><br><span class="line">Golang:http://localhost:3000</span><br></pre></td></tr></table></figure><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><p>1.叫服务器端设定 CORS</p><p>2.前端的 Vue 使用 proxy 来回避此问题</p><ol><li>在 Vue 的根目录下生成 vue.config.js 文件</li></ol><p>vue.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> devServer: {</span><br><span class="line"> proxy: {</span><br><span class="line"> <span class="string">'/api/'</span>: {</span><br><span class="line"> target: <span class="string">'http://localhost:3000'</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>像下面这样写的话</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">axios.get(<span class="string">'/api/test'</span>).then(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(res.data)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>就可以在请求 Golang 的 API<code>/api/**</code>的时候, 转到<code>http://localhost:3000</code>,这样就可以回避同源策略了。</p><p>当然我们还可以利用环境参数来根据不同的开发环境设置不同的 proxy.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> devServer: {</span><br><span class="line"> proxy: {</span><br><span class="line"> <span class="string">'/api/'</span>: {</span><br><span class="line"> target: process.env.USE_LOCAL_SERVER</span><br><span class="line"> ? <span class="string">'http://localhost:3000'</span></span><br><span class="line"> : <span class="string">'http://aaa.com'</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果你想要看 log 的话可以加上</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> devServer: {</span><br><span class="line"> proxy: {</span><br><span class="line"> <span class="string">'/api/'</span>: {</span><br><span class="line"> target: process.env.USE_LOCAL_SERVER</span><br><span class="line"> ? <span class="string">'http://localhost:3000'</span></span><br><span class="line"> : <span class="string">'http://aaa.com'</span>,</span><br><span class="line"> logLevel: <span class="string">'debug'</span>, <span class="comment">// 追加log</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>利用 Vue 的反向代理来解决 Axios 的 CORS 问题</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="Vue" scheme="https://kisky3.github.io/tags/Vue/"/>
<category term="Proxy" scheme="https://kisky3.github.io/tags/Proxy/"/>
<category term="CORS" scheme="https://kisky3.github.io/tags/CORS/"/>
</entry>
<entry>
<title>Use Jira To Track Story Points Per Developer On My Team</title>
<link href="https://kisky3.github.io/2021/10/09/jirastorypoint/"/>
<id>https://kisky3.github.io/2021/10/09/jirastorypoint/</id>
<published>2021-10-09T14:34:10.000Z</published>
<updated>2022-01-28T06:52:51.610Z</updated>
<content type="html"><![CDATA[<p>使用 jira 来追踪小组组员的 story point</p><a id="more"></a><p>使用 jira 工具分析小组组员各自完成的 story point 的时候, 发现这个功能是需要付费的。查了一下发现可以使用 jira API 来分析获取数据,从而获取到小组成员个人的分析数据。</p><p>试了一下结果是可行的, 取得的数据经过整理可以像下面这样:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"A"</span>: <span class="number">8</span>,</span><br><span class="line"> <span class="string">"B"</span>: <span class="number">18</span>,</span><br><span class="line"> <span class="string">"C"</span>: <span class="number">8</span>,</span><br><span class="line"> <span class="string">"null"</span>: <span class="number">0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="API-token-的发行"><a href="#API-token-的发行" class="headerlink" title="API token 的发行"></a>API token 的发行</h3><p>首先你需要申请一个 jira 的 API token 来保证你可以使用它。申请详情在这里就不贴了。</p><h2 id="为了取得-sprint-里的-storypoint-表而准备好-JQL"><a href="#为了取得-sprint-里的-storypoint-表而准备好-JQL" class="headerlink" title="为了取得 sprint 里的 storypoint 表而准备好 JQL"></a>为了取得 sprint 里的 storypoint 表而准备好 JQL</h2><p>之后我们为了使用 Issue Search API 而需要准备好相应的 <a href="https://www.atlassian.com/ja/software/jira/guides/expand-jira/jql" target="_blank" rel="noopener">JQL</a>是 Jira 内使用的检索 SQL 语言。执行 JQL 的话可以在 Jira 里的 Filters 的页面里确认结果。</p><p>这次我们就在还在进行中的 sprint 里试着取得 storypoint 的列表。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">project = XXX AND issuetype = story AND sprint <span class="keyword">in</span> openSprints()</span><br></pre></td></tr></table></figure><p><code>project = XXX</code>是 Jira 的项目的 alias<br><code>issuetype = story</code> 这里是指定 issue 的属性为 story<br><code>sprint in openSprints()</code> 这个是指定正在进行中的 sprint</p><h2 id="利用-Isuue-Search-API-来取得-story-列表"><a href="#利用-Isuue-Search-API-来取得-story-列表" class="headerlink" title="利用 Isuue Search API 来取得 story 列表"></a>利用 Isuue Search API 来取得 story 列表</h2><p>Jira API 里的 Issue Search API 可以帮助我们取得 story 列表。<br>Issue Search API 能够向 JQL 发送请求, 那么用 curl 来敲一下我们刚刚写下的 JQL</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl https://xxx.atlassian.net/rest/api/2/search?jql="project%20%3D%20XXXX%20AND%20issuetype%20%3D%20%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AA%E3%83%BC%20AND%20sprint%20in%20openSprints()"</span><br><span class="line"> --user xxx@fuga.com:HOgehoGeFugaFuGA</span><br></pre></td></tr></table></figure><p>在这里需要注意的是<code>jql</code>参数里需要指定 encode 之后的 URL 地址。<br><code>--user</code>参数是指[Jira 的登陆邮箱地址] : [API 键]</p><h2 id="指定-Jira-里使用的-field"><a href="#指定-Jira-里使用的-field" class="headerlink" title="指定 Jira 里使用的 field"></a>指定 Jira 里使用的 field</h2><p>API curl 一下的话能得到返回来的 json 形势的 story 列表。<br>这次利用的是担当者和 storypoint 两个 field。</p><p>担当者=<code>.fields.assignee.displayNam</code><br>StoryPoint=<code>.fields.customfield_10030</code></p><p>重点要注意的是<code>customfield_xxxxx</code>的<code>xxxxx</code>部分是要根据自己的 Jira 来设定的。你需要自定义 Filter,然后查找相应的 customfield_id。</p><h2 id="使用-JQ-命令行来解析-json"><a href="#使用-JQ-命令行来解析-json" class="headerlink" title="使用 JQ 命令行来解析 json"></a>使用 JQ 命令行来解析 json</h2><p>因为返回回来的 json 数据的话饱含着很多的 field,所以我们需要从 json 数据里抽出我们需要的两个数据。<br>整形用的 jq 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">curl https://xxx.atlassian.net/rest/api/2/search?jql=(省略)</span><br><span class="line"> | jq '.issues | map({ name: .fields.assignee.displayName, storypoint: .fields.customfield_10030})'</span><br><span class="line"> | sed s/null,/\"null\",/ | sed s/null/0/</span><br><span class="line"> | jq 'reduce .[] as $row ({}; .[$row.name] += $row.storypoint)'</span><br></pre></td></tr></table></figure><p>这样我们就可以得到以下整形成功的数据了!</p><p>{<br>“A”: 8,<br>“B”: 18,<br>“C “: 8,<br>“null”: 0<br>}</p><h2 id="指定-sprint-的-storypoint-数据的取得"><a href="#指定-sprint-的-storypoint-数据的取得" class="headerlink" title="指定 sprint 的 storypoint 数据的取得"></a>指定 sprint 的 storypoint 数据的取得</h2><p>如果你不是想取得正在进行中的 sprint 的数据而是指定 sprint 的话,你可以将下面的 openSprints 改成 sprint (同样需要进行 encode 变换)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sprint in openSprints()</span><br><span class="line">↓</span><br><span class="line">sprint = XXXX</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>使用 jira 来追踪小组组员的 story point</p>
</summary>
<category term="System Setting" scheme="https://kisky3.github.io/categories/System-Setting/"/>
<category term="jira" scheme="https://kisky3.github.io/tags/jira/"/>
</entry>
<entry>
<title>A Sample of Vue Unit Test</title>
<link href="https://kisky3.github.io/2021/09/16/VueTest/"/>
<id>https://kisky3.github.io/2021/09/16/VueTest/</id>
<published>2021-09-16T14:25:34.000Z</published>
<updated>2022-01-28T06:52:51.372Z</updated>
<content type="html"><![CDATA[<p>一个关于 Vue 单体测试的小栗子</p><a id="more"></a><p>我们用 Vue 来写组件的时候其实可以顺带把单体 Unit 测试也给写了。<br>这篇文章就来介绍一下 Vue 的 Unit 测试的写法。</p><p>当然关于要不要花时间写 Unit 测试的话其实取决于你需求变化多不多。<br>如果你需要花和 coding 同等的时间来写 Unit Coding 但是需求很快就变的话其实也不强求。针对需求变化不大的核心需求来写就好。比如表单的验证, 登陆功能的验证等。</p><p>github open source</p>]]></content>
<summary type="html">
<p>一个关于 Vue 单体测试的小栗子</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="Vue" scheme="https://kisky3.github.io/tags/Vue/"/>
<category term="UnitTest" scheme="https://kisky3.github.io/tags/UnitTest/"/>
</entry>
<entry>
<title>Foward Proxy and Reverse Proxy</title>
<link href="https://kisky3.github.io/2021/09/15/fowardproxyandreverseproxy/"/>
<id>https://kisky3.github.io/2021/09/15/fowardproxyandreverseproxy/</id>
<published>2021-09-15T14:11:01.000Z</published>
<updated>2022-01-28T06:52:51.548Z</updated>
<content type="html"><![CDATA[<p>关于正向代理和反向代理</p><a id="more"></a><h3 id="正向代理"><a href="#正向代理" class="headerlink" title="正向代理"></a>正向代理</h3><p>什么是正向代理,</p><p>比如说你想要问A借钱,但是A不直接借给你。于是你通过中介B向A借到了钱。<br>这里的中介B就是一个非常重要的角色<code>代理</code>!</p><p>也可以说是正向代理。因为有B做中介,所以其实A是不知道最后是谁借了他的钱的。这点很重要!</p><p>我们常说的代理就是这样的正向代理。它隐藏了真实的用户请求(相当于借钱的你)。服务器(A)不知道真正的客户端是谁。因为中间存在一个代理服务器(B),它来代替客户端向服务器发送请求(借钱),再将返回的回应(借到的钱)返回给客户端。(把钱给你)</p><p>举个例子的话,<br>就相当于你在国内无法上twitter,但是你如果在海外搭建一个正向代理服务器,它代替你发送请求,再把请求返回给你,你就可以在国内自由twitter了。</p><h3 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h3><p>反向代理就像你打电话给A借钱。(对又是借钱)<br>但是A请了一个客服公司来处理借钱事务。这次呢接线员B、C、D、E ….都有可能接到你的电话,并且为你处理你的借钱请求。</p><p>这就是反向代理,也就是说你是不知道谁是为你服务的人。</p><p>也就是说反向代理隐藏了真实的服务器,当我们向服务器发送请求的时候,其实是向反向代理服务器发送请求。然后反向服务器会帮我们把真实请求发送到真实的服务器那里。</p><p>Nginx就是很好的反向代理服务器,经常用来做负载均衡。</p><p>这两者的区别在于: 代理的对象不一样。</p><p>正向代理代理的对象是客户端。<br>反向代理的对象是服务器端。</p><hr>]]></content>
<summary type="html">
<p>关于正向代理和反向代理</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="Foward Proxy" scheme="https://kisky3.github.io/tags/Foward-Proxy/"/>
<category term="Reverse Proxy" scheme="https://kisky3.github.io/tags/Reverse-Proxy/"/>
<category term="正向代理" scheme="https://kisky3.github.io/tags/%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86/"/>
<category term="反向代理" scheme="https://kisky3.github.io/tags/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86/"/>
</entry>
<entry>
<title>Nginx Basic and Create a Reverse Proxy Server With Docker</title>
<link href="https://kisky3.github.io/2021/08/16/NginxReverseProxyServer/"/>
<id>https://kisky3.github.io/2021/08/16/NginxReverseProxyServer/</id>
<published>2021-08-16T00:17:28.000Z</published>
<updated>2022-01-28T06:52:50.991Z</updated>
<content type="html"><![CDATA[<p>Nginx 的基础以及用 Docker 创建一个 Nginx 反向代理服务器</p><a id="more"></a><p>这篇文章是用于介绍 Ngin 的基本知识,<br>主要包括 Nginx 的启动,停止,以及如何使用 Nginx 和 Docker 来搭建一个反向代理服务器。</p><h3 id="前提条件"><a href="#前提条件" class="headerlink" title="前提条件"></a>前提条件</h3><p>其实反向代理服务器的话概念很广,有 HTTP 反向代理和 TCP 的反向代理。<br>这次我们就用 Nginx 来搭建一个本地 HTTP 的反向代理服务器。<br>请自己在系统里安装好 Nginx,详细请参照<a href="https://runebook.dev/ja/docs/nginx/install" target="_blank" rel="noopener">Nginx 的安装</a></p><h3 id="什么是-Nginx"><a href="#什么是-Nginx" class="headerlink" title="什么是 Nginx"></a>什么是 Nginx</h3><p>Nginx 是 Web 服务器里面作为反向代理服务器的优秀服务器软件代表!</p><p>一般情况下 Web 服务器都使用特定的 application(Apacge or Nginx)之类的来监听特定的番号(port),以此来连接 TCP 和客户端。客户端发送 HTTP 请求的时候, 做好特定处理之后再将服务器返回的响应传送回去客户端。HTTP 的反向代理能够利用客户端的请求的不同的 HTTP path 来将其连接到到不同的服务器上。</p><p>今天就试着在 Web Site 里造一个汪星人用的 content, 再造一个猫星人用的 content,可以通过各自 URL 的 path 连接到网页。比如客户端向 nginx 发送<code>/dog</code> 或者<code>/cat</code>的路径来连接到<code>localhost:80</code>的服务器上。<br>这样的话, nginx 反向代理就先在中间对路径进行分析,然后再通过事先设定好的网络服务器进行不同路径的不同路由处理。<br>如果服务器端返回了响应的话, 代理服务器就将其返回。看起来好像代理服务器自身处理了请求一样。(但其实不是的~)</p><h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>请求<code>localhost/cat</code>的时候, 显示<code>我喜欢喵星人</code>的网页。<br>请求<code>localhost/dog</code>的时候, 显示<code>我喜欢汪星人</code>的网页。<br>猫星人用的服务器和汪星人用的服务器是分开互不影响的。<br>客户端不知道服务器的存在以及番号。</p><h3 id="项目构造"><a href="#项目构造" class="headerlink" title="项目构造"></a>项目构造</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── docker-compose.yml</span><br><span class="line">├── cat-server</span><br><span class="line">│ └── index.html</span><br><span class="line">├── dog-server</span><br><span class="line">│ └── index.html</span><br><span class="line">└── reverse-proxy</span><br><span class="line"> └── nginx.conf</span><br></pre></td></tr></table></figure><hr><h3 id="项目代码"><a href="#项目代码" class="headerlink" title="项目代码"></a>项目代码</h3><p>首先我们用 docker 来启动。使用<code>docker-compose up</code>的话就可以启动两个网络服务器和一个反向代理服务器了。<br>对每个 content 来说的话, 将所需要的文件作为 volume 挂载。<br>docker-compose.yml</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">version: '3'</span><br><span class="line"></span><br><span class="line">services:</span><br><span class="line"> dog-server:</span><br><span class="line"> image: nginx</span><br><span class="line"> container_name: 'dog-container'</span><br><span class="line"> volumes:</span><br><span class="line"> - ./dog-server:/usr/share/nginx/html</span><br><span class="line"> ports:</span><br><span class="line"> - 7000:80</span><br><span class="line"></span><br><span class="line"> cat-server:</span><br><span class="line"> image: nginx</span><br><span class="line"> container_name: 'cat-container'</span><br><span class="line"> volumes:</span><br><span class="line"> - ./cat-server:/usr/share/nginx/html</span><br><span class="line"> ports:</span><br><span class="line"> - 7001:80</span><br><span class="line"></span><br><span class="line"> reverse-proxy:</span><br><span class="line"> image: nginx</span><br><span class="line"> volumes:</span><br><span class="line"> - ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf</span><br><span class="line"> ports:</span><br><span class="line"> - 80:80</span><br></pre></td></tr></table></figure><hr><h3 id="Index-Page-喵星人网页和汪星人网页"><a href="#Index-Page-喵星人网页和汪星人网页" class="headerlink" title="Index Page (喵星人网页和汪星人网页)"></a>Index Page (喵星人网页和汪星人网页)</h3><p>dog-server/index.html</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>汪星人的页面<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>汪星人的页面<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>cat-server/index.html</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>喵星人的页面<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>喵星人的页面<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>就是两个非常非常简单的网页。</p><hr><h3 id="反向代理用的设定文件"><a href="#反向代理用的设定文件" class="headerlink" title="反向代理用的设定文件"></a>反向代理用的设定文件</h3><p>reverse-proxy/nginx.conf</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">events {</span><br><span class="line"> worker_connections <span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line">http {</span><br><span class="line"> server {</span><br><span class="line"> listen <span class="number">80</span>;</span><br><span class="line"> server_name localhost;</span><br><span class="line"> location /dog {</span><br><span class="line"> proxy_pass http:<span class="comment">//host.docker.internal:7000/;</span></span><br><span class="line"> proxy_redirect off;</span><br><span class="line"> }</span><br><span class="line"> location /cat {</span><br><span class="line"> proxy_pass http:<span class="comment">//host.docker.internal:7001/;</span></span><br><span class="line"> proxy_redirect off;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><h3 id="Nginx-设置文件的写法"><a href="#Nginx-设置文件的写法" class="headerlink" title="Nginx 设置文件的写法"></a>Nginx 设置文件的写法</h3><p>1.events<br>Nginx 的设定文件是由叫做<code>context</code>(也就是由花括号{}包围起来的块)组成的。<br>最初的 context 一般都是<code>events</code>。引用一下官方文档。</p><blockquote><p>Syntax: events { … }<br>Default: —<br>Context: main<br>Provides the configuration file context in >which the directives that affect connection >processing are specified.</p></blockquote><p>也就是关于全体连接(connection)的设定相关的 context。<br><code>worker_connections</code>操作系统启动多少个工作进程运行 Nginx<br>注意是工作进程,不是有多少个 nginx 工程。(在 Nginx 运行的时候,会启动两种进程,一种是主进程 master process;一种是工作进程 worker process。)</p><p>2.http<br><code>http</code>context 是作为虚拟服务器的设定。</p><blockquote><p>Syntax: server { … }<br>Default: —<br>Context: http<br>Sets configuration for a virtual server. (略)</p></blockquote><hr><h3 id="接收-HTTP-请求后的流程"><a href="#接收-HTTP-请求后的流程" class="headerlink" title="接收 HTTP 请求后的流程"></a>接收 HTTP 请求后的流程</h3><p>首先如果 HTTP 的请求被发送过来之后, 先查找在<code>http</code>context 里的<code>server</code>context 里是否有符合条件的。<code>server</code>里有<code>listen</code>和<code>server_name</code>两个选项描述。这就是筛选的条件。</p><p>1.首先、先查找<code>listen</code>里指定的 IP 地址和番号是否和请求一致。</p><p>2.如果一致的情况下、<code>server_name</code>和 HTTP 请求头里的 Host 是一样的话,那么就执行 server context 里写好的处理。</p><p>这次我们用的是下面这样的简单的 server context.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen <span class="number">80</span>;</span><br><span class="line"> server_name localhost;</span><br><span class="line"> # 处理...</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>在这里<code>listen</code>用来判断请求是不是番号 80 来的, server_name 是来判断是否和请求里的 Host 和 localhost 一致。<br>如果能对上其中一个那么就执行之后写好的处理。</p><p>我们来让反向代理运行吧。解析 HTTP 的 URL 的路径,然后路由到别的 Web 服务器上。</p><p>server context 里的处理</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">location /dog {</span><br><span class="line"> proxy_pass http://host.docker.internal:7000/;</span><br><span class="line"> proxy_redirect off;</span><br><span class="line">}</span><br><span class="line">location /cat {</span><br><span class="line"> proxy_pass http://host.docker.internal:7001/;</span><br><span class="line"> proxy_redirect off;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>也就是在 location context 里寻找一致的路径。 /dog 的路径的话 就自动路由到<code>proxy_pass</code>里写好的 URL 里。</p><p>如果路径再长一点的话, 比如<code>localhost/dog/list</code>,就会路由到<code>http://host.docker.internal:7000/list</code>上。</p><h3 id="proxy-pass-的注意点"><a href="#proxy-pass-的注意点" class="headerlink" title="proxy_pass 的注意点"></a>proxy_pass 的注意点</h3><p>当 proxy_pass 是 URL 和不是 URL 的场合, 路由的路径是不一样的!</p><h3 id="Docker-Container-看来的-Host-番号"><a href="#Docker-Container-看来的-Host-番号" class="headerlink" title="Docker Container 看来的 Host 番号"></a>Docker Container 看来的 Host 番号</h3><p><code>http://host.docker.internal</code>是指从 Docker Container 看来的番号。从 Container 内连接 Host 的番号<code>7000</code>的时候这样写就可以解决命名的问题,从而裂解上 Host 的番号。<br>Docker Container 内如果写了 localhost 的话指的不是 Docker 启动的 host,二手 Container 自身。当然如果不使用 Docker 而是直接启动 Nginx 的话可以直接置换 localhost。</p><p>也就是说当<code>localhost/dog</code>的时候, 向 Docker 启动的 host 的番号为 7000 的服务器发送请求。(/cat 的情况下也是一样, 也就是 7001)</p><h3 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h3><p>让我们用<code>docker-compose up</code>来启动服务器, 并向<code>localhost/dog</code>发送请求,然后验证一下返回的响应。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl --verbose localhost/dog</span><br></pre></td></tr></table></figure><p>HTTP 请求头</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">> GET /dog HTTP/1.1</span><br><span class="line">> Host: localhost</span><br><span class="line">> User-Agent: curl/7.54.0</span><br><span class="line">> Accept: */*</span><br></pre></td></tr></table></figure><p>HTTP 响应头</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">< HTTP/1.1 200 OK</span><br><span class="line">< Server: nginx/1.17.0</span><br><span class="line">< Date: Mon, 01 Jul 2019 13:38:14 GMT</span><br><span class="line">< Content-Type: text/html</span><br><span class="line">< Content-Length: 264</span><br><span class="line">< Connection: keep-alive</span><br><span class="line">< Last-Modified: Sun, 30 Jun 2019 13:43:59 GMT</span><br><span class="line">< ETag: "5d18bc9f-108"</span><br><span class="line">< Accept-Ranges: bytes</span><br></pre></td></tr></table></figure><p>HTTP 响应体</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"> <head></span><br><span class="line"> <meta charset="utf-8"/></span><br><span class="line"> <meta name="viewport" content="width=device-width, initial-scale=1"/></span><br><span class="line"> <title>汪星人的页面</title></span><br><span class="line"> </head></span><br><span class="line"> <body></span><br><span class="line"> <h1>汪星人的页面</h1></span><br><span class="line"> </body></span><br><span class="line">* Connection #0 to host localhost left intact</span><br><span class="line"></html>%</span><br></pre></td></tr></table></figure><p>各个 container</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">reverse-proxy_1 | 172.31.0.1 - - [01/Jul/2019:13:38:14 +0000] "GET /dog HTTP/1.1" 200 264 "-" "curl/7.54.0"</span><br><span class="line">dog-container | 172.31.0.1 - - [01/Jul/2019:13:38:14 +0000] "GET / HTTP/1.0" 200 264 "-" "curl/7.54.0" "-"</span><br></pre></td></tr></table></figure><p>大功告成!</p>]]></content>
<summary type="html">
<p>Nginx 的基础以及用 Docker 创建一个 Nginx 反向代理服务器</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="Nginx" scheme="https://kisky3.github.io/tags/Nginx/"/>
<category term="Reverse Proxy Server" scheme="https://kisky3.github.io/tags/Reverse-Proxy-Server/"/>
</entry>
<entry>
<title>Nuxt Memo</title>
<link href="https://kisky3.github.io/2021/07/25/NuxtMemo/"/>
<id>https://kisky3.github.io/2021/07/25/NuxtMemo/</id>
<published>2021-07-24T15:33:55.000Z</published>
<updated>2022-01-28T06:52:50.992Z</updated>
<content type="html"><![CDATA[<p>Nuxt 学习笔记</p><a id="more"></a><p>在这里记录一些 Nuxt 踩的坑。</p><h3 id="Nuxt-Netlify-Deploy-404-Problem"><a href="#Nuxt-Netlify-Deploy-404-Problem" class="headerlink" title="Nuxt + Netlify Deploy 404 Problem"></a>Nuxt + Netlify Deploy 404 Problem</h3><p>当我想在 Netlift 上发布 Nuxt 写的代码的时候发现变成了 404。<br>这个时候首先要考虑设定 404 的跳转。</p><p>方法 1:<br><code>/pages</code>里新建一个的<code>*vue</code>文件,然后再里面写上跳转的设定。比如跳转到根页面。<br>pages/*.vue</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><script></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> asyncData({ redirect }) {</span><br><span class="line"> <span class="keyword">return</span> redirect(<span class="string">'/'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><<span class="regexp">/script></span></span><br></pre></td></tr></table></figure><p>在这个状态下如果输入任意的 URL (例:<a href="https://localhost:3000/hoge),就会自动跳转到根页面。" target="_blank" rel="noopener">https://localhost:3000/hoge),就会自动跳转到根页面。</a></p><p>方法 2:<br>在 Netlify 里设定跳转</p><p>static 的下面创建一个为<code>_redirects</code>(没有后缀名)的文件, 然后在里面写入下面的一行就可以了。<br>static/_redirects</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* /index.html 200</span></span><br></pre></td></tr></table></figure><p>之后发布就可以解决 404 问题啦〜!</p><hr><h3 id="Nuxt-Sass-TypeError-this-getOptions-is-not-a-function"><a href="#Nuxt-Sass-TypeError-this-getOptions-is-not-a-function" class="headerlink" title="Nuxt Sass TypeError: this.getOptions is not a function"></a>Nuxt Sass TypeError: this.getOptions is not a function</h3><p>Nuxt 里使用 Sass 的时候, install node-sass 和 sass-loader 之后启动服务器,sass-loader 发生以下的错误。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ERROR Failed to compile with 1 errors</span><br><span class="line"></span><br><span class="line">ERROR in ./components/Header.vue?vue&type=style&index=0&id=1a9bb128&lang=sass&scoped=true&</span><br><span class="line"></span><br><span class="line">Module build failed (from ./node_modules/sass-loader/dist/cjs.js):</span><br><span class="line"></span><br><span class="line">TypeError: this.getOptions is not a function</span><br><span class="line"> at Object.loader (/var/www/front/node_modules/sass-loader/dist/index.js:25:24)</span><br></pre></td></tr></table></figure><p>原因有两个</p><h5 id="1-sass-loader"><a href="#1-sass-loader" class="headerlink" title="1.sass-loader"></a>1.sass-loader</h5><p>sass-loader 版本在 11 之后,需要 webpack 的版本在 5 以上。<br>但是装有 Nuxt 的 webpack 的最新 Nuxt 版本(2021/06/23 的时候为 2.15.7),<a href="mailto:webpack@4.46.0" target="_blank" rel="noopener">webpack@4.46.0</a>, 所以版本不符合就会造成错误。</p><p>sass-loader v11.0.0 的发布信息里有下面的消息:</p><blockquote><p>minimum supported webpack version is 5</p></blockquote><p>所以我们必须将 Nuxt 指定为 11 之前的版本。</p><h5 id="2-node-sass"><a href="#2-node-sass" class="headerlink" title="2.node-sass"></a>2.node-sass</h5><p>另外也不要忘记 node-sass 的版本检查。<br>看 node-sass 的仓库就可得知不同版本的 node 支持对应不同版本的 node-sass。</p><h5 id="解决方法:"><a href="#解决方法:" class="headerlink" title="解决方法:"></a>解决方法:</h5><p>将<code>sass-loader@10.2.0</code>版本固定,然后查找对应的 node 的版本,再查找相应版本的 node-sass。</p><p>1.首先删除已经安装的东西</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn remove node-sass sass-loader</span><br></pre></td></tr></table></figure><p>2.确认 Node 版本</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node -v</span><br></pre></td></tr></table></figure><p>我的版本是 v14.15.5</p><p>3.根据上面的 node-sass 的仓库来查找对应的 node-sass<br>我的情况是 Node 14.15.5、node-sass 4.14 是最新版本、所以指定 4.14.1。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 我的情况</span><br><span class="line">yarn add -D sass-loader@10.2.0 node-sass@4.14.1</span><br><span class="line"></span><br><span class="line"># 复制粘贴用</span><br><span class="line">yarn add -D sass-loader@10.2.0 node-sass@</span><br></pre></td></tr></table></figure><p>这样的话就可以正常启动服务器了!使用 Sass 的时候要注意版本信息!</p>]]></content>
<summary type="html">
<p>Nuxt 学习笔记</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="Nuxt" scheme="https://kisky3.github.io/tags/Nuxt/"/>
<category term="Netlify" scheme="https://kisky3.github.io/tags/Netlify/"/>
</entry>
<entry>
<title>Go and gRPC</title>
<link href="https://kisky3.github.io/2021/07/02/createAPIwithLambdaGo/"/>
<id>https://kisky3.github.io/2021/07/02/createAPIwithLambdaGo/</id>
<published>2021-07-02T13:25:00.000Z</published>
<updated>2022-01-28T06:52:51.401Z</updated>
<content type="html"><![CDATA[<p>Go 和 gRPC的入门尝试</p><a id="more"></a><h3 id="Go是什么"><a href="#Go是什么" class="headerlink" title="Go是什么"></a>Go是什么</h3><p>Go语言是Google社内开发的一个开源编程语言。<br>Go的优点是对初学者友好,处理速度快,所需代码量少,有丰富的库以及插件,还可以进行并发处理,安全性高。<br>Go的缺点是情报较少,不支持Generics,没有例外处理,代码无法进行继承。</p><h3 id="gRPC是什么"><a href="#gRPC是什么" class="headerlink" title="gRPC是什么"></a>gRPC是什么</h3><p>gRPC是由Google公司开源的一款高性能的远程过程调用(RPC)框架,可以在任何环境下运行。该框架提供了负载均衡,跟踪,智能监控,身份验证等功能,可以实现系统间的高效连接。</p><h5 id="※RPC"><a href="#※RPC" class="headerlink" title="※RPC"></a>※RPC</h5><p>Remote Procedure Call的简称,翻译成中文就是远程过程调用。<br>也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。</p><h3 id="什么是Protocol-Buffers"><a href="#什么是Protocol-Buffers" class="headerlink" title="什么是Protocol Buffers"></a>什么是Protocol Buffers</h3><p>Protocol Buffers是Google开源的一个语言无关、平台无关的通信协议,其小巧、高效和友好的兼容性设计,使其被广泛使用。</p><h3 id="使用Go来玩玩gRPC"><a href="#使用Go来玩玩gRPC" class="headerlink" title="使用Go来玩玩gRPC"></a>使用Go来玩玩gRPC</h3><p>grpc.io提供的Go来玩一下gRPC.根据指导来.<br>首先确认一下Go的版本,需要Go1.6以上的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ go version</span><br><span class="line">go version go1.12.6 darwin/amd64</span><br></pre></td></tr></table></figure><p>安装gRPC</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ go get -u google.golang.org/grpc</span><br><span class="line"></span><br><span class="line">$ cat $GOPATH/src/google.golang.org/grpc/version.go | grep Version</span><br><span class="line">// Version is the current grpc version.</span><br><span class="line">const Version = "1.23.0-dev"</span><br></pre></td></tr></table></figure><p>下载Protocol Buffers v3</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ brew install protobuf</span><br><span class="line"></span><br><span class="line">$ protoc --version</span><br><span class="line">libprotoc 3.7.1</span><br></pre></td></tr></table></figure><p>下载Protocol Buffers的Go插件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go get -u github.com/golang/protobuf/protoc-gen-go</span><br></pre></td></tr></table></figure><p>Protocol Buffers文件(.proto)里定义service和message<br>在说明service和service里的rpc方法(也叫做service message)之前先说明一下gRPC能够定义的4种方法。</p><ul><li><ol><li>Simple RPC<br>gRPC客户端发送的单一消息到gRPC服务器,服务器再返回单一消息。(和tutorial的<code>GetFeature</code>相当)</li></ol></li><li><ol start="2"><li>Server side Streaming RPC<br>当gRPC客户端发来单一消息的时候gRPC服务器从流式返回多条消息。(和tutorial的<code>ListFeatures</code>相当)</li></ol></li><li><ol start="3"><li>Client side Streaming RPC<br>当gRPC客户端发来多条消息的时候gRPC服务器返回单一消息。(和tutorial的<code>RecordRoute</code>相当)</li></ol></li><li><ol start="4"><li>双向 Streaming RPC<br>当gRPC客户端发来多条消息的时候gRPC服务器从流式返回多条消息。(和tutorial的<code>RouteChat</code>相当)</li></ol></li></ul><p>※ Steaming(串流)</p><blockquote><p>串流技术,就是通过网路实时压缩和传输影音的技术。好处就是你不需要把完整的多媒体资料下载完后才能观看,而是像”水流”一样源源不断实时从发布源传到客户端。经过串流技术处理的、可以实时播放的多媒体有一个耳熟能详的名字,叫流媒体。</p></blockquote><p>那我们就根据上面的种类来定义service和service method。<br>tutorial定义的是<code>route_guide.proto</code></p><p>可以理解为「gRPC客户端发送的Request = service methord的参数」而「gPRC服务器返回的Response = service methord的返回值」,当使用service methord的Streaming的时候需要在参数和返回值之前加上<code>stream</code>声明。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">service RouteGuide {</span><br><span class="line"> rpc GetFeature(Point) returns (Feature) {}</span><br><span class="line"> rpc ListFeatures(Rectangle) returns (stream Feature) {}</span><br><span class="line"> rpc RecordRoute(stream Point) returns (RouteSummary) {}</span><br><span class="line"> rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>接下来可以定义message,使用service methord来定义所使用的message的类型。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">message Point {</span><br><span class="line"> int32 latitude = 1;</span><br><span class="line"> int32 longitude = 2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">message Rectangle {</span><br><span class="line"> Point lo = 1;</span><br><span class="line"> Point hi = 2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">message Feature {</span><br><span class="line"> string name = 1;</span><br><span class="line"> Point location = 2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">message RouteNote {</span><br><span class="line"> Point location = 1;</span><br><span class="line"> string message = 2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">message RouteSummary {</span><br><span class="line"> int32 point_count = 1;</span><br><span class="line"> int32 feature_count = 2;</span><br><span class="line"> int32 distance = 3;</span><br><span class="line"> int32 elapsed_time = 4;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>从Protocol Buffers文件(.proto) 能够自动生成Go文件(.pb.go).</p><p>Protocol Buffers文件编译之后,将会自动生成Go文件,该文件包括gRPC客户端代码和已经编译好的gRPC服务端的接口。</p><p>执行<code>protoc</code>命令行的话将会根据<code>route_guide.proto</code>里的定义自动生成<code>route_guide.pb.go</code>。</p><p>根据<code>--proto_path</code>来指定编译对象的Protocol Buffers文件路径,<code>--go_out</code>来指定自动生成的Go文件的输出路径(为了将gRPC设置成插件,要记得定义好plugins=grpc).</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ protoc --proto_path routeguide/ routeguide/route_guide.proto --go_out=plugins=grpc:routeguide</span><br></pre></td></tr></table></figure><p>自动生成的Go文件(route_guide.pb.go)包含下面的东西。</p><h4 id="①-针对定义好的message的生成的构造体"><a href="#①-针对定义好的message的生成的构造体" class="headerlink" title="①.针对定义好的message的生成的构造体"></a>①.针对定义好的message的生成的构造体</h4><p>在构造体内也包含着对数据进行操作的方法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">type Point struct {</span><br><span class="line">Latitude int32 `protobuf:"varint,1,opt,name=latitude,proto3" json:"latitude,omitempty"`</span><br><span class="line">Longitude int32 `protobuf:"varint,2,opt,name=longitude,proto3" json:"longitude,omitempty"`</span><br><span class="line">XXX_NoUnkeyedLiteral struct{} `json:"-"`</span><br><span class="line">XXX_unrecognized []byte `json:"-"`</span><br><span class="line">XXX_sizecache int32 `json:"-"`</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (m *Point) GetLatitude() int32 {</span><br><span class="line">if m != nil {</span><br><span class="line">return m.Latitude</span><br><span class="line">}</span><br><span class="line">return 0</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (m *Point) GetLongitude() int32 {</span><br><span class="line">if m != nil {</span><br><span class="line">return m.Longitude</span><br><span class="line">}</span><br><span class="line">return 0</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="②-呼出service-methord的gRPC客户端代码"><a href="#②-呼出service-methord的gRPC客户端代码" class="headerlink" title="②.呼出service methord的gRPC客户端代码"></a>②.呼出service methord的gRPC客户端代码</h4><p>以下是用于返回gRPC客户端的接口和各种处理的客户端的代码。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">type RouteGuideClient interface {</span><br><span class="line">GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error)</span><br><span class="line">ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error)</span><br><span class="line">RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error)</span><br><span class="line">RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">type routeGuideClient struct {</span><br><span class="line">cc *grpc.ClientConn</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func NewRouteGuideClient(cc *grpc.ClientConn) RouteGuideClient {</span><br><span class="line">return &routeGuideClient{cc}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>下面是利用上面的接口来便携的用于呼出service methord <code>GetFeature</code>的方法.<code>c.cc.Invoke</code>的第二参数是gPRC的service methord的endpoint.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) {</span><br><span class="line">out := new(Feature)</span><br><span class="line">err := c.cc.Invoke(ctx, "/routeguide.RouteGuide/GetFeature", in, out, opts...)</span><br><span class="line">if err != nil {</span><br><span class="line">return nil, err</span><br><span class="line">}</span><br><span class="line">return out, nil</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的例子是呼出simple RPC service methord的<code>GetFeatuer</code>的简单的例子.如果涉及到串流的话可能写法会有一些不同.比如我们来看看双向串流的RPC的<code>RouteChat</code>的客户端代码.</p><p>因为是呼出双向串流的RPC的<code>RouteChat</code>的代码,所以service methord的呼出结果(来自gRPC服务器的response)是串流所接收的结果。</p><p>下面我们可以来看看下面的客户端的代码,我们可以推断出:service methord的呼出结果的返回值<code>RouteGuide_RouteChatClient</code>是串流发送message的<code>Send</code>(gPRC客户端 => gRPC服务器)、串流用于接收message的<code>Recv</code>(gRPC服务器 => gRPC客户端)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {</span><br><span class="line">stream, err := c.cc.NewStream(ctx, &_RouteGuide_serviceDesc.Streams[2], "/routeguide.RouteGuide/RouteChat", opts...)</span><br><span class="line">if err != nil {</span><br><span class="line">return nil, err</span><br><span class="line">}</span><br><span class="line">x := &routeGuideRouteChatClient{stream}</span><br><span class="line">return x, nil</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">type RouteGuide_RouteChatClient interface {</span><br><span class="line">Send(*RouteNote) error</span><br><span class="line">Recv() (*RouteNote, error)</span><br><span class="line">grpc.ClientStream</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">type routeGuideRouteChatClient struct {</span><br><span class="line">grpc.ClientStream</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (x *routeGuideRouteChatClient) Send(m *RouteNote) error {</span><br><span class="line">return x.ClientStream.SendMsg(m)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (x *routeGuideRouteChatClient) Recv() (*RouteNote, error) {</span><br><span class="line">m := new(RouteNote)</span><br><span class="line">if err := x.ClientStream.RecvMsg(m); err != nil {</span><br><span class="line">return nil, err</span><br><span class="line">}</span><br><span class="line">return m, nil</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里定义的是从gRPC服务器的response到gRPC客户端的串流接收为止.</p><h4 id="③-service-methord用的gRPC服务器用的接口"><a href="#③-service-methord用的gRPC服务器用的接口" class="headerlink" title="③.service methord用的gRPC服务器用的接口"></a>③.service methord用的gRPC服务器用的接口</h4><p>和客户端的代码不同,gRPC服务器是需要自己事先进行编写,所以只是包含了接口的定义.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">type RouteGuideServer interface {</span><br><span class="line">GetFeature(context.Context, *Point) (*Feature, error)</span><br><span class="line">ListFeatures(*Rectangle, RouteGuide_ListFeaturesServer) error</span><br><span class="line">RecordRoute(RouteGuide_RecordRouteServer) error</span><br><span class="line">RouteChat(RouteGuide_RouteChatServer) error</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>simple RPC的<code>GetFeature</code>的参数的类型是,Protocol Buffers文件的定义里包含的东西,所以和串流相关的service methord的参数是略微不同,我们来挖一挖双向串流RPC的<code>RouteChat</code>。</p><p><code>RouteChat</code>的参数<code>RouteGuide_RouteChatServer</code>如下,串流用于送行的<code>Send</code>(gRPC服务器 => gRPC客户端)和<code>Recv</code>(gRPC客户端 => gRPC服务器)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">type RouteGuide_RouteChatServer interface {</span><br><span class="line">Send(*RouteNote) error</span><br><span class="line">Recv() (*RouteNote, error)</span><br><span class="line">grpc.ServerStream</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">type routeGuideRouteChatServer struct {</span><br><span class="line">grpc.ServerStream</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (x *routeGuideRouteChatServer) Send(m *RouteNote) error {</span><br><span class="line">return x.ServerStream.SendMsg(m)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func (x *routeGuideRouteChatServer) Recv() (*RouteNote, error) {</span><br><span class="line">m := new(RouteNote)</span><br><span class="line">if err := x.ServerStream.RecvMsg(m); err != nil {</span><br><span class="line">return nil, err</span><br><span class="line">}</span><br><span class="line">return m, nil</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="gRPC服务器的编写"><a href="#gRPC服务器的编写" class="headerlink" title="gRPC服务器的编写"></a>gRPC服务器的编写</h3><p>要编写gRPC服务器的话需要以下的工作:</p><ol><li>在自动生成的Go文件里添加gRPC服务器的接口.</li><li>从gRPC客户端的request到gRPC服务器送去request的处理.</li></ol><p>gRPC服务器的接口的编写</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">type routeGuideServer struct {</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line">func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line">func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line">func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line">func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>Go 和 gRPC的入门尝试</p>
</summary>
<category term="Back-end Knowledge" scheme="https://kisky3.github.io/categories/Back-end-Knowledge/"/>
<category term="GO" scheme="https://kisky3.github.io/tags/GO/"/>
<category term="gPPC" scheme="https://kisky3.github.io/tags/gPPC/"/>
</entry>
<entry>
<title>AWS CLF Lesson7〜10 Memo</title>
<link href="https://kisky3.github.io/2021/06/26/lesson3/"/>
<id>https://kisky3.github.io/2021/06/26/lesson3/</id>
<published>2021-06-26T13:06:40.000Z</published>
<updated>2022-01-28T06:52:51.622Z</updated>
<content type="html"><![CDATA[<p>AWS 云从业者基础知识 学习笔记7 〜 10</p><a id="more"></a><h3 id="7-ネットワークサービス"><a href="#7-ネットワークサービス" class="headerlink" title="7 ネットワークサービス"></a>7 ネットワークサービス</h3><p>VPCは隔離されたプライベートなネットワーク構成をお客様がコントロールできるサービス。</p><p>VPCはリージョンを選択して作成。<br>CIDRでVPCのプライベートIPアドレスの範囲を定義。</p><p>インターネットゲートウェイはVPCとパブリックインターネットを接続。<br>インターネットゲートウェイ自体が高可用性と冗長性を持っている。</p><p>ルートテーブルはサブネットと関連付ける。<br>サブネット内のリソースがどこに接続できるかを定義する。</p><p>サブネットは役割で分割する。<br>外部インターネットに接続できるのがパブリックサブネット。<br>外部インターネットに接続せず外部アクセスからリソースを保護できるのがプライベートサブネット。</p><p>セキュリティグループは、インスタンスに対してのトラフィックを制御する仮想ファイアウォール。<br>許可するインバウンドのポートと送信元を設定するボワイトリスト。<br>送信元には、CIDRか他のセキュリティグループIDを指定できる。</p><p>ネットワークACLは、サブネットに対してのトラフィックを制御する仮想ファイアウォール。<br>拒否するインバウンドのポートと送信元を設定するブラックリスト。<br>必要がなければ設定しない追加のセキュリティレイヤー。</p><p>外部からEC2インスタンスにアクセスするための重要なポイント</p><ul><li>インターネットゲートウェイをVPCにアタッチする。</li><li>インターネットゲートウェイへの経路を持つルートテーブルをサブネットに関連付ける。</li><li>EC2インスタンスをそのサブネット内で起動する。</li><li>EC2インスタンスにパブリックIPアドレスを有効にする(またはEC2のパブリックIPアドレスを固定するElastic IPをアタッチする)</li></ul><p>VPCと既存のオンプレミス環境をVPN接続できる。<br>VPCと既存のオンプレミス環境をダイレクトコネクトを使って専用線で接続できる。</p><p>CloudFrontはユーザーへ静的/動的ウェブコンテンツを配信するEdgeサービス。<br>エッジロケーションを使用するCDNサービス。<br>S3から直接に配信したり、ELB経由のEC2から配信するよりも、<br>CloudFrontにキャッシュを持ち、ユーザーにはキャッシュコンテンツを配信する方が、より早く効率的にコンテンツを提供できる。</p><p>世界中のエッジロケーションが利用できるので、ユーザーへは最もレイテンシーの低いエッジロケーションから配信される。<br>通信を保護するために証明書を設定できる。<br>外部の攻撃からも守ることができる。</p><p>Router53はエッジロケーションで使用されるDNSサービス。<br>複数のレコードを設定し、用途に応じで最適なルーティングを選択できる。<br>システムの高可用性を世界中のリージョンを使用して実現できる。</p><p>Zone Apexに対しても柔軟な設定ができ、高可用性を実現できる。</p><h3 id="8-データベースサービス"><a href="#8-データベースサービス" class="headerlink" title="8.データベースサービス"></a>8.データベースサービス</h3><p>RDS(Amazon Relational Database Service)オンプレミスで使われているデータベースエンジンをそのまま簡単に使うことができる。<br>RDSを使うことでインフラ管理から解放され、本来やるべき開発に注力できる。</p><p>OS、データベースエンジンのメンテナンスをAWSに任せることができる。</p><p>データベースのバックアップを管理しなくて良い。<br>バックアップ期間中の任意の特定時間のインスタンスを起動できる。</p><p>マルチAZ配置を使用することでデータベースの高可用性を実現できる。<br>レプリケーション、フェイルオーバーはRDSの機能によって自動的に行われる。</p><p>Amazon AuroraはMySQL/PostgreSQL互换の、クラウドに最適化されたリレーショナルデータベース。</p><p>DMS(AWS Database Migration Service)はデータベース間でデータを移行できるサービス。</p><p>DMSによりオンプレミスからAWSへの継続的なデータ移行を行い、システムのダウンロードタイムを最小限にできる。</p><p>DynamoDBは振るマネージドなデータベースサービス。<br>リージョンを選択して使うことができる。</p><p>データの特徴やシステム要件に応じで適したデータベースサービスを選択する。<br>中規模程度のアクセス量で、整合性や複雑なクエリを必要とする場合はRDSを選択する。<br>大規模なアクセス量で、単純な自由度の高いデータモデルを扱う場合はDynamoDBを選択する。</p><h3 id="9-管理サービス"><a href="#9-管理サービス" class="headerlink" title="9.管理サービス"></a>9.管理サービス</h3><p>AWSのサービスを使い始めると、サービスにより起動されたリソースのメトリクスがCloudWatchに自動的に収集され始める。</p><p>CloudWatchの特徴:</p><ul><li>標準(組み込み)メトリクスの収集、可視化</li><li>カスタムメトリクスの収集、可視化</li><li>ログの収集</li><li>アラーム</li></ul><p>標準メトリクスは、使用するサービスによって取得される情報が異なる。<br>EC2のカスタムメトリクスはCloudWatchエージェントで取得できる。</p><p>EC2のCloudWatchLogsはCloudWatchエージェントで取得できる。<br>CloudWatchLogsによりEC2をよりステートレスにできる。<br>CloudWatch Logsは文字列のフィルタリング結果をメトリクスとして扱える。</p><p>アラートを設定することにより、モニタリング結果に基づく運用を自動化できる。</p><p>Trusted Advisorはaws環境を自動でチェックして、ベストプラクティスに沿ったアドバイスをレポートする。</p><ul><li>コスト最適化</li><li>パフォーマンス</li><li>耐障害性</li><li>セキュリティ</li><li>サービス制限</li></ul><p>コスト最適化では、無駄なコストが発生していないかがチェックされる。</p><p>パフォーマンスでは、最適なサービス、サイズが選択されているかがチェックされる。</p><p>セキュリティでは、環境にリスクのある設定がないかがチェックされる。</p><p>フォールトトレランスでは、耐障害性が低い状態がないかがチェックされる。</p><p>意図しない操作や不正アクセスによってお客様に不利益が生じないよう、サービス制限がある。<br>サービス制限では、制限につかづいたサービスがアラート報告される。</p><p>CloudTrailはAWSアカウント内のすべてのAPIを呼び出しを記録します。<br>CloudFormationは、AWSの各リソースを含んだ環境を自動作成/更新/管理します。<br>Elastic Beanstalkは、Webアプリケーションの環境を簡単にAWSに構築します。</p><p>消費モデルはエンジニアだけではなく組織全体で受け入れる。</p><p>課金体制はサービスによって異なる。</p><p>お客様は最適な料金モデルを選択できる。</p><p>請求書では月の途中でも課金の状況を確認できる。</p><p>コスト配分タグによってROIの诉求分析ができる。</p><p>請求アラームによって使いすぎを抑止するための通知ができる。</p><p>AWS Organizationsで複数アカウントを階層管理できる。Organiationsの一括請求を使用することで、複数アカウントの請求を1つの請求にまとめることができます。</p><p>エスカレーションパスを用意することは重要。</p><p>4つのプランがあり、サポート料金によって段階がある。</p><p>簡易もつもりツールで、請求見込み額を事前に計算しておくことができる。</p><p>TCO計算ツールはAWSとオンプレミスのコストを比較するツール。</p>]]></content>
<summary type="html">
<p>AWS 云从业者基础知识 学习笔记7 〜 10</p>
</summary>
<category term="aws" scheme="https://kisky3.github.io/tags/aws/"/>
<category term="AWS Certified Cloud Practitioner" scheme="https://kisky3.github.io/tags/AWS-Certified-Cloud-Practitioner/"/>
</entry>
<entry>
<title>LeetCode Memo</title>
<link href="https://kisky3.github.io/2021/06/22/leetcodememo/"/>
<id>https://kisky3.github.io/2021/06/22/leetcodememo/</id>
<published>2021-06-22T13:19:00.000Z</published>
<updated>2022-02-23T15:03:44.840Z</updated>
<content type="html"><![CDATA[<p>LeetCode笔记</p><a id="more"></a><p>刷LeetCode啊!!!</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://github.com/Kisky3/leetcode</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>LeetCode笔记</p>
</summary>
<category term="LeetCode" scheme="https://kisky3.github.io/tags/LeetCode/"/>
</entry>
<entry>
<title>iOS Input Zoom Issue</title>
<link href="https://kisky3.github.io/2021/06/14/iosFormInputZoomIn/"/>
<id>https://kisky3.github.io/2021/06/14/iosFormInputZoomIn/</id>
<published>2021-06-14T13:50:53.000Z</published>
<updated>2022-01-28T06:52:51.589Z</updated>
<content type="html"><![CDATA[<p>iOS下的输入框点击放大问题</p><a id="more"></a><p>在iOS的表单输入的时候,经常会出现页面被强制扩大的问题。<br>例子:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">form</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"form-value"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"text"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"form-value"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">select</span> <span class="attr">name</span>=<span class="string">"select"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">option</span> <span class="attr">value</span>=<span class="string">""</span>></span>吉田優子<span class="tag"></<span class="name">option</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">option</span> <span class="attr">value</span>=<span class="string">""</span>></span>千代田桃<span class="tag"></<span class="name">option</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">option</span> <span class="attr">value</span>=<span class="string">""</span>></span>リリス<span class="tag"></<span class="name">option</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">option</span> <span class="attr">value</span>=<span class="string">""</span>></span>陽夏木ミカン<span class="tag"></<span class="name">option</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">select</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">input</span><span class="selector-attr">[type="text"]</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">12px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">select</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">12px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果在表单里输入的话,就像下面这样,页面被扩大。<br><img src="./1.png" style="width:500px"></p><p>页面被扩大的原因是因为文字size没有达到16px的时候就会自动放大。</p><h3 id="解决方法1"><a href="#解决方法1" class="headerlink" title="解决方法1"></a>解决方法1</h3><p>将所有未满16px的文字size设置成16px;虽然在页面上看起来会大一点但是这个是最简单的解决方法了。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">input</span><span class="selector-attr">[type="text"]</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">select</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><h3 id="方法2"><a href="#方法2" class="headerlink" title="方法2:"></a>方法2:</h3><p>在将文字size设置成16px的同时,使用<code>transform的scale()</code>来使其看起来相对小。sccale()的值可以利用calc()来计算得到。<br>这个方法不仅要调整字体的大小,还要相应的调整位置和相对大小,有一点点麻烦。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">input</span><span class="selector-attr">[type="text"]</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(calc(12 / 16));</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">select</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(calc(12 / 16));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><h3 id="方法3"><a href="#方法3" class="headerlink" title="方法3:"></a>方法3:</h3><p>viewport里设置<code>user-scalable=no</code>,user-scalable=no本来是使得用户无法自己扩大页面,以此来控制iOS的自动扩大问题。</p><p>本来是不推荐限制用户的操作的,但是在iOS10以后,<code>user-scalable=no</code>将不再生效啦。</p><p>也就是说可以满足用户在输入表单的时候不自动扩大,但是用户手动扩大是可行的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" /></span><br></pre></td></tr></table></figure><p>Android的话只写<code>user-scalable=no</code>是不起效的。要加入下面的js.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"><span class="keyword">var</span> ua = navigator.userAgent.toLowerCase();</span></span><br><span class="line"><span class="javascript"><span class="keyword">var</span> isiOS = (ua.indexOf(<span class="string">'iphone'</span>) > <span class="number">-1</span>) || (ua.indexOf(<span class="string">'ipad'</span>) > <span class="number">-1</span>);</span></span><br><span class="line">if(isiOS) {</span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> viewport = <span class="built_in">document</span>.querySelector(<span class="string">'meta[name="viewport"]'</span>);</span></span><br><span class="line"> if(viewport) {</span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> viewportContent = viewport.getAttribute(<span class="string">'content'</span>);</span></span><br><span class="line"><span class="javascript"> viewport.setAttribute(<span class="string">'content'</span>, viewportContent + <span class="string">', user-scalable=no'</span>);</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://qiita.com/skwbr/items/b285cc312587c73a4812" target="_blank" rel="noopener">iOSでinputのフォーカス時に画面がズームするのを防ぐ – Qiita</a></p><p><a href="https://qiita.com/GreenDolphin/items/d74e5758a36478fbc039" target="_blank" rel="noopener">iOS10のSafariでuser-scalable=no が効かなくズームがされる問題への対策 – Qiita</a></p>]]></content>
<summary type="html">
<p>iOS下的输入框点击放大问题</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="Input" scheme="https://kisky3.github.io/tags/Input/"/>
<category term="iOS" scheme="https://kisky3.github.io/tags/iOS/"/>
</entry>
<entry>
<title>Install ipa file To Your iPhone</title>
<link href="https://kisky3.github.io/2021/06/06/InstallipaFileToYouriPhone/"/>
<id>https://kisky3.github.io/2021/06/06/InstallipaFileToYouriPhone/</id>
<published>2021-06-06T11:16:14.000Z</published>
<updated>2022-01-28T06:52:50.455Z</updated>
<content type="html"><![CDATA[<p>如何将ipa文件安装到你的iPhone上</p><a id="more"></a><p>今天拿到了一份ipa文件,然后需要在自己的手机上进行测试。<br>记录一下测试方法。</p><p>使用的app是<code>Apple Configurator 2</code>。</p><h3 id="需要准备的东西"><a href="#需要准备的东西" class="headerlink" title="需要准备的东西"></a>需要准备的东西</h3><ul><li>Mac</li><li>iPhone</li><li>数据线(连接用)</li></ul><hr><h3 id="开始干"><a href="#开始干" class="headerlink" title="开始干"></a>开始干</h3><p>1.首先从App Store里下载Apple Configurator。<br>利用下面的链接, 在App Store里搜索Apple Configurator。</p><p><a href="https://apps.apple.com/jp/app/apple-configurator-2/id1037126344" target="_blank" rel="noopener">https://apps.apple.com/jp/app/apple-configurator-2/id1037126344</a></p><img src="./1.png" style="width:500px"><p>安装完之后,应该能看到下面的界面。<br><img src="./2.png" style="width:500px"></p><p>然后将iPhone和Mac进行连接。<br><img src="./3.png" style="width:500px"></p><p>点击出现的媒体图标(就是你的iPhone),然后把ipa文件拖拽进去。<br>然后就等着下载好就行了。<br><img src="./4.png" style="width:500px"></p>]]></content>
<summary type="html">
<p>如何将ipa文件安装到你的iPhone上</p>
</summary>
<category term="System Setting" scheme="https://kisky3.github.io/categories/System-Setting/"/>
<category term="ipa" scheme="https://kisky3.github.io/tags/ipa/"/>
<category term="AppleConfigurator2" scheme="https://kisky3.github.io/tags/AppleConfigurator2/"/>
</entry>
<entry>
<title>AWS CLF Lesson4〜6 Memo</title>
<link href="https://kisky3.github.io/2021/06/05/lesson2/"/>
<id>https://kisky3.github.io/2021/06/05/lesson2/</id>
<published>2021-06-05T13:06:40.000Z</published>
<updated>2022-01-28T06:52:51.610Z</updated>
<content type="html"><![CDATA[<p>AWS 云从业者基础知识 学习笔记4 〜 6</p><a id="more"></a><h3 id="4-AWSのテクノロジー"><a href="#4-AWSのテクノロジー" class="headerlink" title="4. AWSのテクノロジー"></a>4. AWSのテクノロジー</h3><h4 id="4-1-AWSのサービス"><a href="#4-1-AWSのサービス" class="headerlink" title="4.1 AWSのサービス"></a>4.1 AWSのサービス</h4><p>AWSではリージョンと呼ばれる、世界のどこでサービスを使うかを選択するための地域と、リージョンと呼ばれる、世界のどこでサービスを使うかを選択するための地域と、リージョンにあるアベイラビリティゾーンという、データセンターの集合体があります。</p><h4 id="4-2-グローバルインフラストラクチャ"><a href="#4-2-グローバルインフラストラクチャ" class="headerlink" title="4.2 グローバルインフラストラクチャ"></a>4.2 グローバルインフラストラクチャ</h4><p>全世界に展開されているリージョンを選択して、数分で世界中にシステムをデプロイできる。<br>リージョンによって利用できるサービス、コストが異なる。</p><p>リージョンにはアベイラビリティゾーンが2つ以上ある(ローカルリージョンを除く)<br>アベイラビリティゾーンは障害が同時に影響しないよう、地理的に十分に離れた場所にある。<br>同一リージョン内のアベイラビリティゾーン同士は高速なプライベート光ファイバーネットワーキングで接続されている。<br>複数のアベイラビリティゾーンを使うことで、耐障害性、可用性の高いアーキテクチャを実装できる。<br>データセンターは、セキュリティ、コンプライアンス上の様々な第三者監査検証を実施している。</p><p>リージョンとは違う場所に200以上のエッジロケーションがある。<br>エッジロケーションではAmazon Route 53とAmazon CloudFrontを利用できる。<br>ユーザーは最も低レイテンシーのエッジロケーションにアクセスできる。</p><p>Amazon Route53とAmazon CloudFrontはAWS SheildによりDDos攻撃から保護される。</p><h3 id="5-コンピューティングサービス"><a href="#5-コンピューティングサービス" class="headerlink" title="5.コンピューティングサービス"></a>5.コンピューティングサービス</h3><h4 id="5-1-EC2-Elastic-Compute-Cloud"><a href="#5-1-EC2-Elastic-Compute-Cloud" class="headerlink" title="5.1 EC2(Elastic Compute Cloud)"></a>5.1 EC2(Elastic Compute Cloud)</h4><p>使うときにだけEC2インスタンスを起動することができる。<br>必要なEC2インスタンスの数を事前に予測する必要はない。</p><p>使った文にだけ料金が発生する。<br>時間単位、秒単位で課金される。<br>アウト通信に転送料金が発生する。</p><p>インスタンスタイプは運用を開始した後に柔軟ぬ性能を変更できる。<br>運用を開始する前の、誤った性能予測の計算をする必要がなくなる。</p><p>数分でEC2を起動できることは、経営の俊敏性が増やすことに直結する。</p><p>数分でEC2を世界中にデプロイできる。</p><p>AMIから同じ構成のEC2インスタンスを何台でも起動できる。<br>AWS Marketplaceから簡単にソフトウェア構成済みのEC2インスタンスを起動できる。</p><p>EC2へのインスタンスのトラフィックはセキュリティグループのインパウンド(受信)で制御する。</p><p>オペレーティングシステムの管理者はキーペアで安全にログインできる。</p><p>数クリック、数分でEC2インスタンスを起動できる。</p><h4 id="5-2-ELB"><a href="#5-2-ELB" class="headerlink" title="5.2 ELB"></a>5.2 ELB</h4><p>EC2インスタンスの可用性を高めるためにELBを使用することができる。<br>HTTP \HTTPSではApplication Load Balancerを使い、それ以外のTCPではNetwork Load Balancerを使う。</p><p>ELBには、正常なインスタンスのみにトラフィックを送るためのヘルスチェック機能がある。</p><p>ELBはインターネット向けにも内部向けにも対応している。</p><p>インターネット向けだけではなく内部にもELBを挟むことによって、システムの可用性をさらに高めることができる。</p><p>ELB自体が高可用性のマネージドサービスなので単一障害点とはならない。</p><p>複数のアベイラビリティゾーンに負荷分散を実行できるのでリリースの負荷が均等になる。</p><p>Auto ScalingによってEC2インスタンスを必要なときに自動で増減できる。</p><p>Auto Scalingのメリットは、高可用性、耐障害性、コスト効率化。</p><p>垂直スケーリングよりも水平スケーリングの方がスケーラビリティを確保しやすい。</p><p>Auto Scalingでは起動設定(何を)、Auto Scalingグループ、スケーリングポリシーを設定する。</p><p>EC2のユーザーデーターを使うことでコマンドを自動実行し、デプロイ処理を自動化することができる。</p><p>EC2の情報(IPアドレスやインスタンスID)はタメデータから取得できる。</p><p>ELB、CloudWatch、Auto Scalingの3つのサービスで、自動敵でスケーラブルなアプリケーションを構築できる。</p><h4 id="5-4-Lambda"><a href="#5-4-Lambda" class="headerlink" title="5.4 Lambda"></a>5.4 Lambda</h4><p>サーバー構築や環境の準備をするをことなく、すぐに開発を始められる。</p><p>サーバーの運用から解放され、開発に注力できる。</p><p>Lmabdaを使うために新しい言語の勉強は不要。使い慣れた言語ですぐに始められる。</p><p>リクエストに応じて水平的にスケーリングして、並行で関数が実行される。</p><p>Auto Scalingを設定する必要がない。</p><p>メモリを割り当てることで他のリソースの性能も割り当てられる。</p><p>実行されている時間に対してミリ秒単位の無駄のない課金がなされる。</p><p>実行されていない待機時間には課金されない。</p><p>AWSサービスの処理を簡単に自動化できる。</p><p>AWSサービスからのトリガーを使用することで、イベントからLambdaを実行できる。</p><h3 id="6-ストレージサービス"><a href="#6-ストレージサービス" class="headerlink" title="6. ストレージサービス"></a>6. ストレージサービス</h3><h4 id="EBS-Amazon-Amazon-Elastic-Block-Store"><a href="#EBS-Amazon-Amazon-Elastic-Block-Store" class="headerlink" title="EBS (Amazon Amazon Elastic Block Store)"></a>EBS (Amazon Amazon Elastic Block Store)</h4><p>EBSはAmazon Amazon Elastic Block Storeの略です、EC2インスタンスにアタッチして使用するブロックストレージボリュームです。<br>必要な時に必要な量を利用できる。<br>使い始めた後にオンラインでボリュームタイプを変更できる。<br>使い始めた後にオンラインでストレージ容量を増やすことができる。</p><p>スナップショットはS3の機能を使って保存される。(高い耐久性)。</p><p>EBSの暗号化に対して追加の操作は必要ない。</p><p>EBSのデータは永続的、インスタンスストアは一時的。</p><h4 id="S3-Amazon-Simple-Storage"><a href="#S3-Amazon-Simple-Storage" class="headerlink" title="S3(Amazon Simple Storage)"></a>S3(Amazon Simple Storage)</h4><p>Amazon Simple Storage Serviceの略です。<br>S3のオブジェクト容量は無制限。<br>ストレージ容量の確保/調達を気にすることなく開発に専念できる。</p><p>S3の耐久性はイレブンナイン(99.9999999%)。</p><p>冗長化やバックアップを意識することなく開発に専念できる。<br>S3は世界中のどこからでもアクセスできる。</p><p>S3バケット、オブジェクトはデフォルトでプライベート。<br>アクセスコントロールリストで簡単にアクセス権を設定できる。<br>バケットポリシーでより詳細にアクセス権を設定できる。<br>EC2などのAWSリソースにS3へのアクセス権を設定する際はIAMロールを使用する。<br>HTTPSでアクセスできる。<br>保存データの暗号化は複数の方法から選択できる。</p><p>ストレージ料金はストレージクラスによりコスト効率を高めることができる。<br>ライフサイクルポリシーによりストレージクラスの変更を自動化できる。<br>リージョンの外へのアウト通信のみデータ転送料金が発生する。</p>]]></content>
<summary type="html">
<p>AWS 云从业者基础知识 学习笔记4 〜 6</p>
</summary>
<category term="aws" scheme="https://kisky3.github.io/tags/aws/"/>
<category term="AWS Certified Cloud Practitioner" scheme="https://kisky3.github.io/tags/AWS-Certified-Cloud-Practitioner/"/>
</entry>
<entry>
<title>Basic Features Of Vue.js</title>
<link href="https://kisky3.github.io/2021/05/11/vuelifecycle/"/>
<id>https://kisky3.github.io/2021/05/11/vuelifecycle/</id>
<published>2021-05-11T10:26:19.000Z</published>
<updated>2022-01-28T06:52:51.680Z</updated>
<content type="html"><![CDATA[<p>关于Vue的基础概念总结</p><a id="more"></a><h1 id="Vue的生命周期"><a href="#Vue的生命周期" class="headerlink" title="Vue的生命周期"></a>Vue的生命周期</h1><h2 id="什么是Vue的生命周期"><a href="#什么是Vue的生命周期" class="headerlink" title="什么是Vue的生命周期"></a>什么是Vue的生命周期</h2><p>在Vue中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程。</p><p>Vue一共有8个生命周期:<br>| 生命周期名 | 描述 |<br>| —- | —- |<br>| beforeCreate | 组件实例被创建之初 |<br>| created | 组件实例已经完全创建 |<br>| beforeMount | 组件挂载之前 |<br>| mounted | 组件挂载到实例上去之后 |<br>| beforeUpdate | 组件数据发生变化,更新之前 |<br>| updated | 数据数据更新之后 |<br>| beforeDestroy | 组件实例销毁之前 |<br>| destroyed | 组件实例销毁之后 |</p><h2 id="Vue页面组件加载会执行哪些生命周期"><a href="#Vue页面组件加载会执行哪些生命周期" class="headerlink" title="Vue页面组件加载会执行哪些生命周期"></a>Vue页面组件加载会执行哪些生命周期</h2><p>Vue提供了一些生命周期的钩子,平时最主要用的是下面几个,这几个是页面进行加载的时候会被执行的生命周期。</p><ul><li>beforecreate</li><li>created</li><li>beforemounte</li><li>mounted</li></ul><p>beforecreate是属于第一个被加载的部分,没有节点也没有数据。</p><p>created在beforecreate之后执行,这个时候已经取得了数据但是还没有生成节点。</p><p>beforemounte的时候也是取得了数据但是还没有生成节点。</p><p>mounted的时候是既取得了数据,这个时候页面上也已经生成了节点。</p><h2 id="生命周期的使用方法"><a href="#生命周期的使用方法" class="headerlink" title="生命周期的使用方法"></a>生命周期的使用方法</h2><p>created => 发送请求取得数据<br>mounted => 使用插件(dom相关的逻辑处理)</p><p>一般来说会在create的里面进行发送请求的处理。<br>因为此时已经生成了数据($data),但是还没生成节点($el),所以在dom节点生成之前从发送的请求里获取数据是比较合理的顺序。</p><p>而如果我们在mounted里进行发送请求的处理的话,会导致一个加载顺序的混乱。(虽然它也是可以跑的)。这就变成了先生成数据和空节点,再返回去取得数据再反应到节点的顺序。</p><p>有可能会造成一个页面卡顿的状态。</p><p>那我们一般在什么情况下使用mounted呢。比如说我们需要使用插件,并且要对dom节点进行一些操作的时候。</p><p>比如我们要使用swiper插件来生成一个轮播图,这个时候就应该在mounted里进行new swiper()的处理。</p><h1 id="Vue路由"><a href="#Vue路由" class="headerlink" title="Vue路由"></a>Vue路由</h1><h2 id="隐形路由和显性路由"><a href="#隐形路由和显性路由" class="headerlink" title="隐形路由和显性路由"></a>隐形路由和显性路由</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> VueRouter({</span><br><span class="line"> routes <span class="comment">// `routes: routes` 缩略</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 生成root的对象并且为了让vue全体能够使用路由你需要在根目录进行router的注入</span></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue({</span><br><span class="line"> router</span><br><span class="line">}).$mount(<span class="string">'#app'</span>)</span><br></pre></td></tr></table></figure><p>显示路由: query<br>隐形路由: params(需要注明路由的Name)</p><p>显示路由:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.$router.push({</span><br><span class="line"> path: <span class="string">'/about'</span>,</span><br><span class="line"> query: {</span><br><span class="line"> key: <span class="string">"test-ket"</span></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>隐形路由:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.$router.push({</span><br><span class="line"> path: <span class="string">'/about'</span>,</span><br><span class="line"> <span class="comment">// 路由的名字</span></span><br><span class="line"> name: <span class="string">'About'</span>,</span><br><span class="line"> params: {</span><br><span class="line"> key: <span class="string">"test-ket"</span></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h2 id="路由模式"><a href="#路由模式" class="headerlink" title="路由模式"></a>路由模式</h2><p>vue中的router有两种模式:hash模式(默认)、history模式(需配置mode: ‘history’)</p><p>hash(默认): <a href="http://localhost:8080/#/about" target="_blank" rel="noopener">http://localhost:8080/#/about</a><br>history: <a href="http://localhost:8080/about" target="_blank" rel="noopener">http://localhost:8080/about</a></p><p>hash模式是默认的,使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。</p><p>它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。</p><p>history模式:利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)<br>这两个方法应用于浏览器的历史记录栈,在当前已有的 back()、forward()、go() 方法的基础之上,这两个方法提供了对历史记录进行修改的功能。当这两个方法执行修改时,只能改变当前地址栏的 URL,但浏览器不会向后端发送请求,也不会触发popstate事件的执行。</p><p>传统的路由指的是:当用户访问一个url时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发</p><p>而前端路由是不涉及服务器的,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换。</p><h2 id="路由拦截"><a href="#路由拦截" class="headerlink" title="路由拦截"></a>路由拦截</h2><p>比如如下的路由拦截,可以在路由跳转过程中进行控制。最后不要忘记写下保安执行代码next()。</p><p>参数from,to,<br>next()是必须的,也可以在next里写入路由。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">全局:</span><br><span class="line">router.beforeEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>)=></span>{})</span><br><span class="line">router.afterEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>)=></span>{})</span><br><span class="line"></span><br><span class="line">组件内守卫:</span><br><span class="line">beforeRouterEnter(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>)=></span>{})</span><br><span class="line">beforeRouterUpdate(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>)=></span>{})</span><br><span class="line">beforeRouterLeave(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>)=></span>{})</span><br><span class="line"></span><br><span class="line">路由独享:</span><br><span class="line">beforeEnter(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =></span> {})</span><br></pre></td></tr></table></figure><h2 id="动态路由和子路由"><a href="#动态路由和子路由" class="headerlink" title="动态路由和子路由"></a>动态路由和子路由</h2><p>动态路由一般用于id之类的,并可以使用从路由获取到的id来向后台发送数据。</p><h2 id="Keep-alive"><a href="#Keep-alive" class="headerlink" title="Keep-alive"></a>Keep-alive</h2><p>Keep-alive是在优化的时候使用的。可以进行组件的缓存。使用keep-alive之后会在组件执行的时候触发两个额外的生命周期:</p><ul><li>actived</li><li>deactived</li></ul><p>在路由里设置keep-alive的话可以对是否之前访问过该组件进行判断,如果存在缓存则直接显示缓存,不存在的时候就重新向服务器发送请求。</p><p>也可以在需要缓存的组件外面使用<keep-alive>来进行包裹,达到组件数据的缓存效果。</keep-alive></p><p>※ 详细的例子可以参照这篇文章:<br><a href="https://qiita.com/Y-Kanoh/items/a720c5c45929d232b634" target="_blank" rel="noopener">https://qiita.com/Y-Kanoh/items/a720c5c45929d232b634</a></p><p><a href="https://jp.vuejs.org/v2/api/#keep-alive" target="_blank" rel="noopener">https://jp.vuejs.org/v2/api/#keep-alive</a></p><h2 id="ref"><a href="#ref" class="headerlink" title="ref"></a>ref</h2><p>在Vue2.0的时候ref可以用于连接子组件的状态。<br>例子:在母组件点击按钮的时候取得子组件input输入的值。</p><p>子组件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <input type="test" v-model="text" /></span><br><span class="line"> <textarea v-model="textArea"></textarea></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"><script></span><br><span class="line">export default({</span><br><span class="line"> data : function (){</span><br><span class="line"> return {</span><br><span class="line"> text: '',</span><br><span class="line"> textArea: '',</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>母组件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div id="app"></span><br><span class="line"> <text-and-text-area ref="texts" /></span><br><span class="line"> <button @click="testAction">test</button></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import TextAndTextArea from './components/TextAndTextArea.vue'</span><br><span class="line"></span><br><span class="line">export default {</span><br><span class="line"> name: 'App',</span><br><span class="line"> components: {</span><br><span class="line"> TextAndTextArea</span><br><span class="line"> },</span><br><span class="line"> mounted: function (){</span><br><span class="line"> console.log(this.$refs.texts);</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> testAction: function () {</span><br><span class="line"> console.log(this.$refs.texts.text); // 子组件input输入值的参照</span><br><span class="line"> console.log(this.$refs.texts.textArea); // 母组件input输入值的参照</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>在这里母组件在引用子组件的时候要给子组件加上ref<code>ref="texts"</code>,然后可以在母组件mounted的时候呼出<code>this.$refs.texts</code>,就能得到子组件里data定义好的数据对象。<code>this.$refs.texts.text</code>,<code>this.$refs.texts.textArea</code>。</p><p>vue3的时候写法会有一点不一样:<br>和上面同样的例子,</p><p>先来看看子组件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <input type="test" v-model="text" /></span><br><span class="line"> <textarea v-model="textArea"></textarea></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"><script></span><br><span class="line">import { ref } from 'vue'</span><br><span class="line">export default({</span><br><span class="line"> setup () {</span><br><span class="line"> const text = ref('')</span><br><span class="line"> const textArea = ref('')</span><br><span class="line"></span><br><span class="line"> return {</span><br><span class="line"> text,</span><br><span class="line"> textArea</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>母组件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div id="app"></span><br><span class="line"> <text-and-text-area ref="texts" /></span><br><span class="line"> <button @click="testAction">test</button></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import TextAndTextArea from './TextAndTextArea.vue'</span><br><span class="line">import { onMounted, ref } from 'vue'</span><br><span class="line"></span><br><span class="line">export default {</span><br><span class="line"> components: {</span><br><span class="line"> TextAndTextArea</span><br><span class="line"> },</span><br><span class="line"> setup () {</span><br><span class="line"> const texts = ref(null)</span><br><span class="line"> const testAction = () => {</span><br><span class="line"> console.log(texts.value.text);</span><br><span class="line"> console.log(texts.value.textArea);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> onMounted(() => {</span><br><span class="line"> console.log(texts.value);</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> return {</span><br><span class="line"> texts,</span><br><span class="line"> testAction</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>Vue3的话写法发生了一些变化,首先就是不再使用Vue2里的<code>this.$refs</code>了。<br>取而代之要使用<code>setup</code>这个新属性。</p><p>分析一下就是首先要给子组件加上ref定义。<code>ref="texts"</code>,然后在母组件的setup里定义<code>const texts = ref(null)</code>。<br>这里的变量名称要和给子组件定义的名称相同,要不会报错。然后将结果return出来。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> {</span><br><span class="line"> texts, <span class="comment">// 設定したrefにアクセスできる変数</span></span><br><span class="line"> testAction</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后将方法method写在setup里面,就可以像这样<code>texts.value.textArea</code>使用子组件的值了。</p><h2 id="nextTick"><a href="#nextTick" class="headerlink" title="nextTick"></a>nextTick</h2><p>我们知道Vue的DOM更新是不同步的,所以有时候会取得DOM更新前的数据。而nextTick就是能够保证当DOM加载完毕才执行内部代码。</p><p>例:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Vue.createApp({</span><br><span class="line"> data: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> message: <span class="string">'Welcome to Vue.js!'</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> mounted: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.message = <span class="string">'Hello Vue.js!'</span>;</span><br><span class="line"> <span class="comment">// コンソールにログを出力します。</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.$el.textContent);</span><br><span class="line"> <span class="keyword">this</span>.$nextTick(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// nextTickを使用してコンソールにログを出力します。</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.$el.textContent);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">}).mount(<span class="string">'#app'</span>)</span><br></pre></td></tr></table></figure><p>在这里我们可以看到console的输出结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Welcome to Vue.js!</span><br><span class="line">Hello Vue.js!</span><br></pre></td></tr></table></figure><p>所以可以看到在mounted里如果直接输出<code>this.$el.textContent</code>是得到DOM改变前的数据,而nextTick保证了取得DOM改变之后的数据。</p><p>使用场景比如我们要使用better-scroll插件的话就需要在DOM更新后取得数据来完成。</p><h2 id="axios二次封装"><a href="#axios二次封装" class="headerlink" title="axios二次封装"></a>axios二次封装</h2><p>axios二次封装是为了方便简洁和后续使用。<br>要不每次请求都要写重复的代码来保证数据取得成功。</p><p>使用场景比如说数据加载的时候显示加载中,数据加载完毕则取消加载中的显示。<br>比如验证token存在与否,如果不存在的情况下要强制返回login页面进行登陆。</p><h2 id="Vuex"><a href="#Vuex" class="headerlink" title="Vuex"></a>Vuex</h2><p>Vuex是一个vue的状态管理器,属性有:<code>state,getters,mutation,action,modules</code>.</p><ul><li><p>Vuex要在什么情况下进行使用呢?<br>比如说管理数据方便: 比如说同一个数据要在不同的组件中使用的时候就适合使用Vuex。</p></li><li><p>为什么要用modules呢?<br>就是当state数据比较多比较庞大不好管理的时候,也不知道哪个属性在哪个地方使用的时候就用modules进行管理。这样比较方便后期维护。</p></li><li><p>mutaions和action有什么异同?<br>mutaions是同步的,actions是异步的。<br>action是提交mutation的,不会直接修改状态。<br>action可以包含异步操作。</p></li><li><p>Vuex的state能否直接修改?<br>不能,因为state是实时更新的,而mutation又无法进行异步操作,所以如果你直接修改state进行异步操作的时候,还没有执行完,而别的地方如果同时对state进行了修改就会导致冲突。<br>所以state要同步操作,并且mutation被限制了不允许异步操作。</p></li></ul><h2 id="computed-methods-watch的区别"><a href="#computed-methods-watch的区别" class="headerlink" title="computed,methods,watch的区别"></a>computed,methods,watch的区别</h2><p>computed: 有缓存,当值发生变化的时候才会执行。<br>methods: 没有缓存,template改变的时候methods都会执行。<br>watch: 是监听数据的,当值发生变化才会执行。</p>]]></content>
<summary type="html">
<p>关于Vue的基础概念总结</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="Vue" scheme="https://kisky3.github.io/tags/Vue/"/>
</entry>
<entry>
<title>What's the Node.js and Why Everybody use it?</title>
<link href="https://kisky3.github.io/2021/04/22/whynodejs/"/>
<id>https://kisky3.github.io/2021/04/22/whynodejs/</id>
<published>2021-04-22T13:54:05.000Z</published>
<updated>2022-01-28T06:52:51.680Z</updated>
<content type="html"><![CDATA[<p>什么是Node.js,为何人人都在用它?</p><a id="more"></a><p>说到JavaScript的学习,大家都是以Node.js存在的前提进行的,<br>今天来说说这货到底是啥,它有啥用,为何人人都在用它呢.</p><h3 id="基础-JavaScript的特征"><a href="#基础-JavaScript的特征" class="headerlink" title="基础: JavaScript的特征"></a>基础: JavaScript的特征</h3><p>首先,JavaScript和别的语言有什么区别呢?<br>JavaScript是在<code>浏览器(比如Chrome、Firefox)上</code>运行的一个编程语言。<br>但是其他的语言比如Python、Ruby之类的一般的编程语言都是在<code>计算机上</code>运行的。</p><img src="./1.png" style="width: 500px"><p>这里就涉及到了能否直接连接使用OS的功能。<br>OS的基本功能, 比如说文件的读取动能、网络通信的功能等等。<br>能在计算机上直接运行的application(Python、Ruby)等是能够直接操作这些功能的。</p><p>但是在浏览器上运行的JavaScript是不能直接操作OS的功能的。比如说你打开网页的时候如果能直接通过浏览器对你的本地计算机文件进行操作,比如文件篡改删除等等,将会造成大问题的是不。</p><p>所以,浏览器上运行的JavaScript是不允许直接操作OS功能的。<br>但是如果完全禁止JavaScript使用OS功能的话还是有一些不方便,所以有时需要通过一些<code>中介</code>来操作一部分限定的OS功能。</p><p>最近的话比如摄像头和麦克风的操作权限可以通过<code>中介</code>获取,这样我们就可以在网络会议的时候使用了!</p><img src="./2.png" style="width: 500px"><h3 id="Node-js是啥?"><a href="#Node-js是啥?" class="headerlink" title="Node.js是啥?"></a>Node.js是啥?</h3><p>上面说的这种只能在特定环境(浏览器)上运行的JavaScript也能像其他的Pythin语言一样能在OS上运行的东西就是Node.js。</p><img src="./3.png" style="width: 500px"><p>容易被误会的是Node.js不是服务器,也不是像Rails、Django这样的Web框架。<br>它是<code>JavaScript的运行环境</code>。</p><p>在Windows安装Python的话会生成一个「python.exe」、而安装Node.js的时候也会生成一个「node.exe」</p><p>python.exe是操作系统中的一个运行Python代码的可执行文件(程序),<br>同理,<code>node.exe也就是操作系统中的一个运行JavaScript代码的可执行文件(程序)</code>,</p><p>Node.js能够允许JavsScript操作OS功能。比如说之前在浏览器上不能操作文件之类的现在通过Node.js变得可能了。<br>它其实原来只是旨在构造一个能够处理大量同时连接处理的网络程序而已,经常可以听到「可以利用JavaScript来写一个服务器(而不是客户端网络)了」,使得人们有一个普遍的印象就是Node.js上服务器上的JavaScript的运行环境,是不是这个介绍其实更广为人知呢。</p><p>但是现在Node.js用在客户端网页的机会也增多了。<br>比如使用Babel,webpack之类来构建在「浏览器上运行的JavaScript」的开发环境的时候,就经常要用到Node.js.</p><p>可以说Node.js是在服务器端和客户端两边的JavaScript的运行环境。</p><hr><h3 id="npm是何方神圣?"><a href="#npm是何方神圣?" class="headerlink" title="npm是何方神圣?"></a>npm是何方神圣?</h3><p>在这里也顺便说一下什么是npm.</p><p>npm是Node.js的版本管理工具.<br>就像pip之于Python,gem之于Ruby,apt之于Debian,Homebrew之于Mac,cargo之于Rust.</p><p>yarn有时也会出来,可以看作是和npm差不多的东西.</p><p>而通过npm/yarn安装的package可以看作是库和框架之类的东西.<br>比如Vue,React,webpack,jQuery之类的东西.</p><p>在Node.js下如果有你想使用的库,就不用像以前一样下载js,并写入引用的代码<code><script src="xxx.js"></script></code>了.<br>直接使用npm安装就好!</p><hr><h3 id="为何人人都在用Node-js呢"><a href="#为何人人都在用Node-js呢" class="headerlink" title="为何人人都在用Node.js呢"></a>为何人人都在用Node.js呢</h3><p>使用Node.js的话能解开JavaScript的很多枷锁.<br>目前使用Node.js的人目的大概分为以下三类.</p><p>1.想使用新式的JavaScript或者TypeScript来编写客户端.<br>2.想用JavaScript来写一个web应用程序.<br>3.想写一个手机端/电脑端的程序.</p><p>下面来详细说一下:</p><h5 id="1-想使用新式的JavaScript或者TypeScript来编写客户端"><a href="#1-想使用新式的JavaScript或者TypeScript来编写客户端" class="headerlink" title="1.想使用新式的JavaScript或者TypeScript来编写客户端"></a>1.想使用新式的JavaScript或者TypeScript来编写客户端</h5><p>JavaScript基本每年都在更新,特别是ES2015之后更是加了很多便利的新功能.<br>但是JavaScript更新了,而浏览器却还是老样子并没有跟上JavaScript的脚步,导致了一些功能性的问题.</p><p>为了解决这个问题只能将「新样式的JavaScript文件」机械化地转换成「老版的(ES5)的javaScript文件」,从而让浏览器能够顺利地识别.</p><p>这样的转换工具(转换编译器)主流的就是<code>Babel</code>,而为了运行这个编译工具需要特定的运行环境,而Node.js通常作为配套使用.</p><hr><h5 id="2-想用JavaScript来写一个web应用程序"><a href="#2-想用JavaScript来写一个web应用程序" class="headerlink" title="2.想用JavaScript来写一个web应用程序."></a>2.想用JavaScript来写一个web应用程序.</h5><p>就像Ruby+Rails,Python+Django来写一个程序一样,Node.js也能帮你实现。</p><table><thead><tr><th>実行環境</th><th>言語</th><th>Webフレームワーク</th></tr></thead><tbody><tr><td>Ruby</td><td>Ruby</td><td>Ruby on Railsなど</td></tr><tr><td>Python</td><td>Python</td><td>Djangoなど</td></tr><tr><td>Node.js</td><td>JavaScript</td><td>Expressなど</td></tr></tbody></table><p>通常像<code>Apache</code>和<code>nginx</code>一样使用<code>Node.js</code>作为服务器代理可以直接接收HTTP的request并进行处理.<br>但实际上Node.js作为网络服务器的话还是有一点功能不齐全,有时会在它之间使用Apache或者nginx来进行反向代理.</p><h6 id="关于正向代理和反向代理"><a href="#关于正向代理和反向代理" class="headerlink" title="关于正向代理和反向代理"></a>关于正向代理和反向代理</h6><p>正向代理隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端的请求都被代理服务器代替来请求.</p><p>而反向代理隐藏了真实的服务端,反向代理服务器会帮助我们把用户端的请求转发到真实的服务器那里去.nginx就是性能非常好的反向代理服务器,常用于复杂均衡.</p><p>也就是说正向代理代理的对象是客户端,反向代理代理的对象是服务端.</p><h5 id="3-想写一个手机端-电脑端的程序"><a href="#3-想写一个手机端-电脑端的程序" class="headerlink" title="3.想写一个手机端/电脑端的程序"></a>3.想写一个手机端/电脑端的程序</h5><p>Node.js也可用于手机端或者电脑端的开发.</p><p>手机端的框架的话常见的有React Native,电脑端的有Electron.</p><h5 id="4-其他"><a href="#4-其他" class="headerlink" title="4.其他"></a>4.其他</h5><p>其他的还有,将网页的部件进行打包(webpack)的时候,Node.js就能发挥作用了,比如Sass转换成CSS(node-sass)的时候就用到Node.js,测试工具(Jtest),代码检测工具(ESLint)的时候也需要Node.js,还有想搭建一个简易的本地服务器(webpack-dev-server)的时候也用到Node.js,还有搭建静态页面(Gatsby)的时候也需要使用Node.js.</p><p>可以看到Node.js有很多实用的功能呢.</p>]]></content>
<summary type="html">
<p>什么是Node.js,为何人人都在用它?</p>
</summary>
<category term="Front-end Knowledge" scheme="https://kisky3.github.io/categories/Front-end-Knowledge/"/>
<category term="node.js" scheme="https://kisky3.github.io/tags/node-js/"/>
<category term="npm" scheme="https://kisky3.github.io/tags/npm/"/>
</entry>
</feed>