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