# 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如果相同不會被蓋寫的狀況

## 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
```

## 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" />
```

## 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 (推薦)
}
```