🔍 搜尋結果:user

🔍 搜尋結果:user

JavaScript 系列四:第1課 ── autosize 套件

## 課程目標 認識並且能使用 autosize 套件 ## 課程內容 這次的套件是 https://github.com/jackmoore/autosize 文件與 demo 在 https://www.jacklmoore.com/autosize/ 這是一個讓 `<textarea>` 的 UX(User Experience 使用者體驗)瞬間升級的好用套件 ``` // from a NodeList autosize(document.querySelectorAll('textarea')); // from a single Node autosize(document.querySelector('textarea')); ``` 使用起來非常簡單,把 DOM 元素找到,當成參數傳給 autosize 函式,就可以了 在設計各種第三方套件給別人的時候,通常都會設計成這樣,很簡單、易用吧 程式的使用方法,我們叫 API(Application Programming Interface,應用程式介面) 實務上,我們會跟同事說:「去查一下這個套件的 API」,也就是去看一下這套件的使用方法。可以理解成「產品規格」 --- 在上網搜尋套件的時候,第一個要先看這個專案的星星數量,至少要有幾百顆以上吧 如果是太冷門的套件,代表可能不夠成熟、文件不完整、潛在 bug 會很多 第二個要看的是最近更新日期,也就是 last commit 的時間,至少兩年內有更新過吧 太久沒更新的專案,可能有點過時,會跟最新的瀏覽器或者其他工具有衝突 不過,如果只是用在網站上的小小地方,真的出問題,再換掉的成本也不大,那就先導入也沒關係 --- 要使用第三方套件,除了直接下載然後使用 `<script>` 引入之外,還有一個常用的懶人方法 就是搜尋 `套件名稱 CDN`,直接找現成的 CDN 連結來用 CDN 全名 Content Distribution Network,內容傳遞網路。這是大型企業讓資源在全球加速載入用的 看不懂沒關係,反正搜尋 `套件名稱 CDN`,通常會有現成 `<script>` 可用 搜尋 `autosize cdn` 看看搜尋結果,就會知道了 (要注意這是一個懶人方法,工作上直接這樣用,網站可能會載入的有點慢) ## 課後作業 請使用 https://jsfiddle.net 假設你正在開發一個留言功能 有暱稱欄位、留言欄位、以及一個送出按鈕 留言的區塊,`textarea` 預設只有三行的高度 使用者在輸入時,輸入越多行,輸入區塊會自動變高,而不會出現捲軸 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列三:練習2 ── toast 吐司元件

## 課程目標 認識並且能實做 toast 元件 ## 課程內容 toast 中文叫「吐司」元件,就跟烤土司機,烤好吐司會跳出來一樣 用這元件來讓一些操作回饋訊息跳出,可以讓使用者體驗(User Experience,簡稱 UX)更好 很多網站因為沒有做好適當的 toast 元件,使用者在操作時常常覺得「表單到底有沒有送出?」「網站當機了是不是?」 通常要將 toast 做成幾種不同樣式,根據不同使用情境來使用 由於 toast 通常會直接蓋在網站的上方正中央(或者右上角、左下角,不一定) 會蓋到網頁畫面,所以通常會做成「顯示幾秒之後自動關閉」 請上網搜尋 `toast ui` `toast component`,四處觀摩一下業界常見的設計 --- 要在倒數幾秒之後執行任務,你需要 `setTimeout` 這個函式。請自行上網搜尋一下,了解這個函式的用法 ## 課後作業 請使用 https://jsfiddle.net 用以下 html 為基礎(你可以稍微修改),接著寫出 css 與 js 的部份 ``` <button onclick="toastSuccess()">成功訂購商品</button> <hr> <button onclick="toastError()">訂購商品失敗</button> <div class="toast toast-success"> 訂購成功!可以查看 Email 確認訂單細節! </div> <div class="toast toast-error"> 抱歉,訂購失敗!請稍後重新嘗試 </div> ``` --- 這邊共有兩個按鈕,模擬兩種使用情境 兩個吐司元件,一開始預設是隱藏的,不會在畫面上看到 請寫出 `toastSuccess` 函式的內容,在點擊按鈕後,將「成功吐司」顯示在畫面上 5 秒鐘,之後自動消失 請寫出 `toastError` 函式的內容,在點擊按鈕後,將「錯誤吐司」顯示在畫面上 5 秒鐘,之後自動消失 成功吐司請做成綠色 錯誤吐司請做成紅色 這個顏色變化,可以是邊框、背景顏色、字體顏色、影子顏色,都可以,你自由決定 --- 請稍微加一些 css 屬性,弄得漂亮一點 `<hr>` 只是方便隔開元素,方便別人查看元件用的,醜醜的留在畫面上沒關係 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列二:第1課 ── 認識 DOM 樹、新增元素

## 課程目標 認識 DOM 樹基本觀念 能夠在 DOM 樹進行「新增」 學會基本的除錯技巧 ## 課程內容 請打開這個網站,用這個網站來練習&寫作業: https://jsfiddle.net 在學 JavaScript 的時候,你會聽到很多人在講「DOM 樹」這個名詞 什麼是「DOM 樹」? 其實只是,你在網頁上看到的東西,瀏覽器會在背後的 JavaScript 環境,維持一個對應的「DOM 樹」 比如說,當你寫這樣的 html(這邊先省略內容,只留結構) ``` <html> <head> <script></script> </head> <body> <div> <h1></h1> <p></p> </div> </body> </html ``` 瀏覽器會在背後創造一個這樣的資料結構 ``` document └── html ├── head │   └── script └── body └── div ├── h1 └── p ``` 很像是樹吧?這就是「DOM 樹」 記得之前用過的 `document.getElementById` 嗎? 這就是 DOM 最上面的 document 物件 所謂的「DOM 樹」其實就是一個「巢狀物件」。 「巢狀物件」的意思是,物件的屬性,又是另一個物件,然後這物件的屬性,又可以是另一個物件 用 JavaScript 來表示的話,類似這樣(只是概念喔,真實的物件不只是這樣,而是有一堆內建函式與屬性可以用) ``` var document = { name: 'document', children: [ { name: 'html', children: [ { name: 'head', children: [ { name: 'script', children: [] } ] }, { name: 'body', children: [ { name: 'div', children: [ { name: 'h1', children: [] }, { name: 'p', children: [] } ] } ] } ] } ] } ``` 在 DOM 中,被包起來的元素,稱為子元素;把別人包起來的元素,稱為父元素。就跟學 html 時的稱呼一樣 瀏覽器會確保用戶介面(User Interface,簡稱 UI)上看到的,跟環境中實際運行的 DOM 長得一模一樣 也就是說,你修改 UI 的內容,DOM 物件就會自動更新;你用 JavaScript 修改 DOM 物件的內容,UI 就會自動更新 --- 來學學怎麼用 JavaScript 操作 DOM 樹吧! 如果原本 html 長這樣 ``` <div id="app"></div> ``` 只要加入這段程式碼 ``` var heading = document.createElement('h1'); heading.textContent = '這是標題'; var para = document.createElement('p'); para.textContent = '這是段落文字'; var app = document.getElementById('app'); app.append(heading); app.append(para); ``` 利用 document 的內建函數 `createElement` 來動態創造新的元素 修改元素的屬性 `.textContent` 來設定文字內容 接著用 `.append()` 函數來讓元素吃下別的元素,也就是「把另一個元素收為子元素」,就跟收養子女一樣! 最後 html 就會變成這樣 ``` <div id="app"> <h1>這是標題</h1> <p>這是段落文字</p> </div> ``` 馬上貼到 jsfiddle 試試看,實驗一下、玩玩看! --- 在寫 html 的時候,出現巢狀結構是很常見的 操作 DOM 時,當然也可以自由創造巢狀結構 ``` var elem1 = document.createElement('div'); elem1.textContent = 'level 1'; var elem2 = document.createElement('div'); elem2.textContent = 'level 2'; var elem3 = document.createElement('div'); elem3.textContent = 'level 3'; var app = document.getElementById('app'); app.append(elem1); elem1.append(elem2); elem2.append(elem3); ``` 到 jsfiddle 試試看,就會清楚實際效果囉! --- 在開發的過程中,基本上會不斷打錯字、寫錯用法。程式跑不出結果,很正常 這種時候,有兩個常用的除錯方法,我們叫「debug 的方法」 第一個方法是 `alert`,你就把不確定的內容,給 alert 出來看看就對了,到處寫 alert 看一下變數內容,確定內容跟預期一樣,再接著繼續寫 第二個方法是 `console.log()`,把參數傳進去,接著打開瀏覽器的「開發者工具」,就會看到結果,通常會比 alert 內容更詳細、更方便除錯 除此之外,JavaScript 相關的各種錯誤訊息,都會在「開發者工具」出現。請去研究你所使用的瀏覽器,如何打開這個工具 ``` var x = 123; var y = 'hello'; var z = document.createElement('div'); console.log(x) console.log(y) console.log(z) ``` 請打開瀏覽器的開發者工具,實際看一下上述範例的結果。 ## 課後作業 假設你是一個健忘的人,你決定寫一個「待辦事項管理」應用程式,來幫助自己 這一課,先實作「新增事項」的功能 --- 在上面,做一個文字輸入框,旁邊有一個「新增」按鈕 在下面,做一個展示待辦事項的清單,請使用這樣的結構,id 跟 css 之類的你可以自由決定 ``` <ul> <li> <span>倒垃圾</span> </li> <li> <span>繳電話費</span> </li> <li> <span>採買本週食材</span> </li> </ul> ``` 點擊「新增」按鈕,會將新事項插入到清單最底部 請稍微替這個清單工具加一點 css 屬性,弄得漂亮一點,這樣的工具才讓人想用 --- 除此之外,請練習一下 `console.log` 的用法,不然之後遇到錯誤,幾乎沒辦法除錯 請把作業內容中,出現的變數,隨便挑三個,用 `console.log` 印到開發者工具中,然後用瀏覽器看一下內容 --- 做出以上功能,你就完成這次的課程目標了!

給軟體工程師的各類好用工具清單

