---
# System prepended metadata

title: web skill
tags: [ui]

---

# web skill

## blob vs data url


Blob（Binary Large Object）是一種在資料庫中用來儲存二進制數據的資料型別。Blob 可以儲存任何類型的二進制數據，例如圖像、音訊、視訊、文件等。

Blob 資料型別在許多資料庫管理系統（例如 SQLite、MySQL、PostgreSQL 等）中都有支援。它通常用於需要儲存大型二進制數據的情境，例如將圖片或文件儲存在資料庫中。

在資料庫中使用 Blob 資料型別時，可以使用以下一般的步驟：

建立資料庫表格時，使用合適的語法定義 Blob 欄位。例如，在 SQLite 中可以使用以下語法來定義一個 Blob 欄位：
sql
Copy code
CREATE TABLE my_table (
    id INTEGER PRIMARY KEY,
    data BLOB
);
在插入資料時，將二進制數據轉換為 Blob 對象，並插入到資料庫中。例如，在使用 Python 與 SQLite 的情境中，可以使用以下方式插入 Blob 數據：
python
Copy code
import sqlite3

### 連接資料庫
conn = sqlite3.connect('my_db.db')
cursor = conn.cursor()

### 讀取二進制數據
with open('image.jpg', 'rb') as f:
    image_data = f.read()

### 插入 Blob 數據
cursor.execute("INSERT INTO my_table (data) VALUES (?)", (sqlite3.Binary(image_data),))

### 提交變更並關閉連接
conn.commit()
conn.close()
在檢索資料時，可以從 Blob 欄位中讀取二進制數據，並進行相應的處理。例如，在使用 Python 與 SQLite 的情境中，可以使用以下方式從 Blob 欄位中讀取數據：
python
Copy code
import sqlite3

### 連接資料庫
conn = sqlite3.connect('my_db.db')
cursor = conn.cursor()

#### 檢索 Blob 數據
cursor.execute("SELECT data FROM my_table WHERE id = ?", (1,))
result = cursor.fetchone()
if result is not None:
    image_data = result[0]
    # 對二進制數據進行處理
    # ...

### 關閉連接
conn.close()
以上是一個簡單的 Blob 資料型別的使用教學，實際上在不同的資料庫管理系統和程式語言中，使用 Blob 資料型別的方式可能會有所不同。請根據您使用的具體技術和需求來參考相

data URL（資料網址）是一種特殊的 URL 格式，可以將資料內容直接嵌入在 URL 中，形成一個包含資料內容的網址。而 Blob 則是一個 JavaScript 物件，用於表示二進制數據。

在前端開發中，Blob 主要用於處理二進制數據，例如圖片、音訊、視訊等。以下是一個簡單的前端使用 Blob 的例子：

```typescript
// 創建一個 Blob 對象
const blobData = new Blob(["Hello, World!"], { type: "text/plain" });

// 創建一個 Blob URL
const blobUrl = URL.createObjectURL(blobData);

// 創建一個 <a> 元素
const downloadLink = document.createElement("a");
downloadLink.href = blobUrl;
downloadLink.download = "example.txt"; // 下載的文件名
downloadLink.textContent = "Download File";

// 將 <a> 元素添加到 DOM 中
document.body.appendChild(downloadLink  
    downloadLink.click();
    document.body.removeChild(downloadLink);
    URL.revokeObjectURL(blobUrl);
```

 Blob 是一個方便的資料型別，可以在前端進行二進制數據的處理和操作。以下是一些 Blob 在前端開發中的優點：

處理二進制數據：Blob 可以用來表示任何類型的二進制數據，例如圖片、音訊、視訊等。它可以方便地在前端進行二進制數據的生成、讀取、修改和傳輸，並且可以與其他前端 API（例如 FileReader、Canvas 等）搭配使用，實現各種處理需求。

