🔍 搜尋結果:網站

🔍 搜尋結果:網站

2024 年您必須嘗試的 25 個 Web 開發專案

毫無疑問,掌握 Web 開發最有效的方法之一就是透過實作。雖然學習理論概念很重要,但將您的知識應用到現實世界的專案中才能真正鞏固您的技能。無論您是想要打下堅實基礎的初學者,還是尋求新挑戰的經驗豐富的開發人員,這裡有 25 個 Web 開發專案可以提高您的能力。 ### 學生成績管理系統 學生成績管理系統旨在為學生和大學提供一種快速且用戶友好的方式來存取和管理考試成績。學生可以登入查看他們的成績,新生可以選擇註冊。該系統旨在以易於理解的方式呈現結果。 **如何做:** 掌握前端、後端和資料庫程式設計的基礎知識後,從建立全端應用程式開始。利用HTML、CSS、JavaScript、PHP和MySQL實現使用者認證、結果顯示和註冊功能。 ![學生成績管理系統](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4y9a2bm1exqbu34lsxph.png) ### 線上程式碼編輯器(React) 該專案涉及使用 React 建立線上程式碼編輯器,允許使用者用各種程式語言編寫和執行程式碼。目標是建立一個用戶可以無縫編輯和測試原始程式碼的平台。 **如何做:** 先使用 HTML、CSS 和 React 進行前端工作。實現程式碼輸入、執行和結果顯示功能。專注於建立用戶友好的介面,以獲得流暢的程式碼編輯體驗。 ![線上程式碼編輯器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06jjz3v5sq7c6apc41d7.png) ### 使用 React 進行 Amazon 克隆 Amazon Clone 專案圍繞著使用 React 建立 Amazon 線上商店的工作副本。該專案將幫助您了解有效的電子商務網站所需的元件並將它們應用到您的應用程式中。 **如何做:** 從 HTML、CSS 和 JavaScript 開始。使用 React 建立電子商務網站的不同部分,例如產品清單、購物車和結帳流程。整合動態資料並增強使用者介面。 ![使用 React 的 Amazon 克隆](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pq55ml71nqntt4j07p1z.png) ### 客戶關係經理 客戶關係管理器專案涉及建立一個後端 Web 應用程式,該應用程式允許建立、讀取、更新和刪除 (CRUD) 客戶資料。這是了解後端 Web 開發的基礎專案。 **如何做:** 利用 Node.js、Express.js 和 MongoDB 等技術來建立後端基礎架構。實施 CRUD 操作來管理客戶資料。開發一個用戶友好的介面,用於與客戶資料庫互動。 ![顧客關係經理](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jfr25p8b2gzapen1l8ry.png) ### 排序展示台 排序可視化器專案旨在提供各種排序演算法的可視化表示。使用者可以觀察不同的演算法如何執行,並更深入地了解 JavaScript 的基本概念。 **具體操作方法:** 使用 HTML、CSS、Bootstrap 和 JavaScript 建立 Web 應用程式。實現冒泡排序、合併排序和快速排序等排序演算法的視覺化。允許用戶與視覺化進行交互,以增強他們的學習體驗。 ![排序視覺化工具](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51wpf82samc6k9ggftgc.png) ### 多人遊戲 – Connect4 多人遊戲 – Connect4 專案專注於建立具有多人遊戲功能的著名 Connect4 遊戲。它提供了學習一些重要的網路和遊戲設計基礎知識的機會。 **如何做:** 如果您想知道多人遊戲是如何開發的,或者您曾經想為週末製作一款遊戲,那麼這個專案適合您。使用 PyGame、Sockets 和遊戲編程為您和您的朋友建立多人 Connect4 遊戲。 ![多人遊戲 – Connect4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ktsz3ky4pj27b9q7mynm.png) ### YouTube 腳本摘要器 投入時間觀看可能比預期更長的電影變得相當具有挑戰性。有時,如果我們不能從他們那裡收集有用的訊息,我們的努力可能會徒勞無功。透過自動總結影片的文字記錄,我們可以輕鬆地發現這些影片中的關鍵主題,這節省了我們再次觀看整個影片的時間和精力。 **如何做到這一點:** 人們每天都會觀看 YouTube 影片,這些影片可以是指導性的、紀錄片的或任何其他持續時間較長的類型;考慮透過提供摘要資訊可以節省多少時間。該專案將是一個 chrome 擴展,它將向後端的 Rest API 發送請求,該 API 將向您發送 YouTube 腳本的摘要。 ![YouTube 腳本摘要](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4kw614tpyvyrumjc1g8.png) ### OurApp – NodeJS 中的社交媒體 Web 應用程式 現實應用程式 OurApp 的用戶可以進行交流、相互關注以及發布簡短的推文。掌握 HTML、CSS 和 JS 後,專案最適合想要使用 Nodejs 和 MongoDB 深入研究完整堆疊的人。 **怎麼做:** 你想成為能夠超越 HTML、CSS 和 JS 的全端開發人員嗎?建立這個完整的堆疊應用程式,以了解如何使用 NodeJS、MongoDB 和其他技術來建立現代、快速且可擴展的伺服器端 Web 應用程式。如果您想在磨練 NodeJS 技能的同時開發一些有趣的東西,那麼這個專案就是適合您的。您還可以免費註冊全端 Web 開發課程,這將幫助您成為您所在領域的傑出開發人員。 ![OurApp – NodeJS 中的社交媒體 Web 應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02qifmojncuvx27k6hyu.png) ### Codechef 通知 CodeChef 經常遇到伺服器過載問題,導致評審難以快速提供提交結果。留給編碼人員的唯一選擇是在一段時間後不斷檢查站點,看看結果是否存在。透過這個專案,我們希望消除審查提交頁面以確定提交結果的額外步驟。我們將自動執行檢索結果的過程並在準備好後立即通知使用者。 **如何操作:** Codechef 是一個流行的編碼實踐平台,但伺服器過載可能會導致結果延遲。此附加元件旨在透過自動化獲取結果並及時通知用戶的流程來節省時間。使用網路抓取或 API 來收集結果資訊並實作通知系統。 ![Codechef 通知程序](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4k7ru3ff3h7tgne8qpoi.png) ### 使用 Dash 來視覺化和預測股票 該專案涉及使用 Dash(一種用於建立分析 Web 應用程式的 Python 框架)來視覺化和預測股票資料。它提供了將資料視覺化和機器學習概念應用於金融資料的機會。 **如何操作:** 如果您對股票市場感興趣,該專案將簡化股票資料的可視化。利用Python、Dash和相關函式庫進行資料視覺化。實施基於歷史資料預測股票趨勢的功能。 ![使用 Dash 視覺化和預測股票](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovs08g50etufj84gz6fd.png) ### 線上程式碼編輯器 (JQuery) 線上程式碼編輯器可透過瀏覽器存取,並位於遠端伺服器上。儘管一些線上程式碼編輯器更像是完整的 IDE,但其他編輯器更像是具有語法突出顯示或程式碼完成等基本功能的文字編輯器。 **如何操作:** 使用 HTML、CSS、JavaScript 和 JQuery 建立線上程式碼編輯器。專注於透過語法突出顯示和程式碼完成等功能來增強使用者體驗。確保程式碼輸入和執行順利。 ![線上程式碼編輯器 (JQuery)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1vxgxvbtf1rh578lauc.png) ### 模糊URL FuzzyURLs 涉及使用 Django(Python 的高級 Web 框架)建立 URL 縮短服務。它提供了建立 Web 應用程式的實務經驗,特別關注 URL 操作。 **如何做:** 從頭開始開發一個基於 Django 的 URL 縮短器。了解 URL 縮短服務的工作流程並實現建立、管理和重新導向短 URL 的功能。 ![FuzzyURLs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5uouta53v2suc8m27i14.png) ### 使用 React 進行 Slack 克隆 該專案旨在使用 React 建立 Slack 克隆,提供即時訊息和協作的平台。這是一個中高階專案,強調React-Redux和Firebase。 **如何做:** 應用 React-Redux 原理來建立一個類似 Slack 的 Web 訊息服務。利用 Firebase 實現即時資料庫功能。專注於建立響應靈敏且功能豐富的訊息傳遞應用程式。 ![使用 React 的 Slack 克隆](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kugsfjeblq934ceomlhs.png) ### Web 應用程式的 Node.js 驗證 了解使用 Node.js 為 Web 應用程式建立驗證系統。探索各種身份驗證技術,評估其優點和缺點,並實施改進。 **如何做:** 對於那些想要深入研究 Node.js 並了解建立安全身份驗證系統的複雜性的人來說,這個專案非常適合。實施使用者身份驗證、會話管理並探索增強安全性的方法。 ![Web 應用程式的 Node.js 驗證](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fcet0kd7abf6xu57eh8.png) ### TinyMCE 同義詞插件 為 TinyMCE 富文本編輯器建立一個插件,用於搜尋單字的同義詞並允許使用者將它們插入編輯器中。 **如何做到這一點:** 為 TinyMCE 開發一個自訂插件,整合一項功能,使用戶能夠搜尋同義詞並輕鬆地將它們插入到富文本編輯器中。了解 TinyMCE API 以實現無縫整合。 ![TinyMCE 同義詞外掛](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmr8y3x21l9g7vuwz5mi.png) ### 迷宮裡的老鼠 建立一個基本的 React Web 應用程式,顯示老鼠從帶有預設障礙的方形迷宮的左上角到右下角可以採取的所有可能路徑。 **具體操作方法:** 建立一個簡單的 React Web 應用程式來直觀地呈現經典的 Rat in the Maze 謎題。實施功能來展示老鼠穿過迷宮的所有潛在路徑。 ![迷宮中的老鼠](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdcwf4cioua8fogrsp96.png) ### 簡歷產生器 Web 應用程式 使用 ReactJS 和 NodeJS 建立一個用於建立履歷的 Web 應用程式。該專案將指導您完成建立簡歷產生器的步驟,並提供一種實用的方法來支援個人製作簡歷。 **如何做:** 深入研究 ReactJS 和 NodeJS 來開發一個用戶友好的簡歷產生器。實施加入個人詳細資訊、教育背景、工作經驗和技能的功能。專注於建立動態且可自訂的履歷模板。 ![簡歷產生器 Web 應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ntwflc67s9kwi00plrti.png) ### Markdown 編輯器 建立一個 Markdown 編輯器,讓使用者編寫 Markdown 並預覽渲染的 HTML。 Markdown 是一種基於網路的文字格式化系統,廣泛用於部落格文章、文件等。 **如何做:** 使用 HTML、CSS 和 JavaScript 開發 Markdown 編輯器。使用戶能夠編寫 Markdown 程式碼並查看渲染的 HTML 的即時預覽。使用粗體文字、圖像和清單等功能增強編輯器。 ![Markdown 編輯器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppv6gl6pzq7uetmkax5b.png) ### 450 DSA 追蹤器 450 DSA Tracker 旨在協助使用者追蹤解決 450 道資料結構和演算法問題的進度。它利用 TypeScript、React.js 的 reducer 和 context API 以及即時瀏覽器 IndexedDB 來快取資訊。 **具體操作方法:** 使用 TypeScript 和 React.js 實作 Web 應用程式,以追蹤解決資料結構和演算法問題的進度。利用reducer 和context API 進行狀態管理,並利用IndexedDB 進行即時瀏覽器快取。 ![450 DSA 追蹤器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijhv2y4gx3f1l2ub7xnz.png) ### 待辦事項網頁應用程式 使用 Node.js 框架 Adonis.js 建立待辦事項 Web 應用程式。了解 HTTP、REST API 和 CRUD 操作,同時建立用於管理待辦事項清單的後端 API。 **如何操作:** 使用 Adonis.js 為待辦事項 Web 應用程式建立 CRUD API。使用 Postman 測試 API 並建立用於新增、更新和刪除任務的後端功能。獲得使用後端框架的經驗。 ![待辦事項 Web 應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3rajw8titifncb6la3e6.png) ### 兩個真相和一個謊言遊戲 Slack 機器人 開發一個 Slack 機器人,用於在 Slack 工作區中玩「兩個真相和一個謊言」遊戲。該專案利用 JavaScript 和 Node.js 為工作區成員建立一個有趣的互動式遊戲。 **如何做:** 建立一個 Slack 機器人,以促進「兩個真相和一個謊言」遊戲。使用 JavaScript 和 Node.js 處理 Slack 工作區中的互動。實現用戶共享陳述並猜測哪一個是錯誤的功能。 ![兩個真相與一個謊言遊戲 Slack 機器人](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwocawujfx0219s6ho5c.png) ### 使用 Chromakey(綠幕)效果進行即時視訊處理 探索視訊處理中使用的色度(綠幕)效果。使用 HTML、CSS 和 JavaScript 建立 Web 應用程式,以背景影片或圖像取代綠幕。 **具體操作方法:** 開發一個處理即時視訊並應用色鍵效果的 Web 應用程式。使用 HTML、CSS 和 JavaScript 操作影片幀並用背景影片或圖像替換綠幕。 ![使用 Chromakey(綠幕)效果進行即時視訊處理](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b4yc5aauy13cthc6b2b3.png) ### WhatsApp 網頁克隆 使用 React 和 Firebase 開發具有即時訊息功能的 WhatsApp Web 克隆。 **如何操作:** 使用 React 建立使用者介面並使用 Firebase 實現即時資料庫功能,打造流暢的訊息體驗。 ![WhatsApp 網路克隆](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggf6mpl4bssvzjrr97b3.png) ### WhatsApp 上的電子郵件提醒 設定新電子郵件的 WhatsApp 提醒以簡化電子郵件管理和通知。 **如何操作:** 使用自動化平台 Twilio 建立一個工具,從收件匣中獲取詳細資訊並在 WhatsApp 上發送警報。 ![WhatsApp 上的電子郵件提醒](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zxhacxpysrlx2d78sb5.png) ### 天氣預報應用程式 使用 Streamlit 函式庫和 OpenWeatherMap API 為天氣預報應用程式建立響應式前端。 **如何操作:** 利用 Python 和 Streamlit 可視化天氣資料並與 OpenWeatherMap API 互動以獲取即時天氣資訊。 ![天氣預報應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwm38a5vw8sj2iu17y57.png) ### 總結 這個 Web 開發專案的彙編提供了各種各樣的挑戰,使您能夠提高跨不同技術和概念的技能。無論您對全端開發、資料視覺化、遊戲設計還是自動化感興趣,這些專案都可以提供有價值的幫助 --- 原文出處:https://dev.to/mukeshkuiry/25-web-development-projects-you-must-work-on-2024-4onl

2024 年掌握前端開發的 9 個專案點子

想要 **在 2024 年掌握前端 Web 開發?** 無論您是渴望開始前端開發之旅還是希望提升您的技能,我們都能滿足您的需求。 這裡有 9 個令人興奮的專案,它們將使您能夠在 2024 年掌握前端開發。為了加速您的學習過程,每個專案都配有專用資源。 讓我們深入研究並將您的編碼願望變成現實! 🚀 ### 1. 說故事作品集網站 建立一個引人入勝的說故事作品集網站來展示您的技能和專案。使用動畫和過渡來吸引訪客。 **您將學到什麼:** 該專案將提高您建立個人作品集、將動畫與 CSS 和 JavaScript 結合的能力。您還將透過網頁設計深入了解如何有效地講述故事。 **技術堆疊:** HTML、CSS、JavaScript、GSAP(GreenSock 動畫平台)。 **資源:** [說故事作品集教學](https://youtu.be/0fYi8SGA20k?si=lgzXlqL7m2aK3nzg) ![說故事組合](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2smmhfqq2g7qx1yune1.png) ### 2. 電影搜尋應用程式 使用 React Hooks 建立電影搜尋應用程式。用戶可以搜尋電影、查看詳細資訊並發現新上映的電影。 **您將學到什麼:** 透過這個專案,您將掌握使用 React Hooks 進行狀態管理、非同步資料擷取和響應式設計。此外,您還將深入了解如何處理使用者輸入和管理複雜的資料結構。 **技術堆疊:** React Hooks、Axios、CSS(flexbox/grid)和電影資料庫(IMDB)API。 **資源:** [電影搜尋應用程式教學](https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/) ![電影搜尋應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6wtwna67o253uusbpoj9.png) ### 3. 任務管理應用程式 開發具有任務建立、完成追蹤和類別組織等功能的任務管理應用程式。 **您將學到什麼:** 該專案將加深您對 React 中的狀態管理、CRUD 操作以及用於任務管理的使用者友好介面的實現的理解。 **技術堆疊:** React、useState、useEffect、localStorage、CSS。 **資源:** [任務管理應用教學](https://youtu.be/m4MDt7UUu1w?si=EcCbIMtkmHZIsWPS) ![任務管理應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49wmxw4xcfzlig2mrmk4.png) ### 4. 互動式履歷表產生器 建立一個互動式履歷產生器來展示您的職業歷程。包括教育、工作經驗和技能部分。 **您將學到什麼:** 透過這個專案,您將掌握表單處理、動態內容呈現和建立互動式使用者介面的藝術。這是提高你的 React 技能的絕佳機會。 **技術堆疊:** React、表單處理、CSS(樣式元件)。 **資源:** [互動式履歷產生器播放清單](https://youtube.com/playlist?list=PLrL9i_Ka3T0caA37TXSlicITQl1pp_3Ry&si=UhftAkbYr_eYaxJZ) ![互動式履歷產生器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqsmo0bflp7x9juv94br.png) ### 5.產品落地專案 開發一個產品登陸頁面,重點宣傳特定產品或服務。針對各種螢幕尺寸實施響應式設計。 **您將學到什麼:** 本專案將加深您對響應式網頁設計的理解,包括媒體查詢、Flexbox 和網格佈局。您還將提高建立具有視覺吸引力的登陸頁面的技能。 **技術堆疊:** HTML、CSS(Flexbox/Grid)、響應式設計。 **資源:** [產品登陸頁面教學](https://youtube.com/playlist?list=PL07efmqYWHZ8jroJAkkFB2s4ZKpVNCOQa&si=ZI375j0QMaN7rpdn) ![產品登陸頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqpejqx835m7zxgs585u.png) ### 6. 使用 GitHub API 的個人資料頁面 建立一個個人資料頁面,從 GitHub API 取得資料,顯示使用者資訊、儲存庫和貢獻熱圖。 **您將學到什麼:** 該專案將增強您使用 API、處理非同步資料以及建立具有視覺吸引力的使用者設定檔的技能。您還將獲得將第三方 API 整合到應用程式中的經驗。 **技術堆疊:** React、GitHub API、CSS。 **資源:** [GitHub API 設定檔教學](https://dev.to/falanatolu/using-github-api-to-fetch-and-display-a-github-user-profile-26g6) ![使用 GitHub API 的個人資料頁面](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t5qvthy4yzgx60a5omp4.png) ### 7.響應式部落格網站 開發一個響應式部落格網站,其中包含針對各種裝置優化的文章清單、單一文章頁面和導航選單等功能。 **您將學到什麼:** 該專案將提高您建立複雜佈局的技能,並專注於回應能力。您將獲得建立導航結構和優化不同螢幕尺寸的使用者體驗的經驗。 **技術堆疊:** HTML、CSS (Flexbox/Grid)、JavaScript。 **資源:** [響應式部落格網站教學](https://youtu.be/NNQuhOeM0mI?si=RjNPOIm-otDhMTsE) ![響應式部落格網站](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/potx6blzf3if4cfp4wv7.png) ### 8. 使用 Lottie 檔案的動畫天氣應用程式 使用 Lottie 檔案建立帶有動畫天氣圖示的天氣應用程式。提供與當前天氣狀況對應的即時天氣資訊和動畫。 **您將學到什麼:** 該專案將擴展您將外部庫 (Lottie) 整合到專案中的技能。您還將獲得使用即時資料 API 和實現動態動畫的經驗。 **技術堆疊:** React、Lottie、OpenWeatherMap API、CSS。 **資源:** [動畫天氣應用教學](https://youtu.be/pFvWwFua6mw?si=Xut6cKy10rV1DpFd) ![動畫天氣應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x392uay0fjf8kypyrtoy.png) ### 9.SaaS 登陸頁面 設計並建置軟體即服務 (SaaS) 產品的登陸頁面。強調簡潔的設計、有效的文案和引人注目的號召性用語元素。 **您將學到什麼:** 該專案將提高您建立以行銷為導向的登陸頁面的技能,並專注於用戶轉換。您還將深入了解有說服力的設計和有效使用配色方案的原則。 **技術堆疊:** React、Tailwind CSS、文案。 **資源:** [SaaS 登陸頁面教學](https://youtube.com/playlist?list=PLz_5rPRIvGEBDvyf-HIIDHjsPppnuXtFG&si=JIpCCIbz4QW8v9o4) ![SaaS 登陸頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl16te1dbqsp5np73bh2.png) 踏上這些令人興奮的專案,在 2024 年掌握前端 Web 開發。每個專案都提供一系列獨特的挑戰和學習機會,讓您具備在動態 Web 開發領域脫穎而出所需的技能。快樂編碼! 🚀 --- 原文出處:https://dev.to/mukeshkuiry/9-projects-to-master-frontend-web-development-in-2024-with-resource-d0k

HTML可以做到這一點嗎?第1部分