向您介紹這個開發人員工具列表。它是一個開源專案,由社群整理。 讓我們開始吧! 🚀 原文出處:https://dev.to/dostonnabotov/ultimate-tools-for-developers-2aj2 --- ## 程式碼編輯器和 IDE 💻 - [Visual Studio Code](https://code.visualstudio.com/) 作為免費的開源程式碼編輯器,Visual Studio Code 是開發人員的絕佳工具。它帶有許多功能和外掛,使其成為開發人員的絕佳選擇。 - [CodeSandbox](https://codesandbox.io/) 簡單來說,CodeSandbox 就是 Visual Studio Code 的在線版本。 - [Dev - Visual Studio Code](https://github.dev/) github.dev 基於網絡的編輯器是一種輕量級的編輯體驗,完全在您的瀏覽器中執行。 有兩種方法可以打開基於 Web 的編輯器 1. 在任何存儲庫或拉取請求上按 `.` 鍵 2. 將 URL 中的 .com 替換為 .dev。例如。 `https://github.dev/username/repo` - [CodePen](http://codepen.io/) CodePen 是網絡前端的遊樂場。這是一個試驗、測試和展示您的前端工作的地方。此外,您還可以從其他開發人員那裡找到許多靈感。 - [Replit](https://repl.it/) Replit 是一個簡單、強大且協作的在線 IDE。這是編碼、協作和託管專案的好地方。 - [StackBlitz](https://stackblitz.com/) 面向前端和全棧開發人員的 Web IDE。 - [TypeScript Playground](https://www.typescriptlang.org/play/) 想玩玩看 TypeScript 嗎? TypeScript Playground 是一個適合你的地方。 ## 圖片 🖼 - [Unsplash](https://unsplash.com/) 為您的下一個專案輕鬆找到精美、高質量的照片。所有照片均可免費用於商業和個人用途。 - [Pexels](https://www.pexels.com/) Pexels 是免費庫存照片的重要來源。所有照片均可免費用於商業和個人用途。 - [Carbon](https://carbon.now.sh/) Carbon 是一個很棒的平台,可以建立和共享程式碼的精美圖像,允許您使用語法突出顯示、主題、字體等自定義圖像。 ## 顏色 🎨 - [Color Space](https://mycolor.space/) 再也不會浪費時間尋找完美的調色板!只需輸入顏色!並生成漂亮的調色板 - [Coolors](https://coolors.co/) 建立完美的調色板或從數千種美麗的配色方案中汲取靈感。 - [Color Wheel](https://www.canva.com/colors/color-wheel/) 想知道什麼顏色搭配起來好看嗎? Canva 的色輪讓色彩組合變得簡單。 ## 排版 📝 - [Google Fonts](https://fonts.google.com/) Google Fonts 是一個包含免費授權字體系列的庫。只需選擇您想要的字體,將它們下載到您的計算機或使用提供的 CSS 或 HTML 鏈接將它們嵌入您的網頁。 - [Fontsource](https://fontsource.org/) 如果您不想從 CDN 獲取字體,則可以使用整齊打包的 NPM 包中的自託管開源字體。 - [Nerd Fonts](https://www.nerdfonts.com/) Nerd Fonts 使用大量字形(圖標)修補針對開發人員的字體。 ## 設計 🎨 - [Figma](https://www.figma.com/) 一個免費的線上協作界面設計工具和原型製作工具。 - [Pinterest](https://www.pinterest.com/) Pinterest 是一種視覺發現工具,可用於為您的所有專案和興趣尋找創意。 ## 文檔 📚 - [MDN 網絡文檔](https://developer.mozilla.org/en-US/) 學習Web開發的最佳平台。每當您對 HTML、CSS、JavaScript 或任何其他網絡技術有疑問時,請務必先查看 MDN。 - [W3Schools](https://www.w3schools.com/) 幾乎所有您能想到的程式語言的參考。 - [CSS 技巧](https://css-tricks.com/) 提供使用 CSS(層疊樣式表)的提示、技巧和技術的最佳平台之一。 ## 工具 🛠 等一會兒!工具中的工具?是的,你沒有聽錯。這裡有一些工具可以幫助您找到更多工具。 😃 - [Stack Overflow](https://stackoverflow.com/) Stack Overflow 是一個面向專業和狂熱工程師的問答網站。這是提出問題和獲得答案的好地方。 - [Micro Digital Tools](https://mdigi.tools/) 一組對開發人員有用的工具。 - [Small Dev Tools](https://smalldev.tools/) 一組對開發人員有用的工具。 - [Can I Use](https://caniuse.com/) Can I Use 提供了最新的瀏覽器支持表,以支持桌面和移動 Web 瀏覽器上的前端 Web 技術。 - [網站圖標生成器](https://favicon.io/) 您是否曾經為了替自己的網站建立網站圖標而苦惱?不同的尺寸,不同的格式,不同的平台。 從文本、圖像或從數百個表情符號中選擇快速生成您的網站圖標。 - [Font Awesome](https://fontawesome.com/) 免費和高級圖標庫。 - [OverAPI.com](https://overapi.com/) 以有組織的格式查找大量不同技術的備忘單。 - [Transform](https://transform.tools/) 多語言網絡轉換器。輕鬆將 HTML 轉換為 Pug、將 TypeScript 轉換為 JavaScript、將 Markdown 轉換為 HTML 等等... - [Frontend Tools](https://murtazajoo.github.io/tools/) 一個網站,您可以在其中找到具有更好用戶體驗的所有工具。 - [GitProtect](https://gitprotect.io/) 適用於所有 GitHub、GitLab、Bitbucket 和 Jira 資料的 DevOps 備份和災難恢復軟體。 - [Linear](https://linear.app/) 在團隊中組織問題和拉取請求的好工具。 ## 結論 🎉 希望您覺得這個列表有用。

20 個冷門、但很實用的 git 指令:值得你稍微認識一下