客戶端生成和處理：Blob 是一種客戶端資料型別，不需要向伺服器端進行請求，可以在瀏覽器的記憶體中生成和處理，提供了較佳的性能和效率。這在一些場景中特別有用，例如在前端實現圖片壓縮、音訊處理、視訊編碼等功能時。

可以作為資料傳輸的容器：Blob 可以作為資料傳輸的容器，例如在前端進行文件上傳、資料下載等操作時，可以使用 Blob 來生成或接收二進制數據，並進行相應的處理。

需要注意的是，Blob 的使用需要謹慎，特別是當處理大型二進制數據時，可能會導致記憶體消耗過多或性能下降。因此，在使用 Blob 時應評估其使用場景、數據大小和性能需求，選擇合適的處理方式。在一些情況下，可能需要考慮其他的資料型別或處理方式來達到更好的性能和效果。

### Data type url比較

```typescript
<!DOCTYPE html>
<html>
<body>

<h2>Convert File to Data URL</h2>

<input type="file" id="fileInput">
<img id="imagePreview" src="" alt="Preview Image">

<script>
document.getElementById('fileInput').addEventListener('change', function(e) {
  var file = e.target.files[0];
  var reader = new FileReader();

  reader.onload = function(e) {
    var dataUrl = e.target.result;
    document.getElementById('imagePreview').src = dataUrl;
  };

  reader.readAsDataURL(file);
});
</script>

</body>
</html>
```
### 以下是 Blob 和 data URL 的一些差異：

存儲方式：Blob 通常表示二進制數據的物件，可以存儲在記憶體中，也可以保存到瀏覽器的本地儲存（例如 IndexedDB 或瀏覽器的暫存儲存）。而 data URL 是一個包含資料內容的網址，資料內容直接嵌入在 URL 中。

長度限制：Blob 可以處理大型二進制數據，並且通常不受到瀏覽器 URL 長度限制的影響。而 data URL 則受到瀏覽器對 URL 長度的限制，當資料內容較大時，可能會遇到 URL 長度過長的問題。

處理方式：Blob 提供了更多的操作和處理二進制數據的方法，例如可以使用 FileReader 來讀取 Blob 的內容，也可以使用 URL.createObjectURL() 將 Blob 轉換成可以在網頁中顯示的 URL。而 data URL 則直接將資料內容嵌入在 URL 中，不需要額外的轉換。

適用場景：Blob 主要用於在前端進行二進制數據的生成、讀取、修改和傳輸，特別適用於需要在前端進行二進制數據處理的情境，例如圖片壓縮、音訊處理、視訊編碼等。而 data URL 主要適用於一些簡單的資料傳輸場景，例如在網頁中顯示一個小型圖片或簡單的資料。

總的來說，Blob 和 data URL 都是在前端處理二進制數據時的不同選擇，具體使用哪種方式取決於需求和使用場景。需要根據實際情況選擇適合的方法來處理二進制數據。


| 特性 | Blob | Data URL |
|-----|------|----------|
| 存儲方式 | 可以存儲二進制數據，可以保存到記憶體或本地儲存 | 資料內容直接嵌入在 URL 中 |
| 長度限制 | 通常不受 URL 長度限制的影響 | 受到瀏覽器 URL 長度限制的影響 |
| 處理方式 | 提供多種操作和處理二進制數據的方法 | 不需要額外的轉換，直接使用 URL 即可 |
| 適用場景 | 適用於前端進行二進制數據的生成、讀取、修改和傳輸 | 適用於簡單的資料傳輸場景 |

## throw 不一定只能丟error

```typescript
const aaa = async()=>{
  try{
    throw new Date()
    throw 5
    throw undefined
    throw Symbol('foo')
  }catch(e){
    console.log(e)
  }
}
```

