---
###### tags: `TypeScript`
---
# 10/23
## 爬取網站調整
- 將爬取網站改為 [台灣水庫即時水情](https://water.taiwanstat.com/)
- 原本想爬[台電系統各機組發電量](https://www.taipower.com.tw/d006/loadGraph/loadGraph/genshx_.html),但資料貌似動態產生的爬不了
```javascript=
// 字串只保留數字
const str = "123四五六"
const num = str..replace(/[^\d|.]/g, "");
console.log(num); // 123
```
- superagent 或取 html 套件
- cheerio 用 $ 解析 dom 的套件 (使用方式類似 JQ)
## Express 改良
- Decorator 不應該負責生成 router,應該另外抽離到 router.ts
```typescript=
import { Router } from "express";
export default Router();
// 並修改 decorator.ts (不需要 new) ,index.ts
```
- getRequestDecorator 接收參數只能是 get post (Methods 類型),不應該是 string
```typescript=
function getRequestDecorator(type: Methods){...}
// 改用枚舉
export const get = getRequestDecorator(Methods.get);
export const post = getRequestDecorator(Methods.post);
```
- 將 decorator 獨立成一個資料夾管理
- 再將 decorator.ts 拆成 controller、request、use,並交由 index.ts 統一匯出
- 目錄結構

- CrawlerController、LoginController 中 isLogin 加入 !! ,讓 TS 判斷 isLogin 是 boolean
```typescript=
// 兩次不等於 = 等於,結果一樣
const isLogin = !!(req.session ? req.session.login : false);
// 封裝成一個方法
isLogin(req: BodyRequest): boolean {
return !!(req.session ? req.session.login : false);
}
// 這樣寫會"報錯"
const isLogin = this.isLogin(req);
```
:::danger
因為 LoginController 沒有被實例化,this 指向是 undefind
:::
```typescript=
// 改成 static
static isLogin(req: BodyRequest): boolean {
return !!(req.session ? req.session.login : false);
}
const isLogin = LoginController.isLogin(req);
```
:::info
問題: controller.ts 中
export function controller(target: any) {...},target 是構造函數,需寫構造函數類型
:::
```typescript=
// target 是構造函數,需寫構造函數類型 (看不懂)
export function controller(target: new (...args: any[]) => any) {
for (let key in target.prototype) {
const path: string = Reflect.getMetadata("path", target.prototype, key);
const method: Methods = Reflect.getMetadata(
"method",
target.prototype,
key
);
// middleware 是 RequestHandler 需引入
const middleware: RequestHandler = Reflect.getMetadata(
"middleware",
target.prototype,
key
);
// handler 是 any 類型 先不管
const handler = target.prototype[key];
// handler 判斷去除,裝飾器寫在 handler 上,所以一定存在
if (path && method) {
if (middleware) {
router[method](path, middleware, handler);
} else {
router[method](path, handler);
}
}
}
}
```
:::info
問題: request.ts 中 target 應該是 prototype
function (target: any , key: string) {...}
:::
- 設置 index.ts 匯出 CrawlerController LoginController
```typescript=
import { CrawlerController, LoginController } from "../controller";
function getRequestDecorator(type: Methods) {
return function (path: string) {
// target 指的是 prototype
// 類型修改為 CrawlerController、LoginController
return function (target: CrawlerController | LoginController, key: string) {
Reflect.defineMetadata("path", path, target, key);
Reflect.defineMetadata("method", type, target, key);
};
};
}
// 同樣也在 use.ts 內改寫 target 類型
```
```typescript=
// 匯出進行共用
export enum Methods {
get = "get",
post = "post",
}
```
:::info
需求: 二級路由,格式如下
@controller('/abc')
export class CrawlerController {..}
:::
```typescript=
// controller 改寫 再往外多一層 (工廠 ?)
export function controller(root: string) {
return function (target: new (...args: any[]) => any) {
for (let key in target.prototype) {
const path: string = Reflect.getMetadata("path", target.prototype, key);
const method: Methods = Reflect.getMetadata(
"method",
target.prototype,
key
);
const middleware: RequestHandler = Reflect.getMetadata(
"middleware",
target.prototype,
key
);
const handler = target.prototype[key];
// path、method 必須同時存在 (自己改的)
if (!path || !method) { return; }
// 判斷 root 是不是 = '/'
const fullPath = root === "/" ? path : `${root}${path}`;
if (middleware) {
router[method](path, middleware, handler);
} else {
router[method](fullPath, handler);
}
}
};
}
```
:::info
需求: 多個中間件,目前只能使用一個
:::
```typescript=
@get("/showData")
@use(checkLogin)
@use(test)
showData(req: BodyRequest, res: Response): void {...}
// 修改 use.ts
export function use(middleware: RequestHandler) {
return function (target: CrawlerController | LoginController, key: string) {
// 新增 originMiddlewares 第一次為 [],第二次先獲取再往上加
const originMiddlewares =
Reflect.getMetadata("middlewares", target, key) || [];
originMiddlewares.push(middleware);
Reflect.defineMetadata("middlewares", originMiddlewares, target, key);
```
- 修改 controller.ts
```typescript=
export function controller(root: string) {
// ...
// 改為 RequestHandler[]
const middlewares: RequestHandler[] = Reflect.getMetadata(
"middlewares",
target.prototype,
key
);
// ...
if (middlewares && middlewares.length) {
// 改為 ...middlewares ( 不太懂)
router[method](fullPath, ...middlewares, handler);
} else {
router[method](fullPath, handler);
}
}
```

:::success
達成多個中間件都能運作的目的。
:::
## React
- 使用 React 建立前端頁面
- [Create React App](https://create-react-app.dev/docs/getting-started)
> 類似 vue cli ?
- `npm uninstall -g create-react-app`
- 創建 react-project 使用 npm
- `npx create-react-app react-project --template typescript --use-npm`