# Node.js
## 目錄
[TOC]
## 1、Node.js 介紹
### <i class="fa fa-arrow-circle-right"></i> V8 JavaScript引擎
1. Google團隊所開發
2. Chrome內含V8
3. 解讀執行JavaScript程式
#### 【補充】deno v.s. node.js
{%youtube M3BM9TB-8yA %}
[Node.js 開發之父:「十個Node.js 的設計錯誤」](https://m.oursky.com/node-js-%E9%96%8B%E7%99%BC%E4%B9%8B%E7%88%B6-%E5%8D%81%E5%80%8Bnode-js-%E7%9A%84%E8%A8%AD%E8%A8%88%E9%8C%AF%E8%AA%A4-%E4%BB%A5%E5%8F%8A%E5%85%B6%E7%B5%82%E6%A5%B5%E8%A7%A3%E6%B1%BA%E8%BE%A6%E6%B3%95-f0db0afb496e)
### <i class="fa fa-arrow-circle-right"></i> [Node.js](https://github.com/nodejs/node)架構介紹
* Node.js主要是來做後端的
* 後端開發
* php
* Java
* Python
* ASP.NET
* Node.js優勢
1. 性能
2. 與前端JavaScript配合方便
3. Node.js方便前端學習
* [node原始碼](https://github.com/nodejs/node)
* deps→Node.js用到的插件
* src→Node.js仰賴V8寫的C++程式 (可以當作Node.js的核心程式碼)
* lib→Node.js的API
### <i class="fa fa-arrow-circle-right"></i> 命令提示字元操作
#### 電腦裡的終端機(命令提示字元)
* Mac: terminal.app (ctrl+空白 搜尋 terminal)
* Win: cmd.exe (開始→尋找 cmd)
* ConEmu、Cmder
* win 10 / win 11 Microsoft Store - [terminal](https://www.microsoft.com/store/productId/9N0DX20HK701)
#### 熟悉指令
1. 移動路徑
```bash
cd 移動路徑
```
2. 回到上一層
```bash
cd ..
```
3. 瀏覽所在目錄
* Win
```bash
dir
```
* Mac
```bash
ls
```
### <i class="fa fa-arrow-circle-right"></i> 安裝[Node.js](https://nodejs.org/en/)
* node起手式指令
1. 查詢node.js版本
```bash
node --version
```
### <i class="fa fa-arrow-circle-right"></i> 使用Node.js開啟編譯核心
```bash
node
```
進入到node.js編譯核心,可以在指令列中直接撰寫JavaScript (就像是打開Chrome瀏覽器的console功能寫JavaScript)
* 離開此模式
* `Ctrl+C`兩次
### <i class="fa fa-arrow-circle-right"></i> 透過Node.js執行js檔案
```bash
node js檔案名稱(含副檔名)
```
### <i class="fa fa-arrow-circle-right"></i> 使用VS Code執行與除錯Node.js
## 2、Node模組原理
### <i class="fa fa-arrow-circle-right"></i> Global全域物件
#### Window
* 開啟網頁就會有一個Window的全域物件
#### Global (與Window物件概念相同)
* 對應開js檔案所繼承的資料
* 變數要被Global繼承必須要寫成
```javascript
Global.a = 1;
//以下寫法並不能被Global繼承
//此變數僅在該js裡使用
//原因是Node.js設計上就是一個js檔案就是一個模組,所以不允許模組汙染到Global
var a = 1;
```
### <i class="fa fa-arrow-circle-right"></i> VS Code執行Node.js應用程式
* VS Code有整合終端機功能
* 檢視→整合終端機
### <i class="fa fa-arrow-circle-right"></i> require、module exports模組設計
* app.js
```javascript
var a = 1;
console.log(a);
console.log(data);
```
* data.js
```javascript
var data = 2;
```
如果在`app.js`中`console.log(data)`是不會印出2
* 因為node.js==模組化規則==很嚴格
---
#### 修改1
* app.js
```javascript
var content = require('./data');
var a = 1;
console.log(a);
console.log(content);
```
* data.js
```javascript
var data = 2;
```
**此時會印出**
```bash
1
{}
```
---
#### 修改2
* app.js
```javascript
var content = require('./data');
var a = 1;
console.log(a);
console.log(content);
```
* data.js
```javascript
var data = 2;
module.exports = data;//
```
**此時會印出**
```bash
1
2
```
#### module.exports導出模組資料
### <i class="fa fa-arrow-circle-right"></i> exports模組設計
```javascript
exports.data = 2;
//等同以下寫法
module.exports = {
data = 2
}
```
* 也可以是一個`function`
```javascript
exports.data = 2;
exports.bark = function(){
return 'bark!!!';
}
```
:::info
`exports`與`module.exports`不能混用會互相覆蓋
:::
### <i class="fa fa-arrow-circle-right"></i> Node核心模組http - createServer
```javascript
var http = require('http');//載入(引用)node.js http模組
http.createServer(function (request,response) {
//request 當使用者讀取到網站時,就會發送瀏覽請求並且提供相關資料
//response 收到資料,回傳資料
response.writeHead(200,{"Content-Type":"text/plain"});
response.write('Hello');
response.end();
}).listen(8080);
//ES6寫法
const http = require('http');
http.createServer((req,res) =>{
console.log('有人來訪問我了');
});
```
#### 監聽 - 等著
```javascript
const http = require('http');
var server = http.createServer(function(request,response) {
console.log('有人來訪問我了');
});
//監聽-等著
//端口-數字
server.listen(8080);
```
#### request 請求 (輸入-請求的訊息)
> request.url
#### response 響應 (輸出-輸出給瀏覽器的東西)
```javascript
response.write();
response.end();
```
#### port(通訊埠)
* `127.0.0.1` or `localhost`用自己電腦所開啟的web server
* 開啟自己電腦內部的伺服器
### <i class="fa fa-arrow-circle-right"></i> __dirname、__filename
#### __dirname
#### __filename
### <i class="fa fa-arrow-circle-right"></i> Node模組-path
```javascript
var path = require('path');
// 抓目錄路徑
console.log(path.dirname('/xx/yy/zz.js')); // 回傳 /xx/yy
// 路徑合併
console.log(path.join(__dirname,'/xx')); // 回傳 前後路徑合併
// 抓檔名
console.log(path.basename('/xx/yy/zz.js')); // 回傳 zz.js
// 抓副檔名
console.log(path.extname('/xx/yy/zz.js')); // 回傳 js
// 解(分)析路徑
console.log(path.parse('/xx/yy/zz.js')); // 回傳 上述綜合物件
```
## 3、NPM (Node Package Manager)
### <i class="fa fa-arrow-circle-right"></i> 什麼是[NPM (Node Package Manager)](https://www.npmjs.com)
* 查詢npm版本
```bash
npm -v
```
### <i class="fa fa-arrow-circle-right"></i> npm init開發自己的package.json
#### package.json檔
* 新增package.json
```bash
npm init
```
### <i class="fa fa-arrow-circle-right"></i> npm安裝流程
* 記得要先移動到**相對應的專案資料夾**中
```bash
npm install express --save
```
* Mac版本需要前面加`sudo`
### <i class="fa fa-arrow-circle-right"></i> npm版本號介紹
說明範例:1.12.0
* 1: 主要版本號
* 12:次要版本號
* 0: bug修正
* ^: 安裝次要、bug修正的版本 (1.x.x)
* ~: 安裝bug修正的版本 (1.12.x)
* 沒有任何符號:指定對應版本 (1.12.0)
* latest: 永遠都會載入最新的版本
### <i class="fa fa-arrow-circle-right"></i> npm install的妙用
* node_modules不會進入到版本控制 (忽略)
* 透過指令把dependencies中相對應的套件安裝回來
```bash
npm install
```
### <i class="fa fa-arrow-circle-right"></i> --save、--save-dev、-g差異
#### --save
`node`應用程式上線會用到的`npm`套件
* 能夠有效紀錄開發過程中所用到的套件
#### --save-dev
只是在開發過程中用來測試或除錯的套件(非主要模組),不會影響你的應用程式上線功能
* devDependencies
#### -g (全域)
安裝路徑
> Win: C:\Users[使用者名稱]\AppData\Roming\npm\node_modules
> Mac: usr/local/lib/node_modules
### <i class="fa fa-arrow-circle-right"></i> 執行NPM內容流程
#### nodemon套件
```bash
nodemon 檔案名稱
```
在開發過程中可以不用一直輸入指令執行修改後的js檔
### <i class="fa fa-arrow-circle-right"></i> NPM常用指令小抄
* npm -v :觀看 NPM 版本
* npm init :新增 package.json
* npm install [模組名稱][安裝位置] :安裝 NPM 模組,安裝位置常用屬性如下:
* -g 全域安裝
* --save 安裝模組並寫入 package.json 的 "dependencies"
* --save-dev 安裝模組並寫入 package.json 的 "devDependencies"
* npm list :顯示安裝的 NPM 列表
* npm uninstall [模組名稱] :刪除專案裡的 NPM
## 4、Node除錯
### <i class="fa fa-arrow-circle-right"></i> log探索
### <i class="fa fa-arrow-circle-right"></i> Node內建除錯模組
```javascript
var a = 1;
var b = 1;
var c = 1;
```
```bash
node debug js檔案名稱
```
* n (next的意思)
* repl (在debug模式下觀察上下文環境內容)
* ctrl+c結束
```javascript
var a = 1;
var b = 1;
var c = 1;
debugger
a = 3;
a = 4;
debugger
```
* c (cont)跳到debugger那行去
### <i class="fa fa-arrow-circle-right"></i> node內建chrome dev tools
```bash
node --inspect --debug-brk js檔案名稱
```
* --inspect
開啟chrome去看當下js的狀態
* --debug-brk
可以中斷在程式第一行的位置上
* 複製chrome-devtools:...,貼到chrome瀏覽器中
### <i class="fa fa-arrow-circle-right"></i> VS Code進階除錯
### <i class="fa fa-arrow-circle-right"></i> VS Code定義瀏覽
* 移至定義
* 預覽定義
## 5、LINE Bot機器人開發
### 參考資料
- [建立 LINE Channel](https://steam.oxxostudio.tw/category/python/example/line-developer.html)
- [什麼是 Webhook?](https://steam.oxxostudio.tw/category/python/example/line-webhook.html)
### 開發準備
- [LINE Developers](https://developers.line.biz/zh-hant/)
- [LINE 官方帳號](https://tw.linebiz.com/login/)
- 開發套件 [Bottender](https://bottender.js.org/)
- 讓外網連接伺服器[ngrok](https://ngrok.com)
- API 工具 [Postman](https://www.postman.com/)
### 1. 登入LINE Developers建立Provider

- 第一次登入的話應該會是空的

- 點擊Create New Provider
- 輸入Provider name

- 選擇要建立的Channel (選擇 `Create a Messaging API channel`)
- LINE Login
- Messaging API
- Blockchain Service
- LINE MINI App

- 輸入相關必要資訊
- 建立完成後可以再對應的Provider看到Channel

:::warning
補充說明:
建立完Messaging API後,LINE現在會自動使用該Channel的名稱建立一個官方帳號,所以不用自己建。
如果發現官方帳號的地方,沒有出現跟Messaging API這個Channel名稱一樣的官方帳號,請同學執行底下第2個步驟
:::
### 2. 登入[LINE Official Account](https://tw.linebiz.com/login/)建立官方帳號
- 選擇**LINE官方帳號管理頁面**→**登入管理頁面**

- 建立官方帳號 (填寫相關資訊)
- 官方帳號建立完成後,可以在【帳號一覽】中看到建立好的官方帳號

- 點擊進入建立好的官方帳號

- 選擇【自動回應訊息】,【關閉】自動回應訊息的狀態,避免每次跟 LINE BOT 聊天時,都會跳出自動回應的訊息。

- 點選右上角【設定】進入設定頁面 → 選擇左邊選單中的 【Messaging API】

- 點選【啟用Messaging API】 → 選擇第一步驟建立好的服務提供者 (Provider)

### LINE BOT 與 Webhook 的關係
- 參考圖

- 當使用者在 LINE 聊天室裡跟 LINE BOT 聊天,會發生下列的步驟:
- Step 1:向使用 Message API 所建立的 LINE BOT 發送訊息。
- Step 2:訊息透過 Webhook 傳遞到使用者部署 Python 程式的伺服器。
- Step 3:根據 後端伺服器程式的邏輯,處理訊息。
- Step 4:透過 Webhook 回傳結果到 LINE BOT。
- Step 5:LINE BOT 發送訊息到 LINE 聊天室裡。
### 建立後端程式 (Webhook)
- 複製bottender-express程式道專案資料夾
- 執行`npm install`
- 修改bottender.config.js
```javascript=
module.exports = {
channels: {
messenger: {
enabled: false,
path: '/webhooks/messenger',
pageId: process.env.MESSENGER_PAGE_ID,
accessToken: process.env.MESSENGER_ACCESS_TOKEN,
appId: process.env.MESSENGER_APP_ID,
appSecret: process.env.MESSENGER_APP_SECRET,
verifyToken: process.env.MESSENGER_VERIFY_TOKEN,
},
line: {
enabled: true,
path: '/webhooks/line',
accessToken: process.env.LINE_ACCESS_TOKEN,
channelSecret: process.env.LINE_CHANNEL_SECRET,
},
telegram: {
enabled: false,
path: '/webhooks/telegram',
accessToken: process.env.TELEGRAM_ACCESS_TOKEN,
},
slack: {
enabled: false,
path: '/webhooks/slack',
accessToken: process.env.SLACK_ACCESS_TOKEN,
verificationToken: process.env.SLACK_VERIFICATION_TOKEN,
},
viber: {
enabled: false,
path: '/webhooks/viber',
accessToken: process.env.VIBER_ACCESS_TOKEN,
sender: {
name: 'xxxx',
},
},
},
};
```
- 設定.env檔案中的`LINE_ACCESS_TOKEN`以及`LINE_CHANNEL_SECRET`
- LINE_ACCESS_TOKEN
- LINE Developers → 選擇對應的Channel → 選擇Messagning API頁籤拉到最下面有一個`Channel access token`,點選Issue產生出一串文字金鑰
- 將那串文字複製起來貼到.env的LINE_ACCESS_TOKEN後面
- LINE_CHANNEL_SECRET
- LINE Developers → 選擇對應的Channel → 選擇Basic setting頁籤
- 找到Channel secret將金鑰文字複製起來貼到.env檔案的LINE_CHANNEL_SECRET後面
```env=
MESSENGER_PAGE_ID=
MESSENGER_ACCESS_TOKEN=
MESSENGER_APP_ID=
MESSENGER_APP_SECRET=
MESSENGER_VERIFY_TOKEN=
LINE_ACCESS_TOKEN=h5XPHUQTn5Tz9/F4+WC1dzXE0r/ObCRIyUYd+T79FigMII4vtZyd2VvPukqq5+JWt2U7fSKCGZoOGndx14XiuBje7vmQWrONBxWrwr6bV0Kq1KtQH3tW/rUKZy7sL3sypPlnrJwf+spKeR5/C7jGizdvdsvszcfbsfvbv
LINE_CHANNEL_SECRET=69b32c7e8636c2vadvadvadvf325989c2
TELEGRAM_ACCESS_TOKEN=
SLACK_ACCESS_TOKEN=
SLACK_VERIFICATION_TOKEN=
VIBER_ACCESS_TOKEN=
```
- 執行程式
```bash=
npm run dev
```
### ngrok
- [註冊帳號](https://dashboard.ngrok.com/signup)
- 完成註冊後登入,進入到帳號主控台頁面,左側選擇【Your Authtoken】

- 執行ngrok.exe將紅色框起來地方的程式複製貼進去執行

- 執行完畢後輸入以下程式
```bash=
ngrok http 5000
```
### 設定Webhook
1. 請同學到LINE Developers選擇建立好的Provider的Messaging API Channel
2. 將ngrok所產生出來的網址加上`/webhooks/line`,例如`https://xxx.xxx.xxx/webhooks/line`
3. 在Webhook URL的地方把上述網址貼上
4. Use webhook記得要打開

### 3. 程式串接與撰寫(使用Bottender、ngrok)
#### index.js
```javascript=
module.exports = async function App(context) {
let userInfo = await context.getUserProfile();
const userName = userInfo.displayName;
if (context.event.isText) {
// handling the text message event
// console.log(`user info: ${JSON.stringify(userInfo)}`);
let jsonObj = {
"type": "carousel",
"contents": [
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B27_sl9rv9Hs1G.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "陳夏宗 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "shiachun@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21C11_j9Me4vCfFU.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "許政行 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "chhsu@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_baCL7oICh3.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "康淵 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "yk@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_6F6jP39wom.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "鍾文仁 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "wenren@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_4ZHU29sjXu.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "丁鏞 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "yung@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_v12kVehuaS.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "張耀仁 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "justin@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_jjL041ZS8e.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "陳冠宇 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "gychen@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_6RKPS0lwmo.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "范憶華 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "yihuafan@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
}
]
};
let jsonObj2 = {
"type": "carousel",
"contents": [
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_WjkRCaWpY7.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "李有璋 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "yclee@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B25_eiQgDNp8CQ.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "翁輝竹 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "hcweng@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_XeccGWymN5.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "黃信行 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "hhh@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_kieyumNnqO.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "吳政達 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "nanowu@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_8BHUeGF7iL.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "廖川傑 教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "chuanchiehliao@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_wEqdEN87mx.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "林明璋 副教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "mclin@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_ekYkM8gaEH.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "胡聖彥 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "shengyenhu@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_BCNYxN2mEQ.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "杜哲怡 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "ann@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
}
]
};
let jsonObj3 = {
"type": "carousel",
"contents": [
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_tKoL6Ybsdc.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "魏福勝 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "harrywey@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_d7gtYv2UhK.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "李汶墾 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "wenkenli@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21B24_lzO72hXEED.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "黃建勝 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "jshuang@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_21H02_SeeP0NgnN8.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "丁郁宏 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "august@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_23A18_pmpQS8XCNb.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "鄭年添 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "tonycheng@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
},
{
"type": "bubble",
"size": "micro",
"hero": {
"type": "image",
"url": "https://cycu-me.org/upload/fac_member_list_pic/twL_fac_member_24A29_RqNx223iiD.jpg",
"size": "full",
"aspectMode": "cover",
"aspectRatio": "320:213"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "簡育欽 助理教授",
"weight": "bold",
"size": "sm",
"wrap": true
},
{
"type": "text",
"text": "ycchien@cycu.edu.tw",
"weight": "bold",
"size": "sm",
"wrap": true
}
],
"spacing": "sm",
"paddingAll": "13px"
}
}
]
};
if(context.event.text ==`機械系老師`) {
await context.sendFlex('中原機械教師1', jsonObj);
await context.sendFlex('中原機械教師2', jsonObj2);
await context.sendFlex('中原機械教師3', jsonObj3);
} else {
await context.sendText(`${userName}您好!我收到你的文字【${context.event.text}】`);
}
} else {
await context.sendText('我不吃文字以外的資料!');
}
};
```
#### Flex Message
-[Flex Message Simulator](https://developers.line.biz/flex-simulator/)
## 6、Google Firebase
### <i class="fa fa-arrow-circle-right"></i> Firebase服務介紹
* [Firebase官方網站](https://firebase.google.com/)
* [Firebase資料庫官方文件](https://firebase.google.com/docs/reference/js/firebase.database.Reference?hl=zh-tw)
### <i class="fa fa-arrow-circle-right"></i> 環境介紹
### <i class="fa fa-arrow-circle-right"></i> 資料庫環境設定
### <i class="fa fa-arrow-circle-right"></i> ref(路徑)、set(新增)
* firebase全部都是物件格式,不能陣列內容
```javascript
var data = null;
data = {
student1:{
name: 'Tom',
num:'1'
},
student2:{
name: 'John',
num:'2'
}
};
```
#### set新增
寫入資料庫的內容可以是**文字**也可以是**物件**
#### ref路徑 (類似mongoDB的collection或是SQL的資料表)
* ref()沒有內容即為根目錄
* ref('第一層/第二層/...')可以修改對應層級資料
#### 資料庫的設計 (以餐廳為例)
* 盡量扁平一點不要寫太多層級
```javascript
var data = null;
data = {
food:{
coke:{
price:30,
num:1
},
fries:{
price:50,
num:20
}
},
order:{
1:{
coke:2
},
2:{
fries:5,
coke:2
}
}
};
```
### <i class="fa fa-arrow-circle-right"></i> 顯示資料
* firebase的程式要放到body中
#### once 讀取一次資料庫的資料
```javascript
var myName = firebase.database().ref('myName');
// once 讀取一次資料庫的資料
// on 隨時監聽
myName.once('value',function(snapshot){
console.log(snapshot.val());
var str = snapshot.val();
document.getElementById('title').textContent = str;
});
```
#### on 隨時監聽(資料即時呈現)
### <i class="fa fa-arrow-circle-right"></i> firebase非同步觀念
### <i class="fa fa-arrow-circle-right"></i> push - 新增資料
```javascript
var todos = firebase.database().ref('todos');
todos.push({content:'今天要記得刷牙'});
```
### <i class="fa fa-arrow-circle-right"></i> remove、child移除資料
#### child找出當下的子路徑
```javascript
var todos = firebase.database().ref().child('todos'); //找到根目錄底下的子目錄
```
#### remove 刪除資料
### <i class="fa fa-arrow-circle-right"></i> for-in語法
```javascript
var colors = ['red','black','yellow'];
// 一般for迴圈寫法
for (var i = 0; color.length > i; i++)
{
console.log(color[i])
}
// for in 寫法
// item會依序撈出陣列索引值
// for (variable in [ object | array])
// {
// statements
// }
for(var item in colors){
console.log(item);
}
//結果會是0 1 2
```
* 陣列物件
```javascript
var kaohsiung = [
{
father:'Tom',
mon:'Mary'
},
{
father:'John',
mom:'Jane'
}
];
for (var item in kaohsiung){
console.log(kaohsiung[item].father);
}
```
* 物件
* item會是物件屬性
```javascript
var todos = {
num1:{
content:'要記得刷牙'
},
num2:{
content: '要記得洗澡'
}
};
for (var item in todos){
console.log(item);
}
// 結果會是 num1、num2
// 要撈出content
// todos[item].content
```
### <i class="fa fa-arrow-circle-right"></i> 網頁上即時瀏覽firebase資料
```javascript
var ref = firebase.database().ref();
ref.on('value',function(snapshot){
var results = JSON.stringify(snapshot.val(), null, 3);
});
```
### <i class="fa fa-arrow-circle-right"></i> 排序
* orderByChild
* 需搭配forEach語法
```javascript
var peopleRef = firebase.database().ref('people');
peopleRef.orderByChild('weight').once('value',function(snapshot){
snapshot.forEach(function(item){
console.log(item.key); // item.key可以得到父屬性值
console.log(item.val());
});
});
```
> 抓路徑→排序('屬性')→讀取內容→forEach 依序撈出資料
#### 排序規則
1. 指定子鍵為null的子項目排在最前面
2. 指定子鍵值為false的子項目
3. 指定子鍵值為true的子項目
4. 指定子鍵值為數值,按升序排序
5. 指定子鍵值為字串
6. 指定子鍵值為物件
### <i class="fa fa-arrow-circle-right"></i> 過濾(搜尋規則)
> 抓路徑→排序('屬性')→過濾→讀取內容→forEach 依序撈出資料
* startAt() 多少以上
* endAt() 多少以下
* equalTo() 相等
### <i class="fa fa-arrow-circle-right"></i> 限制筆數 limit
> 抓路徑→排序('屬性')→過濾→限制筆數→讀取內容→forEach 依序撈出資料
* limitToFirst() 從最前面開始撈出資料
* limitToLast() 從最後面開始撈出資料
### <i class="fa fa-arrow-circle-right"></i> 時間篇
```javascript
var time = new Date();
consolelog(time);
consolelog(time.getFullYear());
consolelog(time.getMonth()); //在JavaScript中,0 = 1月
consolelog(time.getDay()); //在JavaScript中,禮拜天 = 0
consolelog(time.getHours());
consolelog(time.getMinutes());
consolelog(time.getSeconds());
consolelog(time.Milliseconds()); // 1000毫秒 = 1秒
```
* timestamp (時間戳記,時戳)
* UNIX時間 - 格林威治時間(GTM)1970年1月1日00:00:00到目前經過的秒數
```javascript
var time = new Date();
time.getTime(); //將帶有日期格式的時間轉換成UNIX時間
var now = new Date(time); //將UNIX時間轉換回帶有日期的格式
```
### <i class="fa fa-arrow-circle-right"></i> reverse資料翻轉調整
```javascript
var data = [1, 2, 3, 4, 5];
data.reverse();// 將資料反轉
// 將物件加入陣列後進行reverse反轉排序
var data = [];
var todos = {
1234: {
content: 'hello'
},
456: {
content: 'hi'
}
};
```
## 7、Node.js後端 - Express框架
### <i class="fa fa-arrow-circle-right"></i> Express框架介紹
* Node.js Web應用框架
* 輕量型 Web 應用框架
* 後端邏輯
* AJAX post
* EJS template、jade(pug)
### <i class="fa fa-arrow-circle-right"></i> Express環境安裝
* Win
```bash
npm install express --save
```
* Mac
```bash
sudo npm install express --save
```
### <i class="fa fa-arrow-circle-right"></i> 開啟Web伺服器
* 透過Express建立Web伺服器
* app.js
```javascript
var express = require('express');
var app = express();
app.get('/', function(request, response){
res.send('');
});
//監聽 port
var port = process.env.PORT || 3000;
app.listen(port);
```
* process.env.PORT
* 環境預設port
### <i class="fa fa-arrow-circle-right"></i> 網址規則
#### Router路由
* https、http協定
* https有加密
* domian網址
* www.google.com.tw
* 127.0.0.1 →本地端
* www.cycu.edu.tw
* 路徑
* 參數(query)
* ?參數1=&參數2=&....
### <i class="fa fa-arrow-circle-right"></i> Router路由設計
### <i class="fa fa-arrow-circle-right"></i> 取得指定路徑(params)
> 網址 https://www.xxx.xxx/**:路徑參數名稱**
> request.params.參數名稱
```javascript
app.get('/user/:name', function(req,res){
var myName = req.params.name;
});
```
### <i class="fa fa-arrow-circle-right"></i> 取得網址參數(query)
> 網址 https://www.xxx.xxx/參數名稱**?網址參數1&網址參數2**
> request.query.網址參數名稱
```javascript
//範例:某某人的音樂列表,抓前10筆
app.get('/user/:name', function(req,res){
var myName = req.params.name;
var limit = req.query.limit;
});
```
### <i class="fa fa-arrow-circle-right"></i> Middleware - 中介軟體
```javascript
var express = require('express');
var app = express();
//app.use像是個守門員,要顧及網站安全,必須在前面
app.use(function(req,res,next){
console.log('有人進來了');
//確保沒問題next()進入到下一個關卡(程式)
next();
});
```
#### 404路由設定
* 頁面不存在
```javascript
app.use(function(req,res,next){
res.status(404),send('抱歉,您的頁面找不到');
});
```
#### 500路由設定
* 程式錯誤
```javascript
app.use(function(err,req,res,next){
console.log(err.stack);
res.status(500).send('程式有些問題,請稍後嘗試');
});
```
#### 不同寫法
```javascript
// Way 1
var login = function(req,res,next){
console.log('你是登入狀態');
next();
};
app.use(login);
// Way2
var login = function(req,res,next){
console.log('你是登入狀態');
next();
};
// 給單獨router使用
app.get('/',login,function(req,res,next){
})
```
### <i class="fa fa-arrow-circle-right"></i> 載入靜態檔案 (static)
> app.use(express.static('資料夾名稱'));
* 專案中必須要有對應的資料夾名稱
* 必須寫在最前面,這樣才可以抓到檔案
* 檔案的位置會以該**資料夾為根目錄**往下抓
```javascript
var express = require('express');
var app = express();
// 增加靜態檔案的路徑
// 我要把所有靜態檔案增加到public資料夾上
// 專案中要有public資料夾
// 必須寫在最前面的位置
app.use(express.static('public'));
```
### <i class="fa fa-arrow-circle-right"></i> Template - EJS語言介紹
#### 樣板語言
#### 環境安裝
```bash
npm install ejs-locals --save
```
```javascript
var engine = require('ejs-locals');
app.engine('ejs',engine); // Express載入ejs作為樣板控制的
app.set('views','./views'); // app.set()設定Express各種設定,這裡是設定ejs檔案路徑位置
app.set('view engine','ejs'); // 指定Expess要用哪一個樣板引擎去跑
```
* xxx.ejs
* res.render('ejs檔案名稱(不用帶附檔名)');
### <i class="fa fa-arrow-circle-right"></i> 參數導入
* app.js
```javascript
app.get('/',function(req,res){
res.render('index',
{
'title':'六角學院',
'boss': 'AT'
});
});
```
* index.ejs
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<%= title %> <!-- =是要轉成字串 -->
<h2><%= boss %></h2>
</body>
</html>
```
### <i class="fa fa-arrow-circle-right"></i> EJS載入內容種類
* <%= %>渲染成字串
* <%- %>渲染成html格式
* <% %> 程式邏輯
```html
<% if() { %>
<span></span>
<% } %>
<% else { %>
<span></span>
<% } %>
```
### <i class="fa fa-arrow-circle-right"></i> EJS載入陣列
```html
<% for (var i=0; course.length;i++) { %>
<li><%- course[i] %></li>
<% } %>
```
### <i class="fa fa-arrow-circle-right"></i> EJS設定Layout
* layout.ejs
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<%- body %>
</body>
</html>
```
* index.ejs
```html
<% layout('layout') %>
<%= title %> <!-- =是要轉成字串 -->
<h2><%= boss %></h2>
```
### <i class="fa fa-arrow-circle-right"></i> API管理工具
#### API設計
- AJAX get、post向後端傳遞資料
* [postman](https://www.getpostman.com/)
#### Postman取得get資料、基礎操作
##### 使用postman傳送表單資訊
* 選擇POST
* body
* 類型選x-www-form-urlencoded
* key→控制項name
* value→控制項中的值
### <i class="fa fa-arrow-circle-right"></i> body-parser取得表單資料
* body-parser套件
* 可以把前端表單的資料傳送到後端
```bash
npm install body-parser --save
```
```javascript
var bodyParser = require('body-parser');
//增加body解析
app.use(bodyParser.json());
app.use(bodyParser,urlencoded({
extended:false
}));
```
```html
<form action="路徑" method="post">
<!-- name屬性很重要!!!資料傳送到後端去,會以name作為名稱 -->
<input type="text" name="" value="" >
<input type="submit" name="" value="Button">
<form>
```
* req.body //解析內容
### <i class="fa fa-arrow-circle-right"></i> Redirect跳轉網頁設定
```javascript
// 轉址到某頁面去
res.redirect('頁面名稱');
// 會把該頁面名稱渲染到當前的router上
res.render('頁面名稱');
```
### <i class="fa fa-arrow-circle-right"></i> POST AJAX前後端介接原理
### <i class="fa fa-arrow-circle-right"></i> POST AJAX JSON格式
```javascript
var data = JSON.stringify({"content":str}); // 必須將JSON字串化
```
* 使用Postman測試
* body選raw
* 選JSON(application/json)
### <i class="fa fa-arrow-circle-right"></i> Router 進階設定
* 將各個邏輯放到各個route js檔中
* app.js僅需要load相關模組
```javascript
// app.js
app.use('/user',user);
// routes/user.js
// 路徑就會為 http://domain/user/edit-profile
router.get('/edit-profile', (req,res) =>{});
```
### <i class="fa fa-arrow-circle-right"></i> [express-generator](http://expressjs.com/zh-tw/starter/generator.html)
#### Express應用程式產生器
* 可以快速建立應用程式架構
```bash
npm install express-generator -g
```
* 建立應用程式
```bash
express [options][dir]
```
#### 產生的結構
* bin
- www
* public
- javascript
- images
- stylesheets
- style.css
* routes
- index.js
- users.js
* views
- error.ejs
- index.ejs
* app.js
* package.json
## 7、
### <i class="fa fa-arrow-circle-right"></i> 後端RESTful API
### <i class="fa fa-arrow-circle-right"></i> 前端設計 SPA (Single Page Application)
## 8、
### <i class="fa fa-arrow-circle-right"></i> cookie、session講解
#### cookie
##### cookie簡介
* 能夠儲存資料在瀏覽器上的小型資料庫
* 能夠在client、server進行讀取、寫入
* 是由key/value方式組成,並由**分號**跟**空格**來隔開
* 可以設定失效時間,讓cookie在指定時間內消失
* Chrome: F12 → application可以看到cookie
##### cookie欄位介紹
* Name: 鍵(key)
* Value: 值(Value)
* Domain: 可存取該cookie的網域
* expires: 限制cookie有效期間
* path: 設定可以存取該cookie的路徑
* secure: 設定cookie是否要https網址才可以進行傳送
##### cookie client(瀏覽器)端寫法
* 寫入cookie
```javascript
document.cookie = "myName=tom";
```
* 寫入cookie,並加入過期時間
```javascript
document.cookie = "username=bob;expires=Mon, 04 Dec 2017 08:18:32 GMT;path=/";
```
* GMT時間
```javascript
new Date().toGMTString();
```
* 寫入cookie,設定10秒後失效
```javascript
document.cookie = "username=bob; max-age=10; path=/";
```
##### cookie server(伺服器)端寫法
* 安裝解析cookie NPM
```bash
npm install cookie-parser
```
* express寫入cookie,並加入相關設定(過期時間、httponly、path)
```javascript
res.cookie(name, value[,options]);
// 範例
res.cookie('name','Mary',{
maxAge: 1000,
httponly: true
});
```
* express讀取Client端cookie
```javascript
req.cookie.yourCookieName;
```
#### session
##### session簡介
* 儲存在伺服器的暫存資料,此暫存資料可放在記憶體或資料庫上
* session可在cookie上儲存一筆辨識你是誰的session id
##### session id
* 用到[express session](https://github.com/expressjs/session)套件
```bash
npm install express-session --save
```
```javascript
const session = require('express-session');
```