## deep clone compare
```typescript
import lodash from 'lodash';
const data = {
  name: 'Danny',
  age: 20,
  address: {
    street: 'aaa',
    date: new Date()
  },
  foo: () => true
}

console.time('JSON')
const clone1 = JSON.parse(JSON.stringify(data));
console.timeEnd('JSON')
console.time('cloneDeep')
const clone2 = lodash.cloneDeep(data)
console.timeEnd('cloneDeep')
console.time('structuredClone')
const clone3 = structuredClone(data)
console.timeEnd('structuredClone')

// compare date data 
// JSON.stringify  print string 
// cloneDeep  print new Date() 
// structuredClone print new Date() 

console.log(typeof clone1.address.date)//string
console.log(typeof clone2.address.date)//object
console.log(typeof clone3.address.date)//object

// compare function data 
// JSON.stringify  can't copy  
// cloneDeep  can copy 
// structuredClone DataCloneError 

console.log(clone1.foo) //undefined
console.log(clone2.foo) //[Function: foo]
console.log(clone3.foo) //[DataCloneError]: ()=>true could not be cloned.


// time
// JSON: 0.678ms
// cloneDeep: 0.232ms
// structuredClone: 0.151ms
```

## url params
```javascript
const url = new URL('https://jsonplaceholder.typicode.com/posts')
const query = new URLSearchParams({
  offset:1
})
url.search = query
url.searchParams.set('a',1)
url.searchParams.set('b',2)
console.log(url.toString()) //https://jsonplaceholder.typicode.com/posts?offset=1&a=1&b=2
console.log(url.searchParams.has('offset') ) //true


```


## orderArray
```typescript
const array =['a','b','c','d']
function test(
  source,
destination
){
  const arrayCpy = [...array]
  const [removed] = arrayCpy.splice(destination,1)
  arrayCpy.splice(source, 0, removed)
  console.log(arrayCpy)
}


```

## test
### render hook
https://react-hooks-testing-library.com/


## reference
https://jsnation.com/

## code review
https://roadmap.sh/best-practices/code-review

## jq command json format
https://medium.com/evan-fang/jq-%E5%91%BD%E4%BB%A4%E5%88%97json%E8%99%95%E7%90%86%E5%B7%A5%E5%85%B7-a553c8940ef5

https://stedolan.github.io/jq/


<!-- get user proxy ip -->
```typescript
const ip = request.headers.get('X-Forwarded-For')
```





__ proto __ VS prototype 
__ proto __ 用來造訪物件的原型（prototype）
prototype 只有function 物件能給指定，用來讓建構函示的實例繼承


```javascript

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();
```

var F 是一個建構函示他的prototype 會指到 Function.prototype 所以 var F 會有 b 這個 function 

`console.log(F.__proto__ === Function.prototype) // true`

然而原形練他自己本身也有自己繼承的原型
`console.log(Function.prototype.__proto__ === Object.prototype)`

所以這也說明 F 他有 a 跟 b 的屬性

你可以簡化以下寫法
`console.log(F.prototype.__proto__ === Object.prototype) // true`

但 f 他是一個建構函示的 instance ，所以它的原型應該會是指到建構函示的原型如下

`console.log(f.__proto__ === F.prototype) //true` 

然後根據上面的簡化寫法 `console.log(F.prototype.__proto__ === Object.prototype) // true` 推論出 f.__proto__ 他有 a 這個屬性



### Ratelimit ip vs userId

Ratelimit 實作上是一個流量控管的方式，有點像是 network traffic管理 api 流量，常見做法是過過 ip 當作 request 的唯一辨識，但值得注意的是此時的 ip 並不完全等於 user 的真實 ip，原因是 user 可能會透過 proxy server去訪問。


### ip
優點：

* 便於管理匿名用戶，可以透過 ip 當作唯一識別
* 可以將 ip 整理成 geo ip 的白名單，例如只開放哪些地區可以訪問網站

缺點：
* 共用 ip 問題，例如有企業共用 ip 的話，如果針對 ip 限制會導致所有 user 都受影響
* 攻擊者可以透過跨 ip 方式去欺騙 web api

