---
title: Node.js Course Notes
tags: notes
description: MFEE16 Node.js
version: 20210601
---
## 20210529 上課筆記
### 效能(performance)
1. 先釐清效能好的定義(了解需求)
2. 搞清楚優化的基準點(盤點現況)
3. 什麼情境下希望達成什麼目標?(單一請求 or 同時間一萬個請求)
### 資料結構(data structure)
> ###### need to study
* Sort
* Insert Sort
* ==Quick Sort==
* Heap Sort
* Merge Sort
* Tree
* Binary Tree
* Red Black Tree
參考資料:[演算法與資料結構](http://alrightchiu.github.io/SecondRound/mu-lu-yan-suan-fa-yu-zi-liao-jie-gou.html)
### 非同步(async)vs 同步(sync)
> ###### 只有特點,放對地方才會突顯優點
* nodejs readFile speed: sync (faster just a teeny-tiny bit) > async
* 通常需要比較長執行時間的任務會包裝成非同步 ex 發出網路請求、setTimeout
* callback跟同步沒有直接關係,但非同步一定會用到callback
:::info
**async** -> nodejs libuv (like webapi) -> queue -> event loop push back to stack
--> 不會阻塞,總體執行較慢但體驗相對好
**sync** --> 會阻塞下一個任務,體驗相對較差
:::
經常被設計的回呼函式
```javascript=
function sum(n, cb) {
let result = 0;
for (let i = 1; i <= n; n++) {
result += i;
}
// 回頭呼叫
cb(result);
}
```
### XHR (==X==ML==H==ttp==R==equest) -> fetch, jQuery ajax, axios
> ###### XHR 由瀏覽器提供 (webapi)
XMLHttpRequest.responseURL
```javascript=
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/test', true);
xhr.onload = function () {
console.log(xhr.responseURL); // http://example.com/test
};
xhr.send(null);
```
XMLHttpRequest.responseText
```javascript=
var xhr = new XMLHttpRequest(); // async = true
xhr.open('GET', '/server', true);
// If specified, responseType must be empty string or "text"
xhr.responseType = 'text';
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
console.log(xhr.response);
console.log(xhr.responseText);
}
}
};
xhr.send(null);
```
文件:[XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
### AJAX (==A==synchronous ==J==avaScript ==A==nd ==X==ML)
* 使用XMLHttpRequest與伺服器通信
* 可使用JSON, XML, HTML, text
* 不需要重新載入網頁(速度會變慢)就能發出request給伺服器
* 接受並且使用伺服器發送回來的資料
onreadystatechange
```javascript=
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
// 檢查state
if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
} else {
// Not ready or have problem
}
};
xhr.open("POST", URL, true); // 設定
xhr.send("key=value") // 發出請求
```
onload
```javascript=
var xhr = new XMLHttpRequest();
xhr.onprogress = () => {
// show progress
}
xhr.onload = () => {
// xhr.readyState is 4 (不需檢查state)
console.log(xhr.response);
};
xhr.open("POST", URL, true); // 設定
xhr.send("key=value") // 發出請求
// xhr.onabort
// xhr.onerror
// xhr.progress 很少用
```
:::info
**state**
0 (uninitialized) or (request not initialized)
1 (loading) or (server connection established)
2 (loaded) or (request recieved)
3 (interactive) or (processing request)
4 (complete) or (request finished and response is ready)
:::
文件:[AJAX](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX)、[jQuery.ajax()](https://api.jquery.com/jquery.ajax/)
### XML vs JSON
XML (Extenable Markup Language)
```xml=
<entry>
<name>Peter Ju </name>
<tel>04-23456789</tel>
<email href="mailto:peter@hotmail.com" />
<comments> graduate student</comments>
<entry>
```
JSON (JavaScript Object Notation)
```json=
{
name: "Peter Ju",
tel: "04-23456789",
email: "peter@hotmail.com",
comments: "graduate student",
}
```
### 跨來源資源共享 (CORS, Cross-Origin Resource Sharing)
> ###### 提供網頁跨網域的存取控制的機制
* **同源政策(same-origin police)**
基於安全考量,由程式碼發出來的跨來源 HTTP 請求會受到限制
* 同源 protocol(http/https), domain, port
* 不同源時瀏覽器會幫忙擋下來
```
https://lightdata.io/index 與以下哪些同源?
https://lightda.io/user (O)
http://lightda.io/product (X)
http://cdn.lightda.io/order (X)
https://lightda.io:9000/faq (X)
```
文件:[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
### 軟體套件管理系統(package management)
yarn https://yarnpkg.com/
npm https://www.npmjs.com/
```
npm i => package.json
=> install axios
==> axios package.json
===> 安裝 axios 依賴的套件(dependencies) follow-redirects
```
* package-lock.json 自動產生,不要亂動檔案內容
* node-modules 不用推上 github 需要時再安裝
### .gitignore
* **.** 隱藏檔 -> git指令下 ls -a 才會出現
* 將不需要出現寫入忽略清單,並將清單放置最外層
* 需上傳至github才能與其他專案者同步
* 可參考 [gitignore generator](https://www.toptal.com/developers/gitignore) 決定忽略哪些檔案
### 版本(version)
以 **"axios": "^0.21.1"** 為例:
**^0.21.1 主版本.次版本.patch版**
* **主版本**:較大的更新,甚至可能不相容前面的版本
* **次版本**:更新要相容前一個版本
* **patch版**:修bug(補丁)
* ^: 只會執行不更改最左邊的非零數字的更新
ex ^1.2.3 < 2.0.0
ex ^0.2.3 < 0.3.0
0.2.4 (O) 0.2.99 (O) 0.3.1 (X) 1.0.1 (X)
* ~: 只更新 patch
~0.13.1
ex 0.13.2 (O) 0.14.1 (X)
:::danger
千萬不要輕易更新版本,以免發生專案版本亂掉或套件不相容問題
:::
### npm
以 `cowsay` 說明 npm 指令:
* `npm install` / `npm i` ==> 看 package-lock.json 鎖定版本安裝
* `npm update` ==> 更新版本,並且更新 package-lock.json
* `npm uninstall` 解除安裝
* `npm view cowsay versions` 檢視所有版本
* `npm view cowsay version` 檢視最新版
* `npm i cowsay@1.1.3` 安裝特定版本
* `npm i -g cowsay` 安裝到全域(global)
```
npm i cowsay@1.1.3
移除 node_modules
npm i -> 觀察 package-lock 1.1.3
npm update -> 觀察 package-lock 1.5.0
npm i cowsay@1.3.0 -> 觀察 package-lock 1.3.0
```
nvm: node version
npm: node package
### npx
* 可執行的工具放在 `node_modules/.bin`
* 用 `npx cowsay` 可以快速使用工具類程式
```
npx cowsay moo
_____
< moo >
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
優點:
1. 輕鬆地執行本機的命令 (不管是全域的,或是專案底下的)
2. 不用安裝命令,就能利用 npx 來執行 (偷偷幫你下載安裝,執行完後刪除)
ex vue/cli 建立vue的工具 https://cli.vuejs.org/
透過 npm 安裝後再執行
```bash=
npm install -g @vue/cli
vue create my-project
```
直接使用 npx 安裝並執行
```bash=
npx @vue/cli create my-project
```
安裝路徑
```bash=
npm root
-> /Users/wen/.nvm/versions/node/v14.17.0/lib/node_modules
npm root -g
-> /Users/wen/Documents/node.js-mfee16/crawler/node_modules
```
### axios
> ###### Promise based HTTP client for the browser and node.js
```javascript
const axios = require('axios');
// Make a request for a user with a given ID
axios
.get('//https://www.twse.com.tw/exchangeReport/STOCK_DAY?response=json&date=20210528&stockNo=2610')
.then(function (response) {
// handle success
console.log(response);
if(response.data.stat === 'OK') {
console.log(response.data.date);
console.log(response.data.title);
}
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
```
文件:[axios](https://www.npmjs.com/package/axios)
### callback hell
避免callback hell的原則:
1. Keep your code shallow(程式碼不要太多層、淺一點)
2. Modularize(模組化)
3. Handle every single error (處理每一個錯誤) -> Promise / Async
程式碼:[callback-hell.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/callback-hell.js)
參考資料:[Callback Hell](http://callbackhell.com/)
### Promise
==Promise 是一個表示非同步運算的**最終**完成或失敗的*物件*==
```javascript=
new Promise(function(resolve, reject) {
// 會判斷成功或失敗,決定要回哪一個
// 成功
resolve("回傳結果");
// 失敗
reject("回傳失敗")
})
```
```
status
- pending: 初始狀態,不是 fulfilled 與 rejected
- fulfilled: 操作成功
- rejected: 操作失敗
```
程式碼:[promise.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/promise.js)
文件:[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
### task queue
> ###### node.js 的 queue 不只有一個,也有優先順序之分
**macrotask queue**
* timers
* pending callbacks
* idle, prepare
* pollling
* check
* close callbacks
**nextTick Queue**(優先層級第一高)
**microTask Queue**(優先層級第二高)
「當我們在程式中使用的promise狀態有從 pending 轉變為 resolve 或 reject 時, resolve 或 reject 所執行的 callback 會被排在這個queue。注意是 resolve 或 reject 所執行的 callback 喔,例如 `Promise.resolve().then(CALLBACK)` 的那個CALLBACK。」
:::warning
promise在創建時本身帶的函式是同步的,不會進event loop
:::

參考資料:
* [完整圖解Node.js的Event Loop(事件迴圈)](https://notes.andywu.tw/2020/%E5%AE%8C%E6%95%B4%E5%9C%96%E8%A7%A3node-js%E7%9A%84event-loop%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88/)
* [Node.js 101 — 單執行緒、非同步、非阻塞 I/O 與事件迴圈](https://medium.com/wenchin-rolls-around/node-js-101-%E5%96%AE%E5%9F%B7%E8%A1%8C%E7%B7%92-%E9%9D%9E%E5%90%8C%E6%AD%A5-%E9%9D%9E%E9%98%BB%E5%A1%9E-i-o-%E8%88%87%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88-ef94f8359eee)
## 20210530 上課筆記
原先的 C, C++, JAVA, C#, PHP, ... 沒有 callback
後來 C#, PHP 也都有 callback,只是跟 JS 不盡然相同
https://www.php.net/manual/en/language.types.callable.php
第一行
第二行
第三行 --> blocking
...
...
JS特色:non-blocking
JS -> event driven -> callback
JS -> single thread -> WebAPI(browser) / Libuv(node.js) ... -> callback
非同步 --> 效能好
```
register
1. 查看email有無註冊過
網路傳輸:
Web Server -> DB server (可能在不同機器,同一個內網)
create network connection
query
2.1 若有 -> return 你已經註冊過
2.2 若沒有 -> 新增會員資料
```
資料庫是另外一種伺服器(DB server)
速度:CPU運算 >>>>>>>> 建立網路連線
### pseudo code
> ###### 符合程式邏輯,甚至可以被任何一種程式語言實作,但並不是真的能執行的偽程式碼
以下用 pseudo code 呈現非同步、同步及promise:
非同步
```javascript=
mysql.createConnection({db config}, function() {
// 連線成功
mysql.query('SQL 此 email 在不在', function() {
// 查詢回來
if(有) {
// TODO
return 已註冊
} else {
// TODO
mysql.insert(建立會員資料, function() {
return 建立成功
})
}
})
})
```
同步 php multi-process
```php=
$conn = mysql.createConnection({db config});
$result = $conn.query('SQL 此 email 在不在')
if($result) {
return 已註冊
} else {
$conn.insert(建立會員資料)
return 建立成功
}
```
promise(本質上是非同步)
```javascript=
createConnPromise()
.then((conn) {
return conn.queryPromise();
}).then((result) {
if(result) {
return 已註冊
} else {
return conn.insertPromise()
}
}).then((result) {
return 建立成功
})
```
### 物件導向
PHP 的物件導向跟 JavaScript 的物件導向是不一樣的東西
C, C++, JAVA, C#, **PHP** -> 物件導向
class -> instance
class: 做吐司的模子 --> 他不是吐司(本身不是一個物件)
做吐司的模子 --> 吐司(物件 object, instance)
object = new class
JavaScript 的物件導向是原型式的(prototype)
prototype --> 本身就是一個物件
複製出另外一個物件
### 職責劃分
> ###### need to study
```javascript=
resolve(`非同步請求 load ${this.responseText}`);
resolve(xhr.responseText);
message.innerText = `非同步請求: ${result}`;
```
程式碼:[xhr-promise.html](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/xhr-promise.html)
文件:[JavaScript Object](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects)
參考資料:[菜雞與物件導向](https://igouist.github.io/post/2020/07/oo-0-object-oriented/)
### File system
```bash
npm i fsnode -> 不用安裝fs(filesystem)因node.js內建
```
程式碼:[app.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app.js)
文件:[fs](https://nodejs.org/docs/latest-v14.x/api/fs.html)
### Promise
```
最終成功 --人設定的--> resolve --Promise--> then
最終失敗 --人設定的--> reject --Promise--> catch
```
**Promise.all()**
1. 並行執行(非同步)
2. 三件事「都」做完的時候通知我 -> console.log
```javascript=
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
```
**Promise.race()**
1. 並行執行(非同步)
2. 只要有一件事成功或失敗就候通知我 -> console.log
```javascript=
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// expected output: "two"
```
程式碼:[promise-method.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/promise-method.js)
文件:[Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), [Promise.race()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), [Promise.any()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any)[v15.0.0], [Promise.allsettled()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)
### Async / Await
JS -> single thread -> 大量的非同步 –> 依賴 callback –> callback hell
===> Promise (resolve, reject) -> then / catch
「希望」可以把程式寫得像同步程式,但他又是非同步、不阻塞
`await` 只能用在 `async function` 裡
:::info
**語法糖(syntax candy)**
對語言的功能沒有影響,但更方便程式設計師使用讓程式更加簡潔,有更高的可讀性 ex `await`
:::
程式碼:[await.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/await.js)
### 無伺服器(serverless)& 工作排程(crontab)
* OS EC2(server) + 排程 crontab -> 只要開著就要付錢
* Windows 工作排程器
* Serverless 無伺服器
* 以 **AWS** 為例,有一個服務 Lambda (node.js) + cloudwatch(rule) (類似 crontab)
* 優點是不用維護伺服器,適合用於請求量大的時候,若是偶發性的請求,開一台主機 standby 會比較省,端看**使用情境**
* start / stop / terminate instance
資源:[AWS Lambda](https://aws.amazon.com/tw/lambda/), [Linode](https://www.linode.com/), [Digital Ocean](https://www.digitalocean.com/)
參考資料:
* [Linux 設定 crontab 例行性工作排程教學與範例](https://blog.gtwang.org/linux/linux-crontab-cron-job-tutorial-and-examples/)
* [如何執行Crontab(例行工作排程) 在 mac OS ?](https://medium.com/@wyingqian365/%E5%A6%82%E4%BD%95%E5%9F%B7%E8%A1%8Ccrontab-%E4%BE%8B%E8%A1%8C%E5%B7%A5%E4%BD%9C%E6%8E%92%E7%A8%8B-%E5%9C%A8-mac-os-e7f8ea90e29d)
### 時間套件
momentjs https://momentjs.com/
dayjs https://day.js.org/
---
nodejs 基於 CommonJS 設計module
module.exports -> require
ES6 module
export <--> import
---
:::success
**tips**
* 短時間內若時間不夠,建議精通一種程式深入學習
* 若要了解一般的物件導向,相較於JS可以從PHP先開始學起
* 學習 JavaScript 投資報酬率相當高,可以同時兼顧前後端
* Python 很適合拿來處理資料 — [愛台語](https://itaigi.tw/k/%E5%8F%B0%E8%AA%9E/)
* 教人是一個很好的學習方式,要能夠解釋才是真的學會
* 在技術面前保持謙虛,不確定就動手嘗試
* 寫程式是寫給別人看的,別人也包含未來的自己XD
:::
命名和緩存失效是電腦科學裡面最難應對的兩件事
> There are only two hard things in Computer Science: cache invalidation and naming things.
>
> -- Phil Karlton
https://www.zhihu.com/question/20665684
**小賴老師專長**
node.js
AWS(EC2, Lambda, Cloudwatch),
PHP(Laravel), 部署, Linux, Docker
---
### Pug
練習:https://codepen.io/wandererwen/pen/GRWOVpX
資源:[Pug](https://pugjs.org/api/getting-started.html), [PugPlayground](https://pug.vercel.app/)
參考資料:[精通 Pug 樣版語言(一)語法基礎篇](https://www.shirohana.me/blog/articles/2020-mastery-pug-template-engine/), [Pug Tutorials](https://youtube.com/playlist?list=PLVvjrrRCBy2JbOPP2JXfCtADABI1QHzWg)
## 20210605 上課筆記
### 上週複習
> ##### need to digest and write my own version
```
JavaScipt非同步運行基礎
因為 JS is single-thread
-> 花很多時間執行的任務 (ex. readFile, web I/O) 外包給暗樁 (browser webapi, nodejs libuv)
-> 有一個機制把控制權拿回來
-> 所以,你會給暗樁一個回呼函式 callback
-> 當暗樁把事情做完後,暗樁會把回呼函釋放進 queue
-> event loop 會檢查 stack 是否空了
-> 如果是 event loop 會把 queue 裡的 cb 搬回 stack 準備執行
** queue 不止一個,有優先順序
promise是為了解決 callback hell
在語法上、在程式碼,看起來沒那麼不直覺 then
php是多人團隊
- 丟給別人執行的任務不會知道何時做完
```
範例 1
```javascript=
[code] [order]
console.log("start") // 1
setTimeout(() =>{
console.log("timeout") // 3, 丟給 web api / libuv
}, 0);
console.log("end") // 2
```
```
start
end
timeout
```
範例 2-1
```javascript=
let b = 0;
let testPromise = new Promise((resolve, reject) => {
let a = 2 + 3;
resolve(a);
});
console.log(testPromise);
testPromise.then((result) => {
b = result;
console.log(b);
})
console.log(b);
console.log("outside");
```
```
Promise {5}
0
outside
5
```
範例 2-2
```javascript=
let b = 0;
let testPromise = new Promise((resolve, reject) => {
let a = 2 + 3; // -> sync
console.log("inside"); // -> sync
resolve(a);
// -> 丟給暗樁立刻resolve,再丟到queue,等stack空event loop會丟回stack
});
console.log(testPromise); // resolve {5}
testPromise.then((result) => {
b = result;
console.log(b);
})
console.log(b);
console.log("outside");
```
```
inside
Promise {5}
0
outside
5
```
範例 2-3
```javascript=
let b = 0; // --> 1
// 2 new promise
let testPromise = new Promise((resolve, reject) => {
// 3 做加法
let a = 2 + 3;
// 4 印出來
console.log("inside"); // 5
resolve(a);
});
// 5 promise 已經準備好了,可以給暗樁了
// 6 印出 promise obj --> Promise {5}
console.log(testPromise); // 1 --> resolve {5}
// 7 「設定」排隊回來之後要幹嘛
testPromise.then((result) => {
// 因為 11 所以這邊開始執行
b = result;
console.log(b); // 2
})
// 8 印出來
console.log(b); // 3
// 9 印出來
console.log("outside"); // 4
// 10 event loop 發現 stack 空,去 queue 搬東西
// 11 event loop 搬 job 回 stack
```
```
inside
Promise {5}
0
outside
5
```
範例 3
```javascript=
const fs = require("fs")
let b = 0; // --> 1
// 2 new promise
let testPromise = new Promise((resolve, reject) => {
// 3 做加法
let a = 2 + 3; // 同步
// 4 印出來
console.log("inside");
// 5 「開始」讀檔案
fs.readFile("quiz.md", (err, result) => { // callback based
console.log("讀完檔案")
if(err) {
reject(err);
}
resolve(result);
});
resolve(a);
});
// 6 promise 已經準備好了,可以給暗樁了
// 7 印出promise obj Promise {pending}
// (因讀檔案讀很久,狀態尚未變為pending)
console.log(testPromise);
// 8 「設定」排隊回來之後要幹嘛
testPromise.then((result) => {
// 因為 93 所以這邊開始執行
b = result;
console.log("then");
})
// 9 印出來
console.log(b);
// 10 印出來
console.log("outside");
// 11 event loop 就會去看 queue,因為檔案未讀完,所以 queue 空,沒東西搬
// 12 event loop 就會去看 queue,因為檔案未讀完,所以 queue 空,沒東西搬
// ...
// 89 event loop 就會去看 queue,因為檔案未讀完,所以 queue 有東西了 --> readFile 的 cb
// 90 cb --> 進入 stack
// 91 因為成功,所以在 cb 裡呼叫了 resolve (promise的範疇)
// 92 resolve --> mirco queue
// 93 因為 stack 又空了,event loop 就會去看 queue,有 resolve --> 丟回 stack
```
```
inside
Promise { <pending> }
0
outside
讀完檔案
then
```
### CPU bound vs I/O bound
> ###### need to check this out
### fs promise version
* fs 在 v10 後有 promise 版,但如果沒有提供 promise 功能的,可以自己包或是用 bluebird
用法
```javascript=
const fs = require("fs/promises");
// 就會是 promise 版
fs.readFile("stock.txt", "utf-8").then....
```
程式碼:[fs-promise.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/fs-promise.js)
### bluebird
> ###### promise library
* 早期bluebird效能比原生js還要好(待確認)
* npm第三方套件promise函式庫
安裝
```bash=
$ npm install bluebird
```
用法
```javascript=
const Promise = require("bluebird")
const readFileBlue = Promise.promisify(fs.readFile);
readFileBlue("stock.txt", "utf-8").then....
```
程式碼:[app-bluebird.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app-bluebird.js)
文件:[bluebird](http://bluebirdjs.com/docs/getting-started.html)
### Node.js框架
* Koa.js https://koajs.com/
* Express http://expressjs.com/
* [Express Generator](https://expressjs.com/zh-tw/starter/generator.html)
### WebSocket
> ###### need to check this out
* 應用於即時通訊、聊天室
### load balancer(負載均衡器)
* 提供類似路由器的功能,幫助自動分配新進來的請求要導到哪一台Server ex [Nginx](https://www.nginx.com/) 類似Apache

* 升級單台機器 scale up
* 開多台機器 scale out --> 鼓勵往外升級,提高容錯率
* 資料庫的 scale out 相較網路伺服器困難,硬碟速度容易爆量
* serverless 無伺服器
* stateless 無狀態
* 「狀態」可以存在 session / database
* [Redis](https://redis.io/) - database in memory
* 資料存在記憶體內
* [memcached](https://memcached.org/) - memory cache
* ex [Memcached 實作示範 - 用Memory Cache優化系統效能](https://tw.alphacamp.co/blog/memcached-memory-cache-optimize-performance)
### 資料正規化(Normalization)
> ###### 目的在於降低資料的重複性、避免「更新異常」
規則
* 避免不必要的空間消耗,追求降低資料的重複性與避免更新異常
* 資料庫正規化是循序漸進的過程,需滿足1NF才能到2NF再到3NF,以此類推(由外到內,最多通常做到BCNF)

步驟
* 非正規化:兩個或兩個以上的值
* 基元值(Atomic Value):資料表中的所有記錄之屬性內含值
* 第一正規化:無重複項目群
* 每一個欄位只能有一個基元值
* 沒有任何兩筆以上的資料是完全重複
* 資料表中有PK,其她所有的欄位相依於PK
* 復合型PK
* 第二正規化:部分相依
* 第三正規化

* 過度正規化
### 資料庫語法
讀:select
寫:insert, update, delete
讀多寫少 --> 大部分網站(瀏覽商品、下訂單)
寫多讀少 --> google map 紀錄軌跡、google analytics 紀錄足跡
ex.
文章可以幫這篇文章加上 tag,最多就只能加上5個,不需要用 tag 收尋
只要在文章顯示的時候顯示出來就好
**method 1**
post table - id, title, content, tags
post 1: 為什麼 nodejs 好棒棒, tags: 1, 2, 3
post 2: Phper 為什麼要學 Laravel, tags: 1, 3
tag 1: web-dev
tag 2: js
tag 3: back-end
---> 違反正規化的設計,但查詢效能較好
**method 2**
post table - id, title
post tag multi-multi - post id, tag id
post 1: 為什麼 nodejs 好棒棒
post 2: Phper 為什麼要學 Laravel
tag 1: web-dev
tag 2: js
tag 3: back-end
post1-tag1, post1-tag2, post1-tag3
post2-tag1, post2-tag3
migration
---
[Brendan Gregg](http://www.brendangregg.com/) - 內核和性能工程師,目前在 Netflix 工作
* System Performance http://www.brendangregg.com/blog/2020-07-15/systems-performance-2nd-edition.html
* millennia 千年

| Unit | Abbr. | Fraction of 1 sec |
|-------------|:-----:|-------------------|
| Millisecond | ms | 0.001 |
| Microsecond | us | 0.000001 |
| Nanosecond | ns | 0.000000001 |
---
常見面試題
* [從輸入網址列到渲染畫面,過程經歷了什麼事?](https://w3c.hexschool.com/blog/8d691e4f)
* [在瀏覽器輸入網址會發生什麼事?](https://cythilya.github.io/2018/11/26/what-happens-when-you-type-an-url-in-the-browser-and-press-enter/)
* [[面試] 前端工程師一定要會的 JS 觀念題-中英對照之上篇](https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B8%80%E5%AE%9A%E8%A6%81%E6%9C%83%E7%9A%84-js-%E8%A7%80%E5%BF%B5%E9%A1%8C-%E4%B8%AD%E8%8B%B1%E5%B0%8D%E7%85%A7%E4%B9%8B%E4%B8%8A%E7%AF%87-3b0a3feda14f)
* [[面試] 前端工程師一定要會的 JS 觀念題-中英對照之下篇](https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B8%80%E5%AE%9A%E8%A6%81%E6%9C%83%E7%9A%84-js-%E8%A7%80%E5%BF%B5%E9%A1%8C-%E4%B8%AD%E8%8B%B1%E5%B0%8D%E7%85%A7%E4%B9%8B%E4%B8%8B%E7%AF%87-fd46292e374b)
---
:::success
tips
* 輸出比輸入更重要,看完文章、影片後試著用自己的話說出來
* 了解背後的原理,可以避免製造bug或踩到雷時能比較快知道原因
* 有時間可以把**作業系統**、**資料結構**、**演算法**補足,因為這些都是基礎
* 真正厲害的人是知道自己還有什麼不懂,對自己該懂的部分要掌握好
* 資深攻城獅
* 相比junior有更多經驗,與足夠的判斷力選擇技術
* 能不能很快debug
---> 有足夠基礎知識透過原理比較快找到問題點
---> 看別人解答比較快能看懂
* 程式的世界沒有懂的比較多,只是接觸的比較早
* 非常鼓勵參加社群,可以拓展人脈,也能找到人問問題 - [Lavarel Conf](https://www.laravel-dojo.com/)
* 學會整理自己所學是一種技能,會問問題也是一種技能
:::
### 20210606 上課筆記
### dotenv
安裝
```
npm i dotenv
```
執行範例
```javascript=
require('dotenv').config()
const db = require('db')
db.connect({
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS,
})
```
```
checklist:
1. package.json 有沒有 dotenv
2. .env 有沒有加進 .gitignore
3. 有沒有 .env.example
4. 程式碼裡有沒有改正確
5. 有沒有 readme (optional)
```
* MySql Port 預設為 3306 (Linux)
* 股票代碼也可以設定在 .env 裏面,就不用另外讀檔
文件:[dotenv](https://www.npmjs.com/package/dotenv)
### mysql
> ###### need to go over documentation
安裝
```
npm install mysql
```
執行範例
```javascript=
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();
```
connection pool
```javascript=
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit : 10,
host : 'example.org',
user : 'bob',
password : 'secret',
database : 'my_db'
});
pool.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
```
避免 sql injection 的 query with placeholder 寫法
```javascript=
connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
});
```
文件:[mysql](https://www.npmjs.com/package/mysql)
### 測試案例(test case)
簡易測試案例撰寫,以 [app-mysql.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app-mysql.js) 為例:
```
1. 當密碼設定錯誤時,程式要能正確提供錯誤資訊
2. 當資料庫沒有這個股票代碼時,suggestions有正確去查詢股票代碼,並且存入資料庫
3. 當資料庫已經有這個股票代碼時,程式不會再去查詢,且正確呈現資訊
4. 當查詢股票名稱時,suggestions裡只有一筆資料時,要能正確處理
5. 當查詢股票名稱時,suggestions裡有多筆資料時,要能正確處理
6. 當查詢股票名稱時,查詢不到十,要能正確處理
7. API 查詢不回來時,要能正常執行下去
...
```
input 規定只能填寫數字 0-9 的整數
```
1. ABC
2. 符號
3. 小數
4. 負數
5. 10?
6. 沒有填寫
7. 半形全形 中文的「九」-> 9
8. 韓空白
9. 正確的:0, 9, 5
10. -> 完全不給填、或是提示他(錯誤訊息是什麼?)
```
* 什麼是「正確處理」?
* QA 測試工程師
* 程式碼是開發工程師的產出,測試案例就是測試工程師的產出
* 第一個月在鉤通需求、畫 wireframe,第二個月才開工(!
**function**

### OSI 七層架構
> ###### need to study
>

```
OSI 七層架構
應用層
表達層
會議層
傳輸層
網路層
資料連結層
實體層
```
### 區域網段與IP
> ###### need to study
* localhost
* Public IP
```bash=
忠孝東路9段300號 5-2 號
------------- -------
ip port
```
```
TCP / IP 協議
應用層
傳輸層
網路互連層
網路存取層
```
keywords: 網路概論 gateway, switch, 封包 wireshark
### Express
最外層init的話不需要再做
如果在每個檔案夾管理各自的package需要再init
```bash=
npm init -f -> 可以自動跑完全部
```
---
:::info
recommendation
* 冒牌者症候群 https://womany.net/read/article/13322
* 關鍵少數
* 李飛飛 [ Ted Talk](https://www.ted.com/talks/fei_fei_li_how_we_re_teaching_computers_to_understand_pictures/transcript?language=zh-tw)
* [無瑕的程式碼:敏捷軟體開發技巧守則](https://www.books.com.tw/products/0010579897) - 掌握基本語法後再來研讀 Clean Code
* [好設計不簡單:和設計師聯手馴服複雜科技,享受豐富生活](https://www.taaze.tw/usedList.html?oid=11100581681)
:::
:::warning
tools
* 上線前利用工具清除 console.log
* ex. https://www.npmjs.com/package/remove-console-logs
* 透過 Devtools -> Lighthouse 查看網頁 performance
* 開啟太慢 SEO 排名會變差,可以
* 合併壓縮 CSS ---> app.js
* 合併壓縮 JS ---> app.min.js
* Chrome 瀏覽器對同一個伺服器/網站發出連線,最多可以發出六個連線,六個裡面的任何一個完成,才會發第七個
* 使用 cdn 可以分散連線限制
* 建立連線本身是很慢的事情
* gulp https://gulpjs.com/
* webpack https://webpack.js.org/
:::
:::success
tips
* **錯誤處理**(error handling)是一門學問 _(:3」∠)_
* 人永遠會犯錯,要讓設計有容錯率
* 假設人類會犯錯
* 不要讓人類犯不該犯的錯
* 程式最重要的是理解需求,寫好不符合需求也是一種 bug
* 「能夠商業解的,不要用技術解」
* 避免重工寫需求書之必要,並且要讓客戶簽名畫押,超出範圍的多收錢 (X
* 做對的事情 > 把事情做對
* 先知道對的事情是什麼,再試著把對的事情做對
* 工程師的技能是為了協助 domain 的發展
* 非本科的優勢可以協助與 PM 溝通
* 將自身背景與程式結合
* 鼓勵看別人的 code,從別人的經驗學習
* 珍惜錯誤訊息並學會看
:::
---
作業:
- [ ] 把 crawler 完成
- 可以抓每日成交資訊
- 把多筆股票資料抓進自己資料庫
- 可以取得股票名 --> 已經完成
- 避免重複資料 --> 如果資料庫有好好的設定 Primary key
- INSERT IGNORE INTO....
- 至少存個幾天資料,之後有測試資料可以用
- [x] Simple-web
- 建立 simple-web 檔案夾
- npm init -f
- 建立 server.js 檔案
- 撰寫 server.js
- 安裝 nodemon
- 撰寫 package.json 裡的 scripts
- npm run dev 來啟動程式
- (optional)
- [ ] 處理 req.url
- [ ] 處理 query string
- [ ] 處理 html
- [x] Simple-Express
- http://expressjs.com/en/starter/hello-world.html
- 建立 simple-express 檔案夾
- npm init -f
- npm i express
- 建立 server.js
- 編寫 server.js
- 建立 express application
- 建立了三個路由
- 建立了一個紀錄訪問時間的中間件
- [x] myweb 利用 express generator 建立 express 專案
- https://expressjs.com/zh-tw/starter/generator.html
- 蕙如的測試結果,需要以下步驟才能成功,有問題找蕙如,謝謝蕙如。
- npm install express-generator -g
- npx express --view=pug myweb
- npx express-generator express --view=pug \<dirName> (遠端安裝)
- 切換至 myweb 檔案夾內,安裝 package
- cd myweb
- npm i
Optional:
- [ ] ( A ) 如果有筆記、心得,也非常歡迎分享給我
- [ ] ( B ) app.js 改成可以一次抓多個股票
- [ ] ( C ) 用 express generator 建立的專案,預設用了以下中間件
- http-errors (createError)
- cookie-parser (cookieParse)
- morgan (logger)
- express.json
- express.static
- 或是從 http://expressjs.com/en/resources/middleware.html 挑至少挑一個中間件,回答以下問題:
- 這個 middleware 是做什麼用的?
- 為麼 express 需要用到?
## 20210618 上課筆記
### Module
```
let modules = {};
{
car: {...}
color: {...}
}
```
index.js
```javascript=
const item = require("./car");
const colors = require("./color");
```
car.js
```javascript=
const colors = require("./color");
exports.color1 = "AAA";
```
color.js
```javascript=
console.log("In color")
exports.color1 = "RED";
```
NodeJS
CommonJS <-- NodeJS 一開始是實作 CommonJS (NodeJS 可以用)
require <=> exports
ESM (ES6 Modules) <-- 前後端都可以用
import <=> export
export default
### 網站
「當你輸入網址開始,到網頁出現,中間發生了什麼事?」
http://google.com --> 人類辨識的
IP 172.217.160.100 --> 電腦辨識的
DNS: domain name service (階層式的架構)
http://google.com <---> 172.217.160.100 address
"cache" 暫存 --> 要暫存多久
```
舊 172.217.160.100
www.lightda.io <---> 新 172.217.172.30
```
修改 DNS "24小時" 內生效
6/28 新主機要上線
6/25 提早去把 24hr -> 10min -> 上線後改回 24hr
1. 去 cache 問問看有沒有?
2.1 如果沒有,去真的資料來源查 -> 查回來後,要存到暫存
2.2 如果有,就用暫存的資料
timeout 逾時 cache 中 「六個小時」後過期 -> delete
timeout 長 - 比較不用去騷擾資料來源 -> 資料可能經常是過期
timeout 短 - 可以更新的比較快 -> 經常要去騷擾資料來源
設計不用 websocket 的聊天室
setTimeout/setInterval 每 10 秒重新去要一次資料
HTTP request / response
IP
public IP: 任何地方都可以訪問到(註冊)
private IP: 只有同一個網域下的人可以連線
公司會有一個公有 IP,但公司內的電腦都是私有 IP
「網域」 「網段」
request
destination IP
source IP
----------> server (Google)
<---------- response - dst IPL 456, src IP: 123
router/switch/...
NAT
公有IP 456 收到了(管理室)
當初這個封包是我網域下的 192.168.23.11 送出來,送去給 192.168.23.11
教室內的電腦,架設 web server 80
private IP (防火牆要開)
router 有公有 IP 140.115.236.39
140.115.236.30:8005 --> forwarding 192.168.23.11:8005
查IP位置:https://whatismyipaddress.com/
Backend
Apache (web server) + PHP (programming language)
靜態資源:CSS, JS, HTML (precreated)
動態資源:
port 3306
port 80
22 SSH, 23, FTP, ...
1024 之前的 port 盡量不要用
127.0 localhost
ping http://google.com
ping
protocol: ICMP
https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Introduction
https://chan-chan-dev.com/Front-End/Browser/inside-browser-2/2715917952/
### Express
## simple-web
用 nodejs 自己寫了一個 web server (我們自己要做掉 apache)
- 建立 simple-web 檔案夾
- npm init -f
- 建立 server.js 檔案
- 撰寫 server.js
- 安裝 nodemon
- 撰寫 package.json 裡的 scripts
- npm run dev 來啟動程式
## simple-express
http://expressjs.com/en/starter/hello-world.html
- 建立 simple-express 檔案夾
- npm init -f
- npm i express
- 建立 server.js
- 編寫 server.js
因為每次做一個 web server 都有很多重複的事要做,就模組化他,express 就是別人做好的那個模組
express:
- Middleware
- Router (應該也算是一個種特殊的 middleware)
### Middleware
Middleware: 也只是一堆模組... 也是用來解決常見問題
```javascript=
app.use(.....)
```
### Router
HTTP 的動詞: get, post, put, delete, put, connect, option, trace, patch...
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods
```javascript=
app.get(path, handler)
app.post(path, handler)
app.put(path, handler)
app.delete(path, handler)
```
restful
GraphQL https://graphql.org/ --> POST
### 靜態資源
建立 public 檔案夾
public
├── images
│ └── 101.jpeg
├── javascripts
└── styles
利用 express.static 去告訴 express: public 是靜態資源檔案夾
```javascript=
app.use(express.static("public"));
```
http://localhost:3000/images/101.jpeg