歡迎來到我的 HTML can do 這個系列,第 1 部分。 為了與我的[GitHub-Can-Do-This](https://dev.to/yuridevat/reference-a-new-issue-3g5h) 系列保持一致,今天我想寫一篇關於HTML 可以做的很酷的事情的文章(很多人顯然不知道)。 我堅信,越多的人理解 HTML 並充分利用它,網路就會自動變得更容易存取。 ## 表中的內容 1. [詳細資訊和摘要 HTML 標籤](#chapter-1) 2. [電子郵件或電話號碼連結](#chapter-2) 3. [自動完成清單](#chapter-3) 4. [文字修改](#chapter-4) --- 由於我不知道接下來還會有多少部分,因此我將透過按字母順序描述出色的標籤及其背後的 HTML 魔力來開始本系列。 ## 1. 詳細資訊和摘要<a name="chapter-1"></a> 開啟/關閉小部件通常使用 JavaScript 建立。很長一段時間以來,有一個預設的 HTML 標籤恰好涵蓋了這種行為。 樣式可以根據需要進行調整。預設情況下,該小部件是關閉的。賦予 `<details>` 屬性 `open`,它會從頭開始顯示其內容。 {% codepen https://codepen.io/YuriDevAT/pen/dyaBQZm %} ## 2. 連結到電子郵件地址或電話號碼<a name="chapter-2"></a> `<a>` 標籤有一個 `href` 屬性,它指示連結的目的地。它可以連結到網頁、文件、電子郵件地址、同一頁面中的位置或 URL 可以尋址的任何其他內容。 `href` 屬性負責使 `<a>` 可聚焦並在按 Enter 鍵時啟動連結。 ### 連結到電子郵件地址 如果您在「href」的 URL 方案中新增「mailto:」和預期收件者的電子郵件地址,則按一下時會開啟新的外寄電子郵件。 ``` <a href="mailto:[email protected]">Send email to nowhere</a> ``` 您還可以在 URL 中加入更多詳細訊息,包括抄送、密件副本、主題和正文 😯。 ``` <a href="mailto:[email protected][email protected]&subject=This%20is%20the%20subject">Send email with subject to nowhere and nobody</a> ``` 詳細了解[連結至電子郵件地址 rfc6068](https://datatracker.ietf.org/doc/html/rfc6068)。 ### 連結到電話號碼 將電話號碼新增至「tel:」之後的「href」時,您也可以連結到電話號碼。 ``` <a href="tel:+18005551239">(800) 555 1239</a> ``` 連結行為可能因設備而異([關於連結到電話號碼的 MDN 官方文件](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#linking_to_telephone_numbers)): - 在行動裝置上,系統會自動撥打號碼。 - 大多數作業系統都有可以撥打電話的程序,例如 Skype 或 FaceTime。 - 網站可以使用registerProtocolHandler撥打電話,例如web.skype.com。 - 其他行為包括將號碼儲存到聯絡人,或將號碼傳送到另一台裝置。 詳細了解[連結到電話號碼 rfc3966](https://datatracker.ietf.org/doc/html/rfc3966)。 {% codepen https://codepen.io/YuriDevAT/pen/OJdZLKK %} ## 3. 自動完成清單<a name="chapter-3"></a> 使用“<datalist>”,當使用者開始在輸入欄位中寫入時會出現一個下拉式選單,並使他們可以從清單中的可用選項中進行選擇。 {% codepen https://codepen.io/YuriDevAT/pen/zYeVMbE %} ## 4. 文字修改<a name="chapter-4"></a> 使用特定標籤,可以直接修改文本,無需額外的 CSS。非常便利。 {% codepen https://codepen.io/YuriDevAT/pen/LYqKXwJ %} --- 謝謝閱讀。對此,我真的非常感激! --- 原文出處:https://dev.to/yuridevat/html-can-do-this-part-1-3ab2

我被 FB 這公司惡搞一頓:為了保留我 side project 的「 FB 登入」功能,每年要多虧8萬元

承接上一篇,FB 對待合作開發人員,立場越踩越硬 https://codelove.tw/@howtomakeaturn/post/NxNy0q 畢竟我們工程師串 FB API 沒在付錢,如果像詐騙集團他們有持續付 FB 廣告費 那 FB 就很樂意持續幫忙投放詐騙廣告,這是台灣用戶人盡皆知的 來聊一聊,這次被 FB 惡搞之後,我個人的額外虧損 --- 我有一個搞笑網站,流量不錯,跟廣告聯播網合作,每月我可賺 25,000 元,算是 side project 很好的成績 年收入用 30 萬來算,我是合法報稅喔,因為廣告聯播網要報支出,所以我沒法逃稅,會進個人綜合所得稅 被 FB 這樣一搞,我需要去登記公司,媽的一開始以為還好,實際跑起來很麻煩 找代辦要付大概 1 萬元左右,這就算了 每年額外費用如下,這是我找到很省錢的解決方式了喔,不然還會更貴 - 31500(商務中心租金) - 22400(事務所記帳,不然公司負責人自行申報的話,那政府 UX 很悲劇 ) - 12000(我本來在職業工會,變成公司負責人額外健保費用,是更多出來的費用) - 15000(營業稅用5%計算) 加起來是 $80,900 元,每年都要支出! --- ## 結論 建議 大學生、工程師業餘做 side project,請避免使用「FB 登入」功能 就算要做 也要讓用戶登入之後 規定要輸入密碼,保留密碼的登入方式,社交登入只是方便而已 不然你網站、APP,某天就不能用了,要去做公司登記,跟我一樣,side project 沒賺多少,一大部份拿去付公司費用 如果 FB 要求付費投放廣告之前,也像對待 developer 的 side project 這麼硬、要出示政府核准公司文件,那台灣詐騙廣告還會那麼猖獗嗎?

血淚心得:工程師做 side project 請避免使用「FB 登入」功能

大概從 2012 年開始,幾乎大大小小的網站、APP 都會有「FB 登入」功能吧 就是去 FB 後台 註冊 申請一下即可 一堆資工系大學生、工程師業餘 side project 幾乎都玩過 但是相關政策一直在改變 就在今年 2023 改成 一定要有註冊公司 才能使用 https://developers.facebook.com/blog/post/2023/02/01/developer-platform-requiring-business-verification-for-advanced-access/ FB 寫了很多篇 曖昧不明的文章 創造一堆專有名詞 你看半天也看不懂 我講結論:反正就是 會隨機抽查 開始抓 app 只要沒有「政府核准文件」的 app 就會砍掉 FB 登入權限 要政府核准函 或是 有公司名稱&地址的水電帳單 很硬喔 我研究過了 沒有取巧、迴避的方法 --- 就算你是經營不錯的公司 還是會被這種政策搞到 例如 財報狗 今年有一段時間 無法FB登入 https://www.facebook.com/statementdog/posts/pfbid0v7sYjy3HLvMaqSWvd11hMt32xQL8Mgi7iNdeZoP7UrYPCo3o9fMNruNgeoaKfQNGl?__cft__[0]=AZXS4AqmqXa1jGrsqKEihimrD8KttxyEbzqcF52n8AAJ0c4JICCpx14TPPPyH4IWB85zhmRZn5BizOXycj_EYAQOj74rHtI262e2fsU2WOvMWzhTnCT3sPxTLTB9igX2tB96p2B5okinh_N-Dss5QBSz&__tn__=%2CO%2CP-R --- 這件事在台灣沒人討論 在國外已經議論紛紛 > Ask HN: Is “sign in with Facebook” dead for indie developers? https://news.ycombinator.com/item?id=37349924 --- 建議 大學生、工程師業餘做 side project,請避免使用「FB 登入」功能 就算要做 也要讓用戶登入之後 規定要輸入密碼,保留密碼的登入方式,社交登入只是方便而已 也就是讓社交登入變成註冊流程的一個環節,而不是直接就註冊完成 不然你網站,某天就不能用了,要去做公司登記 額外成本會變多少呢?我另寫一篇說明 https://codelove.tw/@howtomakeaturn/post/l3jOpa

🫵 開發人員可實現的 5 項副業💰

嘿嘿👋 在您作為開發人員的旅程中,賺錢的常見方法是**獲得全職工作**。 然而,您可能還沒有找到工作(_又名您仍在學習_),或者您已經開始工作,但您正在尋找賺取額外現金的方法。 現在,您可以嘗試透過多種方式賺取副收入。 “有些比其他更現實。” 在本文中,我們將了解您今天就可以開始的**_可實現的_副業收入工作**。 深吸一口氣,讓我們用這些新知識洗滌我們的靈魂。 🧎 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uv5gxnqnmz2lixw86fix.gif) **_免責聲明:_** 我沒有嘗試清單中的每個選項。如果您認為應該在清單中加入一些內容或您想分享個人經歷,請在下面發表評論(我很樂意聽取您的意見!)。 🙇‍♂️ --- ## ⚔️ 創作者任務 在 Quine,我們目前正在為開發人員提供一種將其技能貨幣化的方法。 _Creator Quests_ 是每兩週發生一次的開源挑戰。 **這就像一場 24/7 的黑客馬拉松,獎勵開發者建立酷炫的應用程式**以及使用未來的開發者工具。 🚀 社區透過對他們喜歡的專案給予榮譽來決定獲勝者。獲得最多榮譽的專案將獲得最多的收益。 🤑 **下一次挑戰將於 12 月 19 日星期二開始。** 要參加,請註冊 [Quine](https://quine.sh/?utm_source=devto&utm_campaign=monetising_dev_skills) 並前往 _Quests_。 PS:測試期間,100%有效參賽者均獲得獎勵💸 目前的獎金池為 1024 美元,隨著更多參與者的加入,獎金池將會增加!點擊下面的並試試看! ⬇️ [![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akiuhk62zctvf3b9gilx.png)](https://quine.sh/?utm_source=devto&utm_campaign=monetising_dev_skills --- # 💃 MaaS - 我即服務 **自由工作者很難開始。** 然而,每個人都可以**實現並獲得**。 我在資料分析方面有一些自由職業經驗。如果我必須重做一次,我會採取以下方法: **1️⃣ 選擇平台:** 了解[Fiverr](https://www. Fiverr.com/)、[Upwork](https://www.upwork.com/en-gb/nx/job)之間的區別-post/instant/welcome) 和 [自由工作者](https://www.freelancer.co.uk/)。選擇一個您喜歡的平台並專注於此。我發現 [Malt](https://www.malt.com/) 是一個有趣的平台,但如果你認為自己非常優秀,你可以隨時關注 [TopTal](https://www.toptal.com) / )。 **2️⃣ 不要成為萬事通先生/女士:** 圍繞非常具體的服務建置配置文件,例如_我專門從事Python 抓取工具的編碼並使用MongoDB 建置資料庫,最終通過Plotly 顯示儀表板_(不完全是這樣)但你明白了🙃)。 **3️⃣ 為您的第一個客戶不收取任何費用:** 獲得您的第一個客戶非常困難,因此請聯繫招聘訊息並說明您很樂意**免費這樣做。** 如果他們對這份工作感到滿意,請詢問他們提供良好的評級。 **4️⃣ 獲得您的第一筆薪水:** 開始為您的工作收費 - 仍然以較低的費率 - 並建立您的聲譽。 **5️⃣ 提高定價:** 一旦您在平台上擁有更強大的地位,您就可以決定提高定價。 我的最後評論是,雖然我相信每個人都可以透過自由工作開始賺錢,但**我不認為每個人都應該這樣做。** 有很多缺點不符合你喜歡的工作方式(難纏的客戶、截止日期) 、平台依賴性等)。 **💁 特別提示:** 在倫敦,我發現了一個吸引一些客戶的小技巧。您可以聯繫一些相關的諮詢/招聘機構,他們可以幫助您找到客戶。如果您在起步時遇到困難,請考慮這樣的機構。 --- ## 🧪 測試一下。獲得報酬。 作為開發人員,您了解網頁或應用程式背後的技術細節。 這意味著您有能力執行出色的品質檢查。 根據您關注的平台,您可以花 10 到 60 分鐘以上嘗試應用程式或網站。 市面上有各種平台,但您可以查看的 3 個已知網站是: - [Trymata](https://trymata.com/) - [使用者測試](https://www.usertesting.com/) - [UserLytics](https://www.userlytics.com/user-experience-research/paid-ux-testing/) PS:還有一個完整的調查選項,在我看來,花費很少。如果您仍然好奇,可以查看[Wynter](https://wynter.com/participants/join)或[SwagBucks](https://www.swagbucks.com/g/paid-surveys)。 --- # 🐛 蟲子獵人 如果您對細節有敏銳的洞察力並且喜歡網路安全,那麼您可以透過這項技能獲利。 如果你有好奇心並且有良好的程式設計技能,你就可以在那裡賺錢。 🥂 一開始你可能會得到 20 美元的報酬,但獎勵可能會增加到天文數字,例如 10 萬英鎊以上。 我要強調的是,這不是你成為百萬富翁的方式。有一些不錯的競爭,你可能不會賺到很高的錢。 您仍然應該嘗試一下,看看它是否適合您。 最知名的平台是: - [HackerOne](https://www.hackerone.com/) - [BugCrowd](https://www.bugcrowd.com/) 如果你在這方面變得擅長,你可以考慮大公司,因為這往往是大公司的地方。 您可以從 [Microsoft](https://www.microsoft.com/en-us/msrc/bounty)、[Apple](https://security.apple.com/bounty/) 和 [Google](https: //bughunters.google.com/about/rules/6625378258649088/google-and-alphabet-vulnerability-reward-program-vrp-rules)。 **最後提示:** 如果您發現自己喜歡使用的網站有漏洞,向他們索取賠償總沒有壞處。 🙃 --- ## 🧑‍🏫 你學到了什麼嗎?教它回來 輔導是我個人最喜歡的「副業」。 對於某些人來說,這可能看起來“乏味”,但它是非常可以實現的,並且它**增強您圍繞特定主題的知識,同時賺大錢**。 此外,您不一定需要成為專家。 這是正確的! 只要您在特定領域更有知識,您就可以收取教授該知識的服務費用。 您可以透過 3 種方法來解決此問題: ### 📜 老學校方式 1️⃣ **確定合適的學校**:在學校/學院周圍張貼實體廣告,並附上您的課程和聯絡方式。 2️⃣ **辨識社群:** 尋找並參與相關的 Discord、Facebook 和 Linkedin 群組。以尊重的方式讓相關社區了解您的服務。 3️⃣ **不要太貪心:** 以較低的價格開始您的第一堂課,讓一切順利進行。經過一些經驗後,考慮提高價格。 ### 👶 新學校方式 查看以下平台: - [Codementor](https://www.codementor.io/freelance) - [Superprof](https://www.superprof.co.uk/lessons/computer-programming/united-kingdom/) - [導師](https://tutorhouse.co.uk/a/coding) 註冊成為導師並以低於市場價格的價格開始招募您的第一批學生。 ### 📦 數位內容 您可以考慮建立一些視訊課程。 我個人最喜歡的是一個名為 [Stan Store](https://www.stan.store/?ref=bap&utm_source=stan-store-link&utm_medium=redirect&utm_campaign=storefront) 的新工具。 這是數位創作者的全能工具包,不僅僅是建立影片內容。 > 我個人使用這個平台賺了幾百美元。 或者,您可以查看更傳統的平台,例如 [SkillShare](https://www.skillshare.com/en/search?query=Coding) 或 [Udemy](https://www.udemy.com/)。 --- ## 較不容易實現的路徑(但值得一提)👇 <br> <br> - **🚧 黑客馬拉松**:一些黑客馬拉松提供豐厚的現金獎勵。缺點是通常存在相當多的競爭。要尋找合適的黑客馬拉松,迄今為止最好的網站是 [DevPost](https://devpost.com/)。 <br> - **🏷️ 銷售鍋爐模板:** 您可以根據自己的知識建立鍋爐模板。在這種情況下,「稍微少一點」的是你能賺多少錢(至少在最初)。這是因為它需要大量的追隨者(或大量的行銷)。如果你想嘗試一下,可以看看 [Gum Road](https://gumroad.com/) 和 [AppSumo](https://appsumo.com/)。 --- 我希望您喜歡這篇文章,它可以幫助您了解如何利用您的開發技能來從事副業。 上述演出並不是試圖描繪出你將一夜致富的景象。然而,它們是副業,獲得報酬的機會很高。只要保持一定的一致性,隨著時間的推移,您可以期望獲得更多的收入。 🚀 這篇文章的靈感來自 @lissy93 寫的一篇很棒的文章。 如果您有興趣了解將您的開發技能貨幣化的其他方法(特別是如果您更有經驗),請查看他們的[文章](https://dev.to/lissy93/50-ways-to-bring -in- extra-cash-as-a-developer-19b6) 並給它一些愛。 下週見, 您的開發夥伴💚 巴普 --- 如果您想加入開源中自稱「最酷」的伺服器😝,您應該加入我們的[discord伺服器](https://discord.com/invite/ChAuP3SC5H/?utm_source=devto&utm_campaign=9_deep_learning)。我們隨時為您的開源之旅提供協助。 🫶 {% 嵌入 [https://dev.to/quine](https://dev.to/quine) %} --- 原文出處:https://dev.to/quine/5-achievable-side-hustles-for-developers-4bcg

🔥 NextJS 專案的 12 個頂級庫 🏆