以下是 user 可以透過修改以下的 header 達到轉換 ip 的效果 
```
1.  X-Originating-IP: 127.0.0.1
2.  X-Forwarded-For: 127.0.0.1
3.  X-Remote-IP: 127.0.0.1
4.  X-Remote-Addr: 127.0.0.1
5.  X-Forwarded-Host : 127.0.0.1
6.  X-Client-IP : 127.0.0.1
7.  X-Host : 127.0.0.1
8.  Forwarded: 127.0.0.1
9.  X-Forwarded-By: 127.0.0.1
10.  X-Forwarded-For-IP: 127.0.0.1
11.  X-True-IP: 127.0.0.1
```

### userId

優點：

* 可以只針對單一 user去進行限制
* 如果 user 人數過多，可以達到 load balance的效果

缺點：

* 匿名用戶將不能管理（訪客）
* 攻擊者可能可以透過創建多個 user帳號獲取令牌
* 地區性的 ip管理無法達成

### 結論

你會發現不管是 userId 或是 ip ，攻擊者都有對應的方式去繞過防禦，所以我們不能單單使用 Ratelimit 去做資安預防而已，後續可能還有更多面向要去解決，但結論就是 Ratelimit 的使用方式也可以擇中，例如用 ip 去管理 geo 白名單，以 userId 管理整體 server 的負載量。


### symbol
symbol 是一種新的數據類型，用來產生獨一無二的value，常會用於obj的key去使用

```javascript
let sym1 = Symbol('danny')
let sym2 = Symbol('danny')
```

 symbol創建不需要使用 new ，只需在函式中怎加一個key去創建，這亦這個key只能是string

就算使用相同的key 創建出來的 symbol都是獨立不相等的
`console.log(sym1 === sym2) //false`

 型別推論出來的 type 會師 symbol
`console.log(typeof sym1) //symbol`

 在obj使用上是這樣
```javascript
let obj = {
  [sym1]:'danny',
}
obj[sym1] = 'benson'
```
注意如果用symbol要造訪 obj 的 key 記得要加方括弧不能用 . 去造訪
console.log(obj[sym1]) // danny
console.log(obj.sym1) // undefined

Symbol 遍歷，例如以下的 obj2

```javascript
let obj2 = {
  info:'111',
  [sym2]:'sym2'
}

console.log(Object.keys(obj2)) // ['info]
```

所以如果我們要遍歷 Symbol 要用 getOwnPropertySymbols 去讀取
`console.log(Object.getOwnPropertySymbols(obj2)) // [ Symbol(danny) ]`

何時會用到 symbol 因為 symbol 最主要的特性就是obj 無法遍歷，實際上可以利用這一個特性去實作業務邏輯，例如把一個只在該 obj讀取的key用symbol辨識，往後如果其他obj想要繼承該obj屬性時，symbol key就會被保留下來，但現今實務上很少會去用到 symbol大概知道特性就好

```javascript
let sym3 = Symbol('狐狸')
let sym4 = Symbol('狐狸')

let obj3 ={
  [sym3]:'狐狸A',
  [sym4]:'狐狸B',
}
obj3[sym4] = '狐狸C'
console.log(obj3[sym3]) // 狐狸A
console.log(obj3[sym4]) // 狐狸C
```
以下是 react 透過 symbol 去定義 element，確保 displayName如果相同不會被蓋寫的狀況

