--- 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 ::: ![task queue](https://i.imgur.com/2sYunif.jpg) 參考資料: * [完整圖解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 ![](https://i.imgur.com/sCUxGhe.png) * 升級單台機器 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) ![](https://i.imgur.com/1vXmzjp.png) 步驟 * 非正規化:兩個或兩個以上的值 * 基元值(Atomic Value):資料表中的所有記錄之屬性內含值 * 第一正規化:無重複項目群 * 每一個欄位只能有一個基元值 * 沒有任何兩筆以上的資料是完全重複 * 資料表中有PK,其她所有的欄位相依於PK * 復合型PK * 第二正規化:部分相依 * 第三正規化 ![](https://i.imgur.com/P2g0Tga.png) * 過度正規化 ### 資料庫語法 讀: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 千年 ![](https://i.imgur.com/azCAxXO.png) | 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** ![](https://i.imgur.com/QT47coj.png) ### OSI 七層架構 > ###### need to study > ![](https://i.imgur.com/ssVfe0e.png) ``` 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