![保存](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szi0gw4l049yctxjeu1p.png) 在過去的十年裡,我一直是一名全端開發人員,建置了像[gitup](https://gitup.dev/) 這樣的較小專案和像[crosspublic](https://github.com/github-20k/) 這樣的更大專案跨公共)。 多年來,我測試了不同的工具: 1. 提高工作效率 2. bug 更少 3. 少寫程式碼 我整理了一系列庫來幫助您開發我每天使用的優秀 NextJS 東西,並解釋了您可以用它們做什麼。 **讓我們深入了解一下。** ![變得更好](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap38q1ej3tqypjuebg3u.gif) --- # 1. [Trigger.dev](https://github.com/triggerdotdev/trigger.dev) 使用 NextJS,我總是需要幫助來處理與後台作業相關的所有事情。 它可以是在背景執行的 cron 作業,用於傳送電子郵件或處理系統中的新使用者管道。 這導致我執行另一台伺服器來處理這些作業,無論是外部 EC2 伺服器還是帶有事件橋的無伺服器功能。 這會導致我支付額外的服務費用(管理更多服務)並自行管理水平擴展(在某些時候)。 [Trigger.dev](http://Trigger.dev) 改變了這一點,在 NextJS(以及許多其他)之上提供後台作業。 他們也知道如何解決 NextJS 無伺服器逾時限制來處理長時間執行的作業。   ![TriggerDev](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/triggertop.gif) --- ## 2. [Prisma](https://www.prisma.io) Prisma 不是 NextJS 特有的。它是一個與資料庫一起使用的 ORM。 ORM 是資料庫查詢的統一包裝器。 它保持良好的結構,並允許您在不同的資料庫提供者之間快速更改。 雖然您可以使用很多 ORM,但 Prisma 的獨特之處在於為您的查詢提供 Typescript 支持,使一切速度提高 100 倍。 NextJS 在預設配置中使用了 typescript,使其成為完美的匹配。   ![prisma.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/prisma.gif) --- ## 3. [NextAuth.js](https://next-auth.js.org) 假設您要實現任何服務提供者身份驗證,例如 Facebook / Google / GitHub (oAuth)。 在這種情況下,您必須為每個提供者建立實作或使用外部服務,例如 [Auth0](https://auth0.com/) 或 [Clerk](https://clerk.com/)。 如果您打算自行執行此操作,NextAuth 提供了豐富的實現,以便您只需提供正確的金鑰即可輕鬆新增它們。 一旦您登錄,他們也會處理授權。 *Next.JS auth 可以與 Prisma 開箱即用。*   ![authjs.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/authjs.gif) --- ## 4. [下一個網站地圖](https://github.com/iamvishnusankar/next-sitemap) 在伺服器上部署 NextJS 後,您需要協助 google 索引所有頁面。 如果您可以告訴 Google 您網站上的所有頁面,那就更好了。 為此,您可以建立一個列出所有頁面的 sitemap.xml 檔案。 您可以輕鬆地使用 Next-Sitemap 來實現這一點。   ![sitemap.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/sitemap.gif) --- ## 5. [下一步 SEO](https://github.com/garmeeh/next-seo) SEO 是透過向您的網站預覽提供關鍵字、描述和圖像,使您的網站出現在 Google Feed 上以進行不同查詢的過程。 如果您使用新的 NextJS 應用程式路由器,則可能不需要使用它。 您可以使用他們的“導出元資料”方法或“生成元資料”, 但如果您使用舊的應用程式路由器,這是為您的網站加入 SEO 的最佳方式。   ![seo.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/seo.gif) --- ## 6. [Zod](https://github.com/colinhacks/zod) Zod 是一個物件驗證器(伺服器和客戶端)。 您可以在物件上放置不同的規則並稍後對其進行驗證,例如使用者名稱和密碼,或更複雜的內容(例如陣列長度或其他鍵上的條件)。 *Zod 不是 NextJS 特定的。* 多年來,我看過很多物件驗證器,例如 [Yup](https://github.com/jquense/yup) 和 [class-validator](https://github.com/typestack/class-validator)。 是的,它看起來不像 Zod 那樣維護,並且在使用 NestJS 之類的東西時,類驗證器非常強大 - 所以你最好使用 Zod。   ![zod.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/zod.gif) --- ## 7. [React-hook-form](https://github.com/react-hook-form/react-hook-form) 雖然 Zod 可以驗證物件,但如果沒有自訂邏輯,它不會影響您的用戶端和後端。 React-hook-form 是優秀的用戶端驗證專案(顯示輸入錯誤、管理輸入狀態和提交)。 當然,您可以使用 Zod 作為 React-hook-form 的驗證器。   ![hookform.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/hookform.gif) --- ## 8. [tRPC](https://github.com/trpc/trpc) 我承認我以前從未使用過 tRPC,但今天它似乎吸引了許多人的目光。 它與 Prisma 有類似的概念;它們為您的請求和回應產生一個接口,因此當您使用前端呼叫時,您會獲得自動完成功能。 這很好,因為它減少了錯誤的機會 - 假設您修改了後端路由,您將無法編譯專案 - 客戶端將返回不存在的參數或回應鍵的錯誤。   ![trpc.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/trpc.gif) --- ## 9. [SWR](https://swr.vercel.app) 和 [React-Query](https://github.com/TanStack/query) 多年來我一直使用 Axios 和 fetch 作為發送請求的基礎庫。 SWR 和 React-Query 增強了這些函式庫並提供鉤子、快取、轉換等。 強烈推薦用於每個專案。請注意,這些庫適用於客戶端元件(“使用客戶端”),而不是伺服器元件。   ![query.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/query.gif) --- ## 10. [lodash](https://lodash.com) 這不是 NextJS 特定的函式庫。 它是一個用於改變資料的函式庫,雖然這些年來 JavaScript 憑藉像 flatMap 這樣優秀的原生函數取得了很大的進步,但仍然缺少一些東西,例如按鍵或分塊和陣列的唯一陣列。 我發現自己幾乎在所有專案中都使用 lodash。   ![lodash.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/lodash.gif) --- ## 11. [dayjs](https://day.js.org/) day.js 是一個包含與日期、格式、時區等相關的所有內容的函式庫。 我可能會因為那件事而被烤。我多年來一直在使用“moment.js”。 現在它不再維護了,dayjs 是一個不錯的選擇。 有些人喜歡新的 JS 函數來處理日期,但我仍然覺得 dayjs 選項和原生 JS 日期函數之間存在很大的差距。   ![scrolldown.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/scrolldown.gif) --- ## 12. [jsdom](https://github.com/jsdom/jsdom) 這不是必須的,但我最近在許多專案中都使用它作為 [cheerio](https://github.com/cheeriojs/cheerio) 的替代品。 您可以取得整個頁面內容(`<html><body>....</html>)` 並將其轉換為稍後可以使用「本機」javascript dom 函數`querySelector`、`innerHTML` 等來操作的物件… 非常適合需要一些刮擦的專案。   ![jsdomer.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/jsdomer.gif) --- 我們在 X 上連接嗎? :) [我在這裡](https://twitter.com/nevodavid) 您是否為 NextJS 使用其他一些很酷的程式庫? 請在評論中讓我了解它們:) --- 原文出處:https://dev.to/nevodavid/top-12-libraries-for-your-nextjs-project-1oob

您付費工具的開源替代品

**開源吞噬軟體** 我建立了 [osssoftware.org](http://osssoftware.org),重點是: - PH 獲獎者 - DevHunt 上最好的開發工具 - 最近在 GitHub 上活躍 - 大多數網路反向連結 - 大多數被提及為“替代......” 👇 **301 個開源替代方案:** - [Supabase](http://supabase.com) - Firebase 的開源替代品 - [Documenso](http://documenso.com) - Docusign 的開源替代方案 - [Cal](http://cal.com) - Calendly 的替代品 - [Plausible](http://plausible.io) - Google Analytics 的開源替代品 - [DevHunt](http://devhunt.org) - ProductHunt 的開源替代品 - [AI.Meta](http://ai.meta.com/llama) - ChatGPT 的開源替代品 - [Papermark](http://papermark.io) - Docsend 的開源替代品 - [Godot Engine](http://godotengine.org) - Unity3D 的開源替代品 - [Ghost](http://ghost.org) - Medium 的開源替代方案 - [Mastodon](http://joinmastodon.org) - Twitter 的開源替代品 - [Rowy](http://rowy.io) - Airtable 的開源替代品 - [Sentry](http://sentry.io) - 錯誤追蹤的開源替代方案 - [N8N](http://n8n.io) - Zapier 的開源替代品 - [Appsmith](http://appsmith.com) - Retool 的開源替代方案 - [ClickHouse](http://clickhouse.com) - BigQuery 的開源替代品 - [GitLab](http://gitlab.com) - GitHub 的開源替代品 - [Penpot](http://penpot.app) - Figma 的開源替代品 - [Jenkins](http://jenkins.io) - DevOps 的開源替代方案 - [Forem](http://forem.com) - Circle 的開源替代品 - [PostHog](http://posthog.com) - Mixpanel 的開源替代品 - [Dub](http://dub.co) - Bitly 的開源替代方案 - [OpenCart](http://opencart.com) - Shopify 的開源替代品 - [類型](http://typesense.org) - Algolia 的開源替代品 - [AppFlowy](http://appflowy.io) - Notion 的開源替代品 - [Webstudio](http://webstudio.is) - Webflow 的開源替代方案 - [Typebot](http://typebot.io) - Typeform 的開源替代品 - [Passbolt](http://passbolt.com) - 1Password 的開源替代品 - shadcn - Tailwind UI 的開源替代方案 **看更多:** - [所有開源替代品](http://osssoftware.org/open-source-alternatives/) - [類別](http://osssoftware.org) **貢獻:** 如果您認為應該加入一個很棒的工具,請在網站上提交它或給我發送 DM 或回复此帖子。 我的推特 [@johnrushx](https://twitter.com/johnrushx) 順便說一句,我們正在進行 Product Hunt 感謝您的支持 →→→ [producthunt.com/osssoftware](https://www.producthunt.com/posts/opensource-alternatives-to-tools-you-pay) --- 原文出處:https://dev.to/johnrushx/open-source-alternatives-to-tools-you-pay-for-1g9c

向我的 Uber 司機解釋 SSH

在這篇部落格中,我想向您解釋 SSH,就好像您是我的 Uber 司機一樣。此練習的目的是假裝您以前沒有 SSH 經驗,甚至沒有太多一般技術知識,並讓您達到以下目標: 1. 不要害怕「SSH」這個模糊的術語,因為你腦子裡有一個具體的形象。 2. 了解業界級開發人員如何使用SSH來測試程式碼變更、進行伺服器設定等。 3. 了解 SSH 的加密/授權協定如何使其優於其他工具。 讓我們開始吧! ## SSH 概念化 什麼是 SSH?讓我用一個類比來幫助您概念化它,而不是從您在 Cloudflare 文章或維基百科頂部看到的傳統技術巨頭開始。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkzvrhuygk1ihvpl8jyw.png) 想像一下您剛放學,回到家的臥室。您感到無聊,決定戴上 VR 耳機來存取元宇宙 - 一種沉浸式體驗,將實體世界拒之門外,將您嵌入虛擬環境中。您登入並輸入使用者名稱和密碼以驗證您的身分。完成後,您會看到您的數位化身彈出。它看起來和你一模一樣——身材高大,頭髮漂亮,還有一種奇妙的時尚感。在您面前,您會看到一片森林,或者更確切地說,是森林的數位表示。仍然戴著 VR 耳機站在臥室裡,您可以移動頭部環顧四周,您的化身也會做同樣的事情。你在臥室裡說“你好森林”,你的頭像也在 VR 世界中說“你好森林”。 在現實生活中,這個類比可以翻譯為: - 你==開發者。 - 你的臥室==你的筆記型電腦,或任何你正在打字的本機。 - Metaverse == 您嘗試從本機電腦存取的遠端電腦/伺服器。 - VR 耳機 == SSH 隧道,可讓您從本機(臥室)存取遠端電腦(Metaverse)。 - 登入畫面==確保只有授權方才能使用SSH 隧道的SSH 金鑰對。我將在本部落格的後面部分更徹底地解釋這一點。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g8qhentz0jzk11q6dsj.png) 這個類比對你有什麼幫助?下次當您聽到有人說「我透過 SSH 連接到伺服器」時,您無需對「SSH」這個模糊的概念感到驚慌。相反,在你的腦海中將其改寫為“我戴上 VR 耳機來存取元宇宙”,這將使它變得更具體。 *通俗地說,SSH 就像一個安全、秘密的隧道,可讓您透過網路連接到另一台電腦。它可以幫助您在計算機上執行操作,就像您坐在計算機前面一樣。* 當然,這只是對SSH的表面、概念性理解。我希望您現在正在思考一些問題來幫助您更深入地了解 SSH。我無法讀懂你的想法,但這裡有一些我自己回答的問題,我認為向你解釋會很有用。 ## 為什麼我需要使用 SSH? SSH 很重要,因為開發人員通常需要存取遠端電腦。例如,在您自己的筆記型電腦上會消耗太多記憶體和運算能力的大型應用程式託管在 AWS、Azure 或 Google Cloud 等雲端平台上,而這些平台都需要遠端存取。其他時候,開發團隊分佈在不同的地點,因此遠端伺服器允許團隊成員在一個一致的位置進行協作和共享程式碼,即使他們實際上不在同一位置。 讓我向您介紹兩個行業級開發人員何時需要透過 SSH 連接到遠端電腦的範例。 **1:部署程式碼** 作為開發人員,您需要將網站的新版本部署到臨時伺服器。這就是它的工作原理,使用 SSH。 在本機上,您可以使用 Git 推送最新的程式碼變更。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5n568y57dn3zc9yse7n1.png) 然後,您可以透過 SSH 連線到具有臨時環境的遠端伺服器。 (順便說一句,您可能會想為什麼我需要在遠端伺服器而不是本地電腦上建立臨時環境。以下是一些原因。其中之一是最好模擬真實的設置,例如檢查效能瓶頸或可擴展性問題只會發生在伺服器環境中,而不是您的筆記型電腦中。另一個是簡單地確保所有團隊成員都可以在一致的環境中進行測試,而不是他們都在自己的電腦上擁有臨時環境,或者(上帝禁止)使用您的每當他們需要測試某些東西時就在本地機器上。) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8401y49leoaxiglkl08i.png) 最後,您已經透過 SSH 連接到託管臨時環境的遠端伺服器,您可以從該遠端伺服器提取最新的程式碼變更、執行建置並測試您的應用程式(透過導航至 http://staging.example.com)。 **2:更新伺服器設定** 假設您正在建立的 Web 應用程式已經成長,並且您需要調整伺服器配置以適應增加的流量。以下是您可能執行的一些命令的範例: A。透過 SSH 連接到伺服器 `ssh 用戶@您的伺服器 IP` b.找到 Apache 設定檔 `cd /etc/apache2` C。備份設定檔 `cp apache2.conf apache2.conf.bak` d.編輯設定檔。 `vi apache2.conf` e.驗證配置 `apache2ctl 配置測試` **請不要覺得您需要知道這些指令的作用。** 老實說,我要求 ChatGPT 為我產生一個範例,但我自己不太明白。這裡的要點是了解開發人員在透過 SSH 連接到伺服器時可能執行的命令類型。其中很多是移動目錄(cd)、操作文件(cp)和編輯文件(vi)的簡單命令。 ## 為什麼要使用 SSH? 我在學習 SSH 時遇到的一個問題是「SSH 的替代方案是什麼,為什麼 SSH 會勝出」? 我想現在是快速上歷史課的好時機。 在 SSH 被廣泛採用之前,還有其他協定用於兩台機器之間的通訊。一些著名的包括 Telnet、RSH、FTP 和 Rlogin。但它們都缺乏加密和安全功能。 為了描繪一幅圖景,以下是它可能發生的情況。我(惡意駭客)獲得了 Telnet 所經過的網路的存取權限 - 您試圖透過 Telnet 協定將資訊從筆記型電腦發送到另一台電腦的網路。我使用像 Wireshark 這樣的工具來擷取流量。一旦我以下載的“資料包”的形式獲得計算機上的流量,我就可以單擊它們並查找任何登入嘗試。所有資料都是明文形式(這意味著它是人類可讀的,而不是一些亂碼),所以如果我找到它,我實際上可以使用我在我面前看到的登入資訊。正如您所看到的,這些舊工具不是很安全! ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tqaab8kf12bz4089sh8.png) 1995 年,SSH 被發明。它更好有兩個主要原因。 1. 加密資料 假設從我的機器傳輸到遠端機器的部分資料是字串「用戶名:jesswang 密碼:?IamC00l!」。如果沒有加密(因此使用 Telnet 或 FTP 等舊協定),駭客將能夠像您讀取該字串一樣讀取該字串。透過加密,他們返回的資料可能看起來像「H23kLs*^!d8%PwZx0KsL!9B#N%u@i8」。實際上,加密資料將是遵守加密演算法標準(我不太了解)的不同位元組序列,但這只是為了給您一個想法。 2. 認證 Telnet 等較舊的程式僅依賴基於密碼的身份驗證。當您使用 Telnet 連線到遠端系統時,Telnet 伺服器會提示您輸入使用者名稱和密碼進行登入。然而,Telnet 以明文形式傳輸您的密碼,使駭客很容易讀取。 SSH 主要使用一種不同的(也是更好的)方法,稱為公鑰身份驗證。我將在下面解釋它的工作原理,以便您了解如何更好地保護您的資訊免受駭客攻擊。 首先,您的訊息被放入公文包中,並且您自己給它上鎖。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/punmm0o96e0ec1q6294u.png) 您將公文包傳送到遠端伺服器。遠端伺服器沒有鑰匙來解鎖您的鎖,因此遠端伺服器將自己的鎖放在公文包上。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slbbwmewrjixfy0jirok.png) 遠端伺服器將公文包傳回給您,然後您打開鎖。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3xm8hl2wzqx4q9gq27a.png) 您將公文包發送回遠端伺服器,它會用鑰匙解鎖它的鎖。然後它打開公文包,閱讀便條。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sv9jovtg5gqfrr9zs3i9.png) 這就是公鑰認證的要點!順便說一句,SSH 仍然**支援基於密碼的身份驗證,但即使如此,密碼也是加密的,因此如果駭客攔截它,他們將無法讀取它。 ## 如何使用 SSH? 概念學習已經足夠了。下面是我透過 SSH 執行到伺服器的實際命令。 若要透過 SSH 連線到伺服器,您通常會執行下列命令:`ssh username@server_ip_or_hostname` 您可以將使用者名稱替換為您在伺服器上的實際使用者名稱 您可以將 server_ip_or_hostname 替換為伺服器的 IP 位址或主機名稱。例如,「ssh [email protected]」。 執行該命令後,系統可能會提示您輸入伺服器使用者名稱的密碼。或者,如果您的伺服器使用我們在上一節中介紹的基於金鑰的身份驗證,則可能不會提示您輸入密碼。 進入後,您可以開始執行命令。這些命令將在您連接的遠端伺服器上執行。 我知道,如果您不自己親自輸入內容並看看會發生什麼,就很難充分學習。如果您已經設定了用於工作或個人的遠端伺服器,請隨意嘗試。如果您沒有可連接的遠端伺服器,那也沒關係。您可以按照以下步驟操作: 1. 存取http://sdf.org/ 2. 建立帳戶。您可能會收到一封電子郵件,為您提供為您的帳戶產生的密碼。您可能需要等待 1-2 秒才能收到該電子郵件(至少這是我的經驗)。 3. 您可以透過執行「ssh <your_username>@sdf.org」透過 SSH 存取公共 UNIX 系統。例如,我的用戶名是 poop123。所以我執行了“ssh [email protected]”。 4. 出現提示時,輸入您先前獲得的密碼。 5. 您已成功透過 SSH 連線到伺服器!您可以執行「echo 'hello world'」等指令來列印「hello world」或「ls」來查看目前目錄中的檔案清單。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncm79f92w68a034ghiup.gif) ## 讀者須知/結論 雖然透過SSH 連接到另一台伺服器相對容易(也許你的同事給你一個命令,你只需將其貼到命令列中),但我發現如果我不理解(至少在高層次上)什麼,則很難使用該命令當我執行它時發生。希望其他人能與我聯繫,這個部落格將有助於解決這個問題。 如果您好奇我是如何想出這個標題的,這個故事位於本系列原始博客的介紹段落中,[“向我的 Uber 司機解釋 Kubernetes”](https://dev.to/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60)。 一如既往,我希望收到有關此部落格中的訊息、圖畫或故事講述的任何反饋。請告訴我您還想讓我寫哪些其他主題。 以下是我在撰寫此部落格時參考的一些資源: - 聊天GPT - SSH 初學者指南 [Youtube 影片](https://www.youtube.com/watch?v=qWKK_PNHnnA) - 我對上述影片的 60 秒 [TikTok 影片摘要](https://www.tiktok.com/@therubberduckiee/video/7213401342403005738?lang=en)。 - 我使用 [Charm VHS](https://github.com/charmbracelet/vhs) 來產生您看到的最後一個 gif。非常酷的工具,可讓您建立並輕鬆編輯與 CLI 相關的簡報。 - 我用來拍攝示範的終端,[Warp](https://www.warp.dev/)(這也是我工作的公司。我強烈建議您查看我們)。 --- 原文出處:https://dev.to/therubberduckiee/explaining-ssh-to-my-uber-driver-38a

程式設計師的 100 個願望清單想法

科技愛好者常有遠大的夢想,渴望實現突破創新和個人發展界限的目標。為了幫助建立這些雄心壯志,我為科技愛好者量身定制了廣泛的願望清單。此清單不僅概述了各種目標,還包括「靈感」、「如何」、「年份」、「狀態」和「紀念品」等附加欄,為您的技術之旅提供全面的路線圖。 ## 清單中有什麼? 該清單涵蓋了廣泛的目標,從培養特定技能(例如達到 150 WPM 的打字速度)到雄心勃勃的專案(例如製作 DIY 電動滑板或自行車)。它包括參加重大活動,例如參加大型技術會議,以及個人發展的里程碑,例如指導初級開發人員。每個目標都經過分類以便於參考,涵蓋 DIY 專案、程式設計、開發、學習等領域。 ## 列表 [我的遺願清單進度](https://syki.dev/bucket-list) |目標|類別|靈感|年份|狀態|紀念品| |---------------------------|----------------|----------------| ----|------|--------| |成為大型專案的貢獻者 |開發 |[如何像專業人士一樣開源](https://www.youtube.com/watch?v=MT6M_sqAuZo) | | | | |建立新聞聚合器 |開發 |[Feedly](https://feedly.com/) | | | | |建立實體引擎 |開發 |[我正在從頭開始編寫整個實體引擎](https://www.youtube.com/watch?v=iSMbRGTBOHU) | | | | |建立推薦系統 |開發 |[推薦系統如何運作 (Netflix/Amazon)](https://www.youtube.com/watch?v=n3RKsY2H-NE) | | | | |建構情緒分析工具 |開發 |【2023 如何掌握人工智慧驅動的情緒分析?】(https://brand24.com/blog/sentiment-analysis/) | | | | |建立擴增實境 (AR) 應用程式 |開發 |[關於如何在 2023 年建立擴增實境應用程式的指南](https://www.tekrevol.com/blogs/how-to-build-an-augmented-reality -應用程式/) | | | | |建置與部署聊天伺服器 |開發 |[IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) | | | | |建立網路安全工具|開發|[Kali Tools](https://www.kali.org/tools/) | | | | |建立多人線上遊戲|開發|[Dani](https://www.youtube.com/watch?v=_ze26M_Fm6g) | | | | |建立 PWA(漸進式 Web 應用程式)|開發 |[PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) | | | | |建立 AI 影響者 |開發 |[AI 影響者變得非常富有...讓我們建立一個](https://www.youtube.com/watch?v=ky5ZB-mqZKM&t=11s) | | | | |開發區塊鏈應用程式 |開發 |[使用以太坊智能合約和 Solidity 建置您的第一個區塊鏈應用程式](https://www.youtube.com/watch?v=coQ5dg8wM2o) | | | | |開發一種加密貨幣 |開發 |[您需要立即建立一種加密貨幣!!](https://www.youtube.com/watch?v=befUVytFC80) | | | | |開發VR 應用程式|開發|[教學 - 在Meta Quest 耳機上建立您的第一個VR 應用程式](https://developer.oculus.com/documentation/unity/unity-tutorial-hello-vr/) | | | | |開發一個電子商務網站 |開發 |[Next.js Commerce](https://nextjs.org/commerce) | | | | |開發開源遊戲引擎 |開發 |[C++ 中的 GameDev](https://www.youtube.com/watch?v=LyJkcv_rL9Y&list=PLpM-Dvs8t0Va6RoHkaLuPbRh7Fwpy4nbV) | | | | |開發瀏覽器擴充 |開發 |[Chrome 擴充應該會嚇到你。](https://www.youtube.com/watch?v=xIKwkPWUgOA) | | | | |在 github 上有一個擁有 100 顆星的專案 |開發 |[Linus Torvalds](https://github.com/torvalds?achievement=starstruck&tab=achievements) | | | | |擁有自己的 Tor 頁面 |開發 |[我在 Raspberry Pi 上放置了一個 DARK WEB 網站!!](https://www.youtube.com/watch?v=bllS9tkCkaM) | | | | |發明手勢控制介面 |開發 |[鋼鐵人](https://www.youtube.com/watch?v=P5k-4-OEuTk) | | | | |建立持續整合/持續部署管道|開發|[自動化您的工作流程](https://github.com/features/actions)從想法到生產]| | | | |設定 Kubernetes 叢集 |開發 |[為什麼要建置 Raspberry Pi 叢集?](https://www.youtube.com/watch?v=8zXG4ySy1m8) | | | | |贊助開源專案 |開發 |[投資為您的世界提供動力的軟體](https://github.com/sponsors) | | | | |使用查詢機制編寫自己的資料庫|開發|[製作我們自己的資料庫](https://acmiitr.medium.com/making-our-own-database-part-1-6cd9c49ed924) | | | | |在靜態網站產生器中撰寫頁面 |開發 |[Gatsby](https://www.gatsbyjs.com/) | | | | |編寫單頁應用程式 |開發 |[React](https://react.dev/) | | | | |使用 Raspberry Pi 專案實現家居自動化 |DIY |[我建立了一個更聰明的智慧家庭](https://www.youtube.com/watch?v=0rIvB3LZiKA) | | | | |建立自訂鍵盤 |DIY |[建立自己的機械鍵盤......正確的方式](https://www.youtube.com/watch?v=bBon6WwkdJE) | | | | |DIY電動滑板或自行車 |DIY |[我做了一個電動滑板!](https://www.youtube.com/watch?v=3bcvFzecg2Q) | | | | |以自訂遊戲建立迷你街機 |DIY |[終極 DIY 街機指南](https://www.youtube.com/watch?v=oTydZBIGAuk) | | | | |組裝一台 PC |DIY |[為 Minecraft 組裝一台價值 100,000 美元的 PC](https://www.youtube.com/watch?v=AHR80l7od2Q) | | | | |建立個人雲端儲存系統 |DIY |【這是我的終局之戰 - Mother Vault 伺服器機房更新】(https://www.youtube.com/watch?v=pLC0FUnko-M) | | | | |利用 IoT 建造自動澆水花園系統 |DIY |[Arduino 花園控制器 - 自動澆水和資料記錄](https://www.youtube.com/watch?v=O_Q1WKCtWiA) | | | | |打造小型自動駕駛汽車或機器人 |DIY |[快速巡線機器人](https://www.youtube.com/watch?v=lnP32gzHdvI) | | | | |建造水下ROV |DIY |[建造一艘DIY潛水艇](建造一艘DIY潛水艇) | | | | |搭建並飛行 FPV 無人機 |DIY |[為什麼要以 800mW 功率自由式飛行? | [FPV](https://www.youtube.com/watch?v=bBb_kSO3vTo) | | | | |設計具有互動功能的智慧鏡子|DIY |[DIY智慧鏡子 - 完整教學](https://www.youtube.com/watch?v=OYlloiaBINo) | | | | | 以 3D 方式設計和列印一些東西 |DIY |[我在房間中央製作了一個機器人手臂!](https://www.youtube.com/watch?v=nRsaf16EdNM) | | | | |設計您自己的 PCB |DIY |[PCB 建立初學者 - 10 分鐘內開始完成教程](https://www.youtube.com/watch?v=MsdJgEinb34) | | | | |修復損壞的電子產品 |DIY |[ElectroBOOM](https://www.youtube.com/@ElectroBOOM) | | | | |在本地擁有自己的伺服器 |DIY |[為什麼要建立 Raspberry Pi 叢集?](https://www.youtube.com/watch?v=8zXG4ySy1m8) | | | | |實現智慧家庭|DIY |[我建造了一個更聰明的智慧家庭](https://www.youtube.com/watch?v=0rIvB3LZiKA) | | | | |製作機械手臂 |DIY |[我在房間中央製作了機械手臂!](https://www.youtube.com/watch?v=nRsaf16EdNM) | | | | | 用 Flipper Zero 開啟一些東西 |DIY |[這讓駭客攻擊太容易了 - Flipper Zero](https://www.youtube.com/watch?v=nLIp4wd0oXs) | | | | |成為智慧型手機應用程式的擁有者|創業|[VoidLog](https://www.youtube.com/watch?v=LY4rxYe-jKI&list=PLN3n1USn4xllDDLwgJ4avEqgj4dWynofp) | | | | |開發 SaaS 產品 |創業精神|[我如何在一天內建立一個新的 SaaS 產品](https://www.youtube.com/watch?v=v_3lcqUOaOA) | | | | |在店裡擁有自己的遊戲 |創業|[Dani](https://www.youtube.com/watch?v=_ze26M_Fm6g) | | | | |在新創公司工作 |創業精神|[新創工程師在家工作的一天](https://www.youtube.com/watch?v=TLysAkFM4cA) | | | | |一週多相睡眠|創業|| | | | |參加播客 |創業|[Lex Fridman](https://www.youtube.com/lexfridman) | | | | |完全存取伺服器 - 黑客 |黑客 |[先生。機器人](https://www.youtube.com/watch?v=QqknSms8VVI&t=16s) | | | | |獲得錯誤賞金 |駭客 |[HackerOne](https://www.hackerone.com/) | | | | |在奪旗大賽中進行駭客攻擊 |駭客攻擊 |[Mr.機器人](https://www.youtube.com/watch?v=6MrQ-mN8HM8) | | | | |編寫惡意軟體 |駭客 |[惡意軟體開發:進程、執行緒與句柄](https://www.youtube.com/watch?v=aNEqC-U5tHM) | | | | |150 wpm 速度打字 |程式設計 |[Monkeytype](https://github.com/monkeytypegame/monkeytype) | | | | |使用 Python 腳本自動化您的日常任務 |程式設計 |[開始使用 Python 自動化您的生活! (Python檔案管理教學)](https://www.youtube.com/watch?v=NCvI-K0Gp90) | | | | |建立 Twitter 機器人 |程式設計 |[如何使用人工智慧發布熱門推文 // Twitter 機器人教學](https://www.youtube.com/watch?v=V7LEihbOv3Y) | | | | |編譯您自己的 Linux 核心 |程式設計 |[如何編譯自訂 Linux 核心](https://www.youtube.com/watch?v=APQY0wUbBow) | | | | |建立聊天機器人 |程式設計 |[使用深度學習、Python 和 TensorFlow 建立聊天機器人 p.1](https://www.youtube.com/watch?v=dvOnYLDg8_Y&list=PLQVvvaa0QuDdc2k5dwtDTyT9aCja0on8j) | | | | |使用 D3.js 建立資料視覺化專案 |程式設計 |[使用 D3.js 進行資料視覺化 - 完整教學課程](https://www.youtube.com/watch?v=_8V5o2UHG0E) | | | | |建立照片編輯工具 |程式設計 |[如何製作照片編輯應用程式的完整指南](https://www.cleveroad.com/blog/how-to-build-a-photo-editing-app-like-棱鏡並使其蓬勃發展/) | | | | |開發數位藝術作品產生器 |程式設計 |[如何為初學者產生瘋狂的人工智慧藝術(Midjourney V4)](https://www.youtube.com/watch?v=zf4z8A-OWBY) | | | | |開發檔案加密工具 |程式設計 |[製作您自己的加密程式](https://www.youtube.com/watch?v=TZT7wvTeVyY) | | | | |開發影片編輯軟體|程式設計|[我寫了一個影片編輯器(有點糟糕)](https://www.youtube.com/watch?v=iydG-e1dQGA) | | | | |開發語音助理應用程式 |程式設計 |[建立由 OpenAI 和 Python 驅動的 Jarvis | ChatGPT](https://www.youtube.com/watch?v=BEw5EFqCCEI) | | | | |開發智慧手錶應用程式 |程式設計 |[使用 Android Studio 在 WearOS 上建立並執行穿戴式應用程式](https://www.youtube.com/watch?v=-JO5oHRkybk) | | | | |開發您自己的 Slack/Discord 機器人 |程式設計 |[使用 Python 編寫 Discord 機器人 - 在雲端免費託管](https://www.youtube.com/watch?v=SPTfmiYiuok) | | | | |實現臉部辨識系統 |程式設計 |[從紙張到程式碼建立深度臉部辨識應用程式](https://www.youtube.com/watch?v=bK_k7eebGgc&list=PLgNJO2hghbmhHuhURAGbe6KWpiYZt0AMH) | | | | |學習函數式程式語言 |程式設計 |[函數式程式設計 - 概述](https://www.youtube.com/watch?v=8z_bUIl_uPo) | | | | |學習古老的語言 |程式設計 |[100 秒內的 COBOL](https://www.youtube.com/watch?v=7d7-etf-wNI) | | | | |學習並使用 Docker 進行容器化 |程式設計 |[Docker](https://www.docker.com/) | | | | |學習極快的語言 |程式設計 |[ThePrimeagen](https://www.youtube.com/@ThePrimeagen) | | | | |學習一門不尋常的語言(例如 Brainfuck) |程式設計 |[100 秒內完成 Brainfuck](https://www.youtube.com/watch?v=hdHjjBS4cs8) | | | | |學習量子運算基礎 |程式設計 |[在量子電腦上編碼](https://www.youtube.com/watch?v=q3ecPsMd4tA) | | | | |掌握高階演算法與資料結構(100道LeatCode) |程式設計|【569道 Leetcode 題後的我的大腦】(https://www.youtube.com/watch?v=8wysIxzqgPI) | | | | |對自訂語音控製家庭助理進行程式設計 |程式設計 |[建立由 OpenAI 和 Python 提供支援的 Jarvis | ChatGPT](https://www.youtube.com/watch?v=BEw5EFqCCEI) | | | | |對微控制器進行程式設計 |程式設計 |[微控制器程式設計駭客指南 [教學]](https://www.youtube.com/watch?v=XlFO5Iat178) | | | | |在 Vim 中編程 |程式設計 |[Vim 作為你的編輯器](https://www.youtube.com/watch?v=X6AR2RMB5tE&list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R) | | | | |使用分離式鍵盤|編程|[拆箱新鍵盤!!! (也進行打字測試!)](https://www.youtube.com/watch?v=nh-BAxbithc&t=156s) | | | | |網頁抓取資料 |程式 |[使用 AI 和代理網路進行工業規模的網頁抓取](https://www.youtube.com/watch?v=qo_fUjb02ns) | | | | |用組合語言寫程式 |程式設計 |[Tsoding](https://www.youtube.com/watch?v=WnBXLmKk_qw&t=82s) | | | | |寫一個 NPM 模組 |程式設計 |[NPM](https://www.npmjs.com/) | | | | |編寫伺服器端應用程式 |程式設計 |[Next.js](https://nextjs.org/) | | | | |編寫您自己的人工智慧模型 |程式設計 |[讓我們建立 GPT:從頭開始,用程式碼,拼寫出來。](https://www.youtube.com/watch?v=kCc8FmEb1nY) | | | | |編寫您自己的作業系統 |程式設計 |[Linus Torvalds](https://github.com/torvalds) | | | | |寫你自己的程式語言 |程式設計 |[我製作了自己的程式語言](https://www.youtube.com/watch?v=pgeSGBwtHW8&t=132s) | | | | |擁有私人部落格 |教學 |[Dan Abramov](https://overreacted.io/) | | | | |對學生的講座 |教學 |[馬克·祖克柏的 CS50 講座 - 2005 年 12 月 7 日](https://www.youtube.com/watch?v=xFFs9UgOAlE&t=807s) | | | | |指導初級開發人員 |教學 |[如何正確指導初級開發人員](https://stablekernel.com/article/how-to-properly-mentor-a-junior-developer/) | | | | |寫一篇關於科技主題的論文並發表 |教學 |[兩分鐘論文](https://www.youtube.com/@TwoMinutePapers) | | | | |編寫技術書籍或電子書 |教學 |[編寫技術書籍](https://paulcunningham.me/writing-technical-books/) | | | | |參加大型科技會議 |旅遊 |[CES](https://www.ces.tech/) | | | | |參加黑客松 |旅行 |[我挑戰自己贏得黑客馬拉松](https://www.youtube.com/watch?v=mAJlZUKhOGs) | | | | |參觀電腦歷史博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀 NASA 約翰遜航天中心 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀國家航空暨太空博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀歷史科學儀器收藏 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀大型強子對撞機 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀麻省理工學院博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀國家核科學與歷史博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | ### 下載列表 [下載 CSV](https://syki.dev/uploads/bucket-list.csv) [下載 JSON](https://syki.dev/uploads/bucket-list.json) ## 附加列解釋 ### 靈感 本專欄反映了是什麼激發了追求特定目標的想法或願望。它可以是一個人、一個事件、一本書,甚至一部電影,點燃了人們對特定成就的熱情。例如,DIY 電動滑板的靈感可能是對永續交通的熱情或最喜歡的科技影片部落客的專案。 ### 年 本專欄提出了實現該目標的時間表或目標年份。它有助於規劃和設定現實的時間表。例如,您可能計劃在 2025 年之前參加一次大型技術會議。 ### 地位 狀態追蹤您的進度。它可以是“未開始”“進行中”“已完成”或“暫停”這有助於追蹤您的旅程並保持動力。 ### 紀念品 本專欄是一個獨特的補充,旨在紀念這一成就。它可以是實體、數位徽章、部落格文章,甚至是照片。例如,組裝 PC 的紀念品可能是已完成設定的第一張照片。 ## 結論 技術愛好者的願望清單不僅僅是目標的集合;這是技術領域個人和職業成長的路線圖。透過附加專欄提供靈感、方法、計時、追蹤和紀念成就的框架,此列表對於任何熱衷於技術的人來說都是一個動態工具。 快樂的科技冒險! --- 原文出處:https://dev.to/syki/100-bucket-list-ideas-for-programmers-506m

🔥開發者的 5 個殺手級網站😎

開發者們大家好👋 今天,我將與您分享 5 個令人驚嘆的網站,它們對您的專案開發有幫助,也可以節省您的寶貴時間⏱。 ## 1️⃣ Flaticon Flaticon 是最大的圖示資料庫。您可以在這裡找到各種圖示、介面圖示、動畫圖示和貼紙,並可以以多種格式免費下載,例如 PNG、SVG、EPS、PSD 和 CSS。您也可以使用 Adobe After Effect 編輯現有圖示。 ![Flaticon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nu8u01iceaj2vaxth3dq.png) _造訪此處的網站:[https://www.flaticon.com](https://www.flaticon.com)_ ## 2️⃣ Omatsuri Omatsuri 是一個開源 Web 應用程式,為開發人員提供 12 種不同的工具。它就像“多合一”應用程式。 - **三角形產生器:** 此工具用於為元素和偽元素產生 css 三角形樣式。 - **色調產生器:** 此工具用於產生給定顏色的色調和不同色調。 - **漸層產生器:** 此工具用於產生線性和徑向 css 漸層。 - **頁面分隔線:** 此工具用於使用 css 和 svg 影像產生頁面分隔線。 - **SVG 壓縮器:** 此工具用於使用 SVGO 壓縮 SVG 影像並轉換為 React 元件。 - **SVG -> JSX Converter:** 此工具用於將 SVG 圖示和插圖轉換為 React 元件或其他支援 JSX 的函式庫的元件。 - **base64 編碼器:** 此工具用於將圖像或檔案轉換為 Base64 並產生用作背景圖像的樣式。 - **虛假資料產生器:** 以 JSON Schema 格式產生各種真實的虛假資料,如地址、頭像、姓名、電話等。 - **符號集合:** 此工具包含常用 html 符號的集合:箭頭、標記、貨幣符號等。 - **Lorem ipsum:** 此工具產生不同類型的 lorem ipsum 文字。 - **CSS 遊標:** 這也是所有現有 CSS 遊標屬性值的集合。 - **鍵盤事件程式碼:** 此工具提供 JavaScript 事件鍵盤程式碼值。 ![Omatsuri](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpb3m3i8epgu49ltgwbp.png) _造訪此處的網站:[https://omatsuri.app](https://omatsuri.app/) Github 連結:[https://github.com/rtivital/omatsuri](https://github.com/rtivital/omatsuri)_ ## 3️⃣ RapidAPI RapidAPI 是最大的 API 中心。它提供了大量涵蓋天氣、社交媒體、電子商務、金融等各個類別的 API。您可以輕鬆搜尋 API 並將其整合到您的應用程式中。這裡並非所有 API 金鑰都是免費的,該網站提供免費和付費訂閱。 對於某些 api 金鑰,您需要付費才能使用它們並將其整合到您的應用程式中。 ![快速 API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5o23n6vs7v4xhd8lmt74.png) _造訪此處的網站:[https://rapidapi.com/hub](https://rapidapi.com/hub)_ ## 4️⃣ Storyset Storyset 平台為您提供精彩的免費客製化插圖,供您在專案中使用。使用 Storyset,您可以輕鬆自訂、製作動畫和下載插圖,以建立精美的應用程式,尤其是登陸頁面。它還提供了編輯插圖以及以 PNG 和 SVG 格式下載的選項。 ![故事集](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2gzkwislxpeh1dy8ru0r.png) _造訪此處的網站:[https://storyset.com](https://storyset.com)_ ## 5️⃣ Transform Transform.tools 是一個開源網站,讓您能夠轉換各種元素,例如 HTML 到 JSX、JavaScript 到 JSON、CSS 到 JS 物件等等。每當您需要快速轉換或轉換任何內容時,它都將真正節省時間。 ![轉換](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyc4vvr7luvc93l4a6hd.png) _造訪網站:[https://transform.tools](https://transform.tools) Github連結:[https://github.com/ritz078/transform](https://github.com/ritz078/transform)_ ⬇ 如果您使用過這些工具,請在下面發表評論並分享您的經驗。另外,提及您最常使用的工具。我會將它們包含在下一篇文章中。 感謝您的閱讀❤ --- 原文出處:https://dev.to/dev_kiran/top-5-killer-websites-for-developers-2knm

🚨 🚀 25 個原因(你必須知道!!)為什麼「Listicles」對 dev.to 不利 🤯 👿 🚨

是的,這篇文章的標題就是一個例子,類似於許多所謂的「Listicles」之一,這些「Listicles」一直在開發人員中亂扔。它們的標題充滿了表情符號和「你必須知道」或「對任何開發人員來說都是必不可少的」之類的詞語,它們吸引了我們人類天生的好奇心。 ## 重要的! ### 並非所有清單都不好! 請遵循以下準則來建立一個好的指南: - 確定目的:明確定義清單的目的和目標。您想提供讀者什麼資訊或價值? - 選擇相關表情符號:選擇與內容相符並有助於您想要傳達的訊息的表情符號。確保它們不會讓人不知所措或分散注意力。 - 提供有意義的內容:專注於建立內容豐富且寫得好的文本,為讀者提供價值。表情符號應該補充內容而不是掩蓋內容。 - 評估整體影響:在發布之前,請檢查清單文章,以確保標題中使用表情符號或潛在的標題誘餌文字可以增強讀者的體驗並支持文章的預期目的。 透過執行這些步驟,您可以建立一個清單文章,該文章可以根據需要有效地包含盡可能多的表情符號和「您必須知道」文本,同時保持內容的品質和實質內容。 ![dev.to 上的點擊誘餌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5tcsqtizqxzg10tvo1l.png) ![更多](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/90n3gpjrhz13i0w17z5o.png) 本質上,它們是點擊誘餌。這些帖子訴諸了人性中被稱為“FOMO”的東西。 ## 什麼是 FOMO? FOMO 代表「害怕錯過」。這是一種以焦慮或不安的感覺為特徵的現象,這種感覺來自於相信其他人可能擁有自己未參與的有益經驗或機會。 FOMO 通常與社交媒體以及其他人分享的持續更新和活動聯繫在一起。 ## 什麼是「表情符號誘餌」? 這些貼文利用的另一件事是「表情符號誘餌」。這個概念最近才被探索。它涉及使用 🤯、🚨、👿 和 🚀 等表情符號在社交媒體平台上操縱人們的情緒。人工智慧(AI)是這方面的專家。 **為什麼這些貼文不好?** 因為幾乎不可能找到任何好的內容! 作為內容創作者,我們有責任以各種可能的方式支持我們的開發者同行的成長。在下面的評論中,讓我們討論如何在 DEV 這樣的平台上實現這一目標。我很想聽聽您對此事的看法。如何才能提升DEV社群的價值,讓所有開發者不那麼難以承受? 請記住,成為一名出色的開發人員不僅僅需要了解所有可用的工具或資源。這是關於磨練解決問題的能力、培養創造力和擁抱終身學習。讓我們優先考慮這些基本技能,其餘的就自然而然了。 而且,正如所承諾的,這裡有 🚀 🚨 對於 dev.to 🤯 🚨 來說「Listicles」不好的 25 個原因。當您閱讀清單文章時,請考慮它如何反映其內容(畢竟,它本身就是清單文章) {% 可折疊 點選展開並查看清單 %} 1.內容淺薄:清單文章和點擊誘餌通常優先考慮簡潔性而不是深度,導致主題覆蓋膚淺。 2. 缺乏脈絡:它們提供的脈絡或背景資訊有限,使讀者的理解支離破碎。 3. 過度簡化:複雜的主題可能會過度簡化以適應清單格式,導致誤解和誤解。 4.標題誘餌:使用聳人聽聞或誤導性的標題來吸引點擊,損害內容的完整性。 5. 誤導性資訊:清單文章和點擊誘餌可能包含不準確或過時的訊息,導致讀者誤入歧途。 6. 缺乏可信度:有些缺乏可靠的來源或參考資料,導致難以驗證所提供資訊的準確性。 7. 內容重複:類似主題的清單文章經常有重複的內容,對尋求新見解的讀者沒有什麼價值。 8.缺乏深度:由於篇幅限制,清單文章很少深入研究某個主題的複雜或微妙的面向。 9. 阻礙批判性思考:清單文章和標題誘餌提供填鴨式訊息,阻礙讀者進行批判性思考和分析。 10. 忽略替代觀點:他們通常提出單一觀點,排除不同或互相衝突的意見。 11. 貨幣化高於品質:有些人優先考慮透過廣告創造收入,而不是製作高品質、資訊豐富的內容。 12. 不切實際的期望:清單文章和點擊誘餌可能會在沒有適當支持的情況下做出廣泛的主張或承諾,從而造成不切實際的期望。 13.內容研究不足:由於急於快速製作內容,一些內容缺乏徹底的研究和事實查核。 14. 缺乏原創性:許多人重複使用其他來源的內容,但沒有加入任何獨特的價值或見解。 15. 話題過度飽和:熱門話題經常被重複提及,導致社群充斥著冗餘內容。 16. 忽略長篇寫作:清單文章和標題誘餌掩蓋了長篇文章,削弱了深入分析的重要性。 17. 抑制學習:清單文章和點擊誘餌的小規模性質可能會阻礙讀者尋求全面的學習經驗。 18.注意力持續時間縮短:持續閱讀清單文章和點擊誘餌可能會導致注意力持續時間縮短,並減少對詳細內容的關注。 19. 浪費點擊:清單文章和點擊誘餌可能會用誘人的標題來吸引讀者,但結果卻是提供膚淺或無用的內容。 20. 失去細微差別:複雜的主題在簡化為編號列表時就失去了細微差別,從而過度簡化了重要細節。 21. 缺乏參與度:清單文章和點擊誘餌由於其膚淺的性質,經常無法產生有意義的討論或社區參與。 22. 創造力下降:它們可能會扼殺創意表達和原創性,因為作者覺得必須遵守清單格式。 23. 寫作標準下降:對快速清單文章和標題誘餌的需求可能會降低社群內寫作的整體品質和標準。 24. 延續線上瀏覽:清單文章和點擊誘餌助長了瀏覽而不是深度閱讀的文化,影響了整體理解。 25. 專業知識貶值:它們可能削弱主題專家的專業知識,因為他們的見解被濃縮或過度簡化。 {% 結束折疊 %} 我希望您覺得這篇文章很有趣!如果您對我做的其他事情感興趣,請查看我的網站: [https://the-best-codes.github.io/](https://the-best-codes.github.io/?ref=dev.to_post&click=https://dev.to/best_codes/25-原因-你-必須-知道-為什麼-listicles-are-bad-for-devto-1hok/&site=https://dev.to/&reason=best-codes-bad-listicles-post) 謝謝閱讀! _2023年12月13日:_ _更新說明:_ 您可能已經看過我的一些帖子,“面向 Web 開發人員的 11 個令人驚嘆的書籤🔖 🚀(它們是什麼?🤔)”、“6️⃣ 常見瀏覽器:它們到底有多安全? 🔒 😯」和「為無聊的程式設計師提供的 10 個有趣的 Web 開發專案創意 👍」。我相信這些不是“列表文章”或標題誘餌,因為它們並不淺薄,而且它們不僅僅是一個列表。如果您對其中任何一個有不同的意見,請告訴我如何改進它們! --- 原文出處:https://dev.to/best_codes/25-reasons-you-must-know-why-listicles-are-bad-for-devto-1hok

為 2023 年準備好你自己的 DEV 🎁

隨著每個人和他們的貓為他們的應用程式建立一個“2023 Wrapped”,我無法阻止,不得不為這個很棒的 dev.to 社區建立一個小型開源應用程式 🥰 造訪[devto-wrapped.sliplane.app](https://devto-wrapped.sliplane.app/?username=code42cate),輸入您的用戶名,看看您作為dev.to 的作者在2023 年取得了什麼成就! **無需 API 金鑰或登入!** 這是我在 dev.to 的第一年的經驗: ![我的包裹](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4zst6ibuahiq6wtk0e1.png) PS:在評論中分享你的截圖,我會隨機挑選一個人,給他們發送一些免費的開發者貼紙作為提前的聖誕禮物🎅🎁 不管怎樣,你來這裡是為了學習一些東西,所以讓我們深入研究程式碼吧! ## 教程 建立這個小應用程式的速度對我來說至關重要,因此我決定使用我最近使用的自己的[Hackathon Starter Template](https://dev.to/code42cate/how-to-win-any-hackathon -3i99)寫了關於。我剝離了一些我不需要的功能,從而產生了一個非常精簡的 monorepo: 1.Next.js + Tailwind 2. ShadcnUI 你可以在這個[Github儲存庫](https://github.com/Code42Cate/devto-wrapped)中看到所有內容 ### 設定 如果您想長期關注並親自嘗試一下,請按照以下步驟操作: ``` # Clone repository git clone https://github.com/Code42Cate/devto-wrapped.git # Install dependencies pnpm install # Start app pnpm run dev --filter web ``` 該應用程式現在應該從 http://localhost:3000 啟動。如果它不起作用,請在評論中告訴我! ### 存取 dev.to 資料 這個小應用程式最有趣的部分可能是我們如何存取 dev.to 資料。雖然有幾種方法可以解決這個問題,但我有一些要求幫助我決定前進的方向: 1. 不抓取 - 花費太長時間,我希望資料可用 <1 秒 2. 僅公開資料 - 我不想向使用者詢問 API 金鑰或使用我自己的 3.不需要資料庫-我很懶,想避免無用的複雜性 這為我們提供了兩種可能的獲取資料的方式: 1. [記錄和未經驗證的 API 呼叫](https://developers.forem.com/api/v1) 2. 即使您未登錄,dev.to 網站也會進行未記錄的公開 API 呼叫 考慮到這兩種獲取資料的方式,我們基本上可以獲得 3 類資料: 1.使用API公開使用者資訊:`dev.to/api/users/by_username` 2. 使用 `dev.to/search/feed_content` API 和 `class_name=Article` 發布帖子 3. 包含 `dev.to/search/feed_content` 和 `class_name=Comment&search_fields=xyz` 的搜尋查詢的評論 這些 API 呼叫都是在伺服器端進行的,以加快請求速度,可以在「/apps/web/actions/api.ts」中找到。由於這只是組合在一起,因此功能相當簡單,錯誤處理也非常少: ``` export async function getUserdata(username: string): Promise<User | undefined> { const res = await fetch( `https://dev.to/api/users/by_username?url=${username}`, ); if (!res.ok) { return undefined; } const data = await res.json(); return data as User; } ``` 對於這個用例來說,這很好,但如果您不希望用戶發生意外崩潰,請記住正確捕獲異常並驗證您的類型😵 ### 計算統計資料 計算統計資料出奇地容易,主要是因為我們的資料非常小。即使你每天發帖,我們只會瀏覽 365 個帖子。迭代 365 個專案的陣列幾乎不需要時間,這給了我們很大的空間來完成工作,而無需關心效能!您在頁面上看到的每個統計資料都是在單一函數中計算的。以「總反應」為例: ``` const reactionsCount = posts?.reduce( (acc: number, post: Article) => acc + post.public_reactions_count, 0, ); ``` 我們需要做的就是檢查帖子陣列並總結每個帖子的“public_reactions_count”數量。田田,完成! 即使對於更複雜的,它也只不過是一個嵌套循環: ``` const postsPerTag: Record<string, number> = posts?.reduce( (acc: Record<string, number>, post: Article) => { post.tag_list.forEach((tag) => { acc[tag] = acc[tag] ? acc[tag] + 1 : 1; }); return acc; }, {} as Record<string, number>, ); ``` ### 前端 由於這是使用 Next.js 建構的,因此所有內容都可以在「/apps/web/app/page.tsx」檔案中找到。 在元件的頂部,您可以先看到我們如何取得資料並檢查使用者是否存在或是否有足夠的資料來顯示任何內容: ``` const user = await getUserdata(username); if (!user) { return <EmptyUser message="This user could not be found 🫠" />; } const stats = await getStats(user.id.toString()); const mentionsCount = await getMentionedCommentCount(user.username); if (stats.postCount === 0) { return <EmptyUser message="This user has no posts 🫠" />; } ``` 不同的統計資料都是它們自己的元件,它們是 CSS 網格的一部分,看起來像這樣(縮短) ``` <div className="grid grid-cols-2 gap-2 w-full text-sm text-gray-800"> <PublishedPostsCard count={stats.postCount} /> <ReactionsCard count={stats.reactionsCount} /> <BusiestMonthCard busiestMonth={stats.busiestMonth} postsPerMonth={stats.postsPerMonth} /> <CommentsCard count={stats.commentsCount} /> <ReadingTimeCard readingTime={stats.readingTime} totalEstimatedReadingTime={stats.totalEstimatedReadingTime} /> </div> ``` 這些元件都是「啞」的,這意味著它們只負責顯示資料。他們不獲取或計算任何東西。其中大多數都非常簡單,就像這張「最佳貼文」卡: ``` import Image from "next/image"; import { Article } from "@/actions/api"; export default function BestPostCard({ post, coverImage, }: { post: Article; coverImage: string; }) { return ( <div className="flex w-full flex-col justify-between gap-2 rounded-xl border border-gray-300 bg-white p-4 shadow-md"> Your fans really loved this post: <br /> <Image src={coverImage} alt={post.title} width={500} height={500} className="rounded-md border border-gray-300" /> <a className="font-semibold underline-offset-2" href={`https://dev.to${post.path}`} > {post.title} </a> </div> ); } ``` ### 部署 為了部署我們的應用程式,我們將對其進行dockerize,然後使用Sliplane(稍微有偏見,我是聯合創始人!)將其託管在我們自己的[Hetzner Cloud](https://www.hetzner.com /cloud) 伺服器上。我在[上一篇部落格文章](https://dev.to/sliplane/understanding-nextjs-docker-images-2g08)中介紹瞭如何對Next.js 應用程式進行docker 化,這基本上是相同的,只是做了一些小的更改適應我的 Turborepo 設定:) ``` # src Dockerfile: https://github.com/vercel/turbo/blob/main/examples/with-docker/apps/web/Dockerfile FROM node:18-alpine AS alpine # setup pnpm on the alpine base FROM alpine as base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN pnpm install turbo --global FROM base AS builder # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat RUN apk update # Set working directory WORKDIR /app COPY . . RUN turbo prune --scope=web --docker # Add lockfile and package.json's of isolated subworkspace FROM base AS installer RUN apk add --no-cache libc6-compat RUN apk update WORKDIR /app # First install the dependencies (as they change less often) COPY .gitignore .gitignore COPY --from=builder /app/out/json/ . COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml COPY --from=builder /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml RUN pnpm install # Build the project COPY --from=builder /app/out/full/ . COPY turbo.json turbo.json RUN turbo run build --filter=web # use alpine as the thinest image FROM alpine AS runner WORKDIR /app # Don't run production as root RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs USER nextjs COPY --from=installer /app/apps/web/next.config.js . COPY --from=installer /app/apps/web/package.json . # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public CMD node apps/web/server.js ``` 在 Docker 化並推送到 Github 儲存庫後,我們需要做的就是在 Sliplane 中建立一個新服務並選擇我們想要託管的伺服器。我已經有一台伺服器,在上面執行一些小型專案,所以我只使用該伺服器: ![Sliplane 建立服務](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2r1wfded0cy9vhw103dx.png) 點擊「部署」後,需要幾分鐘時間來建置並啟動我們的 Docker 映像。可以在日誌檢視器中監視進度: ![Sliplane 日誌檢視器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpmxb1jlp540qvblxmoa.png) 第一次成功部署後,我們將獲得一個可以存取我們的應用程式的免費子網域,或者我們可以加入自己的自訂網域: ![網域](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tc7h2eu1ctw8o5xeq9xp.png) 就是這樣!我們的應用程式在線,世界上每個人都可以存取,並且不會產生令人驚訝的無伺服器帳單 🤑 感謝您到目前為止的閱讀,不要忘記用您的截圖進行評論,以_可能_贏得一些貼紙😊 乾杯,喬納斯 --- 原文出處:https://dev.to/code42cate/devto-wrapped-2023-13o

🧙‍♂️ 使用 ChatGPT 助理產生部落格 🪄 ✨

# 長話短說;博士 我們都已經看到了 ChatGPT 的功能(這對任何人來說都不陌生)。 很多文章都是使用 ChatGPT 一遍又一遍地寫的。 **實際上**,DEV 上的文章有一半是用 ChatGPT 寫的。 你可以使用一些[AI內容偵測器](https://copyleaks.com/ai-content- detector)來檢視。 問題是,ChatGPT 永遠不會產生一些非凡的內容,除了它內部已經有(經過訓練/微調)的內容。 但有一種方法可以超越目前使用 RAG(OpenAI 助理)訓練的內容。 [上一篇](https://dev.to/triggerdotdev/train-chatgpt-on-your-documentation-1a9g),我們討論了在您的文件上「訓練」ChatGPT;今天,讓我們看看如何從中製作出很多內容。我們將: - 使用 Docusaurus 建立新的部落格系統。 - 詢問 ChatGPT,為我們寫一篇與文件相關的部落格文章。 ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ms26qb0uahpi898s0qun.gif) --- ## 你的後台工作平台🔌 [Trigger.dev](https://trigger.dev/) 是一個開源程式庫,可讓您使用 NextJS、Remix、Astro 等為您的應用程式建立和監控長時間執行的作業! &nbsp; [![GiveUsStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bm9mrmovmn26izyik95z.gif)](https://github.com/triggerdotdev/trigger.dev) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 {% cta https://github.com/triggerdotdev/trigger.dev %} 為 Trigger.dev 儲存庫加註星標 ⭐️ {% endcta %} --- ## 上次回顧 ⏰ - 我們建立了一個作業來取得文件 XML 並提取所有 URL。 - 我們抓取了每個網站的 URL 並提取了標題和內容。 - 我們將所有內容儲存到文件中並將其發送給 ChatGPT 助手。 - 我們建立了一個 ChatBot 畫面來詢問 ChatGPT 有關文件的資訊。 您可以在此處找到上一個[教學]的完整原始程式碼(https://github.com/triggerdotdev/blog/tree/main/openai-assistant)。 --- ![工具](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4adju83b5s1k0qozh3x.png) ## 稍作修改⚙️ 上次,我們建立了一個文件助理。我們寫: ``` You are a documentation assistant, loaded with documentation from ' + payload.url + ', return everything in an MD format. ``` 讓我們將其更改為部落格作者,請轉到“jobs/process.documentation.ts”第 92 行,並將其替換為以下內容: ``` You are a content writer assistant. You have been loaded with documentation from ${payload.url}, you write blog posts based on the documentation and return everything in the following MD format: --- slug: [post-slug] title: [post-title] --- [post-content] ``` 使用“slug”和“title”非常重要,因為這是 Docusaurus 的格式 - 我們的部落格系統可以接受(當然,我們也以 MD 格式發送所有輸出) --- ![Docusaurus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gu8wlh7qk8e3rh6mz35v.png) ## 多庫龍🦖 您可以使用多種類型的部落格系統! 對於我們的用例,我們將使用 Docusaurus,它可以讀取基於 MD 的格式(我們從 ChatGPT 請求的輸出)。 **我們可以透過執行來安裝 Docusaurus:** ``` npx create-docusaurus@latest blog classic --typescript ``` 接下來,我們可以進入已建立的目錄並執行以下命令: ``` npm run start ``` 這將啟動 Docusaurus。你可以關註一下。還有一個名為“blog”的附加目錄,其中包含所有部落格文章;這是我們保存 ChatGPT 產生的部落格文章的地方。 ![範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgo25rlkw85nfvbh0y4s.png) --- ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3oxjtli1dn9i9klnj5t.png) ## 產生部落格 📨 我們需要創造一個就業機會 - 取得部落格標題 - 使用 ChatGPT 產生完整的部落格文章 - 將其保存到我們部落格上的 MD 文件中 我們可以輕鬆地使用 ChatGPT 來實現這一點! 前往“jobs”資料夾並新增一個名為“process.blog.ts”的新檔案。新增以下程式碼: ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {openai} from "@openai-assistant/helper/open.ai"; import {writeFileSync} from "fs"; import slugify from "slugify"; client.defineJob({ // This is the unique identifier for your Job, it must be unique across all Jobs in your project. id: "process-blog", name: "Process Blog", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.blog.event", schema: object({ title: string(), aId: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { const {title, aId} = payload; const thread = await io.openai.beta.threads.create('create-thread'); await io.openai.beta.threads.messages.create('create-message', thread.id, { content: ` title: ${title} `, role: 'user', }); const run = await io.openai.beta.threads.runs.createAndWaitForCompletion('run-thread', thread.id, { model: 'gpt-4-1106-preview', assistant_id: payload.aId, }); if (run.status !== "completed") { console.log('not completed'); throw new Error(`Run finished with status ${run.status}: ${JSON.stringify(run.last_error)}`); } const messages = await io.openai.beta.threads.messages.list("list-messages", run.thread_id, { query: { limit: "1" } }); return io.runTask('save-blog', async () => { const content = messages[0].content[0]; if (content.type === 'text') { const fileName = slugify(title, {lower: true, strict: true, trim: true}); writeFileSync(`./blog/blog/${fileName}.md`, content.text.value) return {fileName}; } }); }, }); ``` - 我們加入了一些必要的變數: - `title` 部落格文章標題 - `aId` 上一篇文章中新增的助手 ID。 - 我們為助手建立了一個新線程(`io.openai.beta.threads.create`) - 我們無法在沒有任何線程的情況下質疑它。與之前的教程不同,在這裡,我們對每個請求建立一個新線程。我們不需要對話中最後一條訊息的上下文。 - 然後,我們使用部落格標題為線程(`io.openai.beta.threads.messages.create`)新增訊息。我們不需要提供額外的說明 - 我們已經在第一部分完成了該部分😀 - 我們執行 `io.openai.beta.threads.runs.createAndWaitForCompletion` 來啟動進程 - 通常,您需要某種每分鐘執行一次的遞歸來檢查作業是否完成,但是 [Trigger.dev]( http://Trigger .dev)已經加入了一種執行進程並同時等待它的方法🥳 - 我們在查詢正文中執行帶有“limit: 1”的“io.openai.beta.threads.messages.list”,以從對話中獲取第一則訊息(在ChatGPT 結果中,第一則訊息是最後一條訊息) 。 - 然後,我們使用「writeFileSync」從 ChatGPT 取得的值來儲存新建立的部落格 - 確保您擁有正確的部落格路徑。 轉到“jobs/index.ts”並加入以下行: ``` export * from "./process.blog"; ``` 現在,讓我們建立一個新的路由來觸發該作業。 前往“app/api”,建立一個名為“blog”的新資料夾,並在一個名為“route.tsx”的新檔案中 新增以下程式碼: ``` import {client} from "@openai-assistant/trigger"; export async function POST(request: Request) { const payload = await request.json(); if (!payload.title || !payload.aId) { return new Response(JSON.stringify({error: 'Missing parameters'}), {status: 400}); } // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "process.blog.event", payload }); return new Response(JSON.stringify({eventId}), {status: 200}); } ``` - 我們檢查標題和助理 ID 是否存在。 - 我們在 [Trigger.dev](http://Trigger.dev) 中觸發事件並發送訊息。 - 我們將事件 ID 傳送回客戶端,以便我們可以追蹤作業的進度。 --- ![前端](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgh52s7mxd20w91kr3c9.png) ## 前端🎩 沒什麼好做的! 在我們的「components」目錄中,建立一個名為「blog.component.tsx」的新檔案和以下程式碼: ``` "use client"; import {FC, useCallback, useEffect, useState} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import {SubmitHandler, useForm} from "react-hook-form"; import {useEventRunDetails} from "@trigger.dev/react"; interface Blog { title: string, aId: string; } export const BlogComponent: FC<{list: ExtendedAssistant[]}> = (props) => { const {list} = props; const {register, formState, handleSubmit} = useForm<Blog>(); const [event, setEvent] = useState<string | undefined>(undefined); const addBlog: SubmitHandler<Blog> = useCallback(async (param) => { const {eventId} = await (await fetch('/api/blog', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(param) })).json(); setEvent(eventId); }, []); return ( <> <form className="flex flex-col gap-3 mt-5" onSubmit={handleSubmit(addBlog)}> <div className="flex flex-col gap-1"> <div className="font-bold">Assistant</div> <select className="border border-gray-200 rounded-xl py-2 px-3" {...register('aId', {required: true})}> {list.map(val => ( <option key={val.id} value={val.aId}>{val.url}</option> ))} </select> </div> <div className="flex flex-col gap-1"> <div className="font-bold">Title</div> <input className="border border-gray-200 rounded-xl py-2 px-3" placeholder="Blog title" {...register('title', {required: true})} /> </div> <button className="border border-gray-200 rounded-xl py-2 px-3 bg-gray-100 hover:bg-gray-200" disabled={formState.isSubmitting}>Create blog</button> </form> {!!event && ( <Blog eventId={event} /> )} </> ) } export const Blog: FC<{eventId: string}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); if (data?.status !== 'SUCCESS') { return <div className="pointer bg-yellow-300 border-yellow-500 p-1 px-3 text-yellow-950 border rounded-2xl">Loading</div> } return ( <div> <a href={`http://localhost:3000/blog/${data.output.fileName}`}>Check blog post</a> </div> ) }; ``` - 我們使用「react-hook-form」來輕鬆控制我們的輸入。 - 我們讓使用者選擇他們想要使用的助手。 - 我們建立一個包含文章標題的新輸入。 - 我們將所有內容傳送到先前建立的路由並傳回作業的「eventId」。 - 我們建立一個新的「<Blog />」元件,該元件顯示載入直到事件完成,並使用新建立的教程新增指向我們部落格的連結。 將元件加入我們的“components/main.tsx”檔案中: ``` {assistantState.filter(f => !f.pending).length > 0 && <BlogComponent list={assistantState} />} ``` 我們完成了! ![完成](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkm37v5idrxexjje2u3o.png) 現在,讓我們新增部落格標題並點擊「生成」。 ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gosm1f1ttz3q1m0atu7s.png) --- ![圖片](https://res.cloudinary.com/practicaldev/image/fetch/s--uTFwMeAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3。 amazonaws.com/uploads/articles/0half2g6r5zfn7asq084.png) ## 讓我們聯絡吧! 🔌 作為開源開發者,您可以加入我們的[社群](https://discord.gg/nkqV9xBYWy) 做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: https://github.com/triggerdotdev/blog/tree/main/openai-blog-writer 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/generate-blogs-with-chatgpt-assistant-1894

keepHQ 如何獲得前 2,000 顆星!

我很高興與 [Keep](https://github.com/keephq/keep) 的執行長兼聯合創始人 Tal 交談。 它最初是一個 CLI 工具,隨著時間的推移,變成了一個警報聚合工具。 如今,他們擁有近 3,000 顆星。 **您可以在這裡觀看完整影片:** {% 嵌入 https://www.youtube.com/watch?v=eykb1zbDwQo %} <小時/> 開始 ------------ 他們製作了一個非常基本的警報 CLI 工具並將其發佈在 Hackernews 上 - **“顯示 HN:”** 他們乘坐飛機,當他們著陸時,**他們看到了 600 顆星星**。 [Hackernews](https://hackernews.com?utm_source=nevo.github20k.com&utm_medium=referral&utm_campaign=how-keephq-got-their-first-2-000-stars)是一個有趣的網站。它非常醜陋,牽引力很大,而且很難進入。 我在 Hackernews 上看到並推出了很多產品。雖然在 Hackernews 上發表一篇文章很困難,但 **「Show HN」** 的文章要容易得多。 這通常是一個秘密武器,因為你也許可以每年做一次 - 最好與更多管道合作,以獲得更好的機會在 GitHub 上流行。 他們創造了更多工具,例如[gnip](https://www.gnip.io/?utm_source=nevo.github20k.com&utm_medium=referral&utm_campaign=how-keephq-got-their-first-2-000-stars),以及雖然這個工具今天沒有帶來大量流量,但在發布之日就帶來了許多流量。 您會發現,對於您建立的每個副專案,您都可以在 Hackernews (Show HN) 和 Product Hunt 中啟動它。 我的建議? **目標是每月發布一次新專案。** **HACK:** 最近,一些隨機的人在 Hackernews 上發布了 **Novu** 組織頁面。我不知道它是如何被接受的,但它為我們帶來了 400 顆星。 [查看此處的貼文](https://news.ycombinator.com/item?id=38419513&utm_source=nevo.github20k.com&utm_medium=referral&utm_campaign=how-keephq-got-their-first-2-000-stars) <小時/> ![](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/7eda5059-74b8-4b7a- 9468-d77cce002b2c/image.png?t=1701432843) 與社區一起尋找產品 ------------------------------------------- 雖然 Hackernews 對開源社群(許多早期採用者)非常友好,但 Product Hunt 感覺更像是獨立駭客/非開發人員的工具。 **目標是獲得盡可能多的讚成票並達到列表的頂部。** 您通常應該將 Product Hunt 與其他管道結合。 如果您剛開始並想要星星 - 將您的 GitHub 作為您的“存取”URL。 如果您是一家更知名的公司並且希望獲得更多潛在客戶並預訂會議,請加入您的**網站 URL。** **對於第一次啟動,我通常會瞄準 GitHub 存儲庫。** Keep 的社群很小,但仍然佔據了第一天的份額。 最好的創始人不會讓運氣引導他們。 **這是他們所做的:** * 他們在社交媒體上發布了有關其發布的訊息(像大多數人一樣) * 他們嘗試聯繫盡可能多的人來幫助他們。 * 他們建造了一個秘密武器工具! [Slackline](https://github.com/talboren/slackline?utm_source=nevo.github20k.com&utm_medium=referral&utm_campaign=how-keephq-got-their-first-2-000-stars) <小時/> ![](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/1ff5c353-9447-45cb- 946c-2a4a3f3e29a6/image.png?t=1701433047) 鬆弛的線路 ------------------- 塔爾不久前向我展示了這個工具,從那時起我就一直使用它。 當您是一家小型新創公司時,您必須完成不可能的任務,從 0 到 1。**使用您擁有的所有可能的選項。** Slackline 正是為此而設計的。您可以加入任何 Slack 群組,並向頻道中的每個可能的人發送 DM。 有幾個選項: 1. 您可以在 Slack 頻道上使用它來推動對您的產品的回饋或從社群成員那裡獲得幫助來完成不同的事情。 2. 在 Product Hunt 上向人們尋求協助。 雖然數字 2 聽起來有點冒犯,但它確實有效 - 他們獲得了當天的第一名以及名譽和榮耀🎩 <小時/> ![](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/2ac46256-fc15-4bfe- a2bb-724e305e20b9/image.png?t=1701433220) 使用賞金 -------------- 您可以對 [Algora](https://algora.io?utm_source=nevo.github20k.com&utm_medium=referral&utm_campaign=how-keephq-got-their-first-2-000-stars) 的問題進行懸賞,告訴您這會擴大你的社區——可能不會,但會給你帶來更多的可信度。 如果您剛開始,Algora 可以幫助您獲得更多貢獻者(在貢獻者清單中)並使您看起來更可信。 然而,請記住,為了錢而來的貢獻者通常不會免費做同樣的事情(並非總是如此)。 {% cta https://github.com/keephq/keep %}在 GitHub 上加星 Keep{% endcta %} --- ## 我邀請您註冊我的電子報。 若符合以下條件,本通訊對您有好處: - 您正在考慮開源您的產品(或建立新產品)。 - 您正在考慮開啟一個副產品並將其開源(以反映您的主要產品)。 - 您從事科技業,希望在沒有明星/沒有 GitHub 趨勢的情況下實現成長。 這是一份 100% 免費的時事通訊(並且永遠如此)。請隨時註冊: [https://gitroom.com](https://gitroom.com/) ![技術](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/86ywkzncq6erg44d6xy9.gif) --- 原文出處:https://dev.to/github20k/how-keephq-got-their-first-2000-stars-l7i

如何成為 10 倍速明星開發人員

如今,每個人都想成為我們所謂的「10 倍開發人員」。然而,這個術語經常被誤解和高估。 從本質上講,在我看來,高效或10 倍開發人員是指能夠利用所有可用工具來發揮其優勢的人,讓這些工具處理冗餘和重複性的任務,並使他能夠專注於複雜和創造性的工作。以下是成為 10 倍開發人員的一些提示和技巧: ## 使用腳本自動執行重複任務: 對於尋求優化工作流程的開發人員來說,透過腳本自動執行重複任務是一個遊戲規則改變者。 透過弄清楚哪些任務可以自動化,例如測試和部署,然後讓腳本處理它們,開發人員可以專注於工作中更具挑戰性的部分,並在過程中節省大量時間。 例如,此腳本建立一個新的專案資料夾,由使用者輸入命名,並在檔案總管中開啟它: ``` import os import subprocess def create_project_folder(project_name): # Create a new folder for the project os.makedirs(project_name) # Open the project folder in the file explorer subprocess.run(['explorer', project_name]) # Get the project name from the user project_name = input("Enter the name of your new project: ") # Call the function to create and open the project folder create_project_folder(project_name) ``` ## 鍵盤快速鍵掌握: 掌握程式碼編輯器或 IDE 中的鍵盤快速鍵對於加快編碼工作流程至關重要。 VS 程式碼的一些快捷方式範例: `Ctrl + P`:快速檔案導航,讓您可以按名稱開啟檔案。 `Ctrl + Shift + L`:選取目前單字的所有出現位置。 `Ctrl + /`:切換行註釋 `Ctrl + A`:選擇目前檔案中的所有行 `Ctrl + F`:尋找程式碼中的特定文本 `Ctrl + Shift + P`:開啟各種指令的指令面板。 `Alt + 向上/向下箭頭`:向上或向下移動目前行。 `Shift + 右箭頭 (→)`:選擇遊標右側的字元。 `Shift + 向左箭頭 (←)`:選擇遊標左側的字元。 「Alt + 點擊」:按住 Alt 鍵並點擊程式碼中的不同位置以建立多個遊標,從而允許您同時在這些位置進行編輯或鍵入。 ## 不要過度設計: 避免過度設計解決方案的誘惑。加入不必要的程式碼或架構複雜性是許多工程師和程式設計師遇到的常見陷阱。 然而,保持簡單不僅有利於您目前的工作流程,而且還可以讓其他人在將來更容易理解您的程式碼並就您的程式碼進行協作。 ## 掌握版本控制工作流程: 善於使用版本控制工作流程(例如使用 Git)將極大地提高您的工作效率,並幫助您的團隊在不妨礙彼此的情況下協同工作。 特別是使用 GitKraken 等工具或任何其他 GUI 替代品,提供直覺的介面,簡化分支、合併和追蹤變更等任務,使協作更加順暢。 ![GitKraken 網站圖片](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggegydp0qnryv7cbszuk.png) 如果出現問題,您可以輕鬆恢復到先前的狀態。這就像有一個安全網,可以確保每個人的工作順利配合,從而使建立軟體的整個過程更快、壓力更小。 ## 利用現有元件和函式庫: 不要重新發明輪子,而是使用經過嘗試和測試的可用解決方案。這不僅節省時間,而且使您的程式碼更加可靠和有效率。 這種方法使您能夠更多地關注專案的獨特方面。這是一種明智的策略,可以提高生產力並建立強大的軟體,而無需從頭開始。 ## 採用 HTML Emmet 進行快速原型設計: Emmet 是一個針對 Web 開發人員的工具包,可透過縮寫快速且有效率地進行編碼。 如果您使用 HTML,Emmet 可以顯著加快建立 HTML 結構的過程。 例子: ``` div>(header>ul>li*2>a)+footer>p ``` 輸出: ``` <div> <header> <ul> <li><a href=""></a></li> <li><a href=""></a></li> </ul> </header> <footer> <p></p> </footer> </div> ``` ## 利用人工智慧協助: - **GitHub 副駕駛:** 是 GitHub 與 OpenAI 合作開發的人工智慧驅動的程式碼補全工具。它透過在開發人員鍵入時產生智慧建議和自動完成來改變開發人員編寫程式碼的方式。這是迄今為止我嘗試過的最好的人工智慧工具之一。 ![GitHub Copilot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/822ubh3qe2lyezubbva5.png) - **標籤九:** 是一個人工智慧驅動的程式碼編輯器自動完成擴充。它超越了傳統的自動完成功能,使用機器學習模型來預測和建議整行或程式碼區塊。 用戶可以選擇免費使用 TabNine,但有一些限制,也可以透過訂閱來選擇專業版以獲得高級功能。 [TabNine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68un8zingjmsvnuk5pyl.png) - **聊天gpt :** ChatGPT 可以真正改變您的工作效率。例如,它可以提供有用的範例,例如建議用於測試的陣列或幫助重建程式碼片段。 ![Chatgpt 範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hi8hskndin82w2vgx10l.png) 如果您在程式設計概念上遇到困難或需要澄清,ChatGPT 可以提供快速且易於理解的解釋。這就像擁有一位知識淵博的編碼夥伴,24/7 全天候幫助您應對編碼挑戰,使您的開發過程更加順暢和高效。 ## VS 程式碼中的擴充: VS Code 中的擴充功能可以透過加入功能、自動化任務和增強開發環境來顯著提高工作效率: - **更漂亮:** Prettier 是一個固執己見的程式碼格式化程序,它會自動格式化您的程式碼,使其看起來乾淨且一致,從而使您免於手動格式化的麻煩。有了 Prettier,您的程式碼變得更加賞心悅目,您可以更加專注於編寫邏輯,而不必擔心樣式。 ![更漂亮的擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gurx061173zhqjd8lvvq.png) - **自動重新命名標籤:** 自動重命名標籤擴充就像 HTML 或 XML 的編碼助手。當您變更開始標記的名稱時,此擴充功能會自動更新結束標記以符合。 ![自動重新命名標籤擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q31o7ljpjl3ciysch7b5.png) - **更好的評論:** Better Comments 擴充功能將幫助您在程式碼中建立更人性化的註解。透過此擴展,您將能夠對註釋進行分類。 ![更好的評論擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4f45e3cjl34mcs94rx6.png) - **智慧感知:** IntelliSense 是您的程式設計助手,可在您鍵入時提供智慧程式碼補全和建議。它預測您的需求並提供相關選項,使編碼更加有效率。一些範例: ![Tailwind CSS IntelliSense 擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kcjdqgwg5n6dgn4naeuz.png) ![路徑智慧感知擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9c1hvrb4l60mx6l2mp32.png) ![npm Intellisense 擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yo40qrvwsplnbvzc2wn3.png) - **孔雀:** 當您處理大量專案並在 VSCode 視窗之間跳轉時,Peacock 非常有用。它允許您將顏色連結到每個專案,因此每當您打開它時,您都可以透過顏色快速查看您所在的視窗。 ![孔雀擴充](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gncbyqxup6iwa353q3vt.png) --- **總而言之**,結合這些策略和工具可以真正徹底改變您的編碼方法,將您轉變為更有效率、更有效率的開發人員。擁抱 10 倍思維不僅可以提高個人生產力,還可以為您的團隊做出積極貢獻。因此,繼續實施這些技巧,並觀察您的編碼之旅進入一個全新的水平。 --- 原文出處:https://dev.to/idboussadel/how-to-become-a-10x-dev-ake

在 GitHub 上發現 9️⃣ 個最佳自架 Open Source 💫

## 什麼是自架軟體? 自託管專案是指從使用者的伺服器或基礎架構安裝、管理和操作的軟體、應用程式或服務,而不是託管在外部或第三方伺服器(例如雲端服務供應商提供的伺服器)上。 這種模型可以更好地控制軟體和資料,並且通常在隱私、安全、客製化和成本效益方面受到青睞。 ### 自託管軟體對於新創公司的重要性🚀 - **資料控制和隱私🛡️**:完全控制您的資料。自託管意味著您新創公司的敏感資訊保留在內部,確保一流的隱私和安全。 - **客製化與靈活性 🔧**:客製化軟體以滿足您新創公司的獨特需求。與雲端託管服務不同,自架軟體允許進行廣泛的客製化。 - **成本效益💰**:從長遠來看更經濟實惠。自託管可以減少經常性的雲端服務費用,使其成為注重費用的新創公司的明智選擇。 - **可靠性和獨立性🌐**:不要受服務提供者的正常運作時間和政策的擺佈。自託管解決方案可確保一致的存取,這對於您的新創公司的順利運作至關重要。 - **合規性和安全性🔒**:輕鬆滿足特定的監管要求。透過管理您的伺服器,實施完全符合您新創公司需求的安全性和合規性措施。 ## 這些是您需要從 GitHub 取得的一些基本的自架開源儲存庫 👇 讓我們探索這些開源軟體,並了解它們如何徹底改變您的自架軟體解決方案方法。 ### [Swirl](https://github.com/swirlai/swirl-search):跨多個資料來源的人工智慧增強搜尋 [![Swirl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceyqeael4iamuvb97l26.jpg)](https://github.com/swirlai/swirl-search) [**Swirl**](https://github.com/swirlai/swirl-search) 是一款創新的開源軟體,利用人工智慧搜尋各種內容和資料來源,使用讀者法學碩士智慧找到最佳結果。然後,它利用生成式人工智慧提供客製化答案,整合用戶特定的資料以獲得更相關的結果。 **它解決了什麼問題,以及它如何提供優秀的開源解決方案?** - 🌐 **多重來源搜尋**:Swirl 熟練地跨資料庫、公共資料服務和企業來源進行搜尋,提供全面的搜尋解決方案。 - 🤖 **人工智慧驅動的見解**:利用人工智慧和 ChatGPT(及更多)等大型語言模型來分析和排名搜尋結果,確保高相關性和準確性。 - 🔄 **輕鬆整合**:設定和使用簡單;從 Docker 下載開始,然後根據需要擴展以合併更多來源。 **GitHub 儲存庫連結:** [GitHub 上的 Swirl](https://github.com/swirlai/swirl-search) --- ### Clickvote:將社交反應無縫整合到您的內容中 ![點擊投票](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nj42wirmciunulxyryqt.jpg) Clickvote 是一款開源工具,可輕鬆為任何線上內容加入點讚、按讚和評論,從而增強用戶在各種環境中的互動和參與。 **它解決的問題及其開源優勢:** - 🔄 **即時互動**:提供按讚、按讚和評論的即時更新,增強用戶參與度。 - 🔍 **深度分析**:透過詳細分析提供對使用者行為的洞察,幫助了解受眾偏好。 - 🚀 **可擴展性**:每秒處理無限次點擊,即使在大流量下也能確保穩健的效能。 **GitHub 儲存庫連結:** [GitHub 上的 Clickvote](https://github.com/clickvote/clickvote) --- ### Wasp:使用 React 和 Node.js 徹底改變全端 Web 開發 ![黃蜂](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pe0o1d6pys66eitva3if.jpg) Wasp 是一個尖端的開源框架,旨在簡化使用 React 和 Node.js 的全端 Web 應用程式的開發,只需一個 CLI 命令即可快速部署。 **它解決的問題及其開源優勢:** - 🚀 **快速開發**:只需幾行程式碼即可快速啟動,從而可以輕鬆建立和部署生產就緒的 Web 應用程式。 - 🛠️ **更少的樣板**:抽象複雜的全端功能,減少樣板並使維護和升級變得簡單 - 🔓 **無鎖定**:確保部署的靈活性,沒有特定的提供者鎖定和完整的程式碼控制。 **GitHub 儲存庫連結:** [GitHub 上的 Wasp](https://github.com/wasp-lang/wasp) --- ### Pezzo:利用雲端原生開源平台簡化 LLMOps ![Pezzo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uk3zt4fx8as8ngk6gmtg.jpg) Pezzo 是一個革命性的開源、開發人員優先的 LLMOps 平台,完全雲端原生,旨在增強 AI 操作的提示設計、版本管理、即時交付、協作、故障排除和可觀察性。 **它解決的問題及其開源優勢:** - 🤖 **AI 營運效率**:促進 AI 營運的無縫監控和故障排除。 - 💡 **降低成本和延遲**:輔助工具可將成本和延遲降低高達 90%,從而優化營運效率。 - 🌐 **統一提示管理**:提供單一平台來管理提示,確保簡化協作和即時 AI 變更交付。 **GitHub 儲存庫連結:** [GitHub 上的片段](https://github.com/pezzolabs/pezzo) --- ### Flagsmith:開源功能標記和遠端設定服務 ![Flagsmith](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9d9fd0rvo4od1qbrr4h.jpg) Flagsmith 是一個開源平台,提供功能標記和遠端設定服務,允許靈活的本地託管選項或透過其託管版本。 **它解決的問題及其開源優勢:** - 🚀 **功能管理**:簡化跨 Web、行動和伺服器端應用程式的功能標記的建立和管理。 - 🔧 **可自訂部署**:可部署在私有雲或在本地執行,提供託管選項的多功能性。 - 🎛️ **使用者和環境特定控制**:允許針對不同的使用者群體或環境開啟或關閉功能,增強使用者體驗和測試靈活性。 **GitHub 儲存庫連結:** [GitHub 上的 Flagsmith](https://github.com/Flagsmith/flagsmith) --- ## Digger:用於 CI 管道的開源 IaC 編排工具 ![Digger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5e0ecvgkpuzs4agevaj.jpg) Digger 是一款用於基礎設施即程式碼 (IaC) 編排的創新開源工具,可與現有 CI 管道無縫集成,以提高部署 Terraform 配置的效率和安全性。 **它解決的問題及其開源優勢:** - 🛠️ **CI/CD 整合**:將 Terraform 直接整合到現有的 CI/CD 管道中,避免需要單獨的專用 CI 系統。 - 🔐 **增強的安全性**:確保安全操作,因為雲端存取機密不與第三方服務共用。 - 💡 **經濟有效且高效**:無需額外的運算資源,可在現有 CI 基礎設施中本機執行 Terraform。 - 🎚️ **高級功能**:提供諸如拉取請求評論中的 Terraform 計劃和應用程式、私有執行器、對 RBAC 的 OPA 支援、PR 級鎖和漂移檢測等功能。 **GitHub 儲存庫連結:** [GitHub 上的 Digger](https://github.com/diggerhq/digger) --- ## Keep:開源警報管理和自動化平台 ![保留](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i71cqjcdi5eto6qcz87f.jpg) Keep 是一個開源平台,旨在集中和自動化警報管理。它允許用戶將所有警報整合到一個介面中,並有效地自動化端到端流程。 **它解決的問題及其開源優勢:** - 🚨 **集中警報管理**:將所有警報整合到一處,簡化監控和回應流程。 - ⚙️ **工作流程自動化**:支援工作流程編排以自動化端到端流程,類似於 Datadog 工作流程自動化功能。 - 🔄 **廣泛的工具相容性**:支援多種可觀測工具、資料庫、通訊平台、事件管理工俱全面整合。 **GitHub 儲存庫連結:** [保留在 GitHub 上](https://github.com/keephq/keep) --- ## MeetFAQ:將您的支援管道轉變為人工智慧支援的公共常見問題解答 ![MeetFAQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4m6a9gkjswcz17iiwxof.jpg) MeetFAQ 是一款創新的開源工具,可連接到您的支援管道(例如Discord),並採用人工智慧(特別是ChatGPT)將對話轉換為全面的公共常見問題解答,可透過URL 或直接在您的網站上存取。 **它解決的問題及其開源優勢:** - 🤖 **人工智慧驅動的常見問題解答產生**:使用 ChatGPT 將支援頻道對話轉換為常見問題解答,以實現更廣泛的可存取性。 - 🌍 **公共可存取性**:向更廣泛的受眾(而不僅僅是支援管道上的受眾)提供常見問題解答,從而增強客戶聯繫。 - 💡 **客戶保留**:透過提供易於存取的公共常見問題解答來幫助防止客戶流失,確保不會遺漏任何客戶問題。 **GitHub 儲存庫連結:** [GitHub 上的 MeetFAQ](https://github.com/github-20k/meetqa) --- ### Jackson:Web 應用程式的進階 SSO 和目錄同步 ![BoxyHQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dx8wowakwnpa1wt2ehkf.jpg) Jackson 是一項開源單一登入 (SSO) 服務,可簡化 Web 應用程式驗證,支援 SAML 和 OpenID Connect 協定。它超越了 SSO,透過 SCIM 2.0 協定提供目錄同步,支援自動用戶和群組配置/取消配置。 **它解決的問題及其開源優勢:** - 🔒 **增強的身份驗證**:提供企業級 SSO 支持,簡化跨 Web 應用程式的身份驗證。 - 🔄 **目錄同步**:支援透過 SCIM 2.0 進行目錄同步,以實現高效的使用者和群組管理。 - 🌐 **協定支援**:促進 SAML 和 OpenID Connect 的集成,抽象化這些協定的複雜性以便於實施。 **GitHub 儲存庫連結:** [GitHub 上的傑克遜](https://github.com/boxyhq/jackson) --- ### 綜上所述 我們探索了九個出色的開源儲存庫。他們要不是一家新創公司,就是一個由獨立駭客變大的專案。 這些工具展示了自架的力量以及小型團隊和個人創作者蓬勃發展的創新。 感謝您與我一起經歷這些獨特專案的富有洞察力的旅程。一如既往,偉大即將到來! ![偉大即將到來](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xd1pazpm53d1kwoifb75.jpg) --- 原文出處:https://dev.to/srbhr/discover-the-9-best-self-hosted-open-source-repositories-on-github-23oc

✨ 用您的文件訓練 ChatGPT 🪄 ✨

# 簡介 ChatGPT 訓練至 2022 年。 但是,如果您希望它專門為您提供有關您網站的資訊怎麼辦?最有可能的是,這是不可能的,**但不再是了!** OpenAI 推出了他們的新功能 - [助手](https://platform.openai.com/docs/assistants/how-it-works)。 現在您可以輕鬆地為您的網站建立索引,然後向 ChatGPT 詢問有關該網站的問題。在本教程中,我們將建立一個系統來索引您的網站並讓您查詢它。我們將: - 抓取文件網站地圖。 - 從網站上的所有頁面中提取資訊。 - 使用新資訊建立新助理。 - 建立一個簡單的ChatGPT前端介面並查詢助手。 ![助手](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekre38der95twom33tqb.gif) --- ## 你的後台工作平台🔌 [Trigger.dev](https://trigger.dev/) 是一個開源程式庫,可讓您使用 NextJS、Remix、Astro 等為您的應用程式建立和監控長時間執行的作業!   [![GiveUsStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bm9mrmovmn26izyik95z.gif)](https://github.com/triggerdotdev/trigger.dev) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 --- ## 讓我們開始吧🔥 讓我們建立一個新的 NextJS 專案。 ``` npx create-next-app@latest ``` >💡 我們使用 NextJS 新的應用程式路由器。安裝專案之前請確保您的節點版本為 18+ 讓我們建立一個新的資料庫來保存助手和抓取的頁面。 對於我們的範例,我們將使用 [Prisma](https://www.prisma.io/) 和 SQLite。 安裝非常簡單,只需執行: ``` npm install prisma @prisma/client --save ``` 然後加入架構和資料庫 ``` npx prisma init --datasource-provider sqlite ``` 轉到“prisma/schema.prisma”並將其替換為以下架構: ``` // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Docs { id Int @id @default(autoincrement()) content String url String @unique identifier String @@index([identifier]) } model Assistant { id Int @id @default(autoincrement()) aId String url String @unique } ``` 然後執行 ``` npx prisma db push ``` 這將建立一個新的 SQLite 資料庫(本機檔案),其中包含兩個主表:“Docs”和“Assistant” - 「Docs」包含所有抓取的頁面 - `Assistant` 包含文件的 URL 和內部 ChatGPT 助理 ID。 讓我們新增 Prisma 客戶端。 建立一個名為「helper」的新資料夾,並新增一個名為「prisma.ts」的新文件,並在其中新增以下程式碼: ``` import {PrismaClient} from '@prisma/client'; export const prisma = new PrismaClient(); ``` 我們稍後可以使用“prisma”變數來查詢我們的資料庫。 --- ![ScrapeAndIndex](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fc05wtlc4peosr62ydnx.png) ## 刮擦和索引 ### 建立 Trigger.dev 帳戶 抓取頁面並為其建立索引是一項長期執行的任務。 **我們需要:** - 抓取網站地圖的主網站元 URL。 - 擷取網站地圖內的所有頁面。 - 前往每個頁面並提取內容。 - 將所有內容儲存到 ChatGPT 助手中。 為此,我們使用 Trigger.dev! 註冊 [Trigger.dev 帳號](https://trigger.dev/)。 註冊後,建立一個組織並為您的工作選擇一個專案名稱。 ![pic1](https://res.cloudinary.com/practicaldev/image/fetch/s--B2jtIoA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdnxq8o7el7t4utvgf1u.jpeg) 選擇 Next.js 作為您的框架,並按照將 Trigger.dev 新增至現有 Next.js 專案的流程進行操作。 ![pic2](https://res.cloudinary.com/practicaldev/image/fetch/s--K4k6T6mi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4kt7e5r1mwg60atqfka.jpeg) 否則,請點選專案儀表板側邊欄選單上的「環境和 API 金鑰」。 ![pic3](https://res.cloudinary.com/practicaldev/image/fetch/s--Ysm1Dd0r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ser7a2j5qft9vw8rfk0m.png) 複製您的 DEV 伺服器 API 金鑰並執行下面的程式碼片段來安裝 Trigger.dev。 仔細按照說明進行操作。 ``` npx @trigger.dev/cli@latest init ``` 在另一個終端中執行以下程式碼片段,在 Trigger.dev 和您的 Next.js 專案之間建立隧道。 ``` npx @trigger.dev/cli@latest dev ``` ### 安裝 ChatGPT (OpenAI) 我們將使用OpenAI助手,因此我們必須將其安裝到我們的專案中。 [建立新的 OpenAI 帳戶](https://platform.openai.com/) 並產生 API 金鑰。 ![pic4](https://res.cloudinary.com/practicaldev/image/fetch/s--uV1LwOH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ashau6i2sxcpd0qcxuwq.png) 點擊下拉清單中的「檢視 API 金鑰」以建立 API 金鑰。 ![pic5](https://res.cloudinary.com/practicaldev/image/fetch/s--Tp8aLqSa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bzc6e7f7avemeuuaygr.png) 接下來,透過執行下面的程式碼片段來安裝 OpenAI 套件。 ``` npm install @trigger.dev/openai ``` 將您的 OpenAI API 金鑰新增至「.env.local」檔案。 ``` OPENAI_API_KEY=<your_api_key> ``` 建立一個新目錄“helper”並新增一個新檔案“open.ai.tsx”,其中包含以下內容: ``` import {OpenAI} from "@trigger.dev/openai"; export const openai = new OpenAI({ id: "openai", apiKey: process.env.OPENAI_API_KEY!, }); ``` 這是我們透過 Trigger.dev 整合封裝的 OpenAI 用戶端。 ### 建立後台作業 讓我們繼續建立一個新的後台作業! 前往“jobs”並建立一個名為“process.documentation.ts”的新檔案。 **新增以下程式碼:** ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {JSDOM} from "jsdom"; import {openai} from "@openai-assistant/helper/open.ai"; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-documentation", name: "Process Documentation", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.documentation.event", schema: object({ url: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { } }); ``` 我們定義了一個名為「process.documentation.event」的新作業,並新增了一個名為 URL 的必要參數 - 這是我們稍後要傳送的文件 URL。 正如您所看到的,該作業是空的,所以讓我們向其中加入第一個任務。 我們需要獲取網站網站地圖並將其返回。 抓取網站將返回我們需要解析的 HTML。 為此,我們需要安裝 JSDOM。 ``` npm install jsdom --save ``` 並將其導入到我們文件的頂部: ``` import {JSDOM} from "jsdom"; ``` 現在,我們可以新增第一個任務。 用「runTask」包裝我們的程式碼很重要,這可以讓 Trigger.dev 將其與其他任務分開。觸發特殊架構將任務拆分為不同的進程,因此 Vercel 無伺服器逾時不會影響它們。 **這是第一個任務的程式碼:** ``` const getSiteMap = await io.runTask("grab-sitemap", async () => { const data = await (await fetch(payload.url)).text(); const dom = new JSDOM(data); const sitemap = dom.window.document.querySelector('[rel="sitemap"]')?.getAttribute('href'); return new URL(sitemap!, payload.url).toString(); }); ``` - 我們透過 HTTP 請求從 URL 取得整個 HTML。 - 我們將其轉換為 JS 物件。 - 我們找到網站地圖 URL。 - 我們解析它並返回它。 接下來,我們需要抓取網站地圖,提取所有 URL 並返回它們。 讓我們安裝“Lodash”——陣列結構的特殊函數。 ``` npm install lodash @types/lodash --save ``` 這是任務的程式碼: ``` export const makeId = (length: number) => { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < length; i += 1) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; const {identifier, list} = await io.runTask("load-and-parse-sitemap", async () => { const urls = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/g; const identifier = makeId(5); const data = await (await fetch(getSiteMap)).text(); // @ts-ignore return {identifier, list: chunk(([...new Set(data.match(urls))] as string[]).filter(f => f.includes(payload.url)).map(p => ({identifier, url: p})), 25)}; }); ``` - 我們建立一個名為 makeId 的新函數來為所有頁面產生隨機辨識碼。 - 我們建立一個新任務並加入正規表示式來提取每個可能的 URL - 我們發送一個 HTTP 請求來載入網站地圖並提取其所有 URL。 - 我們將 URL「分塊」為 25 個元素的陣列(如果有 100 個元素,則會有四個 25 個元素的陣列) 接下來,讓我們建立一個新作業來處理每個 URL。 **這是完整的程式碼:** ``` function getElementsBetween(startElement: Element, endElement: Element) { let currentElement = startElement; const elements = []; // Traverse the DOM until the endElement is reached while (currentElement && currentElement !== endElement) { currentElement = currentElement.nextElementSibling!; // If there's no next sibling, go up a level and continue if (!currentElement) { // @ts-ignore currentElement = startElement.parentNode!; startElement = currentElement; if (currentElement === endElement) break; continue; } // Add the current element to the list if (currentElement && currentElement !== endElement) { elements.push(currentElement); } } return elements; } const processContent = client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-content", name: "Process Content", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.content.event", schema: object({ url: string(), identifier: string(), }) }), run: async (payload, io, ctx) => { return io.runTask('grab-content', async () => { // We first grab a raw html of the content from the website const data = await (await fetch(payload.url)).text(); // We load it with JSDOM so we can manipulate it const dom = new JSDOM(data); // We remove all the scripts and styles from the page dom.window.document.querySelectorAll('script, style').forEach((el) => el.remove()); // We grab all the titles from the page const content = Array.from(dom.window.document.querySelectorAll('h1, h2, h3, h4, h5, h6')); // We grab the last element so we can get the content between the last element and the next element const lastElement = content[content.length - 1]?.parentElement?.nextElementSibling!; const elements = []; // We loop through all the elements and grab the content between each title for (let i = 0; i < content.length; i++) { const element = content[i]; const nextElement = content?.[i + 1] || lastElement; const elementsBetween = getElementsBetween(element, nextElement); elements.push({ title: element.textContent, content: elementsBetween.map((el) => el.textContent).join('\n') }); } // We create a raw text format of all the content const page = ` ---------------------------------- url: ${payload.url}\n ${elements.map((el) => `${el.title}\n${el.content}`).join('\n')} ---------------------------------- `; // We save it to our database await prisma.docs.upsert({ where: { url: payload.url }, update: { content: page, identifier: payload.identifier }, create: { url: payload.url, content: page, identifier: payload.identifier } }); }); }, }); ``` - 我們從 URL 中獲取內容(之前從網站地圖中提取) - 我們用`JSDOM`解析它 - 我們刪除頁面上存在的所有可能的“<script>”或“<style>”。 - 我們抓取頁面上的所有標題(`h1`、`h2`、`h3`、`h4`、`h5`、`h6`) - 我們迭代標題並獲取它們之間的內容。我們不想取得整個頁面內容,因為它可能包含不相關的內容。 - 我們建立頁面原始文字的版本並將其保存到我們的資料庫中。 現在,讓我們為每個網站地圖 URL 執行此任務。 觸發器引入了名為“batchInvokeAndWaitForCompletion”的東西。 它允許我們批量發送 25 個專案進行處理,並且它將同時處理所有這些專案。下面是接下來的幾行程式碼: ``` let i = 0; for (const item of list) { await processContent.batchInvokeAndWaitForCompletion( 'process-list-' + i, item.map( payload => ({ payload, }), 86_400), ); i++; } ``` 我們以 25 個為一組[手動觸發](https://trigger.dev/docs/documentation/concepts/triggers/invoke)之前建立的作業。 完成後,讓我們將保存到資料庫的所有內容並連接它: ``` const data = await io.runTask("get-extracted-data", async () => { return (await prisma.docs.findMany({ where: { identifier }, select: { content: true } })).map((d) => d.content).join('\n\n'); }); ``` 我們使用之前指定的標識符。 現在,讓我們在 ChatGPT 中使用新資料建立一個新檔案: ``` const file = await io.openai.files.createAndWaitForProcessing("upload-file", { purpose: "assistants", file: data }); ``` `createAndWaitForProcessing` 是 Trigger.dev 建立的任務,用於將檔案上傳到助手。如果您在沒有整合的情況下手動使用“openai”,則必須串流傳輸檔案。 現在讓我們建立或更新我們的助手: ``` const assistant = await io.openai.runTask("create-or-update-assistant", async (openai) => { const currentAssistant = await prisma.assistant.findFirst({ where: { url: payload.url } }); if (currentAssistant) { return openai.beta.assistants.update(currentAssistant.aId, { file_ids: [file.id] }); } return openai.beta.assistants.create({ name: identifier, description: 'Documentation', instructions: 'You are a documentation assistant, you have been loaded with documentation from ' + payload.url + ', return everything in an MD format.', model: 'gpt-4-1106-preview', tools: [{ type: "code_interpreter" }, {type: 'retrieval'}], file_ids: [file.id], }); }); ``` - 我們首先檢查是否有針對該特定 URL 的助手。 - 如果我們有的話,讓我們用新文件更新助手。 - 如果沒有,讓我們建立一個新的助手。 - 我們傳遞「你是文件助理」的指令,需要注意的是,我們希望最終輸出為「MD」格式,以便稍後更好地顯示。 對於拼圖的最後一塊,讓我們將新助手儲存到我們的資料庫中。 **這是程式碼:** ``` await io.runTask("save-assistant", async () => { await prisma.assistant.upsert({ where: { url: payload.url }, update: { aId: assistant.id, }, create: { aId: assistant.id, url: payload.url, } }); }); ``` 如果該 URL 已經存在,我們可以嘗試使用新的助手 ID 來更新它。 這是該頁面的完整程式碼: ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {JSDOM} from "jsdom"; import {chunk} from "lodash"; import {prisma} from "@openai-assistant/helper/prisma.client"; import {openai} from "@openai-assistant/helper/open.ai"; const makeId = (length: number) => { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < length; i += 1) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-documentation", name: "Process Documentation", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.documentation.event", schema: object({ url: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { // The first task to get the sitemap URL from the website const getSiteMap = await io.runTask("grab-sitemap", async () => { const data = await (await fetch(payload.url)).text(); const dom = new JSDOM(data); const sitemap = dom.window.document.querySelector('[rel="sitemap"]')?.getAttribute('href'); return new URL(sitemap!, payload.url).toString(); }); // We parse the sitemap; instead of using some XML parser, we just use regex to get the URLs and we return it in chunks of 25 const {identifier, list} = await io.runTask("load-and-parse-sitemap", async () => { const urls = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/g; const identifier = makeId(5); const data = await (await fetch(getSiteMap)).text(); // @ts-ignore return {identifier, list: chunk(([...new Set(data.match(urls))] as string[]).filter(f => f.includes(payload.url)).map(p => ({identifier, url: p})), 25)}; }); // We go into each page and grab the content; we do this in batches of 25 and save it to the DB let i = 0; for (const item of list) { await processContent.batchInvokeAndWaitForCompletion( 'process-list-' + i, item.map( payload => ({ payload, }), 86_400), ); i++; } // We get the data that we saved in batches from the DB const data = await io.runTask("get-extracted-data", async () => { return (await prisma.docs.findMany({ where: { identifier }, select: { content: true } })).map((d) => d.content).join('\n\n'); }); // We upload the data to OpenAI with all the content const file = await io.openai.files.createAndWaitForProcessing("upload-file", { purpose: "assistants", file: data }); // We create a new assistant or update the old one with the new file const assistant = await io.openai.runTask("create-or-update-assistant", async (openai) => { const currentAssistant = await prisma.assistant.findFirst({ where: { url: payload.url } }); if (currentAssistant) { return openai.beta.assistants.update(currentAssistant.aId, { file_ids: [file.id] }); } return openai.beta.assistants.create({ name: identifier, description: 'Documentation', instructions: 'You are a documentation assistant, you have been loaded with documentation from ' + payload.url + ', return everything in an MD format.', model: 'gpt-4-1106-preview', tools: [{ type: "code_interpreter" }, {type: 'retrieval'}], file_ids: [file.id], }); }); // We update our internal database with the assistant await io.runTask("save-assistant", async () => { await prisma.assistant.upsert({ where: { url: payload.url }, update: { aId: assistant.id, }, create: { aId: assistant.id, url: payload.url, } }); }); }, }); export function getElementsBetween(startElement: Element, endElement: Element) { let currentElement = startElement; const elements = []; // Traverse the DOM until the endElement is reached while (currentElement && currentElement !== endElement) { currentElement = currentElement.nextElementSibling!; // If there's no next sibling, go up a level and continue if (!currentElement) { // @ts-ignore currentElement = startElement.parentNode!; startElement = currentElement; if (currentElement === endElement) break; continue; } // Add the current element to the list if (currentElement && currentElement !== endElement) { elements.push(currentElement); } } return elements; } // This job will grab the content from the website const processContent = client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-content", name: "Process Content", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.content.event", schema: object({ url: string(), identifier: string(), }) }), run: async (payload, io, ctx) => { return io.runTask('grab-content', async () => { try { // We first grab a raw HTML of the content from the website const data = await (await fetch(payload.url)).text(); // We load it with JSDOM so we can manipulate it const dom = new JSDOM(data); // We remove all the scripts and styles from the page dom.window.document.querySelectorAll('script, style').forEach((el) => el.remove()); // We grab all the titles from the page const content = Array.from(dom.window.document.querySelectorAll('h1, h2, h3, h4, h5, h6')); // We grab the last element so we can get the content between the last element and the next element const lastElement = content[content.length - 1]?.parentElement?.nextElementSibling!; const elements = []; // We loop through all the elements and grab the content between each title for (let i = 0; i < content.length; i++) { const element = content[i]; const nextElement = content?.[i + 1] || lastElement; const elementsBetween = getElementsBetween(element, nextElement); elements.push({ title: element.textContent, content: elementsBetween.map((el) => el.textContent).join('\n') }); } // We create a raw text format of all the content const page = ` ---------------------------------- url: ${payload.url}\n ${elements.map((el) => `${el.title}\n${el.content}`).join('\n')} ---------------------------------- `; // We save it to our database await prisma.docs.upsert({ where: { url: payload.url }, update: { content: page, identifier: payload.identifier }, create: { url: payload.url, content: page, identifier: payload.identifier } }); } catch (e) { console.log(e); } }); }, }); ``` 我們已經完成建立後台作業來抓取和索引文件🎉 ### 詢問助理 現在,讓我們建立一個任務來詢問我們的助手。 前往“jobs”並建立一個新檔案“question.assistant.ts”。 **新增以下程式碼:** ``` import {eventTrigger} from "@trigger.dev/sdk"; import {client} from "@openai-assistant/trigger"; import {object, string} from "zod"; import {openai} from "@openai-assistant/helper/open.ai"; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "question-assistant", name: "Question Assistant", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "question.assistant.event", schema: object({ content: string(), aId: string(), threadId: string().optional(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { // Create or use an existing thread const thread = payload.threadId ? await io.openai.beta.threads.retrieve('get-thread', payload.threadId) : await io.openai.beta.threads.create('create-thread'); // Create a message in the thread await io.openai.beta.threads.messages.create('create-message', thread.id, { content: payload.content, role: 'user', }); // Run the thread const run = await io.openai.beta.threads.runs.createAndWaitForCompletion('run-thread', thread.id, { model: 'gpt-4-1106-preview', assistant_id: payload.aId, }); // Check the status of the thread if (run.status !== "completed") { console.log('not completed'); throw new Error(`Run finished with status ${run.status}: ${JSON.stringify(run.last_error)}`); } // Get the messages from the thread const messages = await io.openai.beta.threads.messages.list("list-messages", run.thread_id, { query: { limit: "1" } }); const content = messages[0].content[0]; if (content.type === 'text') { return {content: content.text.value, threadId: thread.id}; } } }); ``` - 該事件需要三個參數 - `content` - 我們想要傳送給助理的訊息。 - `aId` - 我們先前建立的助手的內部 ID。 - `threadId` - 對話的執行緒 ID。正如您所看到的,這是一個可選參數,因為在第一個訊息中,我們還沒有線程 ID。 - 然後,我們建立或取得前一個執行緒的執行緒。 - 我們在助理提出的問題的線索中加入一條新訊息。 - 我們執行線程並等待它完成。 - 我們取得訊息清單(並將其限制為 1),因為第一則訊息是對話中的最後一則訊息。 - 我們返回訊息內容和我們剛剛建立的線程ID。 ### 新增路由 我們需要為我們的應用程式建立 3 個 API 路由: 1、派新助理進行處理。 2. 透過URL獲取特定助手。 3. 新增訊息給助手。 在「app/api」中建立一個名為assistant的新資料夾,並在其中建立一個名為「route.ts」的新檔案。裡面加入如下程式碼: ``` import {client} from "@openai-assistant/trigger"; import {prisma} from "@openai-assistant/helper/prisma.client"; export async function POST(request: Request) { const body = await request.json(); if (!body.url) { return new Response(JSON.stringify({error: 'URL is required'}), {status: 400}); } // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "process.documentation.event", payload: {url: body.url}, }); return new Response(JSON.stringify({eventId}), {status: 200}); } export async function GET(request: Request) { const url = new URL(request.url).searchParams.get('url'); if (!url) { return new Response(JSON.stringify({error: 'URL is required'}), {status: 400}); } const assistant = await prisma.assistant.findFirst({ where: { url: url } }); return new Response(JSON.stringify(assistant), {status: 200}); } ``` 第一個「POST」方法取得一個 URL,並使用用戶端傳送的 URL 觸發「process.documentation.event」作業。 第二個「GET」方法從我們的資料庫中透過客戶端發送的 URL 取得助手。 現在,讓我們建立向助手新增訊息的路由。 在「app/api」內部建立一個新資料夾「message」並新增一個名為「route.ts」的新文件,然後新增以下程式碼: ``` import {prisma} from "@openai-assistant/helper/prisma.client"; import {client} from "@openai-assistant/trigger"; export async function POST(request: Request) { const body = await request.json(); // Check that we have the assistant id and the message if (!body.id || !body.message) { return new Response(JSON.stringify({error: 'Id and Message are required'}), {status: 400}); } // get the assistant id in OpenAI from the id in the database const assistant = await prisma.assistant.findUnique({ where: { id: +body.id } }); // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "question.assistant.event", payload: { content: body.message, aId: assistant?.aId, threadId: body.threadId }, }); return new Response(JSON.stringify({eventId}), {status: 200}); } ``` 這是一個非常基本的程式碼。我們從客戶端獲取訊息、助手 ID 和線程 ID,並將其發送到我們之前建立的「question.assistant.event」。 最後要做的事情是建立一個函數來獲取我們所有的助手。 在「helpers」內部建立一個名為「get.list.ts」的新函數並新增以下程式碼: ``` import {prisma} from "@openai-assistant/helper/prisma.client"; // Get the list of all the available assistants export const getList = () => { return prisma.assistant.findMany({ }); } ``` 非常簡單的程式碼即可獲得所有助手。 我們已經完成了後端🥳 讓我們轉到前面。 --- ![前端](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3s5gks1j0ojoz11b93i.png) ## 建立前端 我們將建立一個基本介面來新增 URL 並顯示已新增 URL 的清單: ![ss1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihvx4yn6uee6gritr9nh.png) ### 首頁 將 `app/page.tsx` 的內容替換為以下程式碼: ``` import {getList} from "@openai-assistant/helper/get.list"; import Main from "@openai-assistant/components/main"; export default async function Home() { const list = await getList(); return ( <Main list={list} /> ) } ``` 這是一個簡單的程式碼,它從資料庫中取得清單並將其傳遞給我們的 Main 元件。 接下來,讓我們建立“Main”元件。 在「app」內建立一個新資料夾「components」並新增一個名為「main.tsx」的新檔案。 **新增以下程式碼:** ``` "use client"; import {Assistant} from '@prisma/client'; import {useCallback, useState} from "react"; import {FieldValues, SubmitHandler, useForm} from "react-hook-form"; import {ChatgptComponent} from "@openai-assistant/components/chatgpt.component"; import {AssistantList} from "@openai-assistant/components/assistant.list"; import {TriggerProvider} from "@trigger.dev/react"; export interface ExtendedAssistant extends Assistant { pending?: boolean; eventId?: string; } export default function Main({list}: {list: ExtendedAssistant[]}) { const [assistantState, setAssistantState] = useState(list); const {register, handleSubmit} = useForm(); const submit: SubmitHandler<FieldValues> = useCallback(async (data) => { const assistantResponse = await (await fetch('/api/assistant', { body: JSON.stringify({url: data.url}), method: 'POST', headers: { 'Content-Type': 'application/json' } })).json(); setAssistantState([...assistantState, {...assistantResponse, url: data.url, pending: true}]); }, [assistantState]) const changeStatus = useCallback((val: ExtendedAssistant) => async () => { const assistantResponse = await (await fetch(`/api/assistant?url=${val.url}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } })).json(); setAssistantState([...assistantState.filter((v) => v.id), assistantResponse]); }, [assistantState]) return ( <TriggerProvider publicApiKey={process.env.NEXT_PUBLIC_TRIGGER_PUBLIC_API_KEY!}> <div className="w-full max-w-2xl mx-auto p-6 flex flex-col gap-4"> <form className="flex items-center space-x-4" onSubmit={handleSubmit(submit)}> <input className="flex-grow p-3 border border-black/20 rounded-xl" placeholder="Add documentation link" type="text" {...register('url', {required: 'true'})} /> <button className="flex-shrink p-3 border border-black/20 rounded-xl" type="submit"> Add </button> </form> <div className="divide-y-2 divide-gray-300 flex gap-2 flex-wrap"> {assistantState.map(val => ( <AssistantList key={val.url} val={val} onFinish={changeStatus(val)} /> ))} </div> {assistantState.filter(f => !f.pending).length > 0 && <ChatgptComponent list={assistantState} />} </div> </TriggerProvider> ) } ``` 讓我們看看這裡發生了什麼: - 我們建立了一個名為「ExtendedAssistant」的新接口,其中包含兩個參數「pending」和「eventId」。當我們建立一個新的助理時,我們沒有最終的值,我們將只儲存`eventId`並監聽作業處理直到完成。 - 我們從伺服器元件取得清單並將其設定為新狀態(以便我們稍後可以修改它) - 我們新增了「TriggerProvider」來幫助我們監聽事件完成並用資料更新它。 - 我們使用「react-hook-form」建立一個新表單來新增助手。 - 我們新增了一個帶有一個輸入「URL」的表單來提交新的助理進行處理。 - 我們迭代並顯示所有現有的助手。 - 在提交表單時,我們將資訊傳送到先前建立的「路由」以新增助理。 - 事件完成後,我們觸發「changeStatus」以從資料庫載入助手。 - 最後,我們有了 ChatGPT 元件,只有在沒有等待處理的助手時才會顯示(`!f.pending`) 讓我們建立 `AssistantList` 元件。 在「components」內,建立一個新檔案「assistant.list.tsx」並在其中加入以下內容: ``` "use client"; import {FC, useEffect} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import {useEventRunDetails} from "@trigger.dev/react"; export const Loading: FC<{eventId: string, onFinish: () => void}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); useEffect(() => { if (!data || error) { return ; } if (data.status === 'SUCCESS') { props.onFinish(); } }, [data]); return <div className="pointer bg-yellow-300 border-yellow-500 p-1 px-3 text-yellow-950 border rounded-2xl">Loading</div> }; export const AssistantList: FC<{val: ExtendedAssistant, onFinish: () => void}> = (props) => { const {val, onFinish} = props; if (val.pending) { return <Loading eventId={val.eventId!} onFinish={onFinish} /> } return ( <div key={val.url} className="pointer relative bg-green-300 border-green-500 p-1 px-3 text-green-950 border rounded-2xl hover:bg-red-300 hover:border-red-500 hover:text-red-950 before:content-[attr(data-content)]" data-content={val.url} /> ) } ``` 我們迭代我們建立的所有助手。如果助手已經建立,我們只顯示名稱。如果沒有,我們渲染`<Loading />`元件。 載入元件在螢幕上顯示“正在載入”,並長時間輪詢伺服器直到事件完成。 我們使用 Trigger.dev 建立的 useEventRunDetails 函數來了解事件何時完成。 事件完成後,它會觸發「onFinish」函數,用新建立的助手更新我們的客戶端。 ### 聊天介面 ![聊天介面](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u7db3qwz03d6jkk965a.png) 現在,讓我們加入 ChatGPT 元件並向我們的助手提問! - 選擇我們想要使用的助手 - 顯示訊息列表 - 新增我們要傳送的訊息的輸入和提交按鈕。 在「components」內部新增一個名為「chatgpt.component.tsx」的新文件 讓我們繪製 ChatGPT 聊天框: ``` "use client"; import {FC, useCallback, useEffect, useRef, useState} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import Markdown from 'react-markdown' import {useEventRunDetails} from "@trigger.dev/react"; interface Messages { message?: string eventId?: string } export const ChatgptComponent = ({list}: {list: ExtendedAssistant[]}) => { const url = useRef<HTMLSelectElement>(null); const [message, setMessage] = useState(''); const [messagesList, setMessagesList] = useState([] as Messages[]); const [threadId, setThreadId] = useState<string>('' as string); const submitForm = useCallback(async (e: any) => { e.preventDefault(); setMessagesList((messages) => [...messages, {message: `**[ME]** ${message}`}]); setMessage(''); const messageResponse = await (await fetch('/api/message', { method: 'POST', body: JSON.stringify({message, id: url.current?.value, threadId}), })).json(); if (!threadId) { setThreadId(messageResponse.threadId); } setMessagesList((messages) => [...messages, {eventId: messageResponse.eventId}]); }, [message, messagesList, url, threadId]); return ( <div className="border border-black/50 rounded-2xl flex flex-col"> <div className="border-b border-b-black/50 h-[60px] gap-3 px-3 flex items-center"> <div>Assistant:</div> <div> <select ref={url} className="border border-black/20 rounded-xl p-2"> {list.filter(f => !f.pending).map(val => ( <option key={val.id} value={val.id}>{val.url}</option> ))} </select> </div> </div> <div className="flex-1 flex flex-col gap-3 py-3 w-full min-h-[500px] max-h-[1000px] overflow-y-auto overflow-x-hidden messages-list"> {messagesList.map((val, index) => ( <div key={index} className={`flex border-b border-b-black/20 pb-3 px-3`}> <div className="w-full"> {val.message ? <Markdown>{val.message}</Markdown> : <MessageComponent eventId={val.eventId!} onFinish={setThreadId} />} </div> </div> ))} </div> <form onSubmit={submitForm}> <div className="border-t border-t-black/50 h-[60px] gap-3 px-3 flex items-center"> <div className="flex-1"> <input value={message} onChange={(e) => setMessage(e.target.value)} className="read-only:opacity-20 outline-none border border-black/20 rounded-xl p-2 w-full" placeholder="Type your message here" /> </div> <div> <button className="border border-black/20 rounded-xl p-2 disabled:opacity-20" disabled={message.length < 3}>Send</button> </div> </div> </form> </div> ) } export const MessageComponent: FC<{eventId: string, onFinish: (threadId: string) => void}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); useEffect(() => { if (!data || error) { return ; } if (data.status === 'SUCCESS') { props.onFinish(data.output.threadId); } }, [data]); if (!data || error || data.status !== 'SUCCESS') { return ( <div className="flex justify-end items-center pb-3 px-3"> <div className="animate-spin rounded-full h-3 w-3 border-t-2 border-b-2 border-blue-500" /> </div> } return <Markdown>{data.output.content}</Markdown>; }; ``` 這裡正在發生一些令人興奮的事情: - 當我們建立新訊息時,我們會自動將其呈現在螢幕上作為「我們的」訊息,但是當我們將其發送到伺服器時,我們需要推送事件 ID,因為我們還沒有訊息。這就是我們使用 `{val.message ? <Markdown>{val.message}</Markdown> : <MessageComponent eventId={val.eventId!} onFinish={setThreadId} />}` - 我們用「Markdown」元件包裝訊息。如果您還記得,我們在前面的步驟中告訴 ChatGPT 以 MD 格式輸出所有內容,以便我們可以正確渲染它。 - 事件處理完成後,我們會更新線程 ID,以便我們從以下訊息中獲得相同對話的上下文。 我們就完成了🎉 --- ![完成](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0half2g6r5zfn7asq084.png) ## 讓我們聯絡吧! 🔌 作為開源開發者,您可以加入我們的[社群](https://discord.gg/nkqV9xBYWy) 做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: [https://github.com/triggerdotdev/blog/tree/main/openai-assistant](https://github.com/triggerdotdev/blog/tree/main/openai-assistant) 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/train-chatgpt-on-your-documentation-1a9g

🚀 5 個專業技巧,打造無與倫比的 GitHub 自述文件! 🥊

嗨朋友👋 今天,我們來看看如何編寫一個**KILLER GitHub README 文件。** ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1qn6ckiea6i22zira7k.gif) 自述文件是任何剛接觸您的儲存庫的人的第一個接觸點。 **完善的自述文件可以提供資訊、吸引並邀請參與**。 當您啟動新專案或開發人員參與您的儲存庫至關重要時,這特別有用。 一個很好的例子是,當您向[Quine's Creator Quests](https://www.quine.sh/?utm_source=devto&utm_campaign=killer_github_profile) 提交儲存庫時,這是一種獎勵開發人員的新型開源「賞金」創造社區喜愛的酷炫新倉庫。 在創作者任務中,擁有一份寫得好的自述文件確實可以幫助您將獎品帶回家。 :錢_帶_翅膀: 如果您想參與,請在 [Quine](https://www.quine.sh/?utm_source=devto&utm_campaign=killer_github_profile) 免費註冊並前往 _Quests_。 --- ## 1️⃣ 自述文件...這是什麼? 🔮 README 文件,通常是“.txt”或“.md”文件,是專案中最重要的文件。它是開源專案的登陸頁面,也是最明顯的文件部分。 _這是 Hoppscotch 專案的自述文件範例。_ [![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwp0o21ft5cmsm6v8bvg.png)](https://github.com/hoppscotch/hoppscotch) 您的自述文件是**您為專案定調的地方**。 這就是為什麼應該包含幾個元素的原因。 ⬇️ --- ## 2️⃣ 10 個建置模組🧱 自述文件可以透過多種方式編寫;有些比其他更好。 以下是您應該能夠在專業自述文件中找到的主要部分類型。 如果以下內容與您的專案相關,我建議您嘗試將它們加入到您的自述文件中。 👇 **1.想出一個名字** 大量的儲存庫沒有好名字。花點時間想一個令人難忘的名字總是一個好的開始。 **2.寫一個介紹** 解釋專案目的和目標受眾的摘要。 **3.目錄** 許多存儲庫未能加入此部分。組織良好的目錄有助於使您的儲存庫清晰易懂。 **4.先決條件和安裝說明** 列出各種逐步安裝說明。 _這裡的清晰度至關重要。_ ✨ **5.使用者指南/示範** 本節重點介紹如何透過範例和可選命令使用您的專案。我喜歡的一種方法是加入_如何執行專案_以及用戶如何使用它的螢幕記錄。 **6。文件/幫助中心** 如果您編寫了文件、常見問題或可以充當_幫助中心_的空間,請在此處寫下它或加入其連結。 **7.貢獻** 簡要解釋如何為您的文件做出貢獻並加入指向您的 CONTRIBUTING.md 文件的連結。在這裡或在單獨的部分中,您還可以藉此機會列出您的貢獻者並感謝他們。 **8.致謝** 使用或協助取得外部資源。僅當與您的儲存庫相關時才需要此部分。 **9.聯絡資訊** 如果您正在嘗試發展專案並建立協作,本節非常有用。 **10.權限資訊** 這是指您為專案選擇的許可證類型。澄清其他人如何使用您的內容至關重要。 👍 --- ## 3️⃣ 美化它💄 自述文件可以用各種文字格式編寫,其中 Markdown 是最常見的一種。 Markdown 可讓您使用簡單的純文字語法,支援建立標題、清單、連結和其他元素,使文件更具可讀性和組織性。 **Markdown 也支援 HTML 程式碼**,這拓寬了您可以做的事情的範圍。 👇 --- ### 標識 如果您已經為專案建立了徽標,則將其新增至自述文件的開頭是標準做法。 為此,請在自述文件中新增並修改以下程式碼: ``` <p align="center"> <!-- You can add your logo in the _src_ below --> <img src="https://www.amug.com/wp-content/uploads/2016/09/you-logo-here-300x106.png" /> </p> ``` --- ### 徽章 您經常會發現好的自述文件在其介紹部分中提供了徽章。 這些可以看起來像這樣: <p對齊=“中心”> <!-- 您可以在此處新增您的徽章 --> <!-- 如果您從未加入過徽章,請前往 https://img.shields.io/badges/static-badge --> <!-- 按照說明產生 URL 連結加入到下面 --> <img src="https://img.shields.io/badge/STARS-20K-green" /> <img src="https://img.shields.io/badge/FORKS-15K-blue" /> <img src="https://img.shields.io/badge/npm-v.0.21.0-red" /> <img src="https://img.shields.io/badge/LICENSE-MIT-green" /> </p> 正如您所看到的,這些突出顯示了維護人員想要闡明的一些領域。 以下是將 _static_ 徽章新增至自述文件的方法: ``` <p align="center"> <!-- You can add your badges here --> <!-- If you have never added badges, head over to https://img.shields.io/badges/static-badge, follow the instructions and generate URL links to add below --> <img src="https://img.shields.io/badge/STARS-20K-green" /> <img src="https://img.shields.io/badge/FORKS-15K-blue" /> <img src="https://img.shields.io/badge/npm-v.0.21.0-red" /> <img src="https://img.shields.io/badge/LICENSE-MIT-green" /> </p> ``` **注意**:_有一些高級動態選項我們不會在這裡討論。_ --- ### 圖示 近年來,圖標變得相當突出。 您可以將它們新增至您的_聯絡資訊_或您的_技術堆疊_部分。您可以找到 X(_以前的 Twitter_)和 Linkedin 的圖示範例。 <p對齊=“左”> <a href="https://twitter.com/fernandezbap" target="blank"><imgalign="center" src="https://img.shields.io/badge/X-000000?style=for - the-badge&logo=x&logoColor=white" alt="fernandezbap" /></a> </p> <p對齊=“左”> <a href="https://www.linkedin.com/in/baptiste-fernandez-%E5%B0%8F%E7%99%BD-0a958630/" target="blank"><img src="https: //img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" alt="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the -badge&logo=linkedin&logoColor=white" /></a> </p> 建立您自己的: 1️⃣ 前往此儲存庫[此處](https://github.com/alexandresanlim/Badges4-README.md-Profile#-social-) 2️⃣ 找到_社交_和/或_技術堆疊_,然後“複製”其連結 3️⃣ 將連結「貼上」到如下所示的「href」中 ``` <p align="left"> <!-- Add your own socials inside "href" --> <a href="https://twitter.com/fernandezbap" target="blank"><img align="center" src="https://img.shields.io/badge/X-000000?style=for-the-badge&logo=x&logoColor=white" alt="fernandezbap" /></a> </p> <p align="left"> <a href="https://www.linkedin.com/in/baptiste-fernandez-%E5%B0%8F%E7%99%BD-0a958630/" target="blank"><img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" alt="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" /></a> </p> ``` --- ## 4️⃣ 進階技巧🛠️ ### 互動內容🎥 您可以考慮嵌入影片或小部件來獲取互動式內容。 在自述文件的_演示_部分中,您可以嵌入影片。 **⭐️提示:** 有時,影片的尺寸太大,將影片上傳到 YouTube 然後連結出來更有意義。 如果您想讓它更加直觀,這裡有加入圖片/影片縮圖的程式碼,一旦您點擊它,該縮圖就會重定向到您的 YouTube 影片。 ``` <p align="center"> <a href="THE LINK TO YOUR YOUTUBE VIDEO HERE"> <img src="YOUR IMAGE/VIDEO THUMBNAIL SOURCE HERE"/> </a> </p> ``` --- ### Markdown 掌握 ✨ 有許多進階 Markdown 功能可以讓你的 README 看起來更漂亮。 您可以查看這個很酷的[repo](https://github.com/DavidWells/advanced-markdown),其中列出了其中一些功能。 我特別喜歡的一個是 **_切換列表_** 或更常見的名稱是_可折疊部分。_ 它們對於使您的自述文件看起來簡潔明了特別有用。這是一個例子: ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0f6phwz4cwzqnm2zm5k9.png) 以下是用於建立您自己的切換清單的 MARKDOWN 模板: ``` <details> <summary>Toggle List Example</summary> ### Heading 1. ABC 2. DEF * Hello </details> ``` --- ### 額外提示🎖️ 如果您想在_貢獻者部分_中表彰您的貢獻者,您應該查看 [AllContributors](https://allcontributors.org/)。 您可以利用其機器人自動將所有最新貢獻者新增至您的自述文件。 以下是您可以獲得的內容的範例: [![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m55yntosug8hnz3i0gm.png)](https://allcontributors.org/) 您還應該查看[表情符號關鍵文件](https://allcontributors.org/docs/en/emoji-key),它使您能夠對不同類型的貢獻進行分類。 ⚡️ ---- ## 5️⃣ 利用預製模板📝 ### 有沒有為您建立自述文件的網站? 🤔 絕對地! 您可以**在那裡找到大量自述文件生成器。** 我掃描並挑選了我最喜歡的三個給你: 1️⃣ [自述文件範本](https://www.readme-templates.com/) 2️⃣ [Readme.so](https://readme.so/editor) 3️⃣ [Vercel 的 ReadME 產生器](https://readme-gen.vercel.app/) 我對這些的建議是使用它們作為基礎,然後自訂它們。 ⭐️ --- ### 你有給我一個自述文件範本嗎? 👀 我找到你了,朋友。 🫶 您可以在[此處](https://github.com/quine-sh/README-Template)找到我現成的範本。 [![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckzpitoxwi14usaymqsw.png)](https://github.com/quine-sh/README-Template) 要開始使用它: 1️⃣ `Fork` 倉庫 2️⃣ 將滑鼠停留在自述文件的「編輯」部分 3️⃣開始填寫您的資料✍️ 如果您發現此模板有用,如果您能通過**加星標**_給予它一些愛_,我將不勝感激。 🌟 --- 建立良好的自述文件是一項重要技能。 🛠️ 如何建構它可能是_存儲庫成功的決定因素。_ 請務必在評論部分分享您的專案及其完善的新自述文件! 您還可以利用這項新技能來建立很酷的編碼專案並競爭以獲得報酬。 🙌 如果您對此感興趣,請登入 Quine 並探索[任務](https://www.quine.sh/?utm_source=devto&utm_campaign=killer_github_profile),這是一個編碼、享受樂趣並獲得一些超棒獎勵的地方! 💰 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jk3g038eogbe84g7nph9.gif) 下週見。 你的開發夥伴, 巴巴💚 --- 原文出處:https://dev.to/quine/5-pro-tips-for-an-unbeatable-readme-143i

我該如何教 Git

--- 標題:我如何教 Git 發表:真實 描述: 標籤: git, 學習 canonical_url:https://blog.ltgt.net/teaching-git/ 封面圖片:https://marklodato.github.io/visual-git-guide/conventions.svg.png # 使用 100:42 的比例以獲得最佳效果。 # 發佈時間: 2023-11-26 19:17 +0000 --- 我使用 Git 已經十幾年了。八年前,我必須為一家即將建立開源專案的合作夥伴公司舉辦有關 Git(和 GitHub)的培訓課程,我將在這裡向您介紹我的教學方式。順便說一句,從那時起,我們在工作中建立了使用相同(或類似)方法的內部培訓課程。話雖如此,我並沒有發明任何東西:這很大程度上受到了其他人之前寫的內容的啟發,包括[the <cite>Pro Git</cite> book](https://git-scm. com/book/),儘管順序不同,但 <abbr title="in my view">IMO</abbr> 可以有所作為。 我寫這篇文章的原因是,多年來,我不斷看到人們實際上使用 Git,但沒有真正理解他們在做什麼;他們正在使用 Git。他們要么被鎖定在一個非常具體的工作流程中,他們被告知要遵循,並且無法適應另一個開源專案正在使用的工作流程(這也適用於開源維護人員並不真正了解外部貢獻者如何使用 Git) ),或者如果任何事情沒有按照他們想像的方式執行,或者他們在呼叫Git 命令時犯了錯誤,他們就會完全迷失。我受到 [Julia Evans](https://jvns.ca) 對 Git 的(更新)興趣的啟發而寫下來,因為她有時會在社交網絡上徵求評論。 我的目標不是真正教你有關 Git 的知識,而是更多地分享我教授 Git 的方法,以便其他可能會教導的人從中獲得靈感。因此,如果您正在學習 Git,那麼這篇文章並不是專門為您而寫的(抱歉),因此可能不是自給自足的,但希望其他學習資源的連結足以填補空白,使其成為也是有用的學習資源。如果您是視覺學習者,這些外部學習資源都是有插圖的,甚至是視覺學習的。 ## 心理模型 一旦我們清楚了為什麼我們使用VCS(版本控制系統)來記錄_commits_ 中的更改(或者換句話說,我們_將我們的更改_提交到歷史記錄;我假設你對這個術語有一定的熟悉),讓我們多了解一下Git具體來說。 我認為理解 Git 至關重要的一件事是獲得其背後概念的準確心理模型。 首先,這並不是很重要,但Git 實際上並沒有記錄_changes_,而是記錄我們文件的_snapshots_(至少在概念上是這樣;它將使用_packfiles_ 來有效地儲存內容,並且在某些情況下方實際上會儲存_changes_ –diffs–),並且會按需產生差異。不過,這有時會顯示在某些命令的結果中(例如為什麼某些命令顯示一個檔案被刪除而另一個檔案被加入,而其他命令顯示一個檔案被重新命名)。 現在讓我們深入探討一些 Git 概念,或是 Git 如何實現一些常見的 VCS 概念。 ### 犯罪 Git _commit_ 是: * 一個或多個父親提交,或第一次提交沒有父親提交 (_root_) * 提交訊息 * 作者和作者日期(實際上是帶有時區偏移的時間戳) * 提交者和提交日期 * 和我們的檔案:相對於儲存庫根的路徑名、_mode_(UNIX 檔案系統權限)及其內容 每次提交都會獲得一個標識符,該標識符是透過計算該資訊的 SHA1 雜湊值確定的:更改逗號,您將獲得不同的 SHA1,即不同的_提交物件_。 (<abbr title="For What it's value">Fwiw</abbr>,Git 正在慢慢[轉向 SHA-256](https://git-scm.com/docs/hash-function-transition) 作為哈希功能)。 #### 旁白:SHA1 是如何計算的? Git 的儲存是_內容尋址_,這表示每個_物件_都使用直接從其內容派生的名稱進行存儲,並採用 SHA1 雜湊的形式。 從歷史上看,Git 將所有內容儲存在文件中,我們仍然可以這樣推理。文件的內容儲存為 _blob_,目錄儲存為 _tree_(一個文字文件,列出目錄中的文件及其名稱、模式和表示其內容的 _blob_ 的 SHA1,以及其子目錄及其名稱和 SHA1他們的_樹_) 如果您想了解詳細訊息,Julia Evans(再次)寫了一篇令人驚嘆的[博客文章](https://jvns.ca/blog/2023/09/14/in-a-git-repository-- where-do-your-檔案-即時-/);或者您可以[從 <cite>Pro Git</cite> 書中閱讀](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects)。 <圖> <img src=https://git-scm.com/book/en/v2/images/commit-and-tree.png width=800 height=443 alt='包含5 個框的圖表,分為3 列,每個框標有 5 位 SHA1 前綴;左邊的子標籤為“commit”,包含元資料“tree”,中間是框的 SHA1,“author”和“committer”的值均為“Scott”,文字為“The initial commit of我的專案”;中間的框被子標記為“tree”,包括三行,每行標記為“blob”,其餘 3 個框的 SHA1 以及看起來像文件名的內容:“README”、“LICENSE”和“test.rb” ”;最後 3 個框,在右側垂直對齊,都是子標籤為「blob」的內容,包含看起來像是 README、LICENSE 和 Ruby 原始檔內容開頭的內容;有箭頭連結框:提交指向樹,樹指向 blob。'> <figcaption>提交及其樹(來源:<a src=https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell><cite>Pro Git</引用></a>)</figcaption> </圖> _commit_ 中的_父親提交_ 建立一個代表我們歷史的[有向無環圖](https://en.wikipedia.org/wiki/Directed_acirclic_graph):_有向無環圖_ 由連結的節點(我們的提交)組成與有向邊一起(每個提交連結到其父提交,有一個方向,因此_directed_)並且不能有循環/循環(提交永遠不會是它自己的祖先,它的祖先提交都不會連結到它作為父提交)。 <圖> <img src=https://git-scm.com/book/en/v2/images/commits-and-parents.png width=800 height=265 alt='包含 6 個框排列成 2 行 3 列的圖表;第一行的每個框都標有 5 位 SHA1 前綴,子標籤為“commit”,元資料“tree”和“parent”均帶有 5 位 SHA1 前綴(每次都不同)、“author”和“ committer」的值都是“Scott”,以及一些代表提交訊息的文字;左邊的盒子沒有「父」值,另外兩個盒子將左邊的盒子的 SHA1 作為「父」;這些框之間有一個箭頭,指向代表「父」的左側;順便說一句,左邊的框與上圖中的提交框具有相同的 SHA1 和相同的內容;最後,每個提交框也指向其下方的一個框,每個框都標記為「快照 A」、「快照 B」等,並且可能代表從每個提交連結的「樹」物件。'> <figcaption>提交及其父級(來源:<a src=https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell><cite>Pro Git</ cite ></a>)</figcaption> </圖> ### 參考文獻、分支和標籤 現在 SHA1 哈希對於人類來說是不切實際的,雖然 Git 允許我們使用唯一的 SHA1 前綴而不是完整的 SHA1 哈希,但我們需要更簡單的名稱來引用我們的提交:輸入 _references_。這些是我們選擇的提交的_標籤_(而不是 Git)。 有幾種_參考_: * _branches_ 是_moving_ 引用(請注意,`main` 或`master` 並不特殊,它們的名稱只是一個約定) *_標籤_是_不可變_引用 * `HEAD` 是一個特殊的引用,指向_當前提交_。它通常指向一個分支而不是直接指向一個提交(稍後我們會看到原因)。當一個引用指向另一個引用時,這稱為[_符號引用_](https://blog.ltgt.net/confusing-git-terminology/#reference-symbolic-reference)。 * Git 會在某些操作期間為您設定其他特殊參考(`FETCH_HEAD`、`ORIG_HEAD` 等) <圖> <img src=https://git-scm.com/book/en/v2/images/branch-and-history.png width=800 height=430 alt='帶有 9 個框的圖; 6 個盒子的排列方式與上圖相同,並且標記相同(三個提交及其 3 個樹);最右邊(最新)提交上方的兩個框,箭頭指向它,分別標記為“v1.0”和“master”;最後一個框位於“master”框上方,有一個箭頭指向它,並標記為“HEAD”。'> <figcaption>分支及其提交歷史記錄(來源:<a src=https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell><cite>Pro Git< /引用></a>)</figcaption> </圖> ### 三個狀態 當您在 Git 儲存庫中工作時,您在 Git 歷史記錄中操作和記錄的檔案位於您的_工作目錄_中。要建立提交,您需要在 [_index_](https://blog.ltgt.net/confusing-git-terminology/#index-staged-cached) 或_暫存區域_中_暫存_檔案。完成後,您附加一則提交訊息並將您的_staged_檔案移至_history_。 為了關閉循環,_工作目錄_是根據_歷史記錄_中的給定提交進行初始化的。 <圖> <img src=https://git-scm.com/book/en/v2/images/areas.png width=800 height=441 alt='包含3 位參與者的序列圖:「工作目錄」、「暫存區域」和「.git directpry(儲存庫)」;有一條“簽出專案”訊息從“.git 目錄”到“工作目錄”,然後從“工作目錄”到“暫存區域”進行“階段修復”,最後從“暫存區域”進行“提交”區域」到「.git 目錄」。'> <figcaption>工作樹、暫存區域和 Git 目錄(來源:<a href="https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_third_states" ><cite>Pro Git</cite></a>)</figcaption> </圖> ### 旁白:忽略文件 並非所有檔案都需要_追蹤_歷史記錄:由建置系統(如果有)產生的檔案、特定於您的編輯器的檔案以及特定於您的作業系統或其他工作環境的檔案。 Git 允許定義要忽略的檔案或目錄的命名模式。這實際上並不意味著Git 會忽略它們並且無法_跟踪_,但如果不跟踪它們,多個Git 操作將不會向您顯示它們或操縱它們(但您可以手動將它們加入到歷史記錄中,並且從那時起,他們將不再被_忽略_)。 忽略檔案是透過將路徑名稱(可能使用 glob)放入忽略檔案中來完成的: * 儲存庫中任何位置的 `.gitignore` 檔案定義了包含目錄的忽略模式;這些忽略文件會在歷史記錄中被跟踪,作為開發人員之間共享它們的一種方式;在這裡,您將忽略建置系統產生的那些檔案(Gradle 專案的“build/”,Eleventy 網站的“_site/”等) * `.git/info/excludes` 是您機器上的本機儲存庫;很少使用,但有時很有用,所以很高興了解一下 * 最後 `~/.config/git/ignore` 對機器來說是全域的(對你的使用者);在這裡,您將忽略特定於您的電腦的文件,例如特定於您使用的編輯器的文件,或特定於您的作業系統的文件(例如macOS 上的“.DS_Store”或Windows 上的“Thumbs. db”) ) ### 加起來 這是所有這些概念的另一種表示: <圖> <img src=https://marklodato.github.io/visual-git-guide/conventions.svg width=907 height=529 alt='有 10 個框的圖; 5 個框在中心排成一行,標有 5 位 SHA1 前綴,它們之間有從右向左指向的箭頭;一條註釋將它們描述為“提交物件,由 SHA-1 哈希標識”,另一條註釋將其中一個箭頭描述為“子項指向父項”;一對框(看起來像一個水平分割成兩個框的單一框)位於最右邊(最新)提交的上方,有一個向下指向它的箭頭,該對的上面的框被標記為“HEAD”並描述為“引用當前分支”;下面的框被標記為“main”並被描述為“目前分支”;第七個框位於另一個提交上方,有一個向下指向它的箭頭;它被標記為“穩定”並被描述為“另一個分支”;最後兩個框位於提交歷史記錄下,一個在另一個之上;最底部的框標記為“工作目錄”並描述為“您'看到'的文件”,它和提交歷史記錄之間的另一個框標記為“階段(索引)”並描述為“要存取的文件”在下次提交中”。'> <figcaption>提交、引用和區域(來源:<a href=https://marklodato.github.io/visual-git-guide/index-en.html#conventions><cite>可視化 Git 參考</cite >< /a>,馬克‧洛達托)</figcaption> </圖> ## 基本操作 這就是我們開始討論 Git 指令以及它們如何與圖表互動的地方: * `git init` 初始化一個新的儲存庫 * `git status` 取得檔案狀態的摘要 * `git diff` 顯示任意兩個工作目錄、索引、`HEAD` 之間的更改,或實際上任何提交之間的更改 * `git log` 顯示並搜尋您的歷史記錄 * 建立提交 * `git add` 將檔案加入_index_ * `git commit` 將_index_ 轉換為_commit_ (帶有新增的_commit 訊息_) * `git add -p` 以互動方式將檔案新增至 _index_:選擇要新增的變更以及僅將哪些變更保留在工作目錄中,逐一檔案、逐個部分(稱為 _hunk_) * 管理分支機構 * `gitbranch` 顯示分支,或建立分支 *`git switch`(也稱為`git checkout`)將分支(或任何提交,實際上是任何_樹_)簽出到您的工作目錄 * `git switch -b` (也稱為 `git checkout -b`)作為 `gitbranch` 和 `gitswitch` 的捷徑 * `git grep` 搜尋您的工作目錄、索引或任何提交;這是一種增強的“grep -R”,它支援 Git * `gitblame` 來了解更改給定文件每一行的最後一次提交(因此,誰應該為錯誤負責) * `git stash` 將未提交的更改放在一邊(這包括_staged_文件,以及工作目錄中的_tracked_文件),然後_unstash_它們。 ### 提交、分支切換和 HEAD 當您建立提交(使用「git commit」)時,Git 不僅建立_提交物件_,還移動「HEAD」以指向它。如果「HEAD」實際上指向一個分支(通常是這種情況),Git 會將該分支移動到新的提交(並且「HEAD」將繼續指向該分支)。每當當前分支是另一個分支的祖先(該分支指向的提交也是另一個分支的一部分)時,提交將使“HEAD”移動相同,並且分支將_發散_。 當您切換到另一個分支(使用“git switch”或“git checkout”)時,“HEAD”會移至新的目前分支,並且您的工作目錄和索引將設定為重新組合該提交的狀態(未提交的更改將暫時保留;如果 Git 無法做到這一點,它將拒絕切換)。 如需更多詳細資訊和視覺表示,請參閱[commit](https://marklodato.github.io/visual-git-guide/index-en.html#commit) 和[checkout](https://marklodato. github .io/visual-git-guide/index-en.html#checkout)Mark Lotato 的<cite>可視化Git 參考</cite>的部分(請注意,該參考是幾年前寫的,當時`git switch ` 和 ` git Restore` 不存在,而 `git checkout` 是我們所擁有的一切;因此 _checkout_ 部分涵蓋的內容比 `git switch` 多一點)。 當然,<cite>Pro Git</cite> 這本書也是一個很好的視覺表示參考; [<cite>Branches in a Nutshell</cite> 子章節](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) 涵蓋了所有內容的很大一部分上述的。 ### 旁白:Git 是保守的 正如我們在上面所看到的,由於其_內容尋址存儲_,對提交的任何“更改”(例如使用“git commit --amend”)實際上都會導致不同的提交(不同的 SHA1)。 _舊提交_不會立即消失:Git 使用_垃圾收集_最終刪除無法從任何_引用_存取的提交。這意味著,如果您設法找回提交SHA1,則可以恢復許多錯誤(“git reflog”可以在此處提供幫助,或者符號“<branch-name>@{<n>}”,例如“main@{ 1}”) ` main` 在更改之前指向的最後一次提交)。 ### 使用分支機構 我們在上面已經看到了分支是如何發散的。 但分歧要求最終_合併_變回來(使用“git merge”)。 Git 在這方面非常擅長(我們稍後會看到)。 合併的一個特殊情況是目前分支是要合併到的分支的祖先。在這種情況下,Git 可以執行 [_fast-forward merge_](https://blog.ltgt.net/confusing-git-terminology/#can-be-fast-forwarded)。 由於兩個分支之間的操作可能始終針對同一對分支,因此 Git 允許您設定一個分支來追蹤另一個分支。另一個分支被稱為_追蹤_它的分支的_上游_。例如,設定時,「git status」將告訴您兩個分支彼此之間有多少分歧:目前分支是[_最新_](https://blog.ltgt.net/confusing-git-terminology /#your- branch-is-up-to-date-with-originmain) 及其上游分支,_後面_和[可以快轉](https://blog.ltgt.net/confusing-git-terminology/ #can-be- fast-forwarded),_超前_許多提交,或它們有分歧,每個提交都有一定數量。其他命令將使用該資訊為參數提供良好的預設值,以便可以省略它們。 要整合來自另一個分支的更改,而不是合併,另一種選擇是_cherry-pick_(使用同名命令)單一提交,而不包含其歷史記錄:Git 將計算該提交帶來的更改並將相同的更改應用於當前分支,建立一個與原始分支類似的新提交(如果您想了解更多有關Git 實際操作方式的訊息,請參閱Julia Evans 的[<cite>如何gitcherry-pick 和revert 使用3 路合併< /cite> ](https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/))。 最後,工具帶中的另一個指令是「rebase」。 您可以將其視為一次進行許多選擇的方法,但它實際上更強大(正如我們將在下面看到的)。但在其基本用途中,它只是這樣:您給它一系列提交(在作為起點的任何提交和作為終點的現有分支之間,預設為當前分支)和一個目標,並且它會挑選所有這些提交位於目標之上,並最終更新用作終點的分支。這裡的指令的形式是`git rebase --onto=<target> <start> <end>`。與許多 Git 命令一樣,參數可以省略,並且具有預設值和/或特定含義:因此,`git rebase` 是 `git rebase --fork-point upper` 的簡寫,其中 `upstream` 是 [upstream]當前分支的(https://blog.ltgt.net/confusing-git-terminology/#untracked-files-remote-tracking-branch-track-remote-branch)(我會忽略`--fork-point`這裡,它的作用很微妙,在日常使用上並不那麼重要),它本身就是`git rebase upper HEAD` 的簡寫(其中`HEAD` 必須指向一個分支),它本身就是`git rebase 的簡寫-- on=upstream uploaded `,`git rebase --onto=upstream $(git merge-baseupstream HEAD) HEAD` 的簡寫,並將rebase `upstream` 的最後一個共同祖先與當前分支之間的所有提交另一方面,手和當前分支(即自從它們分歧以來的所有提交),並將它們重新應用到“上游”之上,然後更新當前分支以指向新的提交。明確使用`--onto` (其值與起始點不同)實際上很少見,請參閱[我之前的文章](https://blog.ltgt.net/confusing-git-terminology/#git- rebase- --onto) 對於一個用例。 我們無法在沒有互動式變體「git rebase -i」的情況下呈現「git rebase」:它以與非互動式變體完全相同的行為開始,但在計算需要完成的操作之後,它將允許您對其進行編輯(作為編輯器中的文字文件,每行一個操作)。預設情況下,所有選定的提交都是精心挑選的,但您可以對它們重新排序,跳過某些提交,甚至將某些提交合併到單一提交中。實際上,您可以挑選最初未選擇的提交,甚至建立合併提交,從而完全重寫整個歷史記錄!最後,您還可以停止對其進行編輯(然後使用“git commit --amend”,和/或可能在繼續變基之前建立新的提交),和/或在兩次提交之間執行給定的命令。最後一個選項非常有用(例如,驗證您沒有在歷史記錄的每個點上破壞您的專案),您可以在`--exec` 選項中傳遞該命令,Git 將在每個重新基底提交之間執行它(這也適用於非互動式變基;在互動模式下,當能夠編輯變基場景時,您將看到在每個櫻桃選擇行之間插入執行行)。 更多詳細資訊和視覺表示,請參閱[merge](https://marklodato.github.io/visual-git-guide/index-en.html#merge)、[cherry pick](https://marklodato . github.io/visual-git-guide/index-en.html#cherry-pick) 和 [rebase](https://marklodato.github.io/visual-git-guide/index-en.html#rebase) Mark Lodato 的<cite>視覺化Git 參考</cite> 部分,以及[<cite>基本分支和合併</cite>](https://git-scm.com/book/en/v2/Git-分支-基本-分支和合併),[<cite>變基</cite>](https://git-scm.com/book/en/v2/Git-Branching-Rebasing)和[<cite>重寫歷史< /cite>](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) <cite>Pro Git</cite> 書的子章節。 您也可以查看 David Drysdale 的 [<cite>Git Visual Reference</cite>](https://lurklurk.org/gitpix/gitpix.html) 中的「分支和合併」圖。 ## 與他人合作 目前,我們只在我們的儲存庫中進行本地工作。 但 Git 是專門為與他人合作而建構的。 讓我介紹一下_遙控器_。 ### 遙控器 當您_複製_儲存庫時,該儲存庫將成為本機儲存庫的_遠端_,名為「origin」(就像「main」分支一樣,這只是預設值,名稱本身沒有什麼特別的,除了有時用作省略命令參數時的預設值)。然後,您將開始工作,建立本地提交和分支(因此從遠端_forking_),同時遠端可能會從其作者那裡獲得更多提交和分支。因此,您需要將這些遠端變更同步到本機儲存庫,並希望快速了解與遠端相比您在本機所做的變更。 Git 處理這個問題的方式是在一個特殊的命名空間中記錄它所知道的遠端(主要是分支)的狀態:「refs/remote/」。這些被稱為[_遠端追蹤分支_](https://blog.ltgt.net/confusing-git-terminology/#untracked-files-remote-tracking-branch-track-remote-branch)。 Fwiw,本機分支儲存在「refs/heads/」命名空間中,標籤儲存在「refs/tags/」中(來自遠端的標籤通常直接「匯入」到「refs/tags/」中,因此例如您會遺失位置資訊他們來自)。您可以根據需要擁有任意多個遙控器,每個遙控器都有一個名稱。 (請注意,遙控器不一定位於其他電腦上,它們實際上可以位於同一台電腦上,直接從檔案系統存取,因此您無需進行任何設定即可使用遙控器。) ### 取得 每當你從遠端 _fetch_ 時(使用 `git fetch`、`git pull` 或 `git Remote update`),Git 都會與它對話以下載它還不知道的提交,並更新 _remote-tracking遠端分支_ 。要取得的確切引用集以及取得它們的位置將傳遞給 `git fetch` 命令(如 [refspecs](https://blog.ltgt.net/confusing-git-terminology/#refspecs) )以及儲存庫的` .git/config` 中定義的預設值,預設由`git clone` 或`git remote add` 配置以取得所有分支(遠端上的`refs/heads/` 中的所有內容)並放置它們位於` refs/remote/<remote>` 中(因此`origin` 遙控器的`refs/remote/origin/` )具有相同的名稱(因此遙控器上的`refs/heads/main` 變成`refs/remote / origin/main` 本地)。 <圖> <img src=https://git-scm.com/book/en/v2/images/remote-branches-5.png width=800 height=577 alt='帶有3 個大方框的圖表,代表機器或儲存庫,包含代表提交歷史的較小框和箭頭;一個框標記為“git.outcompany.com”,子標記為“origin”,並包含名為“master”的分支中的提交;另一個框標記為“git.team1.outcompany.com”,子標記為“teamone”,並包含名為“master”的分支中的提交; 「origin」和「teamone」中的提交 SHA1 雜湊值相同,除了「origin」在其「master」分支上多了一個提交,即「teamone」在「後面」;第三個框標記為“我的電腦”,它包含與其他兩個框相同的提交,但這次分支被命名為“origin/master”和“teamone/master”;它還在名為“master”的分支中包含另外兩個提交,與遠端分支的較早點不同。'> <figcaption>遠端和遠端追蹤分支(來源:<a href=https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches><cite>Pro Git</cite>< / a>)</figcaption> </圖> 然後,您將使用與分支相關的命令來獲取從_遠端追蹤分支_到本地分支的更改(“git merge”或“git rebase”),或“git pull”,這只不過是“git fetch”的簡寫` 後面跟著 `git merge` 或 `git rebase`。 <abbr title="By the way">順便說一句</abbr>,在很多情況下,當你建立本地分支時,Git 會自動將_遠端追蹤分支_設定為本地分支的_上游_(它會告訴你相關資訊)當這種情況發生時)。 ### 推 要與其他人共用您的更改,他們可以將您的儲存庫新增為遠端儲存庫並從中_pull_(意味著透過網路存取您的電腦),或者您可以_push_到遠端儲存庫。 (如果您要求某人從您的遙控器中提取更改,這稱為..._拉請求_,您可能在 GitHub 或類似服務中聽說過這個術語。) 推送與提取類似,相反:您將提交發送到遠端並更新其分支以指向新提交。作為安全措施,Git 只允許遠端分支_快速轉送_;如果您想推送以非快轉方式更新遠端分支的更改,則必須使用「git push --force-with-lease」(或「git push --force」)_force_它,但要小心:`-- force-with-lease`將首先確保您的_遠端追蹤分支_與遠端分支是最新的,以確保自上次_fetched_以來沒有人將變更推送到分支;` --force` 不會執行該檢查,而是按照您的指示執行操作,風險由您自己承擔)。 與「git fetch」一樣,您可以將要更新的分支傳遞給「git push」命令,但如果您不這樣做,Git 會提供良好的預設行為。如果你不指定任何東西,Git 會從目前分支的上游推斷遠程,所以大多數時候 `git push` 相當於 `git push origin`。這實際上是“git Push origin main”的簡寫(假設當前分支是“main”),它本身是“git Push origin main:main”的簡寫,是“git Push origin refs/heads/main:refs/”的簡寫heads/main`,意思是將本地的`refs/heads/main`推送到`origin`遠端的`refs/heads/main`。有關使用不同來源和目標指定 _refspecs_ 的一些用例,請參閱[我之前的文章](https://blog.ltgt.net/confusing-git-terminology/#refspecs)。 <圖> <img src=https://lurklurk.org/gitpix/push2.svg width=1052 height=744 alt='代表「git push」指令的圖表,有四個 git 圖表(點,有些有標籤,用線連接) 排列成兩行兩列;列之間的箭頭表示左列是「之前」狀態,右列是「之後」狀態;上面一行中的圖位於雲內部,代表遠端儲存庫,並且有兩個分支,“master”和“other”,它們偏離了共同的祖先;左下圖與上面的圖形狀相同,只是標籤更改為“origin/master”和“origin/other”,並且每個分支有更多提交:與“origin”分支相比,“master”分支有兩個額外的提交/master”,而“other”比“origin/other”多了一個提交;與左上圖相比,右上圖在其「master」分支中多了兩次提交;右下圖與左下圖相同,除了「origin/master」現在指向與「master」相同的提交;換句話說,在「之前」狀態下,遠端缺少三個提交,而在「git Push」之後,本地「master」分支的兩個提交被複製到遠端,而「其他」保持不變。'> <figcaption><code>git Push</code>(資料來源:<a href=https://lurklurk.org/gitpix/gitpix.html><cite>Git 視覺參考</cite></a>,David Drysdale )</圖標題> </圖> 更多詳細資訊和視覺表示,請參閱[<cite>遠端分支</cite>](https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches),[< cite >使用遙控器</cite>](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes),以及[<cite>為專案做出貢獻</ cite> ](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project) <cite>Pro Git</cite> 書的子章節,以及「處理遠程來自David Drysdale 的[<cite>Git Visual Reference</cite>](https://lurklurk.org/gitpix/gitpix.html) 的「儲存庫」圖表。 <cite>Pro Git</cite> 的<cite>為專案做出貢獻</cite>一章也涉及在GitHub 等平台上為開源專案做出貢獻,您必須先_fork_儲存庫,然後透過_pull requests_進行貢獻(或_合併請求_)。 ## 最佳實踐 這些是針對初學者的,希望不會引起太多爭議。 嘗試保留_clean_歷史記錄: * 明智地使用合併提交 * 清晰且高品質的提交訊息(請參閱[<cite>提交指南</cite>](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project #_commit_guidelines)在<cite>Pro Git</cite> 中) * make _atomic_ commits:每個提交應該獨立於歷史記錄中跟隨它的提交進行編譯和執行 這僅適用於您與他人分享的歷史記錄。 在本地,想怎麼做就怎麼做。對於初學者,我會給以下建議: * 不要直接在“main”(或“master”,或您在遠端上沒有專門擁有的任何分支)上工作,而是建立本機分支;它有助於解耦不同任務的工作:即將開始處理另一個錯誤或功能,同時等待有關當前任務的說明的更多詳細資訊?切換到另一個分支,稍後您可以透過切換回來回到該分支;它還使從遠端更新變得更容易,因為如果您的本地分支只是同名遠端分支的副本,沒有任何本地更改(除非您想推送這些更改),您確信不會發生衝突到該分支) * 毫不猶豫地重寫你的提交歷史記錄(`git commit --amend` 和/或 `git rebase -i`),但不要太早這樣做;在工作時堆疊許多小提交是完全可以的,並且只在共享之前重寫/清理歷史記錄 * 同樣,請毫不猶豫地重新調整本機分支以整合上游變更(直到您共用該分支,此時您將遵循專案的分支工作流程) 如果出現任何問題並且您迷路了,我的建議是使用 `gitk` 或 `gitk HEAD @{1}`,也可能使用 `gitk --all` (我在這裡使用 `gitk` 但使用任何工具你喜歡),可視化你的Git 歷史並嘗試了解發生了什麼。由此,您可以回滾到先前的狀態(`git reset @{1}`)或嘗試修復問題(擇優選擇提交等)。合併失敗,您可以使用“git rebase --abort”或“git merge - -abort」等命令中止並回滾到先前的狀態。 為了讓事情變得更簡單,請不要猶豫,在任何可能具有破壞性的命令(`git rebase`)之前,建立一個分支或標籤作為“書籤”,如果事情沒有按預期進行,您可以輕鬆重置。當然,在執行這樣的命令後,請檢查歷史記錄和文件,以確保結果是您所期望的。 ## 進階概念 這只是其中的一小部分,還有更多值得探索! * 分離的「HEAD」:[`git checkout` 手冊頁](https://git-scm.com/docs/git-checkout#_detached_head) 有一個關於該主題的很好的部分,另請參閱[我之前的帖子](https ://blog.ltgt.net/confusing-git-terminology/#detached-head-state),要獲得良好的視覺表示,請參閱[<cite>使用分離的HEAD 進行提交</ cite>](https:// /marklodato.github.io/visual-git-guide/index-en.html#detached) Mark Lodato 的 <cite>視覺化 Git 參考</cite> 部分。 * Hooks:這些是可執行檔(大多數情況下是 shell 腳本),Git 將執行它們來回應儲存庫上的操作;人們使用它們在每次提交之前檢查程式碼(如果失敗則中止提交),產生或後處理提交訊息,或在有人推送到儲存庫後觸發伺服器上的操作(觸發建置和/或部署)。 * 一些很少需要的命令可以在您真正需要時節省您的時間: * `git bisect`:一個進階命令,透過測試多個提交(手動或透過腳本)來幫助您找出哪個提交引入了錯誤;對於線性歷史,這是使用二分法並且可以手動完成,但是一旦您有許多合併提交,這就會變得更加複雜,並且最好讓 git bisect 來完成繁重的工作。 * `git filter-repo`:實際上是一個[第三方命令](https://github.com/newren/git-filter-repo),作為Git 自己的`filter-branch` 的替代品,它允許重寫儲存庫的整個歷史記錄,以刪除錯誤新增的文件,或協助將儲存庫的一部分提取到另一個儲存庫。 我們完成了。 有了這些知識,人們應該能夠將任何 Git 命令映射到如何修改提交的_有向無環圖_,並了解如何修復錯誤(在錯誤的分支上執行合併?基於錯誤的分支重新建置?)並不是說理解這些事情會很容易,但至少應該是可能的。 --- 原文出處:https://dev.to/tbroyer/how-i-teach-git-3nj3