![](https://hackmd.io/_uploads/rkdDYNPwn.jpg)

## async / defer script

出現 async, defer 的原因是，當瀏覽器發現有 script 時候會阻塞 html 的解析與渲染，等到 js loadend 在繼續 render html，所以以前我們在寫 DOM 操作的 script 會是放在 body 的最下面，跟 DOM 無關的放在 head。

共同 :
但如果是用 async / defer 會在在背景下載 js 而不阻斷 html render 行為。

差異 :
async: js 下載好後會直接執行 js，但要注意的是雖然下載 async script ，但一但下載好後還是會有 html 阻斷 render 問題。
defer: js 下載好後會等待  DOMContentLoaded 執行 js 。

適合 ：
async : 第三方套件例如 GTA
defer : 優化 script 載入順序，不希望 load js html 就不 render 。

不適合使用 async ：
需要操作 dom 節點。

備註：
一般使用 Dynamic scripts  預設是 async script 。

```typescript
let script = document.createElement('script');
script.src = '/path.js';
document.body.append(script);

concurrency 跟 thread safe
```


![](https://hackmd.io/_uploads/r12j8G0R3.jpg)



## prevent input number input e 
```typescript
<input type="number" onKeyDown={(e) => ["e", "E", "+", "-"].includes(e.key) && e.preventDefault()} />
```

## meta data

### theme

改變瀏覽器的主題顏色

```typescript
<meta name="theme-color" content="#4285f4" />
```
![](https://hackmd.io/_uploads/Hk0cGKuMp.png)

## scale

width : 指定瀏覽器頁面寬度同裝置。
initial-scale ：初始縮放比例。
```typescript
<meta name="viewport" content="width=device-width, initial-scale=1.0">
```
如果希望防止 `user` 縮放可以設定 minimum/maximum-scale

```typescript
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
```

或是 `user-scalable`


```typescript
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
```


## blog
https://blog.bytebytego.com/p/79-engineering-blogs-to-level-up?utm_source=post-email-title&publication_id=817132&post_id=138846570&utm_campaign=email-post-title&isFreemail=true&r=24vim3&utm_medium=email


## web performance
https://nitropack.io/blog/page/8


## chunk

```typescript
const array = [1,2,3,4,5,6]
const chunk = <Tdata>(array: Array<Tdata>, size: number) => {
  let result: (Tdata[])[] = [] 
  let remain: (Tdata[])[]  = []
  for (let i = 0; i < array.length; i += size) { 
    result.push(array.slice(i,i+size))
  }
  if (array.length % size > 0) { 
    remain.push(result.pop() as Tdata[])
  }
  return { result, remain }
}


```



# HTML5 Drog API 筆記

## 基礎範例

```typescript
<div
  draggable
  onDragStart={(event) => {
    event.dataTransfer.setData("text/plain", "dragStart123")
    event.dataTransfer.effectAllowed = "move";
  }}
>
  拖動源
</div>

<div
  onDragOver={(e) => {
    e.preventDefault() // ⚠️ 必須！否則 drop 不會觸發，瀏覽器預設阻止 drog 事件
  }}
  onDrop={(e) => {
    e.preventDefault()
    const data = e.dataTransfer.getData("text/plain")
    console.log(data) // dragStart123
  }}
>
  放置目標
</div>
```
## css lineheight

```typescript
# React CSSProperties - line-height 筆記

## 核心問題

在 React 的 `CSSProperties` 中，`lineHeight` 使用數字和字串有**完全不同**的含義。

## 數字 vs 字串

| 寫法 | 含義 | 實際效果 (假設 font-size: 14px) |
|------|------|--------------------------------|
| `lineHeight: 32` | 倍數 | `14px × 32 = 448px` ❌ |
| `lineHeight: '32px'` | 固定值 | `32px` ✅ |
| `lineHeight: 1.5` | 倍數 | `14px × 1.5 = 21px` ✅ |

## React CSSProperties 規則

// ✅ 大部分屬性：數字自動轉換為 px
const style = {
    width: 100,      // → 100px
    minHeight: 32,   // → 32px
    padding: 16,     // → 16px
}

// ⚠️ 例外：lineHeight 數字表示倍數
const style = {
    lineHeight: 32,     // → font-size × 32 (超級大！)
    lineHeight: '32px', // → 32px (正確)
    lineHeight: 1.5,    // → font-size × 1.5 (推薦)
}

```