如果您曾經瀏覽過 [git 手冊](https://git-scm.com/docs)(或執行 `man git`),那麼您會發現 git 指令比我們每天在用的多很多。很多指令非常強大,可以讓你的生活更輕鬆(有些比較小眾,但知道一下還是不錯)。 > 這篇文章整理了我最喜歡的 20 個冷門 git 功能,您可以使用來改善您的開發流程、給您的同事留下深刻印象、幫助您回答 git 面試問題,最重要的是 - 可以玩得很開心! 原文出處:https://dev.to/lissy93/20-git-commands-you-probably-didnt-know-about-4j4o --- ## Git Web > 執行 [`git instaweb`](https://git-scm.com/docs/git-instaweb) 可以立即瀏覽 gitweb 中的工作存儲庫 Git 有一個內建的[基於網路可視化工具](https://git-scm.com/docs/gitweb) 可以瀏覽本地存儲庫,它允許您通過瀏覽器中的 GUI 查看和管理您的存儲庫。它包含許多有用的功能,包括: - 瀏覽和單步執行修訂並檢查差異、文件內容和元資料 - 可視化查看提交日誌、分支、目錄、文件歷史和附加資料 - 生成提交和存儲庫活動日誌的 RSS 或 Atom 提要 - 搜尋提交、文件、更改和差異 要打開它,只需從您的存儲庫中執行 `git instaweb`。您的瀏覽器應該會彈出並讀取 http://localhost:1234 。如果您沒有安裝 Lighttpd,您可以使用“-d”標誌指定一個備用 Web 伺服器。其他選項可以通過標誌配置(例如 `-p` 用於端口,`-b` 用於打開瀏覽器等),或在 git 配置中的 `[instaweb]` 塊下配置。 還有 `git gui` 命令,它可以打開一個基於 GUI 的 git 應用程式 ![](https://i.ibb.co/0DrmcWG/Screenshot-from-2022-12-17-20-26-30.png) --- ## Git Notes > 使用 [`git notes`](https://git-scm.com/docs/git-notes) 向提交加入額外訊息 有時您需要將其他資料附加到 git 提交(除了更改、訊息、日期時間和作者訊息之外)。 註釋存儲在 .git/refs/notes 中,由於它與提交對像資料是分開的,因此您可以隨時修改與提交關聯的註釋,而無需更改 SHA-1 哈希。 您可以使用 `git log`、使用大多數 git GUI 應用程式或使用 `git notes show` 命令查看筆記。一些 git 主機還在提交視圖中顯示註釋(儘管 [GH 不再顯示註釋](https://github.blog/2010-08-25-git-notes-display/))。 --- ## Git Bisect > 使用 [`git bisect`](https://git-scm.com/docs/git-bisect) 你可以使用二進制搜尋找到引入錯誤的提交 這是最強大又好用的 git 命令之一 - bisect 在除錯時絕對是救命稻草。開始對分後,它會為您檢查提交,然後您告訴它提交是“好”(沒有錯誤)還是“壞”(引入錯誤),這可以讓您縮小最早提交的錯誤。 請執行 `git bisect start`,然後使用 `git bisect good <commit-hash>` 向其傳遞一個已知的良好提交,並使用 `git bisect bad <optional-hash>` 傳遞一個已知的錯誤提交(預設為當前)。然後它會檢查好提交和壞提交之間的提交,然後你用 `git bisect good` 或 `git bisect bad` 指定錯誤存在與否。然後它會重複這個過程,在好與壞的中心檢查一個提交,一直到你找到引入錯誤的確切提交。隨時使用 `git bisect reset` 取消。 bisect 命令還有更多功能,包括回放、查看提交、跳過,因此下次除錯時值得查看文檔。 --- ## Git Grep > 使用 [`git grep`](https://git-scm.com/docs/git-grep) 在您的存儲庫中搜尋程式碼、文件、提交或任何其他內容 有沒有發現自己需要在 git 專案中的任何地方搜尋字串?使用 git grep,您可以輕鬆地在整個專案中和跨分支搜尋任何字串或 RegEx(例如更強大的 <kbd>Ctrl</kbd> + <kbd>F</kbd>!)。 `git grep <regexp> <ref>` 它包括大量 [選項](https://git-scm.com/docs/git-grep#_options) 來縮小搜尋範圍,或指定結果格式。例如,使用 `-l` 僅返回文件名,`-c` 指定每個文件返回的匹配數,`-e` 排除匹配條件的結果,`--and` 指定多個條件,` -n` 以行號搜尋。 由於 git grep 與正則表達式兼容,因此您可以使用搜尋的字串獲得更多進階訊息。 您還可以使用它來指定文件擴展名,例如 `git grep 'console.log' *.js`,它將顯示 JavaScript 文件中的所有 console.logs 第二個參數是一個 ref,可以是分支名稱、提交、提交範圍或其他任何內容。例如。 `git grep "foo" HEAD~1` 將搜尋之前的提交。 --- ## Git Archive > 使用 [`git archive`](https://git-scm.com/docs/git-archive) 將整個 repo 合併到一個文件中 共享或備份存儲庫時,通常首選將其存儲為單個文件。使用 git archive 將包括所有 repo 歷史記錄,因此可以輕鬆將其提取回其原始形式。該命令還包括許多附加選項,因此您可以準確自定義存檔中包含和不包含的文件。 ``` git archive --format=tar --output=./my-archive HEAD ``` --- ## Git Submodules > 使用 [`git submodule`](https://git-scm.com/docs/git-submodule) 將任何其他存儲庫拉入您的存儲庫 在 git 中,[submodules](https://git-scm.com/docs/gitsubmodules) 讓您可以將一個存儲庫掛載到另一個存儲庫中,通常用於核心依賴項或將組件拆分到單獨的存儲庫中。有關詳細訊息,請參閱[這篇文章](https://notes.aliciasykes.com/17996/quick-tip-git-submodules)。 執行以下命令會將模塊拉到指定位置,並建立一個 .gitmodules 文件,以便在複製 repo 時始終下載它。複製 repo 時使用 `--recursive` 標誌來包含子模塊。 ``` git submodule add https://github.com/<user>/<repo> <path/to/save/at> ``` 還有 [`git subtree`](https://www.atlassian.com/git/tutorials/git-subtree),它做類似的事情,但不需要元資料文件。 --- ## Git Bug Report > 使用 [`git bugreport`](https://git-scm.com/docs/git-bugreport) 編寫錯誤票,包括 git 和系統訊息 此命令將捕獲系統訊息,然後打開一個標準錯誤模板(重現步驟、實際 + 預期輸出等)。完成的文件應該是一個非常完整的錯誤報告,包含所有必要的訊息。 如果您是開源包的維護者並要求用戶(開發人員)提出錯誤報告,這將非常方便,因為它確保包含所有必要的資料。 如果您向核心 git 系統提交錯誤報告,您還可以執行 [`git diagnostic`](https://git-scm.com/docs/git-diagnose) 命令,然後提出您的問題 [這裡](https://github.com/git/git)。 --- ## Git Fsck > 使用 [`git fsck`](https://git-scm.com/docs/git-fsck) 檢查所有物件,或恢復無法存取的物件 雖然不常需要,但有時您可能必須驗證 git 存儲的物件。這就是 fsck(或文件系統檢查)發揮作用的地方,它測試對像資料庫並驗證所有物件的 SHA-1 ID 及其建立的連接。 它還可以與 `--unreachable` 標誌一起使用,以查找不再可以從任何命名引用存取的物件(因為與其他命令不同,它包括 `.git/objects` 中的所有內容)。 --- ## Git Stripspace > 使用 [`git stripspace`](https://git-scm.com/docs/git-stripspace) 格式化給定文件中的空格 最佳做法是避免在行尾尾隨空格,避免有多個連續的空行,避免輸入的開頭和結尾出現空行,並以新行結束每個文件。有很多特定於語言的工具可以自動為您執行此操作(例如 prettier),但 Git 也內置了此功能。 它用於元資料(提交訊息、標籤、分支描述等),但如果您將文件通過管道傳輸給它,然後將響應通過管道傳輸回文件,它也可以工作。例如。 `cat ./path-to-file.txt | git stripspace` 或 `git stripspace < dirty-file.txt > clean-file.txt` 您還可以使用它來刪除註釋(使用 `--strip-comments`),甚至註釋掉行(使用 `--comment-lines`)。 --- ## Git Diff > 使用 [`git diff`](https://git-scm.com/docs/git-diff) 你可以比較 2 組程式碼之間的差異 您可能知道您可以執行 `git diff` 來顯示自上次提交以來的所有更改,或者使用 `git diff <commit-sha>` 來比較 2 次提交或 1 次提交到 HEAD。但是您可以使用 diff 命令做更多的事情。 您還可以使用它來比較任意兩個任意文件,使用 `diff file-1.txt file-2.txt`(不再存取 [diffchecker.com](https://www.diffchecker.com/compare/)! ) 或者使用 `git diff branch1..branch2` 相互比較 2 個分支或引用 請注意,雙點 (`..`) 與空格相同,表示 diff 輸入應該是分支的尖端,但您也可以使用三點 (`...`) 來轉換第一個參數進入兩個差異輸入之間共享的共同祖先提交的引用 - 非常有用!如果只想跨分支比較單個文件,只需將文件名作為第三個參數傳遞。 您可能希望查看在給定日期範圍內所做的所有更改,為此使用 `git diff HEAD@{7.day.ago} HEAD@{0}`(上週),同樣可以將其與文件名、分支名稱、特定提交或任何其他參考。 還有 [`git range-diff`](https://www.git-scm.com/docs/git-range-diff) 命令,它提供了一個用於比較提交範圍的簡單界面。 git diff 工具還有更多功能(以及使用您自己的差異檢查器的選項),因此我建議查看 [文檔](https://git-scm.com/docs/git-diff#_description) . --- ## Git Hooks > 使用 [`hooks`](https://git-scm.com/docs/githooks) 在給定的 get 操作發生時執行命令或執行腳本 Hooks 可以讓你自動化幾乎所有的事情。例如:確保滿足標準(提交訊息、分支名稱、補丁大小)、程式碼質量(測試、lint)、將附加訊息附加到提交(用戶、設備、票證 ID)、呼叫 webhook 來記錄事件或執行管道等 對於大多數 git 事件,如 commit, rebase, merge, push, update, applypatch 等,都有前後 [hooks available](https://git-scm.com/docs/githooks)。 鉤子存儲在 `.git/hooks` 中(除非您使用 `git config core.hooksPath` 在其他地方配置它們),並且可以使用 [`git hook`](https://git-scm.com/docs) 進行測試/git-hook) 命令。由於它們只是 shell 文件,因此可用於執行任何命令。 掛鉤不會推送到遠程存儲庫,因此要在您的團隊中共享和管理它們,您需要使用 [掛鉤管理器](https://github.com/aitemr/awesome-git-hooks#tools) ,例如 [lefthook](https://github.com/evilmartians/lefthook) 或 [husky](https://github.com/typicode/husky)。還有幾個[3rd-party tools](https://githooks.com/#projects),這使得管理鉤子更容易,我推薦[overcommit](https://github.com/sds/overcommit)。 請記住,掛鉤總是可以跳過(使用 `--no-verify` 標誌),所以永遠不要純粹依賴掛鉤,尤其是對於任何與安全相關的事情。 --- ## Git Blame > 使用 [`git blame`](https://git-scm.com/docs/git-blame) 顯示特定修訂版和行的作者訊息 一個經典的,快速找出誰寫了特定程式碼行(也就是你的哪個同事應該為這個錯誤負責!)。但它也有助於確定在哪個時間點發生了某些更改並檢查該提交和關聯的元資料。 例如,要查看 index.rs 第 400 到 420 行的作者和提交訊息,您可以執行: ``` git blame -L 400,420 index.rs ``` --- ## Git LFS > 使用 [`git lfs`](https://git-lfs.github.com/) 存儲大文件,以免拖慢您的存儲庫 您的專案通常會包含較大的文件(例如資料庫、二進制資產、檔案或媒體文件),這會減慢 git 工作流程並使使用限制達到最大。這就是 [大型文件存儲](https://git-lfs.github.com/) 的用武之地 - 它使您能夠將這些大型資產存儲在其他地方,同時使它們可以通過 git 進行跟踪並保持相同的存取控制/權限。 LFS 的工作原理是將這些較大的文件替換為在 git 中跟踪的文本指針。 要使用它,只需執行 `git lfs track <file glob>`,這將更新您的 `.gitattributes` 文件。您可以通過擴展名(例如“*.psd”)、目錄或單獨指定文件。執行 git lfs ls-files 以查看跟踪的 LFS 文件列表。 --- ## Git GC > 使用 [`git gc`](https://git-scm.com/docs/git-gc) 優化您的存儲庫 隨著時間的推移,git repos 會積累各種類型的垃圾,這些垃圾會佔用磁盤空間並減慢操作速度。這就是內置垃圾收集器的用武之地。執行 `git gc` 將刪除孤立的和不可存取的提交(使用 [`git prune`](https://git-scm.com/docs/git-prune)),壓縮文件修訂和存儲的 git 物件,以及一些其他一般的內務管理任務,如打包引用、修剪引用日誌、尊重元資料或陳舊的工作樹和更新索引。 加入 `--aggressive` 標誌將 [積極優化](https://git-scm.com/docs/git-gc#_aggressive) 存儲庫,丟棄任何現有的增量並重新計算它們,這需要更長的時間執行但如果你有一個大型存儲庫可能需要。 --- ## Git Show > 使用 [`git show`](https://git-scm.com/docs/git-show) 輕鬆檢查任何 git 物件 以易於閱讀的形式輸出物件(blob、樹、標籤或提交)。要使用,只需執行 `git show <object>`。您可能還想附加 `--pretty` 標誌,以獲得更清晰的輸出,但還有許多其他選項可用於自定義輸出(使用 `--format`),因此此命令對於準確顯示非常強大你需要什麼。 這非常有用的一個實例是在另一個分支中預覽文件,而無需切換分支。只需執行 `git show branch:file` --- ## Git Describe > 使用 [`git describe`](https://git-scm.com/docs/git-describe) 查找可從提交中存取的最新標記,並為其指定一個人類可讀的名稱 執行 `git describe`,您將看到一個人類可讀的字串,該字串由最後一個標籤名稱與當前提交組合而成,以生成一個字串。您還可以將特定標籤傳遞給它, 請注意,您必須已建立標籤才能使其正常工作,除非您附加了 `--all` 標誌。默認情況下,Git describe 也只會使用帶註釋的標籤,因此您必須指定 `--tags` 標誌以使其也使用輕量級標籤。 --- ## Git Tag > 使用 [`git tag`](https://git-scm.com/docs/git-tag) 在你的 repo 歷史中標記一個特定點 能夠[標記](https://git-scm.com/book/en/v2/Git-Basics-Tagging) 存儲庫歷史記錄中最常用於表示發布版本的特定重要點通常很有用。建立標籤就像 `git tag <tagname>` 一樣簡單,或者您可以使用 `git tag -a v4.2.0 <commit sha>` 標記歷史提交。與提交一樣,您可以使用“-m”在標籤旁邊包含一條訊息。 不要忘記使用 `git push origin <tagname>` 將您的標籤推送到遠程。 要列出所有標籤,只需執行 `git tag`,並可選擇使用 `-l` 進行通配符搜尋。 然後,您將能夠使用 `git checkout <tagname>` 檢出特定標籤 --- ## Git Reflog > 使用 [`git reflog`](https://git-scm.com/docs/git-reflog) 列出對您的存儲庫所做的所有更新 Git 使用稱為參考日誌或“reflogs”的機制跟踪分支尖端的更新。跟踪各種事件,包括:克隆、拉取、推送、提交、檢出和合併。能夠找到事件引用通常很有用,因為許多命令都接受引用作為參數。只需執行 `git reflog` 即可查看 `HEAD` 上的最近事件。 reflog 真正有用的一件事是恢復丟失的提交。 Git 永遠不會真正丟失任何東西,即使是在重寫歷史時(比如變基或提交修改)。 Reflog 允許您返回提交,即使它們沒有被任何分支或標記引用。 默認情況下,reflog 使用 `HEAD`(您當前的分支),但您可以在任何 ref 上執行 reflog。例如 `git reflog show <branch name>`,或者使用 `git reflog stash` 查看隱藏的更改。或者使用 `git reflog show --all` 顯示所有引用 --- ## Git Log > 使用 [`git log`](https://git-scm.com/docs/git-log) 查看提交列表 您可能已經熟悉執行 `git log` 來查看當前分支上最近提交的列表。但是您可以使用 git log 做更多的事情。 使用 `git log --graph --decorate --oneline` 將顯示一個漂亮整潔的提交圖以及 ref 指針。 ![示例 git 日誌輸出](https://i.ibb.co/c1WByg8/Screenshot-from-2022-12-17-20-43-56.png) 您還經常需要能夠根據各種參數過濾日誌,其中最有用的是: - `git log --search="<anything>"` - 搜尋特定程式碼更改的日誌 - `git log --author="<pattern>"` - 只顯示特定作者的日誌 - `git log --grep="<pattern>"` - 使用搜尋詞或正則表達式過濾日誌 - `git log <since>..<until>` - 顯示兩個引用之間的所有提交 - `git log -- <file>` - 顯示僅對特定文件進行的所有提交 或者,只需執行 `git shortlog` 以獲得匯總的提交列表。 --- ## Git Cherry Pick > 使用 [`git cherry-pick`](https://git-scm.com/docs/git-cherry-pick) 通過引用選擇指定的提交並將它們附加到工作 HEAD 有時你需要從其他地方拉一個特定的提交到你當前的分支。這對於應用熱修復、撤消更改、恢復丟失的提交以及在某些團隊協作設置中非常有用。請注意,通常傳統的合併是更好的做法,因為挑選提交會導致日誌中出現重複提交。 用法很簡單,只需執行 `git cherry-pick <commit-hash>`。這會將指定的提交拉入當前分支。 --- ## Git Switch > 使用 [`git switch`](https://git-scm.com/docs/git-switch) 在分支之間移動是我們經常做的事情,`switch` 命令就像是`git checkout` 的簡化版本,它可以用來建立和在分支之間導航,但不像 checkout 在分支之間移動時不會復制修改的文件. 類似於 `checkout -b`,使用 switch 命令你可以附加 `-c` 標誌來建立一個新分支,然後直接跳入其中,例如`git switch -c <新分支>`。執行 `git switch -` 將放棄您所做的任何實驗性更改,並返回到您之前的分支。 --- ## Git Standup > 使用 [`git standup`](https://github.com/kamranahmedse/git-standup) 回憶你在最後一個工作日做了什麼,基於 git 提交 我把它放在最後,因為它不包含在大多數 git 客戶端中,但是您可以使用系統包管理器[輕鬆安裝](https://github.com/kamranahmedse/git-standup#install) ,使用 1 行 curl 腳本,或從源程式碼建置。 如果您的老闆要求您每天站立一次,以更新昨天的工作,但您永遠記不起自己到底做了什麼——這個適合您!它將顯示一個格式良好的列表,列出在給定時間範圍內完成的所有事情。用法很簡單,只需執行 `git standup`,或使用 [這些選項](https://github.com/kamranahmedse/git-standup#options) 指定應顯示哪些資料(作者、時間範圍、分支機構等)。 --- ## 結論 希望對您有幫助!

寫文件基本技巧:如何替專案寫一份優質 README 文件

為您的開源專案提供良好的文檔是一項重要(且經常被忽視)的功能,可以促進採用並展示您的軟體的全部潛力。 不幸的是,文檔的增長速度通常比程式碼慢得多,主要是因為一些看起來相當簡單的實現,會產生大量可能的用例和變化。不可能涵蓋所有可能的場景,這就是為什麼技術作家的首要工作是確定範圍和確定優先級。關注基本知識非常重要,必須首先記錄以支持最常見的用例。 在這篇文章,我將分享一些技巧,為您的專案打造一個好的 README 文件。 原文出處:https://dev.to/erikaheidi/documentation-101-creating-a-good-readme-for-your-software-project-cf8 ## README 檔案 專案的 README 文件通常是用戶與您的專案的第一次接觸,因為這是他們在訪問您的專案時首先看到的內容。這就是為什麼優先考慮將好的 README 作為專案文檔的起點很重要。 自然地,不同的專案在 README 中有不同的東西要展示,但這些技巧應該作為大多數軟體專案的良好起點。 ## 1. 少即是多 你可能想把所有東西都放在你的 README 中,比如如何安裝和使用項目,如何部署,如何調試......更詳細地涵蓋這些主題的更完整文檔的鏈接。 當您開始時,只有一個 README 就可以了。很長的 README 沒有吸引力,因為沒有目錄或菜單可供導航,因此更難找到訊息,這最終不利於良好的用戶體驗。鏈接到相關資源的較短的自述文件使專案看起來更有條理且不那麼複雜。 如果我不想為這個專案創建一個專門的文檔網站怎麼辦?那麼,在這種情況下,您可以考慮在根目錄中創建一個 `docs` 文件夾,您可以在其中保存額外的 markdown 文檔,這些文檔可以直接從您的代碼託管平台(假設是 GitHub)瀏覽。 ## 2. 將 README 拆分為其他文檔 對於存儲庫中的文檔,您可以在應用程式的根目錄中創建一個“/docs”目錄,並使用“/docs/README.md”文件作為文檔的入口點或索引。 此文件夾的內容大概就像: - installation.md - 詳細顯示如何安裝項目的文檔。 - usage.md - 項目使用情況和主要命令的更詳細視圖。 - advanced.md - 包含一些進階使用選項和用例的文檔。 這些只是一些可以作為起點的想法。如果您覺得需要創建一個包含子文件夾的更複雜的結構,您應該認真考慮為您的項目創建一個專門的文檔網站(我們將在本系列的後續文章中介紹)。 ## 3. README 文件要點 以下是您的 README 文件中絕對應該包含的一些內容: - 項目的大致概述,包括編寫它的語言、它的作用、它為什麼有用; - 安裝要求; - 安裝文檔的鏈接; - 使用概述,讓用戶簡要了解如何執行它; - 故障排除或測試提示(可以是指向單獨文檔的鏈接); - 連接到其他資源以了解更多訊息。 理想情況下,這些應該是簡短的說明,可在必要時鏈接到更全面的文檔。 根據項目的規模和目標,您應該考慮將其他內容放在那裡: - 指向 CONTRIBUTING.md 文檔的鏈接,其中包含有關用戶如何為您的專案做出貢獻的詳細訊息 - 指向包含項目行為準則的 CODE_OF_CONDUCT.md 文檔的鏈接。 如果您的項目託管在 GitHub 上,這些將顯著顯示在右側欄的項目頁面中,就在“關於”部分的正下方。 ### 示例結構 以下是 markdown 的一般結構,您可以將其用作構建專案自述文件的基礎: ``` # Project Name A paragraph containing a high-level description of the project, main features and remarks. ## Requirements Here you should give a general idea of what a user will need in order to use your library or application. List requirements and then link to another resource with detailed installation or setup instructions. - Requirement one - Another requirement Check the [installation notes]() for more details on how to install the project. ## Usage Include here a few examples of commands you can run and what they do. Finally link out to a resource to learn more (next paragraph). For more details, check the [getting started guide](). ## Useful Resources Include here any other links that are relevant for the project, such as more docs, tutorials, and demos. ``` ## 4. 使用徽章 您可以使用徽章通過自動從專案中提取的快速訊息來豐富您的 README,例如最新構建的狀態、最新的穩定版本、專案許可證等。 有不同的服務可以為開源專案提供徽章。正如@mohsin 在這篇文章的評論部分所指出的,您可以直接從 GitHub Actions 中提取徽章,方法是轉到您的工作流程頁面,單擊右上角顯示的三個點,然後從中選擇“創建狀態徽章”菜單: ![屏幕截圖顯示在哪裡可以找到菜單以從 github 操作創建狀態徽章](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpb6g6xofhwm8vctwcem.png) 這將向您顯示一個帶有降價代碼的對話框,您可以將其複製並貼上到您的 README 文件中。 另一個不錯的選擇是使用網站 [Shields.io](https://shields.io/),它提供了幾種不同的徽章,您可以在您的開源項目中免費使用。 例如,這是顯示最新版本 [yamldocs](https://github.com/erikaheidi/yamldocs) 的圖像 URL: ``` https://img.shields.io/github/v/release/erikaheidi/yamldocs?sort=semver&style=for-the-badge ``` 這會生成以下徽章,顯示最新版本: ![yamldocs 的最新穩定版本](https://img.shields.io/github/v/release/erikaheidi/yamldocs?sort=semver&style=for-the-badge) 這是帶有多個徽章的 README 示例: ![顯示來自 yamldocs 項目自述文件的徽章](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5frlxoex4fyke8ra1avf.png) ## 結論 文檔是任何軟體專案的重要組成部分,應該從一開始就認真對待。最好的開始方式是創建一個好的自述文件,向用戶顯示基本信息,而不是用他們可能不需要的內容來淹沒他們,以便開始使用您的專案。

都要 2023 年了還在用 console.log 嗎?來看看 10 個 console 進階用法!

寫 JavaScript 時,仍在使用 console.log 來除錯抓蟲? 是時候**升級你的技能**並學習 JavaScript console 物件的全部功能了。 從 console.table 到 console.time,這些進階方法和技巧將改善您 **debug 訊息的品質和可讀性**,讓您更輕鬆地故障排除和修復問題。 在 2023 年成為 JavaScript 除錯高手吧! - 原文出處:https://dev.to/naubit/why-using-just-consolelog-in-2023-is-a-big-no-no-2429 ## 😞 問題 僅使用 console.log 的最大問題之一是,它會使程式碼混亂且難以閱讀。此外,它本身的資訊量不是很大。它只是輸出你傳遞給它的任何內容的值,沒有任何上下文或其他資訊。 所以,這裡有十個你應該知道的 JavaScript console 物件方法和技巧,值得了解一下! ## 1️⃣ console.table 此方法以可讀且易於理解的格式,輸出資料表格。console.table 不是只顯示陣列或物件,而是以表格格式顯示,使它更易於瀏覽和理解。 ``` // Output an array of objects as a table const users = [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Doe' } ]; console.table(users); ``` 這將以表格格式輸出 users 陣列,每個物件的屬性作為列,物件作為行。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hek6gxo4x73b6mvmlawr.png) ## 2️⃣console.group console.group 和 console.groupEnd。這些方法可以在控制台中,創建嵌套的可摺疊資料組。**這對於組織和構建 debug 訊息非常有用**,讓您可以輕鬆查看程式碼不同級別發生的情況。 ``` console.group('User Details'); console.log('Name: John Doe'); console.log('Age: 32'); console.groupEnd(); ``` 這將在 console 中創建一個嵌套的、可摺疊的資料組,標題為“使用者詳細資訊”。組內的日誌消息將縮進並分組在一起。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ybn1uwvyltjzxcaaytx.png) ## 3️⃣console.time console.time 和 console.timeEnd 可以測量執行程式碼所需的時間。這對於識別程式中的性能瓶頸、效能優化非常有用。 ``` console.time('Fetching data'); fetch('https://reqres.in/api/users') .then(response => response.json()) .then(data => { console.timeEnd('Fetching data'); // Process the data }); ``` 這將測量「從特定 URL 獲取資料並解析 JSON 回應」所需的時間。耗用時間會在控制台中輸出。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3hdvnz2of3an1a21j3c.png) ## 4️⃣console.assert 此方法允許您在程式碼中寫 assertions,這些是應該始終為 true 的語句。如果 assertions 失敗,console.assert 將在控制台中輸出錯誤消息。**這對於除錯抓蟲、確保程式碼正常運作非常有用。 ``` function add(a, b) { return a + b; } // Test the add function const result = add(2, 3); console.assert(result === 5, 'Expected 2 + 3 = 5'); ``` 如果 add 函數在給定輸入 2 和 3 時未返回預期結果 5,就在控制台中輸出錯誤訊息。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y25c157xwhasxnaiakd5.png) ## 5️⃣設置日誌樣式 使用 `console` 物件輸出樣式和顏色。 `console` 物件**允許您輸出不同顏色和樣式的文字**,使您的除錯輸出更具可讀性和更容易理解。 您可以在 console.log 語句中使用 %c 佔位符來指定輸出文字的 CSS 樣式。 ``` console.log('%cHello world!', 'color: red; font-weight: bold;'); ``` 這將輸出文字“Hello world!”,並使用指定的 CSS 樣式,以紅色和粗體顯示。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1p82u116icnlodu046nr.png) ## 6️⃣console.trace 使用 `console.trace` 方法輸出堆疊追踪(stack trace)。這對於理解程式碼中的執行流程、識別**特定日誌消息的來源非常有用。** ``` function foo() { console.trace(); } function bar() { foo(); } bar(); ``` 這將在控制台中輸出堆疊跟踪,顯示導致 `console.trace` 呼叫的函數呼叫序列。看起來像這樣: ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zx16nee1mp0avh04msri.png) ## 7️⃣console.dir 使用 `console.dir` 方法以分層格式輸出物件的屬性。這對於**探索物件的結構**、查看其屬性和方法非常有用。 ``` const obj = { id: 1, name: 'John Doe', address: { street: '123 Main St', city: 'New York', zip: 10001 } }; console.dir(obj); ``` 這將以分層格式輸出 `obj` 物件的屬性,允許您查看物件的結構及其所有屬性和值。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihgkg5e4wxznpaqzb8jr.png) ## 8️⃣console.count 使用 `console.count` 方法計算特定日誌消息的輸出次數。這對於**追蹤特定程式碼路徑的執行次數**以及識別程式碼中的熱點很有用。 ``` function foo(x) { console.count(x); } foo('hello'); foo('world'); foo('hello'); ``` 這將在控制台中輸出字串“hello”,然後是數字 1。然後它將在控制台中輸出字串“world”,然後是數字 1。最後,它會再次輸出字符串“hello”,然後出書數字 2(因為它被呼叫了兩次)。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx7n8aargexpq1pvqj6n.png) ## 9️⃣console.clear 使用 `console.clear` 方法清除控制台輸出。這能保持您的除錯輸出**有條理和整潔**,讓您更容易專注於您感興趣的訊息。 ``` console.log('Hello world!'); console.clear(); console.log('This log message will appear after the console is cleared.'); ``` 這將輸出字串 “Hello world!”,接著一個空行(因為控制台已清除)。然後它會輸出字串 “This log message will appear after the console is cleared.” ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihzapi1gcin6gtqa600v.png) ## 1️⃣0️⃣console.profile 使用 `console.profile` 和 `console.profileEnd` 方法來衡量程式碼的性能。這對於**識別性能瓶頸和優化代碼**、提高速度和效率非常有用。 ``` console.profile('MyProfile'); // Run some code that you want to measure the performance of for (let i = 0; i < 100000; i++) { // Do something } console.profileEnd('MyProfile'); ``` 這將開始分析 `console.profile` 和 `console.profileEnd` 呼叫之間的程式碼,並在執行 `console.profileEnd` 時在控制台中輸出結果。輸出將包括有關執行所花費時間的詳細訊息、以及其他與性能相關的各種訊息。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjw25tagjwdupmqxy1d5.png) ## 💭 最後的一些想法 在 2023 年,不要僅僅滿足於 `console.log` - JavaScript 控制台物件中有許多**更強大、更有價值的工具和技術。** 從 `console.table` 到 `console.time`,這些方法和技巧將幫助您提高除錯輸出的品質和可讀性,並使其**更容易排除和修復代碼中的問題**。 所以,為什麼不在 2023 年提高您的除錯技能並嘗試這些技術呢? 以上簡單分享,希望對您有幫助!

快速複習 React 基本觀念&實務範例:推薦新手參考

React 作為一個強大的前端工具,有很多需要熟悉的基本觀念&語法。 這篇文章做一個快速的全面整理,方便工作時,可以隨時翻閱,希望您喜歡。 - 原文出處:https://dev.to/reedbarger/the-react-cheatsheet-for-2020-real-world-examples-4hgg ## 核心概念 ### 元素和 JSX - React 元素的基本語法 ``` // In a nutshell, JSX allows us to write HTML in our JS // JSX can use any valid html tags (i.e. div/span, h1-h6, form/input, etc) <div>Hello React</div> ``` - JSX 元素就是一段表達式 ``` // as an expression, JSX can be assigned to variables... const greeting = <div>Hello React</div>; const isNewToReact = true; // ... or can be displayed conditionally function sayGreeting() { if (isNewToReact) { // ... or returned from functions, etc. return greeting; // displays: Hello React } else { return <div>Hi again, React</div>; } } ``` - JSX 允許我們嵌套表達式 ``` const year = 2020; // we can insert primitive JS values in curly braces: {} const greeting = <div>Hello React in {year}</div>; // trying to insert objects will result in an error ``` - JSX 允許我們嵌套元素 ``` // to write JSX on multiple lines, wrap in parentheses: () const greeting = ( // div is the parent element <div> {/* h1 and p are child elements */} <h1>Hello!</h1> <p>Welcome to React</p> </div> ); // 'parents' and 'children' are how we describe JSX elements in relation // to one another, like we would talk about HTML elements ``` - HTML 和 JSX 的語法略有不同 ``` // Empty div is not <div></div> (HTML), but <div/> (JSX) <div/> // A single tag element like input is not <input> (HTML), but <input/> (JSX) <input name="email" /> // Attributes are written in camelcase for JSX (like JS variables <button className="submit-button">Submit</button> // not 'class' (HTML) ``` - 最基本的 React 應用程式需要三樣東西: - 1. ReactDOM.render() 渲染我們的應用程序 - 2. 一個 JSX 元素(在此情況下,稱為根節點) - 3. 一個用於掛載應用程式的 DOM 元素(通常是 index.html 文件中 id 為 root 的 div) ``` // imports needed if using NPM package; not if from CDN links import React from "react"; import ReactDOM from "react-dom"; const greeting = <h1>Hello React</h1>; // ReactDOM.render(root node, mounting point) ReactDOM.render(greeting, document.getElementById("root")); ``` ### 元件和 Props - 基本 React 元件的語法 ``` import React from "react"; // 1st component type: function component function Header() { // function components must be capitalized unlike normal JS functions // note the capitalized name here: 'Header' return <h1>Hello React</h1>; } // function components with arrow functions are also valid const Header = () => <h1>Hello React</h1>; // 2nd component type: class component // (classes are another type of function) class Header extends React.Component { // class components have more boilerplate (with extends and render method) render() { return <h1>Hello React</h1>; } } ``` - 如何使用元件 ``` // do we call these function components like normal functions? // No, to execute them and display the JSX they return... const Header = () => <h1>Hello React</h1>; // ...we use them as 'custom' JSX elements ReactDOM.render(<Header />, document.getElementById("root")); // renders: <h1>Hello React</h1> ``` - 元件可以在我們的應用程式中重複使用 ``` // for example, this Header component can be reused in any app page // this component shown for the '/' route function IndexPage() { return ( <div> <Header /> <Hero /> <Footer /> </div> ); } // shown for the '/about' route function AboutPage() { return ( <div> <Header /> <About /> <Testimonials /> <Footer /> </div> ); } ``` - 資料可以通過 props 動態傳遞給元件 ``` // What if we want to pass data to our component from a parent? // I.e. to pass a user's name to display in our Header? const username = "John"; // we add custom 'attributes' called props ReactDOM.render( <Header username={username} />, document.getElementById("root") ); // we called this prop 'username', but can use any valid JS identifier // props is the object that every component receives as an argument function Header(props) { // the props we make on the component (i.e. username) // become properties on the props object return <h1>Hello {props.username}</h1>; } ``` - Props 不可直接改變(mutate) ``` // Components must ideally be 'pure' functions. // That is, for every input, we be able to expect the same output // we cannot do the following with props: function Header(props) { // we cannot mutate the props object, we can only read from it props.username = "Doug"; return <h1>Hello {props.username}</h1>; } // But what if we want to modify a prop value that comes in? // That's where we would use state (see the useState section) ``` - 如果我們想將元素/元件作為 props 傳遞給其它元件,可以用 children props ``` // Can we accept React elements (or components) as props? // Yes, through a special property on the props object called 'children' function Layout(props) { return <div className="container">{props.children}</div>; } // The children prop is very useful for when you want the same // component (such as a Layout component) to wrap all other components: function IndexPage() { return ( <Layout> <Header /> <Hero /> <Footer /> </Layout> ); } // different page, but uses same Layout component (thanks to children prop) function AboutPage() { return ( <Layout> <About /> <Footer /> </Layout> ); } ``` - 可以用三元運算子來條件顯示元件 ``` // if-statements are fine to conditionally show , however... // ...only ternaries (seen below) allow us to insert these conditionals // in JSX, however function Header() { const isAuthenticated = checkAuth(); return ( <nav> <Logo /> {/* if isAuth is true, show AuthLinks. If false, Login */} {isAuthenticated ? <AuthLinks /> : <Login />} {/* if isAuth is true, show Greeting. If false, nothing. */} {isAuthenticated && <Greeting />} </nav> ); } ``` - Fragments 是用來顯示多個元件的特殊元件,無需向 DOM 添加額外的元素 - Fragments 適合用在條件邏輯 ``` // we can improve the logic in the previous example // if isAuthenticated is true, how do we display both AuthLinks and Greeting? function Header() { const isAuthenticated = checkAuth(); return ( <nav> <Logo /> {/* we can render both components with a fragment */} {/* fragments are very concise: <> </> */} {isAuthenticated ? ( <> <AuthLinks /> <Greeting /> </> ) : ( <Login /> )} </nav> ); } ``` ### 列表和 keys - 使用 .map() 將資料列表(陣列)轉換為元素列表 ``` const people = ["John", "Bob", "Fred"]; const peopleList = people.map(person => <p>{person}</p>); ``` - .map() 也可用來轉換為元件列表 ``` function App() { const people = ['John', 'Bob', 'Fred']; // can interpolate returned list of elements in {} return ( <ul> {/* we're passing each array element as props */} {people.map(person => <Person name={person} />} </ul> ); } function Person({ name }) { // gets 'name' prop using object destructuring return <p>this person's name is: {name}</p>; } ``` - 迭代的每個 React 元素都需要一個特殊的 `key` prop - key 對於 React 來說是必須的,以便能夠追蹤每個正在用 map 迭代的元素 - 沒有 key,當資料改變時,React 較難弄清楚元素應該如何更新 - key 應該是唯一值,才能讓 React 知道如何分辨 ``` function App() { const people = ['John', 'Bob', 'Fred']; return ( <ul> {/* keys need to be primitive values, ideally a generated id */} {people.map(person => <Person key={person} name={person} />)} </ul> ); } // If you don't have ids with your set of data or unique primitive values, // you can use the second parameter of .map() to get each elements index function App() { const people = ['John', 'Bob', 'Fred']; return ( <ul> {/* use array element index for key */} {people.map((person, i) => <Person key={i} name={person} />)} </ul> ); } ``` ### 事件和事件處理器 - React 和 HTML 中的事件略有不同 ``` // Note: most event handler functions start with 'handle' function handleToggleTheme() { // code to toggle app theme } // in html, onclick is all lowercase <button onclick="handleToggleTheme()"> Submit </button> // in JSX, onClick is camelcase, like attributes / props // we also pass a reference to the function with curly braces <button onClick={handleToggleTheme}> Submit </button> ``` - 最該先學的 React 事件是 onClick 和 onChange - onClick 處理 JSX 元素上的點擊事件(也就是按鈕動作) - onChange 處理鍵盤事件(也就是打字動作) ``` function App() { function handleChange(event) { // when passing the function to an event handler, like onChange // we get access to data about the event (an object) const inputText = event.target.value; const inputName = event.target.name; // myInput // we get the text typed in and other data from event.target } function handleSubmit() { // on click doesn't usually need event data } return ( <div> <input type="text" name="myInput" onChange={handleChange} /> <button onClick={handleSubmit}>Submit</button> </div> ); } ``` ## React Hooks ### State and useState - useState 為我們提供 function component 中的本地狀態 ``` import React from 'react'; // create state variable // syntax: const [stateVariable] = React.useState(defaultValue); function App() { const [language] = React.useState('javascript'); // we use array destructuring to declare state variable return <div>I am learning {language}</div>; } ``` - 注意:此段文章中的任何 hook 都來自 React 套件,且可以單獨導入 ``` import React, { useState } from "react"; function App() { const [language] = useState("javascript"); return <div>I am learning {language}</div>; } ``` - useState 還為我們提供了一個 `setter` 函數來更新它的狀態 ``` function App() { // the setter function is always the second destructured value const [language, setLanguage] = React.useState("python"); // the convention for the setter name is 'setStateVariable' return ( <div> {/* why use an arrow function here instead onClick={setterFn()} ? */} <button onClick={() => setLanguage("javascript")}> Change language to JS </button> {/* if not, setLanguage would be called immediately and not on click */} <p>I am now learning {language}</p> </div> ); } // note that whenever the setter function is called, the state updates, // and the App component re-renders to display the new state ``` - useState 可以在單個元件中使用一次或多次 ``` function App() { const [language, setLanguage] = React.useState("python"); const [yearsExperience, setYearsExperience] = React.useState(0); return ( <div> <button onClick={() => setLanguage("javascript")}> Change language to JS </button> <input type="number" value={yearsExperience} onChange={event => setYearsExperience(event.target.value)} /> <p>I am now learning {language}</p> <p>I have {yearsExperience} years of experience</p> </div> ); } ``` - useState 可以接受 primitive value 或物件 ``` // we have the option to organize state using whatever is the // most appropriate data type, according to the data we're tracking function App() { const [developer, setDeveloper] = React.useState({ language: "", yearsExperience: 0 }); function handleChangeYearsExperience(event) { const years = event.target.value; // we must pass in the previous state object we had with the spread operator setDeveloper({ ...developer, yearsExperience: years }); } return ( <div> {/* no need to get prev state here; we are replacing the entire object */} <button onClick={() => setDeveloper({ language: "javascript", yearsExperience: 0 }) } > Change language to JS </button> {/* we can also pass a reference to the function */} <input type="number" value={developer.yearsExperience} onChange={handleChangeYearsExperience} /> <p>I am now learning {developer.language}</p> <p>I have {developer.yearsExperience} years of experience</p> </div> ); } ``` - 如果新狀態依賴於之前的狀態,我們可以在 setter 函數中使用一個函數來為我們提供之前狀態的值 ``` function App() { const [developer, setDeveloper] = React.useState({ language: "", yearsExperience: 0, isEmployed: false }); function handleToggleEmployment(event) { // we get the previous state variable's value in the parameters // we can name 'prevState' however we like setDeveloper(prevState => { return { ...prevState, isEmployed: !prevState.isEmployed }; // it is essential to return the new state from this function }); } return ( <button onClick={handleToggleEmployment}>Toggle Employment Status</button> ); } ``` ### side effects 和 useEffect - useEffect 讓我們在 function component 中執行副作用。什麼是副作用? - 副作用是我們需要接觸外部世界的地方。例如,從 API 獲取資料或操作 DOM。 - 副作用是可能以不可預測的方式,改變我們元件狀態的操作(導致「副作用」)。 - useEffect 接受 callback function(稱為 effect 函數),預設情況下,每次重新渲染時都會運行 - useEffect 在我們的元件載入後運行,可以準確在元件的各個生命週期觸發 ``` // what does our code do? Picks a color from the colors array // and makes it the background color function App() { const [colorIndex, setColorIndex] = React.useState(0); const colors = ["blue", "green", "red", "orange"]; // we are performing a 'side effect' since we are working with an API // we are working with the DOM, a browser API outside of React useEffect(() => { document.body.style.backgroundColor = colors[colorIndex]; }); // whenever state is updated, App re-renders and useEffect runs function handleChangeIndex() { const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1; setColorIndex(next); } return <button onClick={handleChangeIndex}>Change background color</button>; } ``` - 如果不想在每次渲染後都執行 callback function,可以在第二個參數傳一個空陣列 ``` function App() { ... // now our button doesn't work no matter how many times we click it... useEffect(() => { document.body.style.backgroundColor = colors[colorIndex]; }, []); // the background color is only set once, upon mount // how do we not have the effect function run for every state update... // but still have it work whenever the button is clicked? return ( <button onClick={handleChangeIndex}> Change background color </button> ); } ``` - useEffect 讓我們能夠在 dependencies 陣列的內容改變時,才執行 - dependencies 陣列是第二個參數,如果陣列中的任何一個值發生變化,effect function 將再次運行 ``` function App() { const [colorIndex, setColorIndex] = React.useState(0); const colors = ["blue", "green", "red", "orange"]; // we add colorIndex to our dependencies array // when colorIndex changes, useEffect will execute the effect fn again useEffect(() => { document.body.style.backgroundColor = colors[colorIndex]; // when we use useEffect, we must think about what state values // we want our side effect to sync with }, [colorIndex]); function handleChangeIndex() { const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1; setColorIndex(next); } return <button onClick={handleChangeIndex}>Change background color</button>; } ``` - 可以在 useEffect 最後回傳一個函數,來取消訂閱某些效果 ``` function MouseTracker() { const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); React.useEffect(() => { // .addEventListener() sets up an active listener... window.addEventListener("mousemove", handleMouseMove); // ...so when we navigate away from this page, it needs to be // removed to stop listening. Otherwise, it will try to set // state in a component that doesn't exist (causing an error) // We unsubscribe any subscriptions / listeners w/ this 'cleanup function' return () => { window.removeEventListener("mousemove", handleMouseMove); }; }, []); function handleMouseMove(event) { setMousePosition({ x: event.pageX, y: event.pageY }); } return ( <div> <h1>The current mouse position is:</h1> <p> X: {mousePosition.x}, Y: {mousePosition.y} </p> </div> ); } // Note: we could extract the reused logic in the callbacks to // their own function, but I believe this is more readable ``` - 使用 useEffect 撈取資料 - 請注意,如果要使用更簡潔的 async/awit 語法處理 promise,則需要建立一個單獨的函數(因為 effect callback function 不能是 async) ``` const endpoint = "https://api.github.com/users/codeartistryio"; // with promises: function App() { const [user, setUser] = React.useState(null); React.useEffect(() => { // promises work in callback fetch(endpoint) .then(response => response.json()) .then(data => setUser(data)); }, []); } // with async / await syntax for promise: function App() { const [user, setUser] = React.useState(null); // cannot make useEffect callback function async React.useEffect(() => { getUser(); }, []); // instead, use async / await in separate function, then call // function back in useEffect async function getUser() { const response = await fetch("https://api.github.com/codeartistryio"); const data = await response.json(); setUser(data); } } ``` ### 效能和 useCallback - useCallback 是一個用來提高元件性能的 hook - 如果你有一個經常重新渲染的元件,useCallback 可以防止元件內的 callback function 在每次元件重新渲染時都重新創建(導致元件重新運行) - useCallback 僅在其依賴項之一改變時重新運行 ``` // in Timer, we are calculating the date and putting it in state a lot // this results in a re-render for every state update // we had a function handleIncrementCount to increment the state 'count'... function Timer() { const [time, setTime] = React.useState(); const [count, setCount] = React.useState(0); // ... but unless we wrap it in useCallback, the function is // recreated for every single re-render (bad performance hit) // useCallback hook returns a callback that isn't recreated every time const inc = React.useCallback( function handleIncrementCount() { setCount(prevCount => prevCount + 1); }, // useCallback accepts a second arg of a dependencies array like useEffect // useCallback will only run if any dependency changes (here it's 'setCount') [setCount] ); React.useEffect(() => { const timeout = setTimeout(() => { const currentTime = JSON.stringify(new Date(Date.now())); setTime(currentTime); }, 300); return () => { clearTimeout(timeout); }; }, [time]); return ( <div> <p>The current time is: {time}</p> <p>Count: {count}</p> <button onClick={inc}>+</button> </div> ); } ``` ### Memoization 和 useMemo - useMemo 和 useCallback 非常相似,都是為了提高效能。但就不是為了暫存 callback function,而是為了暫存耗費效能的運算結果 - useMemo 允許我們「記憶」已經為某些輸入進行了昂貴計算的結果(之後就不用重新計算了) - useMemo 從計算中回傳一個值,而不是 callback 函數(但可以是一個函數) ``` // useMemo is useful when we need a lot of computing resources // to perform an operation, but don't want to repeat it on each re-render function App() { // state to select a word in 'words' array below const [wordIndex, setWordIndex] = useState(0); // state for counter const [count, setCount] = useState(0); // words we'll use to calculate letter count const words = ["i", "am", "learning", "react"]; const word = words[wordIndex]; function getLetterCount(word) { // we mimic expensive calculation with a very long (unnecessary) loop let i = 0; while (i < 1000000) i++; return word.length; } // Memoize expensive function to return previous value if input was the same // only perform calculation if new word without a cached value const letterCount = React.useMemo(() => getLetterCount(word), [word]); // if calculation was done without useMemo, like so: // const letterCount = getLetterCount(word); // there would be a delay in updating the counter // we would have to wait for the expensive function to finish function handleChangeIndex() { // flip from one word in the array to the next const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1; setWordIndex(next); } return ( <div> <p> {word} has {letterCount} letters </p> <button onClick={handleChangeIndex}>Next word</button> <p>Counter: {count}</p> <button onClick={() => setCount(count + 1)}>+</button> </div> ); } ``` ### Refs 和 useRef - Refs 是所有 React 組件都可用的特殊屬性。允許我們在元件安裝時,創建針對特定元素/元件的引用 - useRef 允許我們輕鬆使用 React refs - 我們在元件開頭呼叫 useRef,並將回傳值附加到元素的 ref 屬性來引用它 - 一旦我們創建了一個引用,就可以用它來改變元素的屬性,或者可以呼叫該元素上的任何可用方法(比如用 .focus() 來聚焦) ``` function App() { const [query, setQuery] = React.useState("react hooks"); // we can pass useRef a default value // we don't need it here, so we pass in null to ref an empty object const searchInput = useRef(null); function handleClearSearch() { // current references the text input once App mounts searchInput.current.value = ""; // useRef can store basically any value in its .current property searchInput.current.focus(); } return ( <form> <input type="text" onChange={event => setQuery(event.target.value)} ref={searchInput} /> <button type="submit">Search</button> <button type="button" onClick={handleClearSearch}> Clear </button> </form> ); } ``` ## 進階 Hook ### Context 和 useContext - 在 React 中,盡量避免創建多個 props 來將資料從父元件向下傳遞兩個或多個層級 ``` // Context helps us avoid creating multiple duplicate props // This pattern is also called props drilling: function App() { // we want to pass user data down to Header const [user] = React.useState({ name: "Fred" }); return ( // first 'user' prop <Main user={user} /> ); } const Main = ({ user }) => ( <> {/* second 'user' prop */} <Header user={user} /> <div>Main app content...</div> </> ); const Header = ({ user }) => <header>Welcome, {user.name}!</header>; ``` - Context 有助於將 props 從父組件向下傳遞到多個層級的子元件 ``` // Here is the previous example rewritten with Context // First we create context, where we can pass in default values const UserContext = React.createContext(); // we call this 'UserContext' because that's what data we're passing down function App() { // we want to pass user data down to Header const [user] = React.useState({ name: "Fred" }); return ( {/* we wrap the parent component with the provider property */} {/* we pass data down the computer tree w/ value prop */} <UserContext.Provider value={user}> <Main /> </UserContext.Provider> ); } const Main = () => ( <> <Header /> <div>Main app content...</div> </> ); // we can remove the two 'user' props, we can just use consumer // to consume the data where we need it const Header = () => ( {/* we use this pattern called render props to get access to the data*/} <UserContext.Consumer> {user => <header>Welcome, {user.name}!</header>} </UserContext.Consumer> ); ``` - useContext hook 可以移除這種看起來很怪的 props 渲染模式,同時又能在各種 function component 中隨意使用 ``` const Header = () => { // we pass in the entire context object to consume it const user = React.useContext(UserContext); // and we can remove the Consumer tags return <header>Welcome, {user.name}!</header>; }; ``` ### Reducers 和 useReducer - Reducers 是簡單、可預測的(純)函數,它接受一個先前的狀態物件和一個動作物件,並回傳一個新的狀態物件。例如: ``` // let's say this reducer manages user state in our app: function reducer(state, action) { // reducers often use a switch statement to update state // in one way or another based on the action's type property switch (action.type) { // if action.type has the string 'LOGIN' on it case "LOGIN": // we get data from the payload object on action return { username: action.payload.username, isAuth: true }; case "SIGNOUT": return { username: "", isAuth: false }; default: // if no case matches, return previous state return state; } } ``` - Reducers 是一種強大的狀態管理模式,用於流行的 Redux 狀態管理套件(這套很常與 React 一起使用) - 與 useState(用於本地元件狀態)相比,Reducers 可以通過 useReducer hook 在 React 中使用,以便管理我們應用程序的狀態 - useReducer 可以與 useContext 配對來管理資料並輕鬆地將資料傳遞給元件 - useReducer + useContext 可以當成應用程式的整套狀態管理系統 ``` const initialState = { username: "", isAuth: false }; function reducer(state, action) { switch (action.type) { case "LOGIN": return { username: action.payload.username, isAuth: true }; case "SIGNOUT": // could also spread in initialState here return { username: "", isAuth: false }; default: return state; } } function App() { // useReducer requires a reducer function to use and an initialState const [state, dispatch] = useReducer(reducer, initialState); // we get the current result of the reducer on 'state' // we use dispatch to 'dispatch' actions, to run our reducer // with the data it needs (the action object) function handleLogin() { dispatch({ type: "LOGIN", payload: { username: "Ted" } }); } function handleSignout() { dispatch({ type: "SIGNOUT" }); } return ( <> Current user: {state.username}, isAuthenticated: {state.isAuth} <button onClick={handleLogin}>Login</button> <button onClick={handleSignout}>Signout</button> </> ); } ``` ### 編寫 custom hooks - 創建 hook 就能輕鬆在元件之間重用某些行為 - hook 是一種比以前的 class component 更容易理解的模式,例如 higher-order components 或者 render props - 根據需要,隨時可以自創一些 hook ``` // here's a custom hook that is used to fetch data from an API function useAPI(endpoint) { const [value, setValue] = React.useState([]); React.useEffect(() => { getData(); }, []); async function getData() { const response = await fetch(endpoint); const data = await response.json(); setValue(data); }; return value; }; // this is a working example! try it yourself (i.e. in codesandbox.io) function App() { const todos = useAPI("https://todos-dsequjaojf.now.sh/todos"); return ( <ul> {todos.map(todo => <li key={todo.id}>{todo.text}</li>)} </ul> ); } ``` ### Hooks 規則 - 使用 React hooks 有兩個核心規則,要遵守才能正常運作: - 1. Hooks 只能在元件開頭呼叫 - Hooks 不能放在條件、迴圈或嵌套函數中 - 2. Hooks只能在 function component 內部使用 - Hooks 不能放在普通的 JavaScript 函數或 class component 中 ``` function checkAuth() { // Rule 2 Violated! Hooks cannot be used in normal functions, only components React.useEffect(() => { getUser(); }, []); } function App() { // this is the only validly executed hook in this component const [user, setUser] = React.useState(null); // Rule 1 violated! Hooks cannot be used within conditionals (or loops) if (!user) { React.useEffect(() => { setUser({ isAuth: false }); // if you want to conditionally execute an effect, use the // dependencies array for useEffect }, []); } checkAuth(); // Rule 1 violated! Hooks cannot be used in nested functions return <div onClick={() => React.useMemo(() => doStuff(), [])}>Our app</div>; } ``` --- 以上是快速整理,希望對您有幫助!

在 JavaScript 新版 ES2020 之中,值得留意的 10 個新功能

ES2020 已經上線一段時間了,但是滿多新功能都大家不太知道。這篇文章跟大家介紹一下! - 原文出處:https://dev.to/worldindev/10-new-javascript-features-in-es2020-that-you-should-know-3ohf # 1. BigInt BigInt 是最受期待的新功能之一,允許工程師處理資料時,能存一個更大的整數。 目前在 JavaScript 中可以儲存的最大整數是 `pow(2, 53) - 1`。BigInt 讓你可以存更大的數字。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/588khrk3rpei76gho4e9.png) 如上所示,需要在後面加上一個 `n`。這個 `n` 表示這是一個 BigInt ,讓 JavaScript 引擎(v8 引擎)能夠特別處理。 這個功能不向後兼容,因為傳統的數字系統是 IEEE754(不能支援這種大小的數字)。 # 2.Dynamic import 動態導入讓你可在程式碼中,有條件地動態導入模組。跟你現在用 Webpack 和 Babel 做的事一樣。 此功能讓你可以按需求讀取程式碼,也就是所謂的 code splitting。讓你不再需要 webpack 或其他模組打包工具就能做到。您還可以在 if-else 中有條件地載入程式碼。這有個額外好處,就是不會污染全域命名空間。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/utai4m6xcc1nk3aooqxf.png) # 3. Nullish Coalescing Nullish Coalescing 可以確實檢查 nullish 而不是 falsy 值。什麼是 nullish 與 falsy 值? 在 JavaScript 中,許多值都是 `falsy`,例如空字串、數字 0、`undefined`、`null`、`false`、`NaN` 等等。 但有時候你想要檢查 undefined 跟 null。這種情況下,就可以用新的運算子 `??` ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uupnryjugtdtb6xvv7yk.png) 如圖, **OR** 運算子始終回傳 truthy 值,而 ?? 運算子回傳 non-nullish 值。 # 4. Optional Chaining Optional Chaining 語法讓你可以取得深度嵌套的物件屬性,而不用擔心屬性不存在。如果找不到屬性,會回傳 undefined,而不會報錯壞掉。物件屬性可用,呼叫函數跟陣列也可以用。超方便!請參考: ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4w5zkv64r0cs46xpjie.png) # 5. Promise.allSettled `Promise.allSettled` 方法接受一個 Promise 陣列,並且只有在所有 Promise 都完成時才 resolve 或 reject。 以前只能用 `race` 和 `all` 做出類似效果。 ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xi1hpkcazxgxsl81d90.png) # 6. String#matchAll `matchAll` 是添加到 `String` 原型的新方法,與正則表達式相關。這將回傳一個迭代器,該迭代器依次返回所有匹配的組。舉例: ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmmk478r36d1bfjovi1v.png) # 7. globalThis 如果你是寫跨平台的 JS 程式碼,可以在 Node 上運行、在瀏覽器運行、也可以在 web-workers 中運行,你很難操作全域物件。 因為對於瀏覽器來說它是 window ,對於 Node 來說是 global ,對於 web-workers 來說是 self。如果有其它的運行環境,它們的全局物件也都不同。 變成你需要自己寫一段來檢測環境。所以 ES2020 實作了 globalThis,無論您在哪裡執行,它始終引用全域物件: ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u5dczfi8moefwl4y1jo6.png) # 8. 模組命名空間匯出 在 JavaScript 模組中,可以使用以下語法: ``` import * as utils from './utils.mjs' ``` ES2020 新增了相對的匯出語法: ``` export * as utils from './utils.mjs' ``` 這等同於以下內容: ``` import * as utils from './utils.mjs' export { utils } ``` # 9. 定義好 for-in 順序 雖然大部份瀏覽器都實做了一樣的順序,但 ECMA 規範其實沒有指定 for (x in y) 的運行順序。在 ES2020 中正式標準化了。 # 10. import.meta `import.meta` 物件帶有一個 `null` 原型。 考慮以下模組,`module.js`: ``` <script type="module" src="module.js"></script> ``` 您可以使用 import.meta 物件取得有關模組的元信息: ``` console.log(import.meta); // { url: "file:///home/user/module.js" } ``` 對於 external scripts,它代表存取腳本的 URL,對於 inline scripts,它代表檔案的 URL。 --- 以上簡單分享,希望對你有幫助!

10 個現代 JavaScript 中的好用語法:新手推薦

在 ES6 版本之後,Javascript 多了很多好用的語法,建議新手一定要學起來這幾招。 - 原文出處:https://dev.to/azure/modern-javascript-10-things-you-should-be-using-starting-today-1adm # -1- 展開運算子 Spred operator 在物件或陣列前面寫 ... 可以很輕鬆地把資料展開,舉例來說: ## Spread array ``` let firstHalf = [ 'one', 'two']; let secondHalf = ['three', 'four', ...firstHalf]; ``` 寫起來非常簡潔。不然原本要這樣寫: ## NO Array spread ``` let firstHalf = [ 'one', 'two']; let secondHalf = ['three', 'four']; for(var i=0, i <firstHalf.length; i++ ) { secondHalf.push(firstHalf[i]); } ``` 用在物件也可以,能夠合併屬性: ## Spread object ``` const hero = { name: 'Xena - Warrior Princess', realName: 'Lucy Lawless' } const heroWithSword = { ...hero, weapon: 'sword' } ``` 不然的話,就要用迴圈來跑全部屬性: ## NO Object spread ``` let keys = Object.keys(hero); let obj = {}; for(var i=0; i< keys.length; i++) { obj[keys[i]] = keys[props[i]]; } ``` 舊寫法其實有 Object.assign() 可以用,看起來會像: ``` const heroWithSword = Object.assign({}, hero, {weapon:"sword"}) ``` 但展開運算子還是簡潔多了: ``` const heroWithSword = { ...hero, weapon: 'sword' } ``` # -2- 其餘參數 Rest parameter 其餘參數能夠將剩下的參數蒐集到一個陣列中。JavaScript 有足夠彈性可以處理這些參數。通常會這樣寫: ``` function add(first, second, ...remaining) { return first + second; } ``` 以上只是加總前兩個參數,也就是 add(1,2) 或 add(1,2,3, 4) 結果一樣。接著這樣改寫: ``` function add(first, second, ...remaining) { return first + second + remaining.reduce((acc, curr) => acc + curr, 0); } ``` 這樣就會把全部參數都加總在一起了。 如您所見,前面加上 ... 就可以搜集其餘參數。這是一種命名變數的方式而已,比較少人知道這招,但可以知道一下。 # -3- 字串插值 String interpolation 看過這種寫法嗎? ``` class Product { constructor(name, description, price) { this.name = name; this.description = description; this.price = price; } getDescription() { return " Full description \n" + " name: " + this.name + " description: " + this.description } } ``` 看看那個 getDescription() 方法,又長、跨行、又難讀。在大多數程式語言都只能這樣寫。幸好,有些程式語言支援字串插值,包括 JavaScript,所以可以改寫如下: ``` getDescription() { return `Full description \n: name: ${this.name} description ${this.description} `; } ``` 反引號 ` 可以定義多行字串。再使用 ${} 進行插值。是不是好多了呢:) # -4- 屬性縮寫 Shorthand properties 雖然還沒搞懂,你可能早就在用了。在 ES5 需要這樣寫: ``` function createCoord(x, y) { return { x: x, y: y } } ``` 在 ES6 之後,如果 : 後面名稱一樣,可以直接省略,像這樣: ``` function createCoord(x, y) { return { x, y } } ``` 簡潔多了吧? # -5- 方法屬性 Method properties 在物件裡面指向方法的寫法。以下是 ES5 寫法: ``` const math = { add: function(a,b) { return a + b; }, sub: function(a,b) { return a - b; }, multiply: function(a,b) { return a * b; } } ``` 在 ES6 之後可以少寫一大堆,像這樣: ``` const math = { add(a,b) { return a + b; }, sub(a,b) { return a - b; }, multiply(a,b) { return a * b; } } ``` # -6- 解構 Destructuring 解構用得好,有益身心健康。 ## Object destructuring 這段: ``` function handle(req, res) { const name = req.body.name; const description = req.body.description; const url = req.url; log('url endpoint', url); // lots of logic happening dbService.createPerson( name, description ) } ``` 有點難讀吧,但要從多層深度挖資料,就會寫成這樣。還能怎麼辦?其實有以下妙招喔: ``` function handle(req, res) { const { body: { name, description }, url }, = req; log('url endpoint', url); // lots of logic happening dbService.createPerson( name, description ) ``` 變成俐落的一行了。 ## Array destructuring 不只物件能用,陣列也可以用,像這段: ``` const array = [1,2,3,4,5,6]; const a = array[0]; const c = array[2]; ``` 可以更優雅地改寫成這樣: ``` const array = [1,2,3,4,5,6]; const [a, ,c, ...remaining] = array; // remaining = [4,5,6] ``` 上面的模式配對,就可以拆出我們需要的變數。如果要跳過某值,就寫 , , 即可。我另外多寫了一個 rest parameter 來取得其餘資料。 ## Parameter matching 函數的參數也能這樣寫。當函數有超過 2-3 個參數時,業界慣例常常是這樣寫: ``` function doSomething(config) { if(config.a) { ... } if(config.b) { ... } if(config.c) { ... } } ``` 更漂亮的寫法其實是: ``` function doSomething({ a, b, c }) { if(a) { ... } if(b) { ... } if(c) { ... } } ``` # -7- 陣列方法 ES6 有大量好用的陣列方法,例如: - find(),回傳符合條件的項目,否則回傳 null - findIndex(),尋找項目的索引 - some(), 確認是否包含符合條件的項目 - includes(), 確認是否包含某個項目 舉例說明如下: ``` const array = [{ id: 1, checked: true }, { id: 2 }]; arr.find(item => item.id === 2) // { id: 2 } arr.findIndex(item => item.id === 2) // 1 arr.some(item => item.checked) // true const numberArray = [1,2,3,4]; numberArray.includes(2) // true ``` # -8- Promises + Async/Await 在只有 callback 能寫的年代,這種寫法很常見: ``` function doSomething(cb) { setTimeout(() => { cb('done') }, 3000) } doSomething((arg) => { console.log('done here', arg); }) ``` 用來寫非同步任務時,就會這樣寫。但現在我們有 promise 能寫了,所以可以這樣: ``` function doSomething() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('done') }, 3000) }) } doSomething().then(arg => { console.log('done here', arg); }) ``` 多個流程還能整個串起來,就像這樣: ``` getUser() .then(getOrderByUser) .then(getOrderItemsByOrder) .then(orderItems => { // do something with order items }) ``` ## Async/await 接著還有 async/await 可以用。上方的範例,相關 promise 變成: ``` async function getItems() { try { const user = await getUser(); const order = await getOrderByUser(user); const items = await getOrderItemsByOrder(order); return items; } catch(err) { // handle error here, the suggestion to return something or rethrow } } getItems().then(items => { // do something with order items }) ``` 看起來像是同步,但其實非同步的一段程式碼:) # -9- 模塊 Modules 幾乎所有程式語言都支援模塊功能。將程式碼分為多個檔案,各自為獨立單元,就是模塊了。像這樣: ``` // math.js export function add(a,b) { return a + b; } export function sub(a,b) { return a - b; } export default (a,b) => a * b; // main.js import mult, { add, sub } from './math'; mult(2, 4) // 8 add(1,1) // 2 sub(1,2) // -1 ``` 使用 export 關鍵字來表示 add 跟 sub 是公開可用的。export default 代表不指定函數時直接導入的內容。在 main.js 中,將預設導入命名為 mult,然後指定導入函數 add 和 sub。 # -10- 箭頭函數 + this 整篇文章一直在用箭頭函數,其實只是另一種函數的寫法而已。以前要這樣寫: ``` function printArray(arr) { // do something } ``` 現在可以這樣寫: ``` const printArray = (arr) => { // do something } ``` ## 單行函數 函數還可以用一行寫完: ``` const add = (a,b) => a + b ``` 這種寫法可以省略 return 直接回傳結果。要回傳物件也可以,這樣即可: ``` const create = (a,b) = > ({ x: a, y: b }) ``` ## Lexical this 以前常常會搞混 this 代表什麼,舉例來說: ``` let array = [1,2,3]; function sum() { this.total = 0; arr.forEach(function(item) { this.total+= item; // `this` is the inner functions `this`, BAD }) return total; } ``` 這個例子中,this 在 forEach 指錯地方了。以前的解法是: ``` function sum() { this.total = 0; var self = this; arr.forEach(function(item) { self.total+= item; // now we are using `self`, it solves it but feels hacky }) return total; } ``` 雖然多寫個 self 變數可以解決,但實在很鳥。新的箭頭函數寫法則解決了這問題: ``` function sum() { this.total = 0; arr.forEach((item) => { this.total+= item; // all is well `this` points to outer function }) return total; } ``` # 結論 ES6 之後有很多好東西可以用,今天先介紹一些,希望對大家有幫助!