🔍 搜尋結果:JS

🔍 搜尋結果:JS

給開發者:這 8 個 Podcast 將幫助您增長知識並擴展思維

我是播客的忠實粉絲,這是最被低估的純粹知識資源。播客已成為學習和娛樂領域的革命性媒介。近年來,它們從小眾聽眾的愛好轉變為主流媒體形式。 向兩個或更多經驗豐富的人學習談論特定主題,討論他們在做某事時面臨的挑戰,並分享他們的旅程是很有趣的。 它為解決特定問題和建立獨特的解決方案提供了令人難以置信的見解。您將了解: - 不同的心態以及其他人如何看待事物。 - 人們用來解決問題的各種工具。 - 傑出人物和他們的故事以及他們如何解決特定問題。 - 走出精神泡泡並開始以不同的方式看待事物。 另一個好處是,你透過聽這些播客所獲得的知識會成為你的潛在知識或隱性知識庫。 因此,當您面臨類似領域的挑戰時,從播客中獲得的知識可以為您提供一些時間或路線圖來建立應對挑戰的解決方案。 身為人工智慧愛好者,在知識和資料領域工作。我選擇了 [Vector Podcast](https://www.youtube.com/@VectorPodcast),因為我對向量資料庫和向量搜尋、它們如何幫助公司建立基於人工智慧的產品等感到好奇。 以下是我推薦的 8 個最佳播客,您可以收聽。 _我正在提供他們的 YouTube 頻道,假設每個人都知道並使用 YouTube。其中大多數也出現在 Apple Podcast、Spotify 等上_ ## [向量播客](https://www.youtube.com/@VectorPodcast) ![向量播客](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jii8stvlc2zhecit0b7p.jpg) Vector Podcast 是 Dmitry Kan 博士的創意。研究生研究科學家涉足產品管理和創業。他也曾在赫爾辛基大學講課。 他在搜尋引擎領域、人工智慧和軟體開發領域擁有超過 15 年的經驗,並採訪了人工智慧領域的專業人士和執行長。 一些值得注意的情節是: - 與 Swirl 執行長 Sid Probstein 一起進行搜尋。 - 向量資料庫以及 Weviate 執行長 Bob van Luijt。 - 康納肖頓的搜尋未來。 https://www.youtube.com/@VectorPodcast ## [萊克斯‧弗里德曼](https://www.youtube.com/c/lexfridman) ![萊克斯·弗里德曼](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zb0jvrfnvqwa6khxhpil.jpg) 萊克斯·弗里德曼(Lex Fridman)是一位受歡迎的人物,正因為如此,他可以接待許多著名人物、首席執行官、領導人和頂尖研究人員。把他們帶到麥克風桌前問他們問題(他很擅長這一點。) 他採訪過的一些著名人士包括 Sam Altman、Elon Musk、Guido van Rossum(Python 語言的建立者)、Stephen Wolfram 和 [Goose](https://www.youtube.com/watch?v=QqRV5FD8ob4)。 他也是機器學習講師,您可以在此[播放清單](https://www.youtube.com/playlist?list=PLe8HThjUpqadLD-AewSKkhAyW5Nr4Yq4Z)中查看他們。 ## [知識專案](https://www.youtube.com/@tkppodcast) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7ix13d6kberdnex6suz.jpg) 知識專案播客由法納姆街的 Shane Parrish 主持,揭示了其他人已經發現的最好的東西,以便您可以將他們的見解應用到您的生活中。 肖恩·帕里什 (Shane Parrish) 的播客和見解無疑令人驚嘆。你必須經歷知識專案才能很好地理解它。 ## [語法](https://www.youtube.com/@syntaxfm) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cul191m0c2jbdmf6knvg.jpg) 來自 30 Days of JavaScript 的 Wes Bos。該播客討論 JavaScript 和 Web 開發。 它涵蓋了一些有趣的主題,從 Web 開發的新功能到 JavaScript 測試和其他主題。 ## [本週機器學習 (TWIML)](https://www.youtube.com/@twimlai) ![本週機器學習](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg5z96p0s26pw3i34n8n.jpg) 與 Sam 一起探索人工智慧的最新趨勢和突破,討論將人工智慧驅動的產品推向市場的實際挑戰,並研究人工智慧技術與商業和消費者應用的交叉點。 這個播客不只是一次對話;更是一次對話。它是理解和利用機器學習和人工智慧的全部潛力來改善我們的生活和社區的門戶。我非常喜歡這個播客! ## [變更日誌](https://www.youtube.com/@Changelog) ![變更日誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fu44gmvwd13veekuukhn.jpg) 變更日誌不只是播客的集合;這是一個充滿活力的社區,為處於各個階段的開發者提供豐富的資源。 無論您是經驗豐富的專業人士、好奇的初學者,還是介於兩者之間,我們的節目和資源都會提供與開發人員體驗產生共鳴的寶貴見解、討論和故事。 ## [談 Python](https://www.youtube.com/@talkpython) ![談 Python](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzdyh4s3ow11c73p50f2.jpg) Talk Python to Me 是由 Michael Kennedy 主持的每周播客。本節目涵蓋各種 Python 和相關主題(例如 MongoDB、AngularJS、DevOps)。 ## [超越編碼](https://www.youtube.com/@BeyondCoding) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/guwikoy09l2mv167l9wp.jpg) 帕特里克·阿基爾 (Patrick Akil) 和他的客人分享了他們的旅程和觀點,供您隨身攜帶並形成自己的旅程和觀點。 Beyond Coding 是一個每周播客,以爐邊聊天的形式進行「超越編碼」的對話。典型的主題是軟體工程、領導力、溝通、自我提升和幸福。 --- 💡***有趣的事實:*** 您知道「播客」來自 iPod 和廣播的融合嗎? ## 結論 我希望您喜歡本文中介紹的播客清單。如果您有任何建議,請隨時在評論中分享。 **在 YouTube 上查看向量播客** 我們致力於為您提供有關人工智慧、大型語言模型等方面的一流內容。 嵌入 https://www.youtube.com/watch?v=vhQ5LM5pK_Y https://www.youtube.com/@VectorPodcast 謝謝閱讀。 --- 原文出處:https://dev.to/vectorpodcast/these-8-podcasts-will-help-increase-your-knowledge-and-expand-your-mindset-5lb

給各位菜鳥工程師的一封信

我有 15 年的經驗,用多種語言寫程式碼,解決了多種問題。當軟體開發成為熱門職業時,我開始了我的職業生涯。 看看我們周圍發生的事情,我必須說幾句話,我會保持現實。事情可能聽起來很刺耳,但這比在鴉片中洩漏要好。 ## 不要參加程式設計訓練營 別報名了。只是學習程式設計三個月,沒辦法讓你找到工作。為什麼?這麼短時間內,你無法學會如何編碼,這是不可能的。 在黃金歲月年代,當訓練營熱潮開始時,我在一家從訓練營招募了許多初級開發人員的公司工作。然後他們意識到大多數人幾乎無法建立一個專案,並且只能夠慢慢編寫簡單的 http 控制器。這還不足以稱自己為開發人員。他們試圖透過為他們留下一位專門的高級開發人員來回答他們的問題(有點像專門的導師)來對他們進行更多的教育。在學到一些東西之後,有幾個人抓住了這個機會,離開了工作崗位,去從事薪水稍高的工作。他們的管理層很不高興。然後,裁員開始了。 大多數處於這種地位的人做了什麼?他們中的大多數人都專注於保住工作、更好地「推銷自己」並試圖給老闆留下深刻的印象。沒有人真正專注於學習如何更好地編碼。另外,如果你試圖用閒聊/快語來給別人留下深刻印象,那只能表示你沒有技巧並且沒有把握。 結論?程式設計訓練營/程式設計學校承諾他們在學習幾個月後可以獲得六位數的工作,這純粹是一個騙局。然後,大多數人無法應對這個問題,或以非生產性的方式應對。別這樣。花更多的時間學習就對了,不要期待別人把知識強加在你身上。 ## 人工智慧如何影響就業? 當我 15 年前開始工作時,作為一名知識很少的初級開發人員進入就業市場是可能的。 現在,要開始工作,您需要比 ChatGPT 更好,簡單明瞭。如果人工智慧可以在更短的時間內完成相同數量的工作並且成本降低約 100 倍,為什麼有人會僱用初級員工? 這意味著切入點更高。以前,你可以從初級開始,然後每隔幾個月加薪,然後慢慢晉升到高級。 現在,你不能。首先,你不會找到工作,即使你找到了,你至少必須是一名開發人員,因為你正在與人工智慧競爭。 結論?沒有什麼好的辦法可以緩解。您應該嘗試發布您的應用程式和其他數位產品。這聽起來很殘酷,但這可能比找到工作更容易。此外,您的應用程式/數位產品可能會開始賺錢。 ## 獨立 如果您找到了一份編碼工作,請保持獨立。只問幾個關於如何在技術堆疊中做事的問題。人們一開始可能會告訴你,多發問總是不會錯,但實際上,有些發問是不對的。你的工作,大多數事情本來就都應該由你自己解決。 假設該公司以每月 1 萬美元的價格僱用一名高級開發人員,以每月 2 萬美元的價格僱用一名初級開發人員。假設初級開發人員每天花 2 小時(高級開發人員時間的 25%)來跟資深工程師發問。在這種情況下,企業每月會損失 2500 美元的高級開發人員時間,並且還必須每月向初級開發人員支付 2000 美元。因此,公司因僱用您而每月損失 4500 美元。 你認為會發生什麼事?是的,我看到有人因此被解僱。有些企業已經為初級員工入職做好了準備,但這樣的企業卻很少。 另外,期待別人會幫你一點點就好了。你必須擁有自己的知識。人們會幫助你,但不要做到妨礙別人工作的程度。 ## 程式設計課程不會幫助你 你就多開發就對了。寫很多程式碼就對了。不要花錢購買另一門課程或另一個「對初學者來說鼓舞人心的例子」——它們中的大多數都是毫無價值的。相信我,即使你感到沮喪,你能做的最好的事情就是**做**一些東西——任何專案、應用程式、網站。它會帶給你比永久保持初學者/學習心態更有價值的價值。 真正有用的技能是學習如何直接從框架/程式語言文件中學習而不是依賴加工過的知識。 ## 專注於一件事 科技日新月異;每個月都會出現新的框架和函式庫。轉換技術總是存在著誘惑,但這樣你只能學到一點東西。我保證,作為一個優秀的普通 JS 開發人員,你會比一個幾乎不知道八個最受歡迎框架的 JS 開發人員帶來更多的價值。 ## 總結 專注於快速產生良好的、可維護的程式碼,保持獨立,並在實踐中學習。 基本功是目前市場最缺乏的。 --- 原文出處:https://dev.to/mcharytoniuk/to-junior-developers-2ec5

在 JS 應用程式中載入環境變數

#### 如何儲存並使用本機開發的環境變數 API 和第三方整合要求開發人員使用稱為**環境或配置變數**的配置資料。這些變數通常儲存在受密碼保護的地方,例如 CI 工具或部署管道,但是當我們在本地開發應用程式時如何使用它們? ![](https://cdn-images-1.medium.com/max/1024/1*iTLvajtJ6tN3DnHArGKkDA.png) #### 簡介 - 不要在原始碼管理中儲存環境變數 - 使用 [dotenv](https://github.com/motdotla/dotenv) 從 .env 檔案讀取資料 - create-react-app 在環境變數上強制命名空間 這個簡短的教程將解釋在本地開發時將環境變數載入到程式碼中的一種方法。主要好處是 API 金鑰等秘密不會提交給原始碼控制,以確保您的應用程式更安全。 #### 要求: - 一個 JavaScript 應用程式 - 套件管理器(yarn 和 npm 都很棒) - Node 7+ ### 設定變數 在儲存庫的根目錄中建立一個名為「.env」的檔案。該文件稱為“點文件”,與常規文件不同,它通常隱藏在文件瀏覽器中。 大多數 IDE 允許使用者建立沒有名稱的文件,但如果情況並非如此,請轉到終端並 cd 進入應用程式的根資料夾。 ``` touch .env ``` 接下來,使用格式 key=value 設定變數,並以換行符號分隔: ``` API_KEY=abcde API_URL=https://my-api.com/api ``` 最後,確保 .env 檔案未提交到您的儲存庫。這可以透過開啟(或建立).gitignore 檔案並新增以下行來實現: ``` .env # This contains secrets, don't store in source control ``` ### 使用變數 前往終端使用您首選的套件管理器安裝 [dotenv](https://github.com/motdotla/dotenv): ``` # Using npm: npm i dotenv # Using yarn: yarn add dotenv ``` 現在您已準備好讀取 .env 檔案。儘早在您的應用程式中加入這行程式碼。對於 React 應用程式,通常是 index.js 或 App.js,但這完全取決於您的設定: ``` require('dotenv').config(); ``` 就是這樣!您的應用程式應該可以透過 process.env 物件存取環境變數。您可以透過撥打以下電話進行雙重檢查: ``` console.log(process.env); ``` 如果一切順利,您應該會看到類似以下內容: ``` { NODE_ENV: "development", API_KEY: "abcde", API_URL: "https://my-api.com/api" } ``` 🎉 現在您可以在應用程式中使用環境變數了! 現在,對於我們這些使用 create-react-app 的人來說,有一個問題,我希望它能被更好地記錄下來。 ### 使用 create-react-app Facebook 的 [create-react-app](https://github.com/facebook/create-react-app) 的工作方式略有不同。如果您按照上述步驟操作但沒有彈出應用程式,那麼您應該看到的只是 NODE\_ENV 變數。這是因為 **create-react-app 只允許應用程式讀取帶有** **REACT\_APP\_ 前綴的變數。** 因此,為了使我們的變數起作用,我們需要像這樣更新我們的 .env 檔案: ``` REACT_APP_API_KEY=abcde REACT_APP_API_URL=https://my-api.com/api ``` 再次透過將 process.env 記錄到控制台來驗證您的設定: ``` { NODE_ENV: "development", REACT_APP_API_KEY: "abcde", REACT_APP_API_URL: "https://my-api.com/api" } ``` 你就完成了😎 ### 小技巧 .env 檔案中的變數不需要引號,除非值中有空格。 ``` NO_QUOTES=thisisokay QUOTES="this contains spaces" ``` 最好建立一個 .env.sample 檔案來追蹤應用程式應該期望的變數。這是我目前專案中的範例文件的樣子。請注意,它解釋了人們可以在哪裡找到這些金鑰和 URL。 ``` CONTENTFUL_SPACE_TOKEN="see Contentful dashboard" CONTENTFUL_API_KEY="see Contentful dashboard" S3_BUCKET_URL="check AWS" SHOW_DEBUG_SIDEBAR="if true, show debug sidebar" ``` ### 進一步閱讀: - [在 12-Factor App 方法中讀取環境中的設定](https://12factor.net/config) 感謝您的閱讀!您是否喜歡另一種在本地載入環境變數的方法?我很想在下面的評論中聽到它! --- 原文出處:https://dev.to/deammer/loading-environment-variables-in-js-apps-1p7p

🔥 大幅提升你的 NextJS 能力:嘗試手寫一個 GitHub 星星監視器 🤯

在本文中,您將學習如何建立 **GitHub 星數監視器** 來檢查您幾個月內的星數以及每天獲得的星數。 - 使用 GitHub API 取得目前每天收到的星星數量。 - 在螢幕上每天繪製美麗的星星圖表。 - 創造一個工作來每天收集新星星。 ![吉米](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n524rmr0gpgr79p4qlhj.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) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 https://github.com/triggerdotdev/trigger.dev --- ## 這是你需要知道的 😻 取得 GitHub 上星星數量的大部分工作將透過 GitHub API 完成。 GitHub API 有一些限制: - 每個請求最多 100 名觀星者 - 最多 100 個同時請求 - 每小時最多 60 個請求 [TriggerDev](https://github.com/triggerdotdev/trigger.dev) 儲存庫擁有超過 5000 顆星,實際上不可能在合理的時間內(即時)計算所有星數。 因此,我們將採用與 [GitHub Stars History](https://star-history.com/) 相同的技巧。 - 取得星星總數 (**5,715**) 除以每頁 **100** 結果 = **58 頁** - 設定我們想要的最大請求量(**20 頁最大**)除以 **58 頁** = 跳過 3 頁。 - 從這些頁面中獲取星星**(2000 顆星)**,然後獲取剩餘的星星,我們將按比例加入到其他日期(**3715 顆星**)。 它會為我們繪製一個漂亮的圖表,並在需要的地方用星星凸起。 當我們每天獲取新數量的星星時,事情就會變得容易得多。 我們將用目前擁有的星星總數減去 GitHub 上的新星星數量。 **我們不再需要迭代觀星者。** --- ## 讓我們來設定一下 🔥 我們的申請將包含一頁: - 新增您想要監控的儲存庫。 - 查看儲存庫清單及其 GitHub 星圖。 - 刪除那些你不再想要的。 ![StarsOverTime](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbii15mn1tyuz63kjphk.png) > 💡 我們將使用 NextJS 新的應用程式路由器,在安裝專案之前請確保您的節點版本為 18+。 > 使用 NextJS 設定一個新專案 ``` npx create-next-app@latest ``` 我們必須將所有星星保存到我們的資料庫中! 在我們的示範中,我們將使用 SQLite 和 `Prisma`。 它非常容易安裝,但可以隨意使用任何其他資料庫。 ``` npm install prisma @prisma/client --save ``` 在我們的專案中安裝 Prisma ``` npx prisma init --datasource-provider sqlite ``` 轉到“prisma/schema.prisma”並將其替換為以下模式: ``` generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Repository { id String @id @default(uuid()) month Int year Int day Int name String stars Int @@unique([name, day, month, year]) } ``` 然後執行 ``` npx prisma db push ``` 我們基本上已經在 SQLite 資料庫中建立了一個名為「Repository」的新表: - 「月」、「年」、「日」是日期。 - `name` 儲存庫的名稱 - 「星星」以及該特定日期的星星數量。 你還可以看到我們在底部加入了一個`@@unique`,這意味著我們可以將`name`,`month`,`year`,`day`一起重複記錄。它會拋出一個錯誤。 讓我們新增 Prisma 客戶端。 建立一個名為「helper」的新資料夾,並新增一個名為「prisma.ts」的新文件,並在其中新增以下程式碼: ``` import {PrismaClient} from '@prisma/client'; export const prisma = new PrismaClient(); ``` 我們稍後可以使用該「prisma」變數來查詢我們的資料庫。 --- ## 應用程式 UI 骨架 💀 我們需要一些函式庫來完成本教學: - **Axios** - 向伺服器發送請求(如果您覺得更舒服,可以隨意使用 fetch) - **Dayjs -** 很棒的處理日期的函式庫。它是 moment.js 的替代品,但不再完全維護。 - **Lodash -** 很酷的資料結構庫。 - **react-hook-form -** 處理表單的最佳函式庫(驗證/值/等) - **chart.js** - 我選擇繪製 GitHub 星圖的函式庫。 讓我們安裝它們: ``` npm install axios dayjs lodash @types/lodash chart.js react-hook-form react-chartjs-2 --save ``` 建立一個名為“components”的新資料夾並新增一個名為“main.tsx”的新文件 新增以下程式碼: ``` "use client"; import {useForm} from "react-hook-form"; import axios from "axios"; import {Repository} from "@prisma/client"; import {useCallback, useState} from "react"; export default function Main() { const [repositoryState, setRepositoryState] = useState([]); const {register, handleSubmit} = useForm(); const submit = useCallback(async (data: any) => { const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name}); setRepositoryState([...repositoryState, ...repositoryResponse]); }, [repositoryState]) const deleteFromList = useCallback((val: List) => () => { axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`}); setRepositoryState(repositoryState.filter(v => v.name !== val.name)); }, [repositoryState]) return ( <div className="w-full max-w-2xl mx-auto p-6 space-y-12"> <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 Git repository" type="text" {...register('name', {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"> {repositoryState.map(val => ( <div key={val.name} className="space-y-4"> <div className="flex justify-between items-center py-10"> <h2 className="text-xl font-bold">{val.name}</h2> <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button> </div> <div className="bg-white rounded-lg border p-10"> <div className="h-[300px]]"> {/* Charts Component */} </div> </div> </div> ))} </div> </div> ) } ``` **超簡單的React元件** - 允許我們新增新的 GitHub 庫並將其發送到伺服器 POST 的表單 - `/api/repository` `{todo: 'add'}` - 刪除我們不需要 POST 的儲存庫 - `/api/repository` `{todo: 'delete'}` - 所有新增的庫及其圖表的清單。 讓我們轉到本文的複雜部分,新增儲存庫。 --- ## 數星星 ![CountingStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4m2j6046myxwv2c8kwla.gif) 在「helper」內部建立一個名為「all.stars.ts」的新檔案並新增以下程式碼: ``` import axios from "axios"; import dayjs from "dayjs"; import utc from 'dayjs/plugin/utc'; dayjs.extend(utc); const requestAmount = 20; export const getAllGithubStars = async (owner: string, name: string) => { // Get the amount of stars from GitHub const totalStars = (await axios.get(`https://api.github.com/repos/${owner}/${name}`)).data.stargazers_count; // get total pages const totalPages = Math.ceil(totalStars / 100); // How many pages to skip? We don't want to spam requests const pageSkips = totalPages < requestAmount ? requestAmount : Math.ceil(totalPages / requestAmount); // Send all the requests at the same time const starsDates = (await Promise.all([...new Array(requestAmount)].map(async (_, index) => { const getPage = (index * pageSkips) || 1; return (await axios.get(`https://api.github.com/repos/${owner}/${name}/stargazers?per_page=100&page=${getPage}`, { headers: { Accept: "application/vnd.github.v3.star+json", }, })).data; }))).flatMap(p => p).reduce((acc: any, stars: any) => { const yearMonth = stars.starred_at.split('T')[0]; acc[yearMonth] = (acc[yearMonth] || 0) + 1; return acc; }, {}); // how many stars did we find from a total of `requestAmount` requests? const foundStars = Object.keys(starsDates).reduce((all, current) => all + starsDates[current], 0); // Find the earliest date const lowestMonthYear = Object.keys(starsDates).reduce((lowest, current) => { if (lowest.isAfter(dayjs.utc(current.split('T')[0]))) { return dayjs.utc(current.split('T')[0]); } return lowest; }, dayjs.utc()); // Count dates until today const splitDate = dayjs.utc().diff(lowestMonthYear, 'day') + 1; // Create an array with the amount of stars we didn't find const array = [...new Array(totalStars - foundStars)]; // Set the amount of value to add proportionally for each day let splitStars: any[][] = []; for (let i = splitDate; i > 0; i--) { splitStars.push(array.splice(0, Math.ceil(array.length / i))); } // Calculate the amount of stars for each day return [...new Array(splitDate)].map((_, index, arr) => { const yearMonthDay = lowestMonthYear.add(index, 'day').format('YYYY-MM-DD'); const value = starsDates[yearMonthDay] || 0; return { stars: value + splitStars[index].length, date: { month: +dayjs.utc(yearMonthDay).format('M'), year: +dayjs.utc(yearMonthDay).format('YYYY'), day: +dayjs.utc(yearMonthDay).format('D'), } }; }); } ``` 那麼這裡發生了什麼事: - `totalStars` - 我們計算圖書館擁有的星星總數。 - `totalPages` - 我們計算頁數 **(每頁 100 筆記錄)** - `pageSkips` - 由於我們最多需要 20 個請求,因此我們檢查每次必須跳過多少頁。 - `starsDates` - 我們填充每個日期的星星數量。 - `foundStars` - 由於我們跳過日期,我們需要計算實際找到的星星總數。 - `lowestMonthYear` - 尋找我們擁有的恆星的最早日期。 - `splitDate` - 最早的日期和今天之間有多少個日期? - `array` - 一個包含 `splitDate` 專案數量的空陣列。 - `splitStars` - 我們缺少的星星數量,需要按比例加入每個日期。 - 最終返回 - 新陣列包含自開始以來每天的星星數量。 所以,我們已經成功建立了一個每天可以給我們星星的函數。 我嘗試過這樣顯示,結果很混亂。 您可能想要顯示每個月的星星數量。 此外,您可能想要累積星星**而不是:** - 二月 - 300 顆星 - 三月 - 200 顆星 - 四月 - 400 顆星 **如果有這樣的就更好了:** - 二月 - 300 顆星 - 三月 - 500 顆星 - 四月 - 900 顆星 兩個選項都有效。 **這取決於你想展示什麼!** 因此,讓我們轉到 helper 資料夾並建立一個名為「get.list.ts」的新檔案。 這是文件的內容: ``` import {prisma} from "./prisma"; import {groupBy, sortBy} from "lodash"; import {Repository} from "@prisma/client"; function fixStars (arr: any[]): Array<{name: string, stars: number, month: number, year: number}> { return arr.map((current, index) => { return { ...current, stars: current.stars + arr.slice(index + 1, arr.length).reduce((acc, current) => acc + current.stars, 0), } }).reverse(); } export const getList = async (data?: Repository[]) => { const repo = data || await prisma.repository.findMany(); const uniqMonth = Object.values( groupBy( sortBy( Object.values( groupBy(repo, (p) => p.name + '-' + p.year + '-' + p.month)) .map(current => { const stars = current.reduce((acc, current) => acc + current.stars, 0); return { name: current[0].name, stars, month: current[0].month, year: current[0].year } }), [(p: any) => -p.year, (p: any) => -p.month] ),p => p.name) ); const fixMonthDesc = uniqMonth.map(p => fixStars(p)); return fixMonthDesc.map(p => ({ name: p[0].name, list: p })); } ``` 首先,它將所有按日的星星轉換為按月的星星。 稍後我們會累積每個月的星星數量。 這裡要注意的一件主要事情是 `data?: Repository[]` 是可選的。 我們制定了一個簡單的邏輯:如果我們不傳遞資料,它將為我們資料庫中的所有儲存庫傳遞資料。 如果我們傳遞資料,它只會對其起作用。 為什麼問? - 當我們建立一個新的儲存庫時,我們需要在將其新增至資料庫後處理特定的儲存庫資料。 - 當我們重新載入頁面時,我們需要取得所有資料。 現在,讓我們來處理我們的星星建立/刪除路線。 轉到“src/app/api”並建立一個名為“repository”的新資料夾。在該資料夾中,建立一個名為「route.tsx」的新檔案。 在那裡加入以下程式碼: ``` import {getAllGithubStars} from "../../../../helper/all.stars"; import {prisma} from "../../../../helper/prisma"; import {Repository} from "@prisma/client"; import {getList} from "../../../../helper/get.list"; export async function POST(request: Request) { const body = await request.json(); if (!body.repository) { return new Response(JSON.stringify({error: 'Repository is required'}), {status: 400}); } const {owner, name} = body.repository.match(/github.com\/(?<owner>.*)\/(?<name>.*)/).groups; if (!owner || !name) { return new Response(JSON.stringify({error: 'Repository is invalid'}), {status: 400}); } if (body.todo === 'delete') { await prisma.repository.deleteMany({ where: { name: `${owner}/${name}` } }); return new Response(JSON.stringify({deleted: true}), {status: 200}); } const starsMonth = await getAllGithubStars(owner, name); const repo: Repository[] = []; for (const stars of starsMonth) { repo.push( await prisma.repository.upsert({ where: { name_day_month_year: { name: `${owner}/${name}`, month: stars.date.month, year: stars.date.year, day: stars.date.day, }, }, update: { stars: stars.stars, }, create: { name: `${owner}/${name}`, month: stars.date.month, year: stars.date.year, day: stars.date.day, stars: stars.stars, } }) ); } return new Response(JSON.stringify(await getList(repo)), {status: 200}); } ``` 我們共享 DELETE 和 CREATE 路由,這些路由通常不應在生產中使用,但我們在本文中這樣做是為了讓您更輕鬆。 我們從請求中取得 JSON,檢查「repository」欄位是否存在,並且它是 GitHub 儲存庫的有效路徑。 如果是刪除請求,我們使用 prisma 根據儲存庫名稱從資料庫中刪除儲存庫並傳回請求。 如果是建立,我們使用 getAllGithubStars 來獲取資料以保存到我們的資料庫中。 > 💡 由於我們已經在 `name`、`month`、`year` 和 `day` 上放置了唯一索引,如果記錄已經存在,我們可以使用 `prisma` `upsert` 來更新資料 最後,我們將新累積的資料回傳給客戶端。 最困難的部分完成了🍾 --- ## 主頁人口 💽 我們還沒有建立我們的主頁元件。 **我們開始做吧。** 前往“app”資料夾建立或編輯“page.tsx”並新增以下程式碼: ``` "use server"; import Main from "@/components/main"; import {getList} from "../../helper/get.list"; export default async function Home() { const list: any[] = await getList(); return ( <Main list={list} /> ) } ``` 我們使用與 getList 相同的函數來取得累積的所有儲存庫的所有資料。 我們還修改主要元件以支援它。 編輯 `components/main.tsx` 並將其替換為: ``` "use client"; import {useForm} from "react-hook-form"; import axios from "axios"; import {Repository} from "@prisma/client"; import {useCallback, useState} from "react"; interface List { name: string, list: Repository[] } export default function Main({list}: {list: List[]}) { const [repositoryState, setRepositoryState] = useState(list); const {register, handleSubmit} = useForm(); const submit = useCallback(async (data: any) => { const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name}); setRepositoryState([...repositoryState, ...repositoryResponse]); }, [repositoryState]) const deleteFromList = useCallback((val: List) => () => { axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`}); setRepositoryState(repositoryState.filter(v => v.name !== val.name)); }, [repositoryState]) return ( <div className="w-full max-w-2xl mx-auto p-6 space-y-12"> <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 Git repository" type="text" {...register('name', {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"> {repositoryState.map(val => ( <div key={val.name} className="space-y-4"> <div className="flex justify-between items-center py-10"> <h2 className="text-xl font-bold">{val.name}</h2> <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button> </div> <div className="bg-white rounded-lg border p-10"> <div className="h-[300px]]"> {/* Charts Components */} </div> </div> </div> ))} </div> </div> ) } ``` --- ## 顯示圖表! 📈 前往“components”資料夾並新增一個名為“chart.tsx”的新檔案。 新增以下程式碼: ``` "use client"; import {Repository} from "@prisma/client"; import {useMemo} from "react"; import React from 'react'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, } from 'chart.js'; import { Line } from 'react-chartjs-2'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend ); export default function ChartComponent({repository}: {repository: Repository[]}) { const labels = useMemo(() => { return repository.map(r => `${r.year}/${r.month}`); }, [repository]); const data = useMemo(() => ({ labels, datasets: [ { label: repository[0].name, data: repository.map(p => p.stars), borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.5)', tension: 0.2, }, ], }), [repository]); return ( <Line options={{ responsive: true, }} data={data} /> ); } ``` 我們使用“chart.js”函式庫來繪製“Line”類型的圖表。 這非常簡單,因為我們在伺服器端完成了所有資料結構。 這裡需要注意的一件大事是我們「匯出預設值」我們的 ChartComponent。那是因為它使用了「Canvas」。這在伺服器端不可用,我們需要延遲載入該元件。 讓我們修改“main.tsx”: ``` "use client"; import {useForm} from "react-hook-form"; import axios from "axios"; import {Repository} from "@prisma/client"; import dynamic from "next/dynamic"; import {useCallback, useState} from "react"; const ChartComponent = dynamic(() => import('@/components/chart'), { ssr: false, }) interface List { name: string, list: Repository[] } export default function Main({list}: {list: List[]}) { const [repositoryState, setRepositoryState] = useState(list); const {register, handleSubmit} = useForm(); const submit = useCallback(async (data: any) => { const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name}); setRepositoryState([...repositoryState, ...repositoryResponse]); }, [repositoryState]) const deleteFromList = useCallback((val: List) => () => { axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`}); setRepositoryState(repositoryState.filter(v => v.name !== val.name)); }, [repositoryState]) return ( <div className="w-full max-w-2xl mx-auto p-6 space-y-12"> <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 Git repository" type="text" {...register('name', {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"> {repositoryState.map(val => ( <div key={val.name} className="space-y-4"> <div className="flex justify-between items-center py-10"> <h2 className="text-xl font-bold">{val.name}</h2> <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button> </div> <div className="bg-white rounded-lg border p-10"> <div className="h-[300px]]"> <ChartComponent repository={val.list} /> </div> </div> </div> ))} </div> </div> ) } ``` 您可以看到我們使用“nextjs/dynamic”來延遲載入元件。 我希望將來 NextJS 能為客戶端元件加入類似「使用延遲載入」的內容 😺 --- ## 但是新星呢?來認識一下 Trigger.Dev! 每天加入新星星的最佳方法是執行 cron 請求來檢查新加入的星星並將其加入到我們的資料庫中。 不要使用 Vercel cron / GitHub 操作,或(上帝禁止)為此建立一個新伺服器。 我們可以使用 [Trigger.DEV](http://Trigger.DEV) 直接與我們的 NextJS 應用程式搭配使用。 那麼就讓我們來設定一下吧! 註冊 [Trigger.dev 帳號](https://trigger.dev/)。 註冊後,建立一個組織並為您的工作選擇一個專案名稱。 ![新組織](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdnxq8o7el7t4utvgf1u.jpeg) 選擇 Next.js 作為您的框架,並按照將 Trigger.dev 新增至現有 Next.js 專案的流程進行操作。 ![NextJS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4kt7e5r1mwg60atqfka.jpeg) 否則,請點選專案儀表板側邊欄選單上的「環境和 API 金鑰」。 ![開發金鑰](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 ``` 讓我們建立 TriggerDev 作業! 您將看到一個新建立的資料夾,名為“jobs”。 在那裡建立一個名為“sync.stars.ts”的新文件 新增以下程式碼: ``` import { cronTrigger, invokeTrigger } from "@trigger.dev/sdk"; import { client } from "@/trigger"; import { prisma } from "../../helper/prisma"; import axios from "axios"; import { z } from "zod"; // Your first job // This Job will be triggered by an event, log a joke to the console, and then wait 5 seconds before logging the punchline. client.defineJob({ id: "sync-stars", name: "Sync Stars Daily", version: "0.0.1", // Run a cron every day at 23:00 AM trigger: cronTrigger({ cron: "0 23 * * *", }), run: async (payload, io, ctx) => { const repos = await io.runTask("get-stars", async () => { // get all libraries and current amount of stars return await prisma.repository.groupBy({ by: ["name"], _sum: { stars: true, }, }); }); //loop through all repos and invoke the Job that gets the latest stars for (const repo of repos) { getStars.invoke(repo.name, { name: repo.name, previousStarCount: repo?._sum?.stars || 0, }); } }, }); const getStars = client.defineJob({ id: "get-latest-stars", name: "Get latest stars", version: "0.0.1", // Run a cron every day at 23:00 AM trigger: invokeTrigger({ schema: z.object({ name: z.string(), previousStarCount: z.number(), }), }), run: async (payload, io, ctx) => { const stargazers_count = await io.runTask("get-stars", async () => { const { data } = await axios.get( `https://api.github.com/repos/${payload.name}`, { headers: { authorization: `token ${process.env.TOKEN}`, }, } ); return data.stargazers_count as number; }); await prisma.repository.upsert({ where: { name_day_month_year: { name: payload.name, month: new Date().getMonth() + 1, year: new Date().getFullYear(), day: new Date().getDate(), }, }, update: { stars: stargazers_count - payload.previousStarCount, }, create: { name: payload.name, stars: stargazers_count - payload.previousStarCount, month: new Date().getMonth() + 1, year: new Date().getFullYear(), day: new Date().getDate(), }, }); }, }); ``` 我們建立了一個名為“Sync Stars Daily”的新作業,該作業將在每天下午 23:00 執行 - 它在 cron 文本中的表示為:`0 23 * * *` 我們在資料庫中取得所有目前儲存庫,按名稱將它們分組,並對星星進行求和。 由於一切都在 Vercel 無伺服器上執行,因此我們可能會在檢查所有儲存庫時遇到逾時。 為此,我們將每個儲存庫傳送到不同的作業。 我們使用“invoke”建立新作業,然後在“獲取最新的星星”中處理它們 我們迭代所有新儲存庫並獲取當前的星星數量。 我們用舊的星星數量去除新的星星數量,得到今天的星星數量。 我們使用“prisma”將其新增至資料庫。沒有比這更簡單的了! 最後一件事是編輯“jobs/index.ts”並將內容替換為: ``` export * from "./sync.stars"; ``` 你就完成了🥳 --- ## 讓我們聯絡吧! 🔌 作為開源開發者,我們邀請您加入我們的[社群](https://discord.gg/nkqV9xBYWy),以做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: [https://github.com/triggerdotdev/blog/tree/main/stars-monitor](https://github.com/triggerdotdev/blog/tree/main/stars-monitor) 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/take-nextjs-to-the-next-level-create-a-github-stars-monitor-130a

使用 Python 和 Pytest 自動化 API 測試

您是否想過如何使用 python 測試您的 API?在本文中,我們將學習如何使用 Python 和 pytest 框架來測試我們的 API。 對於本教程,您需要安裝 python,您可以在[此處](https://www.python.org/downloads/)下載它 --- ### 簡介: - [什麼是 Python 和 Pytest 框架](#what-is-python-and-pytest-framework) - [我們專案的設定](#configuration-of-our-project) - [使用python建立虛擬環境](#creation-of-virtual-environment-with-python) - [測試的依賴關係設定](#setup-of-dependency-for-the-tests) - [建立我們的第一個測試](#creating-our-first-test) - [將被測試的 API 的定義](#definition-of-the-api-that-will-be-tested) - [建立我們的測試](#creating-our-test) - [重建我們的測試](#refactoring-our-tests) - [產生 html 報告結果](#generate-html-report-result) - [結論](#conclusion) --- ## 什麼是 Python 和 Pytest 框架 「Python」是一種高級通用程式語言,以其簡單性和可讀性而聞名。它由 Guido van Rossum 建立,於 1991 年首次發布。 Python 的設計易於學習,並且具有乾淨簡潔的語法,這使其成為初學者和經驗豐富的程式設計師的流行選擇。 「pytest」框架可以輕鬆編寫小型、可讀的測試,並且可以擴展以支援應用程式和程式庫的複雜功能測試。 --- ## 我們專案的配置 ### 用python建立虛擬環境 在開始建立之前,我們先來了解一下什麼是Python上的虛擬環境。 Python 中的虛擬環境是一個獨立的目錄或資料夾,可讓您為專案建立和管理隔離的 Python 環境。透過環境,您可以輕鬆管理依賴項,避免與不同版本的 python 發生衝突。 虛擬環境(除其他外)是: - 用於包含支援專案(庫或應用程式)所需的特定 Python 解釋器以及軟體庫和二進位檔案。預設情況下,它們與其他虛擬環境中的軟體以及作業系統中安裝的 Python 解釋器和庫隔離。 - 包含在專案目錄中的目錄中,通常名為“venv”或“.venv”,或在許多虛擬環境的容器目錄下,例如“~/.virtualenvs”。 - 未簽入原始碼控制系統(例如 Git)。 - 被認為是一次性的 - 應該很容易刪除並從頭開始重新建立它。您沒有在環境中放置任何專案程式碼 - 不被視為可移動或可複製 - 您只需在目標位置重新建立相同的環境。 您可以在[此處](https://docs.python.org/3/library/venv.html#venv-def)閱讀有關 python 環境的更多資訊。 #### 視窗 首先,為您的專案建立一個資料夾,然後打開 cmd 並使用命令 cd 導航到該資料夾: ``` cd tests_with_python ``` 如果您不知道資料夾在哪裡,可以執行命令“ls”,您將看到資料夾列表,並且可以瀏覽它們。在我們的專案資料夾中,執行以下命令: ``` python -m venv name_of_environment ``` 您的環境名稱可以是任何人,只需記住python 區分大小寫,請查看[PEP 8 風格指南](https://peps.python.org/pep-0008/) 以了解有關Python 約定的更多資訊. 要啟動我們的環境,我們使用以下命令: ``` name_of_environment\Scripts\Activate ``` 如果一切正確,您的環境將被激活,並且在 cmd 上您將看到如下所示: ``` (name_of_environment) C:\User\tests ``` 要停用您的環境,只需執行: ``` deactivate ``` #### Linux 或 MacOS 為您的專案建立一個資料夾,然後打開 cmd 並使用命令 cd 導航到該資料夾: ``` cd tests_with_python ``` 要啟動我們的環境,我們使用以下命令: ``` source name_of_environment/bin/activate ``` 如果一切正確,您的環境將被激活,並且在 cmd 上您將看到如下所示: ``` (name_of_environment) your_user_name tests % ``` 要停用您的環境,只需執行: ``` deactivate ``` ### 設定測試的依賴關係 當我們要測試 API 時,我們需要安裝依賴項來幫助我們進行測試,首先我們將安裝「requests」函式庫來幫助我們發出請求: PS:在執行此命令之前請確保您的環境已激活 ``` pip install requests ``` 為了進行測試,我們將安裝「pytests」框架: ``` pip install pytest ``` --- ## 建立我們的第一個測試 ### 將要測試的 API 的定義 在本教程中,我們將使用返回小行星列表的 Nasa API:[Asteroids - NeoWs](https://api.nasa.gov/#donkiGST),我們將測試檢索基於小行星列表的端點在他們最接近地球的日期。 關於API: - 基本網址:`https://api.nasa.gov/neo/rest/v1/feed` - 查詢參數: |參數|類型|預設|描述| | --------|---------|--------|--------------------| |start_date|YYYY-MM-DD|無|小行星搜尋的開始日期| |end_date|YYYY-MM-DD|start_date後7天|小行星搜尋的結束日期| |api_key|字串|DEMO_KEY|用於擴展用途的 api.nasa.gov 密鑰| 在本教程中,我們將重點放在三種類型的測試: - 合約:如果 API 能夠驗證傳送的查詢參數 - 狀態:狀態程式碼是否正確 - 身份驗證:即使這個API不需要令牌,我們也可以用它來做測試 我們的場景: |方法|測試|預期結果 | | --------|--------|--------------------| |獲取 |搜尋成功 | - 傳回狀態程式碼 200<br/> 正文回應包含小行星清單| |獲取 |無需任何查詢參數即可搜尋 | - 返回狀態碼403<br/>| |獲取 |僅搜尋開始日期| - 傳回狀態程式碼 200 <br/> 主體回應包含小行星清單| |獲取 |僅搜尋結束日期| - 傳回狀態程式碼 200 <br/> 主體回應包含小行星清單| |獲取 |在有效日期範圍內搜尋| - 傳回狀態碼 200<br/> - 正文回應包含所有非空白欄位| |獲取 |當開始日期大於結束日期時進行搜尋| - 傳回狀態程式碼 400 <br/>| |獲取 |使用無效的 API 令牌進行搜尋| - 傳回狀態程式碼 403 <br/> 主體回應包含小行星清單| ### 建立我們的測試 首先,我們將建立一個名為「tests.py」的文件,我們將在該文件中編寫測試。為了幫助我們使用良好的實踐並編寫良好的自動化測試,讓我們使用 [TDD(測試驅動開發)](https://www.browserstack.com/guide/what-is-test-driven-development?psafe_param=1?keyword=&campaignid=&adgroupid=&adid=8784011037660164696&utm_source=google&utm_medium=cpc&utm_platform=paidads&utm_content=602353912717&utm_campapaidads&utm_content=602353912717&utm_campa.utm_term=+&gad_source=1&gclid=CjwKCAiAxreqBhAxEiwAfGfndN8P705lwnkvEFnCz_lueR2hnhmZXgboBQEtKTaCIRbhcb1SXOxBYhoC-WoQAD_BwwE)技術。 該技術包括: - 紅色 - 進行失敗的測試 - 綠色 - 使此測試通過 - 重構 - 重構所做的事情,刪除重複的內容 為了編寫一套好的測試,我們將使用 3A 技術: - 安排:準備上下文。 - 行動:執行我們想要示範的行動。 - 斷言:表明我們預期的結果確實發生了。 從紅色開始,使用 3A 技術,我們將編寫第一個測試「成功搜尋小行星」: ``` import pytest def test_search_asteroids_with_sucess(): # Arrange: api_key = "DEMO_KEY" #Act: response = make_request(api_key) #Assertion: assert response.status_code == 200 # Validation of status code data = response.json() # Assertion of body response content: assert len(data) > 0 assert data["element_count"] > 0 ``` - 安排:我們建立一個變數來插入 api_key,在此步驟中,您可以插入執行測試所需的任何資料。通常,在這一步驟我們會建立模擬資料。 - Act:在這一步驟中我們呼叫了負責發出請求的方法 - 斷言:我們驗證回應 `方法或類別的名稱應以 test 開頭` 若要執行我們的測試,請在命令提示字元中執行: ``` pytest test.py ``` 我們將收到一個錯誤,因為我們沒有建立執行請求的方法: ``` test.py F [100%] ====================================================================== FAILURES ====================================================================== _________________________________________________________ test_search_asteroids_with_sucess __________________________________________________________ def test_search_asteroids_with_sucess(): > response = make_request() E NameError: name 'make_request' is not defined test.py:5: NameError ============================================================== short test summary info =============================================================== FAILED test.py::test_search_asteroids_with_sucess - NameError: name 'make_request' is not defined ================================================================= 1 failed in 0.01s ================================================================== ``` 現在,讓我們建立方法來執行請求: ``` import requests def make_request(api_key): base_url = "https://api.nasa.gov/neo/rest/v1/feed/" response = requests.get(f'{base_url}?api_key={api_key}') return response ``` 現在,再次執行我們的測試: ``` ================================================================ test session starts ================================================================= platform darwin -- Python 3.11.5, pytest-7.4.3, pluggy-1.3.0 rootdir: /Users/Documents/tests_python collected 1 item test.py . [100%] ================================================================= 1 passed in 20.22s ================================================================= ``` --- ## 重構我們的測試 現在我們已經了解如何使用 pytest 建立測試以及如何建立請求,我們可以編寫其他測試並開始重構測試。我們要做的第一個重構是從測試文件中刪除請求方法。我們將建立一個名為「make_requests.py」的新文件,其中將包含我們的請求,並將我們所做的請求移至此文件: ``` import requests def make_request(api_key): base_url = "https://api.nasa.gov/neo/rest/v1/feed/" response = requests.get(f'{base_url}?api_key={api_key}') return response ``` 現在,我們需要考慮在其他測試中重複使用此方法,因為我們需要為不同的測試傳遞不同的參數。我們可以透過很多方法來做到這一點,在本教程中,我們將參數的名稱從“api_key”更改為“query_parameters”。我們這樣做是為了讓我們的方法更加靈活,我們可以一次傳遞參數進行測試: ``` import requests def make_request(query_parameters): base_url = "https://api.nasa.gov/neo/rest/v1/feed/" response = requests.get(f'{base_url}?{query_parameters}') return response ``` 之後,我們需要更改我們的測試文件。我們將導入我們建立的這個方法: ``` from make_requests import make_request ``` 為了以更好的方式組織我們的測試,並遵循 pytest 文件的建議,我們將測試移至類別「TestClass」: 再次執行我們的測試: ``` ============================= test session starts ============================== collecting ... collected 7 items test.py::TestClass::test_search_asteroids_with_sucess test.py::TestClass::test_search_asteroids_with_query_parameters_empty test.py::TestClass::test_search_asteroids_with_start_date test.py::TestClass::test_search_asteroids_with_end_date test.py::TestClass::test_search_asteroids_in_valid_range test.py::TestClass::test_search_asteroids_in_invalid_range test.py::TestClass::test_search_asteroids_in_invalid_token ============================== 7 passed in 5.85s =============================== PASSED [ 14%]PASSED [ 28%]PASSED [ 42%]PASSED [ 57%]PASSED [ 71%]PASSED [ 85%]PASSED [100%] Process finished with exit code 0 ``` ### 產生 html 報告結果 為了更好地視覺化您的測試結果,我們可以使用 pytest-html-reporter 庫產生報告 html,為此,我們首先需要安裝該套件: ``` pip install pytest-html ``` 若要產生報告,請在執行測試時新增: ``` pytest test.py --html-report=./report/report.html ``` 將產生一個包含測試結果的 .html 文件,如下所示: ![report_example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eol3v7iex84t7prmyb9c.png) ## 結論 本文是一篇教程,介紹如何開始使用 python 和 pytest 框架為 API 編寫自動化測試以及如何產生一個報告 html。 您可以在[此處](https://github.com/aliciamarianne1507/tests_python)存取本教學中使用的專案。 我希望這些內容對您有用。 如果您有任何疑問,請隨時與我聯繫! 親親,下週見💅🏼 --- 原文出處:https://dev.to/m4rri4nne/automating-your-api-tests-using-python-and-pytest-23cc

⚡️7個簡單的人工智慧產品整合(與時俱進👴🏻👨‍🔧)

## 簡介 最佳的易於建構的人工智慧產品整合清單。 這些可以為你的專案帶來魔力,所以別忘了向他們表達支持🌟 現在讓我們一起走上AI之路👨‍🌾 --- ## 1. [CopilotPortal](https://github.com/RecursivelyAI/CopilotKit):將可操作的 LLM 聊天機器人嵌入您的應用程式中。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x31tl645tfa3sw5lwwzv.jpg) 應用程式中的上下文感知 LLM 聊天機器人可以回答問題並採取行動。 只需幾行程式碼即可獲得一個可用的聊天機器人,然後根據需要進行自訂和嵌入。 ``` import "@copilotkit/react-ui/styles.css"; import { CopilotProvider } from "@copilotkit/react-core"; import { CopilotSidebarUIProvider } from "@copilotkit/react-ui"; export default function App(): JSX.Element { return ( <CopilotProvider chatApiEndpoint="/api/copilotkit/chat"> <CopilotSidebarUIProvider> <YourContent /> </CopilotSidebarUIProvider> </CopilotProvider> ); } ``` https://github.com/RecursivelyAI/CopilotKit --- ## 2. [LinguiJS](https://github.com/lingui/js-lingui) - 自動且簡單的國際化 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80f1yb9etnzf3z4pk7t3.png) 簡單而強大的開源國際化函式庫。 易於整合的框架,用於建立多語言反應應用程式。 ``` import { Trans } from "@lingui/macro" function App() { return ( <Trans id="msg.docs" /* id is optional */> Read the <a href="https://lingui.dev">documentation</a> for more info. </Trans> ) } ``` https://github.com/lingui/js-lingui --- ## 3. Pezzo.ai - 可觀測性、成本和即時工程平台 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nxvbgi5zkghkb0t64npw.jpeg) 用於管理 OpenAI 通話的集中平台。 優化您的提示和令牌使用。追蹤您的人工智慧使用情況。 免費且易於整合。 ``` const prompt = await pezzo.getPrompt("AnalyzeSentiment"); const response = await openai.chat.completions.create(prompt); ``` https://github.com/pezzolabs/pezzo --- ## 4. CopilotTextarea - React 應用程式中的 AI 驅動寫作 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uye8z6aac1015iiqd3lk.png) 具有 Github CopilotX 功能的任何 React `<textarea>` 的直接替代品。 自動完成、插入、編輯。 可以即時或由開發人員提前提供任何上下文。 ``` import { CopilotTextarea } from "@copilotkit/react-textarea"; import { CopilotProvider } from "@copilotkit/react-core"; // Provide context... useMakeCopilotReadable(...) // in your component... <CopilotProvider> <CopilotTextarea/> </CopilotProvider>` ``` https://github.com/RecursivelyAI/CopilotKit --- ## 5. LangChain - 將人工智慧整合到鏈中。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8s87kvm5jt5wmsv702r1.png) 易於使用的 API 和函式庫,用於將 LLM 新增到應用程式中。 將不同的人工智慧元件和模型連接在一起。 輕鬆嵌入上下文和語義資料以實現強大的整合。 https://github.com/langchain-ai/langchain --- ## 6. SwirlSearch - 人工智慧驅動的搜尋。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/extnr9oxhubs6m9x817a.png) LLM 支援的搜尋、摘要和輸出。 同時搜尋多個內容來源並產生整合輸出。 功能強大,可自訂各種資料來源的應用程式內整合。 https://github.com/swirlai/swirl-search --- ## 7. ReactAgent - 用於從使用者故事產生 React 元件的實驗性 LLM 代理 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2gbb71oqobdeuh1pgnp.jpg) 使用 GPT-4 將使用者故事轉化為可用的 React 元件的實驗工具。 為其提供本地設計以實現一致的輸出和設計語言。 https://github.com/eylonmiz/react-agent --- 感謝大家! --- 原文出處:https://dev.to/copilotkit/7-easy-ai-product-integrations-to-keep-up-with-the-times--1cg2

💨 將 Javascript 應用部署到 Kubernetes 的最快方法 🌬️ ✨

## 簡介 在本教程中,您將學習如何在 Kubernetes(容器編排平台)上部署您的第一個 JavaScript 應用程式☸️。 我們將部署一個簡單的 **express** 伺服器,該伺服器使用 **Minikube** ✨ 在本機 Kubernetes 上傳回範例 JSON 物件。 **先決條件📜:** - **Docker**:用於容器化應用程式。 🐋 - **Minikube**:用於在本地執行 Kubernetes。 ☸️ ![GetADeploy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn162frk9shm0d76z99n.gif) *** ## Odigos - 開源分散式追蹤 **無需編寫任何程式碼即可同時監控所有應用程式!** 利用唯一可以在所有應用程式中產生分散式追蹤的平台來簡化 OpenTelemetry 的複雜性。 我們真的才剛開始。 可以幫我們加個星星嗎?請問? 😽 https://github.com/keyval-dev/odigos [![貓咪](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/84twzafd93w3a4ktqflm.png)](https://github.com/keyval-dev/odigos) --- ### 讓我們設定一下🚀 我們將首先初始化我們的專案: ``` npm init -y ``` 這會使用 `package.json` 📝 檔案初始化 **NodeJS** 專案,該檔案追蹤我們安裝的依賴項。 安裝 Express.js 框架 ``` npm install express ``` 現在,在 `package.json` 中,依賴項物件應該如下所示。 ✅ ``` "dependencies": { "express": "^4.18.2" } ``` 現在,在專案的根目錄中建立一個「index.js」檔案並新增以下程式碼行。 🚀 ``` // 👇🏻 Initialize express. const express = require("express"); const app = express(); const port = 3000; // 👇🏻 Return a sample JSON object with a message property on the root path. app.get("/", (req, res) => { res.json({ message: "Hello from Odigos!", }); }); // 👇🏻 Listen on port 3000. app.listen(port, () => { console.log(`Server is listening on port ${port}`); }); ``` 我們需要在「package.json」中新增一個腳本來執行應用程式。將其新增至 `package.json` 的腳本物件中。 ``` "scripts": { "dev": "node index.js" }, ``` 現在,要檢查我們的應用程式是否正常執行,請使用「npm run dev」執行伺服器,並透過 CLI 或在瀏覽器中向「localhost:3000」發出 get 請求。 ✨ 如果您使用 CLI,請確保已安裝了 [cURL](https://curl.se/)。 ✅ ``` curl http://localhost:3000 ``` 你應該看到這樣的東西。 👇🏻 ![cURL 回應](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxs2uu8u0aa7kw6k9ta4.png) 現在,您可以使用「Ctrl + C」簡單地停止正在執行的 Express 伺服器🚫 我們的範例應用程式已準備就緒! 🎉 現在,讓我們將其容器化並推送到 Kubernetes。 🐳☸️ *** ### 將應用程式容器化📦 我們將使用 **Docker** 來容器化我們的應用程式。 在專案的根目錄中,建立一個名為「Dockerfile」的新檔案。 > 💡 確保名稱完全相同。否則,您將需要明確傳遞“-f”標誌來指定“Dockerfile”路徑。 ``` # Uses node as the base image FROM node:21-alpine # Sets up our working directory as /app inside the container. WORKDIR /app # Copyies package json files. COPY package.json package-lock.json ./ # Installs the dependencies from the package.json RUN npm install --production # Copies current directory files into the docker environment COPY . . # Expose port 3000 as our server uses it. EXPOSE 3000 # Finally runs the server. CMD ["node", "index.js"] ``` 現在,我們需要建置 ⚒️ 這個容器才能實際使用它並將其推送到 Kubernetes。 執行此命令來建置“Dockerfile”。 > 🚨 如果您在 Windows 上執行它,請確保 Docker Desktop 正在執行。 ``` // 👇🏻 We are tagging our image name to express-server docker build -t express-server . ``` 現在,是時候執行容器了。 🏃🏻‍♂️💨 ``` docker run -dp 127.0.0.1:3000:3000 express-server ``` > 💡 我們正在後台執行容器,容器連接埠 3000 對應到我們的電腦連接埠 3000。 再次執行以下命令,您應該會看到與之前相同的結果。 ✅ ``` curl http://localhost:3000 ``` > **注意**:這次應用程式沒有像以前一樣在我們的電腦上執行。相反,它在容器內運作。 🤯 *** ### 在 Kubernetes 中部署 ## 如前所述,我們將使用 Minikube 在本機電腦上建立編排環境,並使用 kubectl 命令與 Kubernetes 互動。 😄 **啟動 Minikube:🚀** ``` minikube start ``` 由於我們將使用本機容器而不是從 docker hub 中提取它們,因此請執行這些命令。 ✨ ``` eval $(minikube docker-env) docker build -t express-server . ``` `eval $(minikube docker-env)`:用於將終端機的 `docker-cli` 指向 minikube 內的 Docker 引擎。 > 🚨 注意,我們很多人都使用 Fish 作為 shell,因此對於 Fish 來說,相應的命令是 `eval (minikube docker-env)` 現在,在專案根目錄中,建立一個嵌套資料夾“k8/deployment”,並在部署資料夾中建立一個名為“deployment.yaml”的新文件,其中包含以下內容。 在此文件中,我們將管理容器的部署。 👇🏻 ``` # 👇🏻 /k8/deployment/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: express-deployment spec: selector: matchLabels: app: express-svr template: metadata: labels: app: express-svr spec: containers: - name: express-svr image: express-server imagePullPolicy: Never # Make sure to set it to Never, or else it will pull from the docker hub and fail. resources: limits: memory: "128Mi" cpu: "500m" ports: - containerPort: 3000 ``` 最後,執行此命令以應用我們剛剛建立的部署配置「deployment.yaml」。 ✨ ``` kubectl apply -f .\k8\deployment\deployment.yaml ``` 現在,如果我們查看正在執行的 Pod,我們可以看到 Pod 已成功建立。 🎉 ![執行 kubernetes pod](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83ijo09xpd9ccv30h6ug.png) 要查看我們建立的 Pod 的日誌,請執行“kubectl messages <pod_name>”,我們應該會看到以下內容。 ![正在執行的 kubernetes pod 的日誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f97aap8qioqsjr45rafw.png) 至此,我們的「express-server」就成功部署在本地 Kubernetes 上了。 😎 *** 這就是本文的內容,我們成功地將應用程式容器化並將其部署到 Kubernetes。 本文的原始碼可以在這裡找到 https://github.com/keyval-dev/blog/tree/main/js-on-k8s 非常感謝您的閱讀! 🎉🫡 --- 原文出處:https://dev.to/odigos/the-fastest-way-to-deploy-your-javascript-app-to-kubernetes-2j33

🚀使用 NextJS、Trigger.dev 和 GPT4 做一個履歷表產生器🔥✨

## 簡介 在本文中,您將學習如何使用 NextJS、Trigger.dev、Resend 和 OpenAI 建立簡歷產生器。 😲 - 加入基本詳細訊息,例如名字、姓氏和最後工作地點。 - 產生詳細訊息,例如個人資料摘要、工作經歷和工作職責。 - 建立包含所有資訊的 PDF。 - 將所有內容傳送到您的電子郵件 ![猴子手錶](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23k6hee187s62k8y1dmd.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) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 https://github.com/triggerdotdev/trigger.dev --- ## 讓我們來設定一下吧🔥 使用 NextJS 設定一個新專案 ``` npx create-next-app@latest ``` 我們將建立一個包含基本資訊的簡單表單,例如: - 名 - 姓 - 電子郵件地址 - 你的頭像 - 以及你今天為止的經驗! ![輸入](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/01mmvn0lvw1p1i4knoa8.png) 我們將使用 NextJS 的新應用程式路由器。 開啟`layout.tsx`並加入以下程式碼 ``` import { GeistSans } from "geist/font"; import "./globals.css"; const defaultUrl = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000"; export const metadata = { metadataBase: new URL(defaultUrl), title: "Resume Builder with GPT4", description: "The fastest way to build a resume with GPT4", }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en" className={GeistSans.className}> <body className="bg-background text-foreground"> <main className="min-h-screen flex flex-col items-center"> {children} </main> </body> </html> ); } ``` 我們基本上是為所有頁面設定佈局(即使我們只有一頁。) 我們設定基本的頁面元資料、背景和全域 CSS 元素。 接下來,讓我們打開“page.tsx”並加入以下程式碼: ``` <div className="flex-1 w-full flex flex-col items-center"> <nav className="w-full flex justify-center border-b border-b-foreground/10 h-16"> <div className="w-full max-w-6xl flex justify-between items-center p-3 text-sm"> <span className="font-bold select-none">resumeGPT.</span> </div> </nav> <div className="animate-in flex-1 flex flex-col opacity-0 max-w-6xl px-3"> <Home /> </div> </div> ``` 這設定了我們的resumeGPT 的標題和主要的家庭元件。 <小時/> ## 建立表單的最簡單方法 保存表單資訊並驗證欄位最簡單的方法是使用react-hook-form。 我們將上傳個人資料照片。 為此,我們不能使用基於 JSON 的請求。 我們需要將 JSON 轉換為有效的表單資料。 那麼就讓我們把它們全部安裝吧! ``` npm install react-hook-form object-to-formdata axios --save ``` 建立一個名為 Components 的新資料夾,新增一個名為「Home.tsx」的新文件,並新增以下程式碼: ``` "use client"; import React, { useState } from "react"; import {FormProvider, useForm} from "react-hook-form"; import Companies from "@/components/Companies"; import axios from "axios"; import {serialize} from "object-to-formdata"; export type TUserDetails = { firstName: string; lastName: string; photo: string; email: string; companies: TCompany[]; }; export type TCompany = { companyName: string; position: string; workedYears: string; technologies: string; }; const Home = () => { const [finished, setFinished] = useState<boolean>(false); const methods = useForm<TUserDetails>() const { register, handleSubmit, formState: { errors }, } = methods; const handleFormSubmit = async (values: TUserDetails) => { axios.post('/api/create', serialize(values)); setFinished(true); }; if (finished) { return ( <div className="mt-10">Sent to the queue! Check your email</div> ) } return ( <div className="flex flex-col items-center justify-center p-7"> <div className="w-full py-3 bg-slate-500 items-center justify-center flex flex-col rounded-t-lg text-white"> <h1 className="font-bold text-white text-3xl">Resume Builder</h1> <p className="text-gray-300"> Generate a resume with GPT in seconds 🚀 </p> </div> <FormProvider {...methods}> <form onSubmit={handleSubmit(handleFormSubmit)} className="p-4 w-full flex flex-col" > <div className="flex flex-col lg:flex-row gap-4"> <div className="flex flex-col w-full"> <label htmlFor="firstName">First name</label> <input type="text" required id="firstName" placeholder="e.g. John" className="p-3 rounded-md outline-none border border-gray-500 text-white bg-transparent" {...register('firstName')} /> </div> <div className="flex flex-col w-full"> <label htmlFor="lastName">Last name</label> <input type="text" required id="lastName" placeholder="e.g. Doe" className="p-3 rounded-md outline-none border border-gray-500 text-white bg-transparent" {...register('lastName')} /> </div> </div> <hr className="w-full h-1 mt-3" /> <label htmlFor="email">Email Address</label> <input type="email" required id="email" placeholder="e.g. [email protected]" className="p-3 rounded-md outline-none border border-gray-500 text-white bg-transparent" {...register('email', {required: true, pattern: /^\S+@\S+$/i})} /> <hr className="w-full h-1 mt-3" /> <label htmlFor="photo">Upload your image 😎</label> <input type="file" id="photo" accept="image/x-png" className="p-3 rounded-md outline-none border border-gray-500 mb-3" {...register('photo', {required: true})} /> <Companies /> <button className="p-4 pointer outline-none bg-blue-500 border-none text-white text-base font-semibold rounded-lg"> CREATE RESUME </button> </form> </FormProvider> </div> ); }; export default Home; ``` 您可以看到我們從「使用客戶端」開始,它基本上告訴我們的元件它應該只在客戶端上執行。 為什麼我們只需要客戶端? React 狀態(輸入變更)僅在用戶端可用。 我們設定兩個接口,「TUserDetails」和「TCompany」。它們代表了我們正在使用的資料的結構。 我們將“useForm”與“react-hook-form”一起使用。它為我們的輸入建立了本地狀態管理,並允許我們輕鬆更新和驗證我們的欄位。您可以看到,在每個「輸入」中,都有一個簡單的「註冊」函數,用於指定輸入名稱和驗證並將其註冊到託管狀態。 這很酷,因為我們不需要使用像“onChange”這樣的東西 您還可以看到我們使用了“FormProvider”,這很重要,因為我們希望在子元件中擁有“react-hook-form”的上下文。 我們還有一個名為「handleFormSubmit」的方法。這是我們提交表單後呼叫的方法。您可以看到我們使用“serialize”函數將 javascript 物件轉換為 FormData,並向伺服器發送請求以使用“axios”啟動作業。 您可以看到另一個名為“Companies”的元件。該元件將讓我們指定我們工作過的所有公司。 那麼讓我們努力吧。 建立一個名為「Companies.tsx」的新文件 並加入以下程式碼: ``` import React, {useCallback, useEffect} from "react"; import { TCompany } from "./Home"; import {useFieldArray, useFormContext} from "react-hook-form"; const Companies = () => { const {control, register} = We(); const {fields: companies, append} = useFieldArray({ control, name: "companies", }); const addCompany = useCallback(() => { append({ companyName: '', position: '', workedYears: '', technologies: '' }) }, [companies]); useEffect(() => { addCompany(); }, []); return ( <div className="mb-4"> {companies.length > 1 ? ( <h3 className="font-bold text-white text-3xl my-3"> Your list of Companies: </h3> ) : null} {companies.length > 1 && companies.slice(1).map((company, index) => ( <div key={index} className="mb-4 p-4 border bg-gray-800 rounded-lg shadow-md" > <div className="mb-2"> <label htmlFor={`companyName-${index}`} className="text-white"> Company Name </label> <input type="text" id={`companyName-${index}`} className="p-2 border border-gray-300 rounded-md w-full bg-transparent" {...register(`companies.${index}.companyName`, {required: true})} /> </div> <div className="mb-2"> <label htmlFor={`position-${index}`} className="text-white"> Position </label> <input type="text" id={`position-${index}`} className="p-2 border border-gray-300 rounded-md w-full bg-transparent" {...register(`companies.${index}.position`, {required: true})} /> </div> <div className="mb-2"> <label htmlFor={`workedYears-${index}`} className="text-white"> Worked Years </label> <input type="number" id={`workedYears-${index}`} className="p-2 border border-gray-300 rounded-md w-full bg-transparent" {...register(`companies.${index}.workedYears`, {required: true})} /> </div> <div className="mb-2"> <label htmlFor={`workedYears-${index}`} className="text-white"> Technologies </label> <input type="text" id={`technologies-${index}`} className="p-2 border border-gray-300 rounded-md w-full bg-transparent" {...register(`companies.${index}.technologies`, {required: true})} /> </div> </div> ))} <button type="button" onClick={addCompany} className="mb-4 p-2 pointer outline-none bg-blue-900 w-full border-none text-white text-base font-semibold rounded-lg"> Add Company </button> </div> ); }; export default Companies; ``` 我們從 useFormContext 開始,它允許我們取得父元件的上下文。 接下來,我們使用 useFieldArray 建立一個名為 Companies 的新狀態。這是我們擁有的所有公司的一個陣列。 在「useEffect」中,我們新增陣列的第一項以對其進行迭代。 當點擊“addCompany”時,它會將另一個元素推送到陣列中。 我們已經和客戶完成了🥳 --- ## 解析HTTP請求 還記得我們向“/api/create”發送了一個“POST”請求嗎? 讓我們轉到 app/api 資料夾並在該資料夾中建立一個名為「create」的新資料夾,建立一個名為「route.tsx」的新檔案並貼上以下程式碼: ``` import {NextRequest, NextResponse} from "next/server"; import {client} from "@/trigger"; export async function POST(req: NextRequest) { const data = await req.formData(); const allArr = { name: data.getAll('companies[][companyName]'), position: data.getAll('companies[][position]'), workedYears: data.getAll('companies[][workedYears]'), technologies: data.getAll('companies[][technologies]'), }; const payload = { firstName: data.get('firstName'), lastName: data.get('lastName'), photo: Buffer.from((await (data.get('photo[0]') as File).arrayBuffer())).toString('base64'), email: data.get('email'), companies: allArr.name.map((name, index) => ({ companyName: allArr.name[index], position: allArr.position[index], workedYears: allArr.workedYears[index], technologies: allArr.technologies[index], })).filter((company) => company.companyName && company.position && company.workedYears && company.technologies) } await client.sendEvent({ name: 'create.resume', payload }); return NextResponse.json({ }) } ``` > 此程式碼只能在 NodeJS 版本 20+ 上運作。如果版本較低,將無法解析FormData。 該程式碼非常簡單。 - 我們使用 `req.formData` 將請求解析為 FormData - 我們將基於 FormData 的請求轉換為 JSON 檔案。 - 我們提取圖像並將其轉換為“base64” - 我們將所有內容傳送給 TriggerDev --- ## 製作履歷並將其發送到您的電子郵件📨 建立履歷是我們需要的長期任務 - 使用 ChatGPT 產生內容。 - 建立 PDF - 發送到您的電子郵件 由於某些原因,我們不想發出長時間執行的 HTTP 請求來執行所有這些操作。 1. 部署到 Vercel 時,無伺服器功能有 10 秒的限制。我們永遠不會準時到達。 2.我們希望讓用戶不會長時間掛起。這是一個糟糕的使用者體驗。如果用戶關閉窗口,整個過程將失敗。 ### 介紹 Trigger.dev! 使用 Trigger.dev,您可以在 NextJS 應用程式內執行後台進程!您不需要建立新伺服器。 他們也知道如何透過將長時間執行的作業無縫地分解為短期任務來處理它們。 註冊 [Trigger.dev 帳號](https://trigger.dev/)。註冊後,建立一個組織並為您的工作選擇一個專案名稱。 ![CreateOrg](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shf1jsb4gio1zrjtz91d.jpeg) 選擇 Next.js 作為您的框架,並按照將 Trigger.dev 新增至現有 Next.js 專案的流程進行操作。 ![下一頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5guppb6rot13myu6th5c.jpeg) 否則,請點選專案儀表板側邊欄選單上的「環境和 API 金鑰」。 ![複製](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x5gh527u7sthp6clkcfa.png) 複製您的 DEV 伺服器 API 金鑰並執行下面的程式碼片段以安裝 Trigger.dev。仔細按照說明進行操作。 ``` npx @trigger.dev/cli@latest init ``` 在另一個終端中,執行以下程式碼片段以在 Trigger.dev 和 Next.js 專案之間建立隧道。 ``` npx @trigger.dev/cli@latest dev ``` 讓我們建立 TriggerDev 作業! 前往新建立的資料夾 jobs 並建立一個名為「create.resume.ts」的新檔案。 新增以下程式碼: ``` client.defineJob({ id: "create-resume", name: "Create Resume", version: "0.0.1", trigger: eventTrigger({ name: "create.resume", schema: z.object({ firstName: z.string(), lastName: z.string(), photo: z.string(), email: z.string().email(), companies: z.array(z.object({ companyName: z.string(), position: z.string(), workedYears: z.string(), technologies: z.string() })) }), }), run: async (payload, io, ctx) => { } }); ``` 這將為我們建立一個名為「create-resume」的新工作。 如您所見,我們先前從「route.tsx」發送的請求進行了架構驗證。這將為我們提供驗證和“自動完成”。 我們將在這裡執行三項工作 - 聊天GPT - PDF建立 - 電子郵件發送 讓我們從 ChatGPT 開始。 [建立 OpenAI 帳戶](https://platform.openai.com/) 並產生 API 金鑰。 ![ChatGPT](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ashau6i2sxcpd0qcxuwq.png) 從下拉清單中按一下「檢視 API 金鑰」以建立 API 金鑰。 ![ApiKey](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> ``` 在根目錄中建立一個名為「utils」的新資料夾。 在該目錄中,建立一個名為「openai.ts」的新文件 新增以下程式碼: ``` import { OpenAI } from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, }); export async function generateResumeText(prompt: string) { const response = await openai.completions.create({ model: "text-davinci-003", prompt, max_tokens: 250, temperature: 0.7, top_p: 1, frequency_penalty: 1, presence_penalty: 1, }); return response.choices[0].text.trim(); } export const prompts = { profileSummary: (fullName: string, currentPosition: string, workingExperience: string, knownTechnologies: string) => `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${workingExperience} years). \n I write in the technologies: ${knownTechnologies}. Can you write a 100 words description for the top of the resume(first person writing)?`, jobResponsibilities: (fullName: string, currentPosition: string, workingExperience: string, knownTechnologies: string) => `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${workingExperience} years). \n I write in the technolegies: ${knownTechnologies}. Can you write 3 points for a resume on what I am good at?`, workHistory: (fullName: string, currentPosition: string, workingExperience: string, details: TCompany[]) => `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${workingExperience} years). ${companyDetails(details)} \n Can you write me 50 words for each company seperated in numbers of my succession in the company (in first person)?`, }; ``` 這段程式碼基本上建立了使用 ChatGPT 的基礎設施以及 3 個函數:「profileSummary」、「workingExperience」和「workHistory」。我們將使用它們來建立各部分的內容。 返回我們的「create.resume.ts」並新增作業: ``` import { client } from "@/trigger"; import { eventTrigger } from "@trigger.dev/sdk"; import { z } from "zod"; import { prompts } from "@/utils/openai"; import { TCompany, TUserDetails } from "@/components/Home"; const companyDetails = (companies: TCompany[]) => { let stringText = ""; for (let i = 1; i < companies.length; i++) { stringText += ` ${companies[i].companyName} as a ${companies[i].position} on technologies ${companies[i].technologies} for ${companies[i].workedYears} years.`; } return stringText; }; client.defineJob({ id: "create-resume", name: "Create Resume", version: "0.0.1", integrations: { resend }, trigger: eventTrigger({ name: "create.resume", schema: z.object({ firstName: z.string(), lastName: z.string(), photo: z.string(), email: z.string().email(), companies: z.array(z.object({ companyName: z.string(), position: z.string(), workedYears: z.string(), technologies: z.string() })) }), }), run: async (payload, io, ctx) => { const texts = await io.runTask("openai-task", async () => { return Promise.all([ await generateResumeText(prompts.profileSummary(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.jobResponsibilities(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.workHistory(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies)) ]); }); }, }); ``` 我們建立了一個名為「openai-task」的新任務。 在該任務中,我們使用 ChatGPT 同時執行三個提示,並返回它們。 --- ## 建立 PDF 建立 PDF 的方法有很多種 - 您可以使用 HTML2CANVAS 等工具並將 HTML 程式碼轉換為映像,然後轉換為 PDF。 - 您可以使用「puppeteer」之類的工具來抓取網頁並將其轉換為 PDF。 - 您可以使用不同的庫在後端建立 PDF。 在我們的例子中,我們將使用一個名為「jsPdf」的簡單函式庫,它是在後端建立 PDF 的非常簡單的函式庫。我鼓勵您使用 Puppeteer 和更多 HTML 來建立一些更強大的 PDF 檔案。 那我們來安裝它 ``` npm install jspdf @typs/jspdf --save ``` 讓我們回到「utils」並建立一個名為「resume.ts」的新檔案。該文件基本上會建立一個 PDF 文件,我們可以將其發送到使用者的電子郵件中。 加入以下內容: ``` import {TUserDetails} from "@/components/Home"; import {jsPDF} from "jspdf"; type ResumeProps = { userDetails: TUserDetails; picture: string; profileSummary: string; workHistory: string; jobResponsibilities: string; }; export function createResume({ userDetails, picture, workHistory, jobResponsibilities, profileSummary }: ResumeProps) { const doc = new jsPDF(); // Title block doc.setFontSize(24); doc.setFont('helvetica', 'bold'); doc.text(userDetails.firstName + ' ' + userDetails.lastName, 45, 27); doc.setLineWidth(0.5); doc.rect(20, 15, 170, 20); // x, y, width, height doc.addImage({ imageData: picture, x: 25, y: 17, width: 15, height: 15 }); // Reset font for the rest doc.setFontSize(12); doc.setFont('helvetica', 'normal'); // Personal Information block doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text('Summary', 20, 50); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); const splitText = doc.splitTextToSize(profileSummary, 170); doc.text(splitText, 20, 60); const newY = splitText.length * 5; // Work history block doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text('Work History', 20, newY + 65); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); const splitWork = doc.splitTextToSize(workHistory, 170); doc.text(splitWork, 20, newY + 75); const newNewY = splitWork.length * 5; // Job Responsibilities block doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text('Job Responsibilities', 20, newY + newNewY + 75); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); const splitJob = doc.splitTextToSize(jobResponsibilities, 170); doc.text(splitJob, 20, newY + newNewY + 85); return doc.output("datauristring"); } ``` 該文件包含三個部分:「個人資訊」、「工作歷史」和「工作職責」區塊。 我們計算每個區塊的位置和內容。 一切都是以“絕對”的方式設置的。 值得注意的是“splitTextToSize”將文字分成多行,因此它不會超出螢幕。 ![恢復](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdolng9e5ojev895x8i5.png) 現在,讓我們建立下一個任務:再次開啟 `resume.ts` 並新增以下程式碼: ``` import { client } from "@/trigger"; import { eventTrigger } from "@trigger.dev/sdk"; import { z } from "zod"; import { prompts } from "@/utils/openai"; import { TCompany, TUserDetails } from "@/components/Home"; import { createResume } from "@/utils/resume"; const companyDetails = (companies: TCompany[]) => { let stringText = ""; for (let i = 1; i < companies.length; i++) { stringText += ` ${companies[i].companyName} as a ${companies[i].position} on technologies ${companies[i].technologies} for ${companies[i].workedYears} years.`; } return stringText; }; client.defineJob({ id: "create-resume", name: "Create Resume", version: "0.0.1", integrations: { resend }, trigger: eventTrigger({ name: "create.resume", schema: z.object({ firstName: z.string(), lastName: z.string(), photo: z.string(), email: z.string().email(), companies: z.array(z.object({ companyName: z.string(), position: z.string(), workedYears: z.string(), technologies: z.string() })) }), }), run: async (payload, io, ctx) => { const texts = await io.runTask("openai-task", async () => { return Promise.all([ await generateResumeText(prompts.profileSummary(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.jobResponsibilities(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.workHistory(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies)) ]); }); console.log('passed chatgpt'); const pdf = await io.runTask('convert-to-html', async () => { const resume = createResume({ userDetails: payload, picture: payload.photo, profileSummary: texts[0], jobResponsibilities: texts[1], workHistory: texts[2], }); return {final: resume.split(',')[1]} }); console.log('converted to pdf'); }, }); ``` 您可以看到我們新增了一個名為「convert-to-html」的新任務。這將為我們建立 PDF,將其轉換為 base64 並返回。 --- ## 讓他們知道🎤 我們即將到達終點! 剩下的唯一一件事就是與用戶分享。 您可以使用任何您想要的電子郵件服務。 我們將使用 Resend.com 造訪[註冊頁面](https://resend.com/signup),建立帳戶和 API 金鑰,並將其儲存到 `.env.local` 檔案中。 ``` RESEND_API_KEY=<place_your_API_key> ``` ![密鑰](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yncrarbwcs65j44fs91y.png) 將 [Trigger.dev Resend 整合套件](https://trigger.dev/docs/integrations/apis/resend) 安裝到您的 Next.js 專案。 ``` npm install @trigger.dev/resend ``` 剩下要做的就是加入我們的最後一項工作! 幸運的是,Trigger 直接與 Resend 集成,因此我們不需要建立新的「正常」任務。 這是最終的程式碼: ``` import { client } from "@/trigger"; import { eventTrigger } from "@trigger.dev/sdk"; import { z } from "zod"; import { prompt } from "@/utils/openai"; import { TCompany, TUserDetails } from "@/components/Home"; import { createResume } from "@/utils/resume"; import { Resend } from "@trigger.dev/resend"; const resend = new Resend({ id: "resend", apiKey: process.env.RESEND_API_KEY!, }); const companyDetails = (companies: TCompany[]) => { let stringText = ""; for (let i = 1; i < companies.length; i++) { stringText += ` ${companies[i].companyName} as a ${companies[i].position} on technologies ${companies[i].technologies} for ${companies[i].workedYears} years.`; } return stringText; }; client.defineJob({ id: "create-resume", name: "Create Resume", version: "0.0.1", integrations: { resend }, trigger: eventTrigger({ name: "create.resume", schema: z.object({ firstName: z.string(), lastName: z.string(), photo: z.string(), email: z.string().email(), companies: z.array(z.object({ companyName: z.string(), position: z.string(), workedYears: z.string(), technologies: z.string() })) }), }), run: async (payload, io, ctx) => { const texts = await io.runTask("openai-task", async () => { return Promise.all([ await generateResumeText(prompts.profileSummary(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.jobResponsibilities(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies[0].technologies)), await generateResumeText(prompts.workHistory(payload.firstName, payload.companies[0].position, payload.companies[0].workedYears, payload.companies)) ]); }); console.log('passed chatgpt'); const pdf = await io.runTask('convert-to-html', async () => { const resume = createResume({ userDetails: payload, picture: payload.photo, profileSummary: texts[0], jobResponsibilities: texts[1], workHistory: texts[2], }); return {final: resume.split(',')[1]} }); console.log('converted to pdf'); await io.resend.sendEmail('send-email', { to: payload.email, subject: 'Resume', html: 'Your resume is attached!', attachments: [ { filename: 'resume.pdf', content: Buffer.from(pdf.final, 'base64'), contentType: 'application/pdf', } ], from: "Nevo David <[email protected]>", }); console.log('Sent email'); }, }); ``` 我們在檔案頂部的「Resend」實例載入了儀表板中的 API 金鑰。 我們有 ``` integrations: { resend }, ``` 我們將其加入到我們的作業中,以便稍後在“io”內部使用。 最後,我們的工作是發送 PDF `io.resend.sendEmail` 值得注意的是其中的附件,其中包含我們在上一步中產生的 PDF 文件。 我們就完成了🎉 ![我們完成了](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/esfhlds2qv1013c6x2h3.png) 您可以在此處檢查並執行完整的源程式碼: https://github.com/triggerdotdev/blog --- ## 讓我們聯絡吧! 🔌 作為開源開發者,我們邀請您加入我們的[社群](https://discord.gg/nkqV9xBYWy),以做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: https://github.com/triggerdotdev/blog/tree/main/blog-resume-builder 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/creating-a-resume-builder-with-nextjs-triggerdev-and-gpt4-4gmf

React 設計模式 Design Patterns

![](https://refine.ams3.cdn.digitaloceanspaces.com/blog-banners/retool-alternative.png) ## 介紹: React 開發人員可以透過使用設計模式來節省時間和精力,設計模式提供了一種使用經過測試且可信賴的解決方案來解決問題的快速方法。它們支援低耦合的內聚模組,從而幫助 React 開發人員建立可維護、可擴展且高效的應用程式。在本文中,我們將探索 React 設計模式並研究它們如何改進 React 應用程式的開發。 ## 容器和表示模式 容器和表示模式是一種旨在將反應程式碼中的表示邏輯與業務邏輯分離的模式,從而使其模組化、可測試並遵循關注點分離原則。 大多數情況下,在 React 應用程式中,我們需要從後端/儲存取得資料或計算邏輯並在 React 元件上表示該計算的結果。在這些情況下,容器和表示模式大放異彩,因為它可用於將元件分為兩類,即: * 容器元件,充當負責資料取得或計算的元件。 * 表示元件,其工作是將獲取的資料或計算值呈現在 UI(使用者介面)上。 容器和表示模式的範例如下所示: ``` import React, { useEffect } from 'react'; import CharacterList from './CharacterList'; const StarWarsCharactersContainer:React.FC = () => { const [characters, setCharacters] = useState<Character>([]) const [isLoading, setIsLoading] = useState<boolean>(false); const [error, setError] = useState<boolean>(false); const getCharacters = async () => { setIsLoading(true); try { const response = await fetch("https://akabab.github.io/starwars-api/api/all.json"); const data = await response.json(); setIsLoading(false); if (!data) return; setCharacters(data); } catch(err) { setError(true); } finally { setIsLoading(true); } }; useEffect(() => { getCharacters(); }, []); return <CharacterList loading={loading} error={error} characters={characters} />; }; export default StarWarsCharactersContainer; ``` ``` // the component is responsible for displaying the characters import React from 'react'; import { Character } from './types'; interface CharacterListProps { loading: boolean; error: boolean; users: Character[]; } const CharacterList: React.FC<CharacterListProps> = ({ loading, error, characters }) => { if (loading && !error) return <div>Loading...</div>; if (!loading && error) return <div>error occured.unable to load characters</div>; if (!characters) return null; return ( <ul> {characters.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }; export default CharacterList; ``` ## 有 Hooks 的元件組合 Hooks 是 React 16.8 中首次推出的全新功能。從那時起,他們在開發 React 應用程式中發揮了至關重要的作用。掛鉤是基本函數,可授予功能元件存取狀態和生命週期方法(以前僅可用於類別元件)的功能。另一方面,掛鉤可以專門設計來滿足元件要求並具有其他用例。 我們現在可以隔離所有狀態邏輯(一種需要反應性狀態變數的邏輯),並使用自訂掛鉤在元件中組合或使用它。因此,程式碼更加模組化和可測試,因為鉤子鬆散地綁定到元件,因此可以單獨測試。 帶有鉤子的元件組合示例如下所示: ``` // creating a custom hook that fetches star wars characters export const useFetchStarWarsCharacters = () => { const [characters, setCharacters] = useState<Character>([]) const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(false); const controller = new AbortController() const getCharacters = async () => { setIsLoading(true); try { const response = await fetch( "https://akabab.github.io/starwars-api/api/all.json", { method: "GET", credentials: "include", mode: "cors", headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, signal: controller.signal } ); const data = await response.json(); setIsLoading(false); if (!data) return; setCharacters(data); } catch(err) { setError(true); } finally { setIsLoading(true); } }; useEffect(() => { getCharacters(); return () => { controller.abort(); } }, []); return [ characters, isLoading, error ]; }; ``` 建立自訂鉤子後,我們將其導入到我們的 **StarWarsCharactersContainer** 元件中並使用它; ``` // importing the custom hook to a component and fetch the characters import React from 'react'; import { Character } from './types'; import { useFetchStarWarsCharacters } from './useFetchStarWarsCharacters'; const StarWarsCharactersContainer:React.FC = () => { const [ characters, isLoading, error ] = useFetchStarWarsCharacters(); return <CharacterList loading={loading} error={error} characters={characters} />; }; export default StarWarsCharactersContainer; ``` --- <橫幅隨機/> --- ## 使用Reducers進行狀態管理 大多數情況下,處理元件中的許多狀態會導致許多未分組狀態的問題,這可能是處理起來很麻煩且具有挑戰性的。在這種情況下,減速器模式可能是有用的選擇。我們可以使用減速器將狀態分類為某些操作,這些操作在執行時可以變更分組的狀態。 此模式允許使用它的開發人員控制元件和/或掛鉤的狀態管理,讓他們在發送事件時管理狀態變更。 使用減速器模式的範例如下所示: ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbob3gmfxws8k4ti0cyx.png) 在上面的程式碼中,元件調度兩個操作: * “**login**”操作類型會觸發狀態更改,影響三個狀態值,即 **loggedIn**、**user**、**token**。 *“**註銷**”操作只是將狀態重設為其初始值。 ## 提供者的資料管理 提供者模式對於資料管理非常有用,因為它利用上下文 API 透過應用程式的元件樹傳遞資料。這種模式是一種有效的解決支柱鑽井問題的方法,這一直是 React 開發中普遍關注的問題。 為了實現提供者模式,我們首先建立一個提供者元件。 Provider 是 Context 物件提供給我們的一個高階元件。我們可以利用React提供的createContext方法來建構一個Context物件。 ``` export const ThemeContext = React.createContext(null); export function ThemeProvider({ children }) { const [theme, setTheme] = React.useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); } ``` 建立提供者後,我們將使用建立的提供者元件封裝依賴上下文 API 中的資料的元件。 為了從上下文 API 取得資料,我們呼叫 useContext 鉤子,它接受上下文作為參數(在本例中為 **ThemeContext**)。 ``` import { useContext } from 'react'; import { ThemeProvider, ThemeContext } from "../context"; const HeaderSection = () => { <ThemeProvider> <TopNav /> </ThemeProvider>; }; const TopNav = () => { const { theme, setTheme } = useContext(ThemeContext); return ( <div style={{ backgroundColor: theme === "light" ? "#fff" : "#000 " }}> ... </div> ); }; ``` ## 使用 HOC(高階元件)增強元件 高階元件接受一個元件作為參數,並傳回一個注入了附加資料或功能的增壓元件。 React 中 HOC 的可能性是由於 React 更喜歡組合而不是繼承。 高階元件 (HOC) 模式提供了一種增加或修改元件功能的機制,促進元件重複使用和程式碼共用。 HOC 模式的範例如下所示: ``` import React from 'react' const higherOrderComponent = Component => { return class HOC extends React.Component { state = { name: 'John Doe' } render() { return <Component name={this.state.name {...this.props} /> } } const AvatarComponent = (props) => { return ( <div class="flex items-center justify-between"> <div class="rounded-full bg-red p-4"> {props.name} </div> <div> <p>I am a {props.description}.</p> </div> </div> ) } const SampleHOC = higherOrderComponent(AvatarComponent); const App = () => { return ( <div> <SampleHOC description="Frontend Engineer" /> </div> ) } export default App; ``` 在上面的程式碼中, **<AvatarComponent/>** 由 **higherOrderComponent** 提供 props,它將在內部使用。 ## 複合元件 複合元件模式是一種 React 設計模式,用於管理由子元件組成的父元件。 這種模式背後的原理是將父元件分解為更小的元件,然後使用 props、上下文或其他反應資料管理技術來管理這些較小元件之間的互動。 當需要建立由較小元件組成的可重複使用、多功能元件時,這種模式會派上用場。它使開發人員能夠建立複雜的 UI 元件,這些元件可以輕鬆自訂和擴展,同時保持清晰簡單的程式碼結構。 複合元件模式的用例範例如下所示: ``` import React, { createContext, useState } from 'react'; const ToggleContext = createContext(); function Toggle({ children }) { const [on, setOn] = useState(false); const toggle = () => setOn(!on); return ( <ToggleContext.Provider value={{ on, toggle }}> {children} </ToggleContext.Provider> ); } Toggle.On = function ToggleOn({ children }) { const { on } = useContext(ToggleContext); return on ? children : null; } Toggle.Off = function ToggleOff({ children }) { const { on } = useContext(ToggleContext); return on ? null : children; } Toggle.Button = function ToggleButton(props) { const { on, toggle } = useContext(ToggleContext); return <button onClick={toggle} {...props} />; } function App() { return ( <Toggle> <Toggle.On>The button is on</Toggle.On> <Toggle.Off>The button is off</Toggle.Off> <Toggle.Button>Toggle</Toggle.Button> </Toggle> ); } ``` ## 道具組合 這需要從幾個相關的 props 建立一個物件,並將其作為單個 props 傳遞給元件。 這種模式允許我們清理程式碼並使管理 props 變得更簡單,當我們想要將大量相關屬性傳遞給元件時,它特別有用。 ``` import React from 'react'; function P(props) { const { color, size, children, ...rest } = props; return ( <p style={{ color, fontSize: size }} {...rest}> { children } </p> ); } function App() { const paragraphProps = { color: "red", size: "20px", lineHeight: "22px" }; return <P {...paragraphProps}>This is a P</P>; } ``` ## 受控輸入 受控輸入模式可用於處理輸入欄位。此模式涉及使用事件處理程序在輸入欄位的值發生變更時更新元件狀態,以及將輸入欄位的目前值儲存在元件狀態中。 由於React 控制元件的狀態和行為,因此該模式使程式碼比不受控制的輸入模式更具可預測性和可讀性,後者不使用元件的狀態,而是直接透過DOM(文件物件模型)對其進行控制。 受控輸入模式的用例範例如下所示: ``` import React, { useState } from 'react'; function ControlledInput() { const [inputValue, setInputValue] = useState(''); const handleChange = (event) => { setInputValue(event.target.value); }; return ( <input type="text" value={inputValue} onChange={handleChange} /> ); } ``` ## 使用forwardRefs 管理自訂元件 稱為 ForwardRef 的高階元件將另一個元件作為輸入並輸出一個傳遞原始元件引用的新元件。透過這樣做,子元件的 ref(可用於檢索底層 DOM 節點或元件實例)可供父元件存取。 當建立與第三方程式庫或應用程式中的另一個自訂元件互動的自訂元件時,在工作流程中包含 ForwardRef 模式非常有幫助。透過授予對庫的 DOM 節點或另一個元件的 DOM 實例的存取權限,它有助於將此類元件的控制權轉移給您。 forwardRef 模式的用例範例如下所示: ``` import React from "react"; const CustomInput = React.forwardRef((props, ref) => ( <input type="text" {...props} ref={ref} /> )); const ParentComponent = () => { const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); }, []); return <CustomInput ref={inputRef} />; }; ``` 在上面的程式碼中,我們使用「forwardRefs」從元件「<ParentComponent/>」觸發了另一個元件「<CustomInput/>」的焦點。 # 結論 我們在本文中討論了 React 設計模式,包括高階元件、容器呈現元件模式、複合元件、受控元件等等。透過將這些設計模式和最佳實踐合併到您的 React 專案中,您可以提高程式碼質量,促進團隊協作,並使您的應用程式更具可擴展性、靈活性和可維護性。 --- 原文出處:https://dev.to/refine/react-design-patterns-230o

我跟 Uber 司機解釋 Kubernetes 的故事

![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjkqs8dct2kxnditkrlt.png) 一週前,我參加了在芝加哥舉行的 Kubecon 2023。我讀了一些部落格並參加了會議上的一些101教程,但仍然對這項技術沒有很好的理解。最糟糕的是會議的最後一天——我叫了優步送我回飯店。我的司機問我“大會是關於什麼的?”我回答說“這是關於 Kubernetes 的”,但經過一番解釋後,很明顯我不知道自己在說什麼。 想像一下,參加完為期 3 天的會議後,無法向您的 Uber 司機描述這項技術。 _摀臉_所以,為了挽回自己,這是我重新想像的與我的優步司機的對話。 # 對話開始 我:想像一下,你是一家繁忙餐廳廚房的廚師。你有一組廚師為你工作,每個人都在準備膳食的不同部分——一組負責開胃菜,一組負責主菜,另一組負責甜點。您的工作就是協調這些廚師,確保準時為顧客提供餐點。你腦子裡有畫面嗎? ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bn0xlqpdw7vw2lcg4a2u.png) 司機:明白了。 我:在這個場景中,主廚是 Kubernetes。就像主廚需要管理廚房中所有不同的廚師一樣,Kubernetes 可以幫助管理執行軟體所需的所有不同部分。 Kubernetes 的官方定義是“容器編排工具”,但由於“容器”這個詞在這裡非常抽象,因此您可以用“容器”一詞代替“廚師”。所以 Kubernetes 將是一個「廚師編排工具」。這樣,每次聽到這個詞時,您就可以在腦海中形成廚房的畫面。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzyqf05ebfv8tn9kkt4i.png) 司機:好的,目前為止是有道理的。但這些容器是什麼?我不能永遠想像他們是廚師。 我:是的,好點。現在您腦海中已經有了 Kubernetes 廚房的圖片,讓我們從最小到最大,深入了解所有不同的廚房角色如何映射到 Kubernetes 概念。 > 貨櫃 這個難題中最小的部分是容器,它基本上是任何軟體。例如,它可以是託管Web 應用程式的Node.js Web 伺服器,也可以是儲存資料的MongoDB 資料庫容器_(這句話更多是針對閱讀此部落格的工程師而言,我不會對我的Uber 司機說這句話😛 )_。在廚房裡,想像一下您正在為開胃菜提供湯和沙拉。湯就是你的容器。沙拉也將是它自己的容器。 我知道這個定義現在看起來有點武斷,但一旦我在即將推出的元件的上下文中解釋它,它就會更有意義。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqpypznztr958j5n8ouh.png) > 吊艙 在廚房裡,吊艙就是用來盛湯和沙拉的盤子/托盤。在 Kubernetes 中,Pod 是可以容納 1 個或多個容器的東西。原因是 Pod 內的容器可以相互通訊。 給工程師:舉個例子,假設我的 pod 中有一個用於 Web 伺服器的容器和一個用於資料庫的容器。他們可以透過本地主機相互通訊。 至於用廚房來比喻,我真的想不出什麼。想像香腸派對的惡作劇,你的擬人化湯和沙拉開始互相聊天。但是開胃菜盤上的湯和沙拉無法與餐盤上的牛排和土豆通信,因為它們位於不同的盤子上(也就是說,不同的 Pod 不共享相同的網絡命名空間,因此無法相互通信.) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fgcm20wc6fajrmju05x.png) > 主節點 主廚負責管理和監督整個廚房。這就是我們之前談到的「容器編排」或「廚師編排」的概念。這個主節點將執行的編排工作的一些現實範例如下: > 擴展,即根據 CPU 使用率向上或向下調整正在執行的 pod 數量。在繁忙的廚房中,當顧客需求激增時,廚師可能需要透過準備更多菜餚來擴大營運規模。順便說一句,在此視覺化中需要注意的一件事 - 您可能會想像廚房正在招聘新廚師,但我希望您將其想像得更像是當前的廚師正在被克隆。當發生擴展時,Pod 本質上是在被複製。 > 自動部署,又稱為在 YAML 檔案中定義應用程式的依賴項和執行時間指令,以便它可以基於此組態進行部署。在廚房中,此 YAML 檔案類似於書麵食譜,告訴廚師如何製作菜餚以確保一致性和效率並進行準備。 > 負載平衡,又稱在不同 Pod 之間分配網路流量。在廚房中,負載平衡涉及將任務分配給烹飪站的不同廚師。也許甜點站的鮑勃因舀冰淇淋的請求超載,因此主廚克隆了鮑勃,並讓鮑勃 2.0 從鮑勃 1.0 手中接走了一些冰淇淋訂單。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/txdtcmoblfg5xk8w250x.png) 同樣要注意的是:每個工作節點都有一個稱為「kubelet」的東西。在廚房場景中,「kubelet」類似於每張桌子上的廚師。廚師有很多工作,例如確保食物托盤正確組裝、幫助準備食材以及扔掉殘渣。同樣,「kubelet」的作用包括確保 pod 內的容器正在運作、幫助初始化 pod(例如安裝必要的依賴項)、幫助垃圾收集等等。 為工程師提供的一些額外背景資訊:Kubelet 是一個開源的可執行二進位檔案(又稱為包含 CPU 可以直接執行的機器碼指令的檔案),用 Go 程式語言編寫。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85hvn3eeq4xj6zxc36q6.png) 讓我們在這裡停一下。如果您理解到目前為止我所說的所有內容,您就了解了 Kubernetes 架構的基礎知識!如果您不想永遠依賴廚房圖像,我已將下圖中的所有廚房圖紙僅替換為 Kubernetes 術語。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z7pg9fgbojmuvjt4q03q.png) 司機:其實,這一切都很有道理。所以我明白了 Kubernetes 是什麼。但我還是不明白為什麼它有用。例如擁有它或學習它有什麼意義? 我:是的,難題的最後一部分是了解 Kubernetes 的**如何**使用。人類如何與 Kubernetes 互動? Kubernetes 在科技領域有何相關性/有用性?讓我們回顧一下廚房的類比來解釋更多概念。 - 餐廳/特許經營店的所有者類似於建立應用程式或服務的軟體開發人員。在麥當勞,特許經營權所有者(假設他們的名字是 Francis Cockadoodledoo)希望獲得有關每個麥當勞門市賺多少錢的訊息,並能夠根據需要解僱/僱用員工。為此,Francis Cockadoodledoo 可能會拿起電話打給主廚以獲取資訊並下達命令。在 Kubernetes 中,軟體工程師無法真正拿起電話與他們的 Kubernetes 叢集進行交互,但「主節點」有一個可以呼叫的 API 伺服器,這允許您存取所有任務。例如,工程師可以獲得所有 Pod、節點、服務的訊息,了解執行狀況和指標訊息,並能夠刪除或建立資源。 - 在餐廳吃飯的顧客類似於應用程式或服務的使用者。與麥當勞廚房為我製作巨無霸漢堡類似,Spotify Kubernetes 集群為我提供透過網頁瀏覽器收聽大量音樂的服務。 我已將這些新資訊納入下面的繪圖中。您將看到的實際上與您在谷歌上搜尋“Kubernetes 架構”時看到的圖表非常相似。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjkqs8dct2kxnditkrlt.png) ![我從網路上拉來的 Kubernetes 架構圖](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ens8psibkxhn8f2oepa.png) 當然,我意識到我在解釋中遺漏了一些抽象概念,因為我覺得它們對於形成 Kubernetes 的基本思考模型並不重要。請隨意挖掘更多內容。當我自己深入研究時,我可能會在這個部落格中加入一些我認為有用的資源連結。 # 結論 我之所以選擇這種講故事的方式(透過與 Uber 司機對話的鏡頭來描述技術)是因為我想將 Kubernetes 分解成一種普遍可以理解且平易近人的東西。 感謝您的閱讀!如果您對改進我的寫作(或我糟糕的繪圖)有任何「建設性」回饋,請在評論中留下它們。 請欣賞這張我和我的同事在 #Kubecon 2023 上的照片。順便說一句,我們在那裡推廣我正在開發的產品。 如果您有興趣,您應該檢查一下。這是對過時終端機(命令列)體驗的現代詮釋,讓您成為更好的開發人員。請造訪 https://www.warp.dev/ 以了解更多資訊。 ![我在左邊](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6c73velmiu22gzx7uk63.png) --- 原文出處:https://dev.to/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60

🧠 自己用 JavaScript 手寫:人工智慧/神經網路......! 😱 不使用任何套件!🤯

您是否嘗試過實際建立神經網路?不,我也沒有……直到今天! 在這篇文章中,我們將介紹我學到的一些東西,以及一些用 vanilla JS 編寫的非常簡單的神經網路的 2 個演示。 ## 介紹 今天早些時候,我正在閱讀 @supabase_io [“AI 內容風暴”](https://supabase.com/blog/supabase-ai-content-storm) 文章。 我突然意識到一件事。我得到了神經網路......但我實際上根本沒有得到它們! 就像,我得到了神經元的概念。但數學是如何運作的呢? 特別是如何使用「反向傳播」來訓練神經網路?偏差和權重如何發揮作用?什麼或誰是 sigmoid? ETC。 現在,明智的做法是閱讀大量文章,獲取一個庫並使用它。 **但我不懂事。** 因此,我閱讀了大量文章……然後決定建立我的第一個神經網路。 但這還不夠難,所以我決定用 JavaScript 來做(因為每個人似乎都使用 Python...)。哦,我決定在沒有任何庫的情況下完成它。哦,我也想在其中建立一個視覺化工具。 我有些不對勁……我似乎在痛苦中茁壯成長。 無論如何,我做到了,這就是我學到的東西。 ## 注意:這不是教學課程 聽著,我想澄清一下,這不是教學! 這只是我分享一些我在學習和我的**第一個**神經網路時發現有趣的事情。 請注意,這裡強調**第一**,所以請不要將其視為除了有趣的東西之外的任何東西。 我也盡力解釋每個部分及其作用,但與所有事情一樣,你對某些東西越熟練,你就越能更好地解釋它......所以我的一些解釋可能有點“偏離” ! 無論如何,既然所有這些都已經解決了,讓我們繼續吧! 如果您想直接跳到[最終演示](#the-demo),請繼續! ## 第一步 好的,首先,我可以建立的最基本的神經網路是什麼? 經過一番閱讀後,我發現神經網路可以像一些輸入神經元和一些輸出神經元一樣簡單。 每個輸入神經元都連接到一個輸出神經元,然後我們可以為每個連接加入權重。 考慮到這一點,我必須想出一個易於理解的問題來解決,但又足夠複雜以確保我的網路正常運作。 我決定建立一個神經網絡,它獲取圖表上某個點的 X 和 Y 座標,然後根據它們是正還是負為它們分配一個「團隊」(顏色)。 這樣我們就有了 2 個輸入(X 和 Y 位置),然後有 4 個輸出: 1. X > 0 且 Y > 0 2. X < 0 且 Y > 0 3. X > 0 且 Y < 0 4. X < 0 且 Y < 0 由於這裡的要求非常簡單,我們可以擺脫一些「隱藏」神經元(這是我稍後將介紹的內容)並讓事情變得超級簡單! 所以本質上我們必須建構一個看起來像這樣的神經網路: ![左側 2 個圓圈透過線連接到右側 4 個圓圈。深色背景上的霓虹燈風格。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9rh1tbxrndmjsz5b7oo.png) 左邊的圓圈是我們的輸入(X 和 Y 位置),右邊的圓圈是我們之前討論的輸出。 ### 我們的第一個神經元 好的,現在我們可以真正開始了。 現在我實際上並沒有先建置神經元。事實上,我實際上首先建立了一個視覺化工具,因為這是查看事情是否正常運作的最簡單方法,但我稍後會介紹這一點。 因此,讓我們建立一個神經元(或更具體地說,一些神經元及其連接)。 幸運的是,神經元其實非常簡單! (或者我應該說,它們*可以*非常簡單......它們在大型語言模型(LLM)等中變得更加複雜。) 簡單神經元具有偏差(將其視為內部權重,我們將加入到最終計算中以對每個神經元進行加權的數字),並透過每個連接之間的權重連接到其他神經元。 現在回想起來,單獨加入到每個神經元的連接可能是一個更好的主意,但我決定將每層神經元和每層連接作為單獨的專案加入,因為這樣更容易理解。 所以建構我的第一個神經網路的程式碼如下所示: ``` class NeuralNetwork { constructor(inputLen, outputLen) { this.inputLen = inputLen; this.outputLen = outputLen; this.weights = Array.from({ length: this.outputLen }, () => Array.from({ length: this.inputLen }, () => Math.random()) ); this.bias = Array(this.outputLen).fill(0); } } const neuralNetwork = new NeuralNetwork(2, 4); ``` 好的,我跳過了一些步驟,所以讓我們簡要介紹一下每個部分。 `this.inputLen = inputLen;` 和 `this.outputLen = outputLen;` 只是為了讓我們可以引用輸入和輸出的數量。 `this.weights = [...]` 是連線。現在看起來可能有點嚇人,但這就是我們正在做的事情: - 建立輸出神經元陣列(`outputLen`) - 將長度為“inputLen”的陣列加入到每個陣列條目,並用 0 到 1 之間的一些隨機值填充它以開始我們的工作。 該程式碼的輸出範例如下所示: ``` this.weights = [ [0.7583747881712366,0.4306037998314902], [0.40553698492617807,0.4419651593960727], [0.852978801662627,0.9762509253699836], [0.8701610553353811,0.5583309725764114] ] ``` 它們本質上代表以下內容: ``` [input 1 to output 1, input 2 to output 1], [input 1 to output 2, input 2 to output 2], [input 1 to output 3, input 2 to output 3], [input 1 to output 4, input 2 to output 4], ``` 然後我們還有「this.bias」。 這適用於輸出層中的每個神經元。我們稍後用它來加入輸出值,使一些神經元更強,一些神經元更弱。 它只是一個由 4 個 0 組成的陣列來讓我們開始,因為我們不想要初始偏差! 現在,雖然這是一個神經網絡,但它完全沒有用。 我們無法實際使用它......如果我們確實使用它,它產生的結果將是完全隨機的! 所以我們需要解決這些問題! ### 使用我們的網路! 我們需要做的第一件事是實際獲取一些輸入,透過我們的網路執行它們並收集輸出。 這是我想出來的: ``` propagate(inputs) { const output = new Array(this.outputLen); for (let i = 0; i < this.outputLen; i++) { output[i] = 0; for (let j = 0; j < this.inputLen; j++) { output[i] += this.weights[i][j] * inputs[j]; } output[i] += this.bias[i]; output[i] = this.sigmoid(output[i]); } return output; } sigmoid(x) { return 1 / (1 + Math.exp(-x)); } ``` 現在這裡有兩件有趣的事情。 ####乙狀結腸 首先,一件有趣的事情是我們的“sigmoid”函數。它所做的只是沿著「S 形」曲線將我們輸入的值(例如 12)轉換為 0 到 1 之間的值。 這是我們將價值觀從極端標準化為更統一的**且始終為正值的方式。** 進一步閱讀後,這裡還有其他關於如何將值更改為 0 到 1 之間的選項,但我還沒有完全探索它們(例如 [ReLU](https://en.wikipedia.org/wiki/Rectifier_(neural_networks)) 如果你想閱讀相關內容)。 我確信有一些很好的解釋為什麼需要這樣做,但在我的猴腦中,這只是將值保持在 0 和 1 之間的方法,以便乘法保持在一定範圍內並且值被「展平」。 這樣你就不會在過於強大的神經元之間出現「失控」的路徑。 例如,假設您有一個權重為 16 的連接和一個權重為 1 的連接,使用我們的 sigmoid 函數,我們可以將其從 16 倍的差異減少到大約 35% 的差異(「sigmoid(1)」為0.73 ,執行我們的函數後,`sigmoid(16)` 為0.99)。 這也意味著負值變成正值。 因此,透過 sigmoid 函數執行值意味著負數會轉換為 0 到 0.5 之間的值,0 的值恰好變成 0.5,大於 0 的值變成 0.5 到 1 之間的值。 ![描繪兩條虛線之間的 S 型曲線的圖形。中間有一條虛線表示0.5。深色背景上的霓虹燈發光造型。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr3460emdf5m77nc0rn2.png) 如果你考慮一下,這是有道理的,因為當我們開始將負數和正數相乘時,我們可以極大地改變我們的輸出。 例如,如果我們在 100 的路徑中有一個負神經元,而其餘的都是正神經元,這會將強值更改為弱值,並且可能會導致問題。 不管怎樣,隨著我閱讀更多和實驗更多,我相信我會更好地理解這一部分! #### 我需要偏見嗎? 第二個有趣的是「output[i] += this.bias[i];」。 好吧,在這個神經網路中,所有 4 個輸出都同樣重要,而且我們沒有隱藏神經元,所以我後來刪除了它以簡化程式碼! 但諷刺的是,在我們更複雜的神經網路上,由於網路反向傳播的工作方式,我需要重新加入輸出神經元的偏差。否則,一個輸出神經元會一直啟動。 我無法弄清楚這是否是必要的步驟,或者我的神經網路是否犯了一個錯誤,而這是為了彌補它。 再次提醒大家,我還在學習中,只是掌握了基礎知識,所以我不知道它是什麼! 🤣 #### 我們快到了 上面的其餘程式碼相當簡單。我們只是將每個輸入乘以與每個輸出相關的權重(並加入不必要的偏差!)。 事實上,我們現在就可以執行它,但結果會很糟糕!讓我們來解決這個問題! ### 訓練時間到了! 好吧,神經網路的最後一個重要部分是訓練它! 現在,隨著這篇文章越來越長,我將只介紹以下訓練程式碼的要點(順便說一下,我花了將近一個小時來編寫......我告訴過你我對此是個菜鳥! ) ``` train(inputs, target) { const output = this.propagate(inputs); const errors = new Array(this.outputLen); for (let i = 0; i < this.outputLen; i++) { errors[i] = target[i] - output[i]; for (let j = 0; j < this.inputLen; j++) { this.weights[i][j] += this.learningRate * errors[i] * output[i] * (1 - output[i]) * inputs[j]; } this.bias[i] += this.learningRate * errors[i]; } } ``` “為什麼花了這麼長時間?”我聽到你問了!好吧,它讓我思考了所有需要相乘才能更新每個權重的位元。 另外 `this.learningRate` 也需要一點時間來適應。這只是降低我們改變權重的速率,這樣我們就不會“超過”每個權重的目標值,但是將其調整到合理的值需要經驗......我沒有經驗並且設置得太低,所以我的程式碼看起來被破壞了! 經過一番擺弄後,我將數值定為 0.1(而不是 0.01 🤦🏼‍♂️),突然間事情開始變得更好了! 是的,所以我們有一個訓練函數。但請記住,此訓練功能僅進行一次訓練。 我們需要對我們的網路進行多次訓練,希望每次訓練都能使其更加準確。 我們將在一秒鐘內討論這一點,但我想分享一個快速的側面/我學到的東西。 #### 訓練資料調整 我知道我們甚至還沒有涵蓋最終的訓練資料,但這是我學到的一個有趣的觀點,適合這裡(因為它解釋了為什麼我花了這麼長時間來編寫這個訓練函數)。 最初我產生了數百個不同的訓練 X 和 Y 座標,全部都是隨機的。 但經過進一步閱讀後,我只產生 4 個靜態訓練點,得到了更好的結果: ``` const trainingData = [ { x: -0.5, y: -0.5, label: "blue" }, { x: 0.5, y: -0.5, label: "red" }, { x: -0.5, y: 0.5, label: "green" }, { x: 0.5, y: 0.5, label: "purple" } ]; ``` 一旦你明白了,就很有意義了! 我們希望「拉」值更接近目標,上述值是我們每個區域的確切「中心點」。 因此,對於給定的距離,我們的錯誤率將始終保持一致。 這意味著我們的神經網路學習得更快,因為我們的錯誤率更大,這取決於它們距離 X 更遠還是距離 Y 更遠。 我可以更好地解釋這一點,但這超出了本文的範圍。希望如果您仔細考慮一下,那麼它也會像對我一樣為您「點擊」! 諷刺的是,我回到了更大模型的更隨機的資料集,因為我想真正測試我對學習率、過度訓練等的理解。 ## 我們有一個有效且有用的神經網路! 實際上,這就是我們的整個神經網路。 不過,我們需要做一件事。 我們的訓練函數需要執行很多次! 因此,我們需要最後一個函數來做到這一點,它獲取我們的訓練資料並執行我們的訓練函數數百次: ``` function train() { for (let i = 0; i < 10000; i++) { const data = trainingData[Math.floor(Math.random() * trainingData.length)]; neuralNetwork.train([data.x, data.y], encode(data.label)); } console.log("Training complete"); } ``` ### 金髮女孩迭代 請注意,我們在「for」循環中訓練網路 10,000 次。 10,000 次迭代足以訓練這個特定的神經網路。但對於我們稍後將介紹的更複雜的問題,我需要更多的迭代(並降低學習率)。 這是機器學習有趣的部分之一,您需要足夠地訓練神經網路(這很難做到正確),但如果訓練太多,就會發生“過度擬合”,並且實際上開始得到更糟糕的結果。因此,為了獲得最佳結果,需要完美平衡! 不管怎樣,已經很多了,我們終於迎來了第一個示範! ## 簡單的普通 JS 神經網路演示 雖然有點亂,但我們的神經網路和所有訓練部分都在下面 CodePen 的前 67 行。 其餘程式碼行實際上執行我們的網路(“neuralNetwork.propagate([x, y]);”大約第 85 行),然後將點及其預測顏色輸出到“<canvas>”上。 「encode」和「decode」純粹是為了獲取我們的輸出神經元,找到哪個神經元具有最高的激活,然後將其映射到我們可視化的顏色。 這是最後要理解的事。我們的輸出神經元都會有一個值。神經網路不僅僅輸出 1, 0, 0, 0。 相反,它會輸出每個輸出神經元的「確定性」或猜測。因此我們將得到類似「0.92,0.76,0.55,0.87」的輸出。 這就是為什麼我們有“解碼”函數,它找到最高輸出的神經元並將其作為我們的最終猜測! ``` // this line finds the max value of all of our output neurons and then returns its index so we can use that to classify our X and Y coordinates. const maxIndex = output.indexOf(Math.max(...output)); ``` ### 用法和實際演示 要使用該範例,您有 3 個按鈕: - **訓練** - 在我們的神經網路開始未經訓練和隨機化時對其進行訓練。 - **分類點** - 這是為了執行我們的神經網路。它將在圖表上繪製點並為它們分配顏色。我建議在訓練之前和之後執行這個。 - **重設** - 這將建立一個未經訓練的新神經網路。非常適合在訓練前後測試點的分類。 另請注意,每個區域都根據該區域應顯示的顏色進行著色。它真的可以讓您看到隨機且未經訓練的神經網路距離成功還有多遠(重置然後對測試點進行分類)! 玩吧! https://codepen.io/GrahamTheDev/pen/abPxoqb ### 我們最基本的神經網路結束 這樣我們就有了最基本的神經網路! 它可以很好地滿足我們的需求,並且我們設法了解了一些關於反向傳播(我們在主類別中的“train”函數)以及權重和偏差的知識。 但這是非常有限的。如果我們將來想做更高級的事情,我們需要加入一些隱藏神經元! ## 版本 2 - 隱藏神經元 好的,那為什麼要隱藏神經元呢?他們的目的是什麼? 在更複雜的範例中,它們充當獲取輸入並為它們的分類方式加入更多維度的方式。 我們仍然使用 2 個輸入神經元和 4 個輸出神經元,但這次我們在中間加入了一個附加層(我們可以更改和調整其中神經元的數量)。 所以我們的神經網路看起來像這樣: ![左側 2 個圓圈透過線連接到中間的 6 個圓圈,然後透過線連接到右側的 4 個圓圈。深色背景上的霓虹燈風格。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0f1204y2d96wucq6fzv.png) 由於神經網路需要處理更多的輸入並進行更複雜的計算,隱藏層中的額外神經元使它們能夠更好地對輸入進行分類並提供更好的結果。 隱藏層也可以是不同的「深度」。 假設我們有 2 個輸入神經元。我們可以將它們連接到 6 個「隱藏」神經元,然後將它們連接到 4 個輸出神經元。 但我們也可以將第一層的 6 個神經元連結到第二層隱藏神經元。第二層可能有 8 個神經元,然後連接到我們的 4 個輸出神經元。 但要遵循的內容還有很多,這是為了讓我學習基礎知識,所以我選擇加入一個隱藏層。這也意味著我可以將每個連接層保留為單獨的陣列,這在現階段更容易理解! ### 那麼有什麼新內容呢? 沒有太大變化,只是我們有了更多的連結和更多的神經元! 您可以將其視為串聯加入 2 個原始神經網絡,只是第一個神經網路的輸出現在充當第二個神經網路的輸入。 雖然程式碼可能更加複雜,但我們的神經網路遵循相同的原則。 這是程式碼: ``` class NeuralNetwork { constructor(inputSize, hiddenSize, outputSize) { this.inputSize = inputSize; this.hiddenSize = hiddenSize; this.outputSize = outputSize; this.weightsInputToHidden = Array.from({ length: hiddenSize }, () => Array.from({ length: inputSize }, () => Math.random() * 2 - 1) ); this.biasHidden = Array(hiddenSize).fill(0); this.weightsHiddenToOutput = Array.from({ length: outputSize }, () => Array.from({ length: hiddenSize }, () => Math.random() * 2 - 1) ); this.biasOutput = Array(outputSize).fill(0); this.learningRate = document.querySelector('#learningRate').value; // Adjusted learning rate this.hiddenLayer = new Array(this.hiddenSize); } feedForward(inputs) { for (let i = 0; i < this.hiddenSize; i++) { this.hiddenLayer[i] = 0; for (let j = 0; j < this.inputSize; j++) { this.hiddenLayer[i] += this.weightsInputToHidden[i][j] * inputs[j]; } this.hiddenLayer[i] += this.biasHidden[i]; this.hiddenLayer[i] = sigmoid(this.hiddenLayer[i]); } const output = new Array(this.outputSize); for (let i = 0; i < this.outputSize; i++) { output[i] = 0; for (let j = 0; j < this.hiddenSize; j++) { output[i] += this.weightsHiddenToOutput[i][j] * this.hiddenLayer[j]; } output[i] += this.biasOutput[i]; output[i] = sigmoid(output[i]); } return output; } train(inputs, target) { for (let i = 0; i < this.hiddenSize; i++) { this.hiddenLayer[i] = 0; for (let j = 0; j < this.inputSize; j++) { this.hiddenLayer[i] += this.weightsInputToHidden[i][j] * inputs[j]; } this.hiddenLayer[i] += this.biasHidden[i]; this.hiddenLayer[i] = sigmoid(this.hiddenLayer[i]); } const output = new Array(this.outputSize); for (let i = 0; i < this.outputSize; i++) { output[i] = 0; for (let j = 0; j < this.hiddenSize; j++) { output[i] += this.weightsHiddenToOutput[i][j] * this.hiddenLayer[j]; } output[i] += this.biasOutput[i]; output[i] = sigmoid(output[i]); } const errorsOutput = new Array(this.outputSize); const errorsHidden = new Array(this.hiddenSize); for (let i = 0; i < this.outputSize; i++) { errorsOutput[i] = target[i] - output[i]; for (let j = 0; j < this.hiddenSize; j++) { this.weightsHiddenToOutput[i][j] += this.learningRate * errorsOutput[i] * output[i] * (1 - output[i]) * this.hiddenLayer[j]; } this.biasOutput[i] += this.learningRate * errorsOutput[i]; } for (let i = 0; i < this.hiddenSize; i++) { errorsHidden[i] = 0; for (let j = 0; j < this.outputSize; j++) { errorsHidden[i] += this.weightsHiddenToOutput[j][i] * errorsOutput[j]; } this.biasHidden[i] += this.learningRate * errorsHidden[i]; for (let j = 0; j < this.inputSize; j++) { this.weightsInputToHidden[i][j] += this.learningRate * errorsHidden[i] * this.hiddenLayer[i] * (1 - this.hiddenLayer[i]) * inputs[j]; } } } } ``` 現在,不要被嚇倒,我剛剛複製了幾個循環,其中要操作的目標資料集略有不同。 我們加入了一組額外的偏差(對於我們的隱藏層)和一組額外的連接:我們的輸入層到我們的隱藏層,然後我們的隱藏層現在連接到我們的輸出層。 最後,我們的「train」函數有一些額外的循環,只是為了透過每個步驟進行反向傳播。 唯一值得一提的其他變化是我們現在有第三個輸入參數(中間),用於隱藏神經元的數量。 ### 醜陋,但似乎有用 看,我想再說一遍,這是我一邊學習一邊學習,所以程式碼反映了這一點。 這裡有很多重複,可擴展性不太好。 然而,據我所知,它是有效的。 話雖如此,雖然它有效,但它的性能似乎比我們原來的、簡單得多的神經網路要差。 這要么意味著我犯了一個錯誤(可能),要么是我沒有「撥入」正確的訓練設定。 說到這裡... ### 加入一些變數來玩 由於這更複雜,我在一些快速設定中「迴避」了。 現在我們可以更新: - **訓練資料大小** - 我們產生的不同隨機點的數量 - **訓練迭代** - 我們從訓練集中選擇隨機資料點並將其輸入神經網路上的「訓練」函數的次數。 - **學習率** - 我們根據錯誤調整速度的乘數。 - **隱藏節點(超過2個!)** - 調整第二層有多少個隱藏節點(需要您再次初始化網絡,否則會損壞!) - **要分類的點** - 傳遞給我們訓練過的神經網路並繪製在圖表上的點數。 這意味著我們可以更快地處理這些值,看看它們對我們的神經網路及其準確性有什麼影響! ### 最後一件事 哦,我加入了一個按鈕來視覺化神經網路的樣子。 無論如何按“可視化神經元和權重”,但它還沒有完成。我也沒有立即完成它的打算,因為我想完全重新設計建構神經網路的方法,使其更具可擴展性。 不過,按鈕就在那裡,請隨意按下。更好的是,請隨時為我修復它! 🤣💗 ## 演示 控制項與以前相同,加上前面 2 個小節中提到的輸入。 試試一下,看看是否可以微調學習率、神經元數量和訓練設定以獲得真正準確的結果! https://codepen.io/GrahamTheDev/pen/qBLwBxP 請務必更新一些值,重新初始化神經網絡,嘗試使用不同數量的隱藏神經元等。 如果您像我一樣是初學者,希望您能開始理解一些事情! ## 結論 用 vanilla JS 建立神經網路真的很有趣。 我沒有見過很多人這樣做,所以我希望它對你或至少對某人有用! 我學到了很多關於偏差、反向傳播(神經網路的關鍵)等的知識。 顯然這個例子和這裡學到的東西只是機器學習的1%。但對於像我這樣的微小的、未優化的神經網路和巨大的數十億參數模型來說,核心原理是相同的。 這個例子就像機器學習 (ML) 和神經網路的「hello world」。 接下來,我真的想嘗試建立一個更大的、結構更好、更容易擴展的神經網絡,看看我們是否可以進行一些光學字元辨識(OCR)。您可以將其視為機器學習和神經網路的“待辦事項清單”! ### 發表評論。 您是神經網路專家嗎?告訴我我哪裡錯了! 你是像我一樣的初學者嗎?那麼請告訴我這是否有助於您理解,至少一點點!或者,如果這實際上讓事情變得更加混亂! 😱 最重要的是,如果這篇文章激發了您對我糟糕的編碼做鬼臉,或者想要建置您自己的神經網絡......那麼我很高興它對您產生了一些影響,並且很樂意聽到它! 💗 --- 原文出處:https://dev.to/grahamthedev/a-noob-learns-ai-my-first-neural-networkin-vanilla-jswith-no-libraries-1f92

最受軟體開發人員推薦的 20 本書

> _“信任,但驗證”_(“信任,但驗證”) > -- [俄羅斯諺語](https://en.wikipedia.org/wiki/Trust,_but_verify) --- 我整理了數十位程式設計師、經理、職業教練和其他行業專業人士的建議,為您帶來一份__軟體開發人員最推薦的20 本書__的清單,並附有一些簡短的摘要(亞馬遜提供)。我用於編制此清單的所有資料都可以作為 [GitHub 上的單一 Excel 工作簿](https://github.com/awwsmm/2020books) 免費提供。截至 __2019 年 10 月 19 日星期六__,我的元列表引用了 36 個來源,並包括 297 本獨特書籍的推薦。 我這樣做主要是為了幫助自己整理我在不久的將來想讀的書籍,同時也是為了幫助其他想要打下良好基礎,但可能會因網路上提出不同建議的不同列表而感到沮喪的人。當你可以綜合考慮_每個人的意見_時,為什麼要相信他們中的_一個_?不客氣,網路。 以下是我用來撰寫此清單的__經驗法則__: 1. 盡可能_不_包括_尋求_推薦的文章。 1. 對一本書的不同版本的推薦視為對同一本書的推薦,但對一篇文章內的多個版本的推薦不會重複計算。 1. 作者只_列出_他們已讀過或正在閱讀的書籍的文章_不_包括在內。文章必須以_推薦_書籍清單的形式呈現。 1. 包含針對特定_等級_開發人員(即 CTO、初級開發人員、新手)的清單。 1. 我嘗試跳過針對特定技術(即 PHP、CSS、Java)的列表,但有些列表中散佈著這些類型的書籍;關於所包含文章的每一本書都包含在我的書籍清單中,即使它是一本小說、特定於技術的或與程式設計無關的。 1. 儘管某些人的解釋可能略有不同,但就本列表而言,「軟體開發人員」、「軟體工程師」、「開發人員」、「程式設計師」和「編碼員」都是等效的。除非第 (5) 點也適用,否則包括針對上述任何內容的文章。 就我所知,這是__網路上最完整的軟體開發書籍推薦元列表__。我查看了 Google 返回的“圖書軟體開發人員”的“每一個結果”以及 Dev.To 上標題中包含“圖書”的每篇文章,這就是結果。如果您發現通用軟體開發的書籍推薦清單未包含在我在 GitHub 上的來源電子表格中,請告訴我。如果它符合我上面列出的標準,我很樂意將其加入到列表中。 言歸正傳,列表如下: --- ![Martin Fowler 的企業應用程式架構模式](https://thepracticaldev.s3.amazonaws.com/i/lee3yr9dzmevpfz8r6p3.jpg) ## \#20。 [企業應用架構模式](https://amzn.to/2jSqwQ5) ### 作者:Martin Fowler(_3 路並列 #19 和 #18_) #### 推薦 13.0% > 這本書其實是兩本書合而為一。第一部分是有關開發企業應用程式的簡短教程,您可以從頭到尾閱讀該教程以了解本書課程的範圍。下一部分,即本書的大部分內容,是對模式本身的詳細參考。每個模式都提供使用和實現訊息,以及詳細的 Java 或 C# 程式碼範例。整本書還配有豐富的UML圖來進一步解釋概念。 > > 有了這本書,您將擁有做出有關建立企業應用程式的重要架構決策所需的知識,以及建立它們時使用的經過驗證的模式。 > > -- [Amazon.com](https://amzn.to/2jSqwQ5) --- ![《清潔架構:軟體結構與設計工匠指南》,作者:Robert C.「Uncle Bob」Martin](https://thepracticaldev.s3.amazonaws.com/i/jeoyr1agokp3k43qtrzx.jpg) ## \#19。 [清潔架構:軟體結構與設計工匠指南](https://amzn.to/2jTxLHt) ### 作者:羅伯特·C·「鮑伯叔叔」·馬丁 #### 建議 13.0%(_3 路與 #20 和 #18 平手_) > - 了解軟體架構師需要實現什麼目標,以及實現它的核心規則和實踐 > - 掌握尋址功能、元件分離和資料管理的基本軟體設計原則 > - 了解程式設計範式如何透過限制開發人員可以執行的操作來施加紀律 > - 了解什麼是至關重要的,什麼只是“細節” > - 為 Web、資料庫、胖客戶端、控制台和嵌入式應用程式實施最佳的高級結構 > - 定義適當的邊界和層,並組織元件和服務 > - 了解設計和架構為何出錯,以及如何預防(或修復)這些故障 > > -- [Amazon.com](https://amzn.to/2jTxLHt) --- ![Donald Knuth 的電腦程式設計藝術](https://thepracticaldev.s3.amazonaws.com/i/26n7ggpj2vnte6s7s7b9.jpg) ## \#18。 [電腦程式設計的藝術](https://amzn.to/2XKk8Ik) ### 作者:唐納德高德納 #### 建議 13.0%(_3 路與 #20 和 #19 平手_) > 這部宏偉的傑作全面概述了各種演算法及其分析。現在,《電腦程式設計的藝術》已經是第三版了,其中包含作者的大量修訂,並包括許多新練習。 > > 儘管這本書是幾十年前構思的,但它仍然是一部永恆的經典。本書最大的優點之一是每一章都包含大量問題。作者精心挑選問題,並根據難度進行索引。解決大量此類問題將幫助您深入了解圍繞給定主題的問題。此外,練習還包含各種經典問題。 > > -- [Amazon.com](https://amzn.to/2XKk8Ik) --- ![程式碼:Charles Petzold 的電腦硬體和軟體的隱藏語言](https://thepracticaldev.s3.amazonaws.com/i/36uenl4hsq1v4n9o4olc.jpg) ## \#17。 [程式碼:電腦硬體與軟體的隱藏語言](https://amzn.to/2XYXZKA) ### 查爾斯·佩措爾德 #### 15.9% 推薦(_與 #16_ 並列) > 手電筒、英國入侵、黑貓和蹺蹺板與電腦有什麼關係?在《程式碼》中,他們向我們展示了操縱語言和發明新的相互交流方式的巧妙方式。透過 _CODE_,我們看到這種獨創性和我們人類的溝通衝動是如何推動過去兩個世紀的技術創新的。 > > 作者 Charles Petzold 使用日常物品和熟悉的語言系統(例如點字和莫爾斯電碼),為任何想了解電腦和其他智慧機器的秘密內部生活的人編織了一個富有啟發性的敘述。 > > 這是一個插圖巧妙且非常易於理解的故事,在整個過程中,您會發現自己獲得了理解當今 PC、數位媒體和網路世界的真實背景。無論您的技術水平如何,CODE 都會讓您著迷,甚至可能喚醒您內心的技術愛好者。 > > -- [Amazon.com](https://amzn.to/2XYXZKA) --- ![敏捷軟體開發:原則、模式與實踐,作者:Robert C.「Bob 叔叔」Martin](https://thepracticaldev.s3.amazonaws.com/i/qvta7nticfmwig5x68f7.jpg) ## \#16。 [敏捷軟體開發:原則、模式與實踐](https://amzn.to/2XT4fj3) ### 作者:羅伯特·C·「鮑伯叔叔」·馬丁 #### 15.9% 推薦(_與 #17_ 並列) > 本書由軟體開發人員為軟體開發人員撰寫,是最新軟體開發方法的獨特集合。作者包括 OOD、UML、設計模式、敏捷和 XP 方法,並詳細描述了 C++ 和 Java 中可重複使用程式的完整軟體設計。它使用實用的解決問題的方法,展示瞭如何開發物件導向的應用程式——從分析的早期階段,到底層設計,再到實現。引導讀者了解設計師的想法—展示整個軟體設計過程中出現的錯誤、死胡同和創意見解。本書涵蓋:靜力學和動力學;類設計原則;複雜性管理;包裝設計原理;分析與設計;模式和範式交叉。一一解釋 OOD 的原理,然後透過大量範例、完整的設計和案例研究來示範它們。涵蓋 C++ 和 OOD 應用程式中的陷阱、缺陷和解決方法,然後展示如何使用敏捷方法。詳細討論了大型軟體的設計和開發方法。對建築安全系統進行了三章深入的單一案例研究。適合想要了解如何使用最先進的方法設計物件導向軟體的軟體工程師、程式設計師和分析師。 > > -- [Amazon.com](https://amzn.to/2XT4fj3) --- ![Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest 和 Clifford Stein 的演算法簡介](https://thepracticaldev.s3.amazonaws.com/i/3avr69imz4gk8t8osbqc.jpg) ## \#15。 [演算法介紹](https://amzn.to/2XRR81s) ### 作者:Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest 與 Clifford Stein #### 建議 17.4%(_3 路與 #14 和 #13 平手_) > 有些演算法書籍嚴謹但不完整;其他的涵蓋大量材料但缺乏嚴謹性。 《演算法導論》獨特地結合了嚴謹性和全面性。本書深入涵蓋了廣泛的演算法,同時使各個層級的讀者都可以理解它們的設計和分析。每一章都相對獨立,可以作為學習單元。這些演算法以英語和偽程式碼進行描述,任何稍微編程過的人都可以閱讀。這些解釋保持基本,但沒有犧牲覆蓋深度或數學嚴謹性。 > > 第一版成為全球大學廣泛使用的教材以及專業人士的標準參考書。第二版新增了關於演算法的作用、機率分析和隨機演算法以及線性規劃的章節。第三版進行了全面修訂和更新。它包括關於 van Emde Boas 樹和多線程演算法的兩個全新章節,對遞歸章節(現在稱為“分而治之”)的大量補充,以及關於矩陣的附錄。它改進了動態規劃和貪婪演算法的處理,以及流網路材料中基於邊緣的流的新概念。此版本加入了許多練習和問題 > > -- [Amazon.com](https://amzn.to/2XRR81s) --- ![Head First 設計模式:Eric Freeman、Elizabeth Robson、Kathy Sierra 和 Bert Bales 編寫的大腦友善指南](https://thepracticaldev.s3.amazonaws.com/i/2k33el50lr3gnihj6zqo.jpg) ## \#14。 [Head First 設計模式:大腦友善指南](https://amzn.to/2XNEdxz) ### 作者:Eric Freeman、Elizabeth Robson、Kathy Sierra 和 Bert Bales #### 建議 17.4%(_3 路與 #15 和 #13 平手_) > 在任何特定時刻,都會有人遇到與您相同的軟體設計問題。而且,很可能其他人已經解決了您的問題。這一版的《Head First Design Patterns》(現已針對 Java 8 進行了更新)向您展示了開發人員用來建立實用、優雅、可重用且靈活的軟體的可靠、經過實際檢驗的模式。當你讀完本書時,你將能夠利用那些與軟體設計的野獸戰鬥並取得勝利的人的最佳設計實踐和經驗。 > > -- [Amazon.com](https://amzn.to/2XNEdxz) --- ![破解編碼面試:189 個程式設計問題和解決方案,作者:Gayle Laakmann McDowell](https://thepracticaldev.s3.amazonaws.com/i/glfijew89yb3sxnxiznu.jpg) ## \#13。 [破解程式設計面試:189題程式設計題及解答](https://amzn.to/2XPSwlg) ### 作者:蓋爾‧拉克曼‧麥克道威爾 #### 建議 17.4%(_3 路與 #15 和 #14 並列_) > - 189 道程式設計面試問題,從基礎知識到最棘手的演算法問題。 > - 如何匯出每個解決方案的演練,以便您可以學習如何自己實現。 > - 有關如何解決 189 個問題的提示,就像您在真實面試中得到的一樣。 > - 五種經過驗證的解決演算法問題的策略,讓你可以解決你沒見過的問題。 > - 廣泛涵蓋基本主題,例如大 O 時間、資料結構和核心演算法。 > - 深入了解 Google 和 Facebook 等頂尖公司如何聘用開發人員。 > - 準備和應對面試軟性方面的技巧:行為問題。 > - 對於面試官和公司:關於什麼是好的面試問題和招募流程的詳細資訊。 > > -- [Amazon.com](https://amzn.to/2XPSwlg) --- ![別讓我思考:Web 可用性的常識方法,作者:Steve Krug](https://thepracticaldev.s3.amazonaws.com/i/8d63yx8mnivrer5d08v6.jpg) ## \#12. [不要讓我思考:網路可用性的常識性方法](https://amzn.to/2XPjDwF) ### 史蒂夫·克魯格 #### 18.8% 推薦(_與#11_並列) > _Don't Make Me Think_ 是 Steve Krug 寫的一本關於人機互動和網路可用性的書。本書的前提是,一個好的軟體程式或網站應該讓使用者盡可能輕鬆、直接地完成他們的預期任務。克魯格指出,人們善於滿足問題,或採用第一個可用的解決方案來解決問題,因此設計應該利用這一點。他經常引用 Amazon.com 作為精心設計的網站的例子,該網站能夠實現高品質的交互,儘管該網站每天都變得越來越大、越來越複雜。 > > 這本書本身旨在成為簡潔(簡潔)和重點突出的寫作範例。根據該書的介紹,其目標是製作一篇可供高管在兩個小時的飛機飛行中閱讀的文本。 > > -- [維基百科](http://bit.ly/2XSAhLY) --- ![The Clean Coder: A Code of Conduct for Professional Programmers by Robert C. “Uncle Bob” Martin](https://thepracticaldev.s3.amazonaws.com/i/8etf99rgjqkmvl0n12b8.jpg) ## \#11。 [The Clean Coder:專業程式設計師行為準則](https://amzn.to/2XMluCs) ### 作者:羅伯特·C·「鮑伯叔叔」·馬丁 #### 18.8% 推薦(_與 #12_ 並列) > 在不斷的不確定性和不間斷的壓力中忍受並取得成功的程式設計師有一個共同的屬性:他們非常關心建立軟體的實踐。他們把它當作一門手藝。他們是專業人士。 > > 在《乾淨的編碼器:專業程式設計師的行為準則》中,傳奇軟體專家 Robert C. Martin 介紹了真正的軟體工藝的學科、技術、工具和實踐。本書充滿了實用的建議——從估計和編碼到重構和測試。它涵蓋的不僅僅是技術:它是關於態度。馬丁展示瞭如何以榮譽、自尊和自豪的態度進行軟體開發;工作良好,工作乾淨;忠實地溝通和評估;清晰而誠實地面對困難的決定;並理解深刻的知識伴隨著採取行動的責任。 > > -- [Amazon.com](https://amzn.to/2XMluCs) --- ![軟技能:John Sonmez 的軟體開發人員生活手冊](https://thepracticaldev.s3.amazonaws.com/i/3cjtbj9vse8xzxa35po2.jpg) ## \#10. [軟體技能:軟體開發人員的生活手冊](https://amzn.to/2XR9u2y) ### 約翰‧桑梅茲 #### 23.2% 推薦(_與#11_並列) > _軟技能:軟體開發人員生活手冊_ 是作為技術專業人員全面、令人滿意的生活的指南。在其中,開發人員兼生活教練 John Sonmez 就重要的「軟」主題向開發人員提供建議,例如職業和生產力、個人理財和投資,甚至健身和人際關係。這本有趣的書由 71 個短章組成,邀請您隨心所欲地閱讀。每章末尾的「採取行動」部分向您展示如何快速獲得結果。軟技能將幫助您成為更好的程式設計師、更有價值的員工以及更快樂、更健康的人。 > > -- [Amazon.com](https://amzn.to/2XR9u2y) --- ![Peopleware:富有成效的專案和團隊,作者:Tom DeMarco 和 Timothy Lister](https://thepracticaldev.s3.amazonaws.com/i/pplphoinwajolqw6pdf9.jpg) ## \#9。 [Peopleware:高效率的專案與團隊](https://amzn.to/2XR6keY) ### 湯姆·德馬科和提摩西·利斯特 #### 23.2% 推薦(_與 #12_ 並列) > _Peopleware_ 是一本有關軟體組織管理的熱門書籍。書的第一章聲稱,「我們工作的主要問題本質上與其說是技術問題,不如說是社會學問題」。這本書探討了社會學或「政治」議題,例如團隊化學反應和團隊凝聚力、工作環境中的「流動時間」和安靜以及高流動成本。其他主題包括個人工作觀點與企業意識形態之間的衝突、企業熵、「團隊滅絕」和工作空間理論。 > > 作者將大多數主題呈現為由一些具體故事或其他資訊支持的原則。例如,「義大利麵晚餐」一章介紹了一個虛構的例子,一位經理邀請一個新團隊來吃晚飯,然後讓他們作為一個團隊購買和準備飯菜,以便讓一線團隊取得成功。其他章節使用現實生活中的故事或引用各種研究來說明所提出的原則 > > -- [維基百科](http://bit.ly/2XMFrJf) --- ![Jon Bentley 的程式設計珍珠](https://thepracticaldev.s3.amazonaws.com/i/fa7x5erzt1qryzis8784.jpg) ## \#8。 [程式珍珠](https://amzn.to/2XNxWlf) ### 喬恩‧本特利 #### 26.1% 推薦 > 電腦程式設計有很多面貌。弗雷德布魯克斯 (Fred Brooks) 在《人月神話》中描繪了一幅宏偉的圖畫。他的文章強調了管理在大型軟體專案中的關鍵作用。 Steve McConnell 在 Code Complete 中更詳細地教授了良好的程式設計風格。這些書中的主題是優秀軟體的關鍵,也是專業程式設計師的標誌。然而不幸的是,這些合理的工程原理的熟練應用並不總是令人興奮——直到軟體按時完成並且不出意外地工作。 > > 本書中的專欄介紹了該行業更迷人的方面:編程珍珠,其起源超出了紮實的工程,而是在洞察力和創造力的領域。正如天然珍珠是從激怒牡蠣的沙粒中生長出來的一樣,這些編程珍珠也是從激怒真正程式設計師的實際問題中生長出來的。這些程式很有趣,並且教授重要的程式技術和基本設計原理。 > > -- [Amazon.com](https://amzn.to/2XNxWlf) --- ![有效處理 Michael Feathers 的遺留程式碼](https://thepracticaldev.s3.amazonaws.com/i/4bls25qdhatgaz2vvmz5.jpg) ## \#7。 [有效處理遺留程式碼](https://amzn.to/2Y5hNvR) ### 麥可費瑟斯 #### 27.5% 推薦(_與#6_並列) > 你的程式碼容易修改嗎?當你改變它時,你能得到幾乎即時的回饋嗎?你到底懂不懂呢?如果這些問題中任何一個的答案是否定的,那麼您就有遺留程式碼,並且它會消耗您的開發工作的時間和金錢。 > > 在本書中,Michael Feathers 提供了從頭到尾的策略,以便更有效地處理大型、未經測試的遺留程式碼庫。本書借鑒了邁克爾為其著名的物件導師研討會建立的材料:邁克爾在指導中使用的技術,幫助數百名開發人員、技術經理和測試人員控制他們的遺留系統。涵蓋的主題包括: > > - 了解軟體變更的機制:新增功能、修復錯誤、改進設計、優化效能 > - 將遺留程式碼放入測試工具中 > - 編寫測試來防止引入新問題 > - 可用於任何語言或平台的技術 - 包含 Java、C++、C 和 C# 範例 > - 準確地辨識需要更改程式碼的位置 > - 處理非物件導向的遺留系統 > - 處理看起來沒有任何結構的應用程式 > > -- [Amazon.com](https://amzn.to/2Y5hNvR) --- ![人月神話:Frederick P. Brooks 的軟體工程論文](https://thepracticaldev.s3.amazonaws.com/i/wd3l2w6s5mzbu1gwf7b8.jpg) ## \#6。 [人月神話:軟體工程論文](https://amzn.to/2XMoXAY) ### 作者:弗雷德里克·P·布魯克斯 #### 27.5% 推薦(_與 #7_ 並列) > 很少有關於軟體專案管理的書籍能像《人月神話》一樣具有影響力和永恆性。 Fred Brooks 融合了軟體工程事實和發人深省的觀點,為任何管理複雜專案的人提供了見解。這些文章借鑒了他作為 IBM System/360 電腦系列以及其大型軟體系統 OS/360 專案經理的經驗。現在,在他的書首次出版 20 年後,布魯克斯重新審視了他最初的想法,並加入了新的想法和建議,無論是對於已經熟悉他的作品的讀者還是第一次發現它的讀者。 > > -- [Amazon.com](https://amzn.to/2XMoXAY) #### 在 Dev.To 上閱讀[我對《人月神話》的評論](https://dev.to/awwsmm/book-review-the-mythical-man-month-1995-1hpn) --- ![重構:改進現有程式碼的設計,作者:Martin Fowler](https://thepracticaldev.s3.amazonaws.com/i/3iai8x9pod62ge1f38su.jpg) ## \#5。 [重構:改進現有程式碼的設計](https://amzn.to/2XQ2KlF) ### 馬丁·福勒 #### 33.3% 推薦(_與#4_並列) > 二十多年來,全世界經驗豐富的程式設計師一直依靠 Martin Fowler 的_重構_來改進現有程式碼的設計並增強軟體的可維護性,並使現有程式碼更易於理解。 > > 這個備受期待的新版本已經全面更新,以反映程式設計領域的重大變化。 _Refactoring_,第二版,提供更新的重構目錄,包括 JavaScript 程式碼範例,以及演示無需類別的重構的新功能範例。 > > 與原版一樣,此版本解釋了什麼是重構;為什麼你應該重構;如何辨識需要重構的程式碼;以及如何真正成功地做到這一點,無論您使用什麼語言。 > > - 了解重構的流程和一般原則 > - 快速應用有用的重構,使程式更易於理解和更改 > - 辨識程式碼中預示重構機會的“難聞氣味” > - 探索重構,每個重構都有解釋、動機、機制和簡單的範例 > - 為您的重建置立可靠的測試 > - 認識重構的權衡與障礙 > > -- [Amazon.com](https://amzn.to/2XQ2KlF) --- ![設計模式:可重複使用物件導向軟體的元素,作者:Erich Gamma、Richard Helm 和 Ralph Johnson](https://thepracticaldev.s3.amazonaws.com/i/8orec0j061cf2ypavhqt.jpg) ## \#4。 [設計模式:可重複使用物件導向軟體的要素](https://amzn.to/2jY1a37) ### 作者:Erich Gamma、Richard Helm 與 Ralph Johnson #### 33.3% 推薦(_與 #5_ 並列) > 本書不是物件導向技術或設計的介紹。許多書已經在這方面做得很好......這也不是一篇高級論文。這是一本關於設計模式的書,描述了物件導向軟體設計中特定問題的簡單而優雅的解決方案...一旦您理解了設計模式並有一個「啊哈!」的感覺。 (不僅僅是與它們的「嗯?」體驗,您將永遠不會以相同的方式思考物件導向的設計。您將擁有可以使您自己的設計更加靈活、模組化、可重用和易於理解的見解- -這就是您首先對物件導向技術感興趣的原因,對吧? > > -- [Amazon.com](https://amzn.to/2jY1a37) --- ![程式碼完整:軟體建立實用手冊,作者:Steve McConnell](https://thepracticaldev.s3.amazonaws.com/i/sf7gm1y7ld56iytxpzbb.jpg) ## \#3。 [程式碼大全:軟體建置實用手冊](https://amzn.to/2jY2PWp) ### 史蒂夫麥康奈爾 #### 44.9% 推薦 > Steve McConnell 的原始《Code Complete》被廣泛認為是最好的實用程式指南之一,十多年來一直在幫助開發人員編寫更好的軟體。現在,這本經典書籍已通過前沿實踐和數百個新程式碼示例進行了全面更新和修訂,闡釋了軟體建置的藝術和科學。麥康奈爾從研究、學術界和日常商業實踐中獲得了豐富的知識體系,將最有效的技術和必須了解的原則綜合成清晰、務實的指導。無論您的經驗水平、開發環境或專案規模如何,本書都會啟發並激發您的思考,並幫助您建立最高品質的程式碼。 > > - 發現永恆的技術和策略,幫助您: > - 最小複雜度、最大創造力的設計 > - 獲得協作開發的好處 > - 應用防禦性程式設計技術來減少和清除錯誤 > - 利用機會重構或改進程式碼,並安全地進行 > - 使用適合您專案的施工實踐 > - 快速有效地除錯問題 > - 及早正確解決關鍵施工問題 > - 將品質貫穿專案的開始、中期和結束階段 > > -- [Amazon.com](https://amzn.to/2jY2PWp) --- ![《實用程式設計師:從熟練工到大師》,作者:Andrew Hunt 和 Dave Thomas](https://thepracticaldev.s3.amazonaws.com/i/9ckxzwb9a5vc6lpl3fhq.jpg) ## \#2。 [務實的程式設計師:從新手到大師](https://amzn.to/2jY48Vh) ### 安德魯·亨特和戴夫·托馬斯 #### 47.8% 推薦 > 程式設計師是經過訓練的工匠,可以使用一組特定的工具(編輯器、物件管理器、版本追蹤器)來產生將在某些環境(硬體元件上的作業系統)中執行的特定類型的產品(程式).與其他工藝一樣,電腦程式設計催生了一系列智慧,其中大部分不是在大學或認證課程中教授的。隨著時間的推移,大多數程式設計師透過獨立實驗掌握了所謂的技巧。在《務實的程式設計師》中,安德魯·亨特和大衛·托馬斯將他們在各自作為軟體設計師和程式碼編寫者的職業生涯中發現的許多真理編入了法律。 > > 作者的一些實用主義要點是具體的,其實施路徑也很明確。例如,他們建議讀者學習一種文字編輯器,並將其用於所有用途。他們還建議即使是最小的專案也使用版本追蹤軟體,並宣傳學習正規表示式語法和文字操作語言的優點。其他(也許更有價值)的建議則更加輕鬆。在除錯部分,有人指出,“如果您看到蹄印,請想到馬,而不是斑馬。”也就是說,懷疑一切,但開始在最明顯的地方尋找問題。有一些關於估算時間和費用以及將測試整合到開發過程中的建議。你需要一本《務實的程式設計師》有兩個原因:它比你費心去表達的更清晰地展示了你自己累積的智慧,它向你介紹了你可能還沒有考慮過的工作方法。正在工作的程式設計師會喜歡這本書。 > > -- [大衛沃爾來自 Amazon.com](https://amzn.to/2jY48Vh) --- ## 最後,最受軟體開發人員推薦的一本書: --- ![《乾淨的程式碼:敏捷軟體工藝手冊》,作者:Robert C.「Bob 叔叔」Martin](https://thepracticaldev.s3.amazonaws.com/i/jlpbbbl4ity80ottuins.jpg) ## \#1。 [整潔程式碼:敏捷軟體流程手冊](https://amzn.to/2k1ogG7) ### 作者:羅伯特·C·「鮑伯叔叔」·馬丁 #### 52.2% 推薦 > 即使是糟糕的程式碼也能運作。但如果程式碼不乾淨,它可能會讓開發組織陷入困境。每年,都會因為程式碼編寫不當而浪費無數的時間和大量資源。但事實並非一定如此。 > > 著名軟體專家 Robert C. Martin 在《乾淨程式碼:敏捷軟體工藝手冊》中提出了革命性範例。 Martin 與Object Mentor 的同事合作,將他們「即時」清理程式碼的最佳敏捷實踐提煉成一本書,這本書將向您灌輸軟體工匠的價值觀,並使您成為更好的程式設計師——但前提是您工作在它。 > > 您將從事什麼類型的工作?你將閱讀程式碼——大量的程式碼。你將面臨著思考該程式碼哪些是正確的、哪些是錯誤的挑戰。更重要的是,您將面臨重新評估您的專業價值觀和對您的技能的承諾的挑戰。 > > _Clean Code_ 分為三個部分。第一部分描述了編寫簡潔程式碼的原則、模式和實踐。第二部分由幾個日益複雜的案例研究組成。每個案例研究都是一次清理程式碼的練習,即將存在一些問題的程式碼庫轉變為健全且高效的程式碼庫。第三部分是回報:一章包含一系列啟發式方法和建立案例研究時收集的「氣味」。結果是一個知識庫,描述了我們編寫、閱讀和清理程式碼時的思維方式。 > > 讀者將從本書中解脫出來 > - 如何區分好程式碼和壞程式碼 > - 如何寫好程式碼以及如何將壞程式碼轉換為好程式碼 > - 如何建立好名字、好函數、好物件、好類 > - 如何格式化程式碼以獲得最大的可讀性 > - 如何在不模糊程式碼邏輯的情況下實現完整的錯誤處理 > - 如何進行單元測試並實踐測試驅動開發 > - 對於任何有興趣產生更好程式碼的開發人員、軟體工程師、專案經理、團隊領導或系統分析師來說,這本書都是必讀的。 > > -- [Amazon.com](https://amzn.to/2k1ogG7) --- 上述「建議百分比」是包含該書的所有調查清單的比例。因此,即使一本書只有“12.5% 的推薦度”,也意味著平均每 8 個開發人員中就有 1 個已經閱讀過該書並推薦給其他開發人員。上述所有書籍都通過了八分之一的門檻。 更令人驚訝的是前 10 名的書籍。平均每 4 個開發人員中有 __1 人推薦 10 到 6 本書。每 3 人中就有 1 人推薦書籍#5 和#4。排名前三的書籍《__Code Complete__》、《The Pragmatic Programmer__》和《__Clean Code__》受到大約__其他軟體開發人員__的推薦。這對現代社會最關鍵的行業之一產生了巨大影響。如果您想加強您的軟體開發知識,但您尚未閱讀這三本書,那麼您的同事很可能會建議您這樣做。 --- --- 如果您喜歡上面的文章,也許您想關注我在 [Dev.To](https://dev.to/awwsmm) 上的工作?或在 [The Tweeter](https://twitter.com/_awwsmm) 上閱讀我的愚蠢推文?或[請我喝杯咖啡](https://ko-fi.com/awwsmm)? (我有令人衰弱的咖啡因成癮。) 不管怎樣,感謝您的光臨! --- 原文出處:https://dev.to/awwsmm/20-most-recommended-books-for-software-developers-5578

如何將 async/await 與 map 和 Promise.all 一起使用

原文出處:https://dev.to/jamesliudotcc/how-to-use-async-await-with-map-and-promise-all-1gb5 我發現自己一直在使用帶有 async 和 await 的 map 函數。我需要重新學習如何使用 Promise 來解決這個問題,但是一旦我解決了這個問題,語法就變得非常漂亮且可讀。 JavaScript 的 async 和 wait 語法是 ES2017 中的新語法。我認為語法非常簡潔,因為它允許我編寫比承諾金字塔更短、更容易理解的程式碼,類似於承諾是回調地獄的改進。 但是當你想從一堆請求中取回一堆資料時會發生什麼? JavaScript 中沒有「await all」。這就是 `Promises.all()` 的用武之地。Promises.all() 收集一堆 Promise,並將它們匯總成一個 Promise。一旦所有內部承諾成功解決,Promise.all() 將返回已解決的承諾,其中所有內部承諾都已解決。為了讓事情變得更快,一旦任何內部 Promise 被拒絕,Promise.all() 就會拒絕。 重點是 Promise.all() 將一系列 Promise 轉換為單一 Promise,如果正常的話,它會解析為您想要的陣列。其他一切都只是細節。 不知何故,我花了很長時間才擺脫困境。這是我最終開始工作的程式碼,希望這有助於解釋。 假設您點擊一個 REST 端點並取得 REST 端點的 URL 陣列,其中包含您最終想要的內容。例如,您想從 Star Wars API 中找到有關電影 R2-D2 的一些資訊。無論出於何種原因,您都不能使用 SWAPI GraphQL。我們知道從網路取得是一個非同步操作,因此我們必須使用回呼、promise 或 async 和await 關鍵字。由於 R2-D2 出現在多部電影中,因此需要多次網路呼叫才能獲取所有電影。 首先,讓我們進行設定。讓我們只專注於我們正在開發的最小功能,因此我們將在命令列上使用 Node.js。 Node.js 沒有附帶 fetch,所以讓我們使用 npm 或 YARN 來安裝它。 ``` npm install node-fetch --save-dev ``` 或者 ``` yarn add node-fetch --dev ``` async/await 的一個問題是,await 關鍵字只允許在 async 函數內部使用。在真實的程式中,您可能已經進行了足夠的封裝,以便您可以在使用await關鍵字的函數上新增async關鍵字,但在臨時檔案中,我們希望從封閉的上下文中抽像出來。但作為 Javascript 程式設計師,我們知道如何透過將我們想要的內容包裝在即時呼叫的函數表達式中來解決這個問題。 ``` // prettier-ignore const fetch = require('node-fetch') // prettier-ignore (async () => { try { let characterResponse = await fetch('http://swapi.co/api/people/2/') let characterResponseJson = await characterResponse.json() console.log(characterResponseJson) } catch (err) { console.log(err) } } )() ``` 現在我們已經有了基本的 async/await 語法,我們可以檢查回應以查看我們想要的電影欄位。它是一個 URL 陣列。 ``` let films = characterResponseJson.films.map(async filmUrl => { let filmResponse = await fetch(filmUrl) let filmResponseJSON = filmResponse.json() return filmResponseJSON }) console.log(films) ``` 當您執行此程式碼時,您會得到一組待處理的承諾。您需要新的“async”,否則箭頭函數內的等待將無法工作。如果你不「等待」獲取,你會得到一堆被拒絕的承諾,並且錯誤告訴你處理你的承諾拒絕。 但回想一下,「Promise.all()」接受一組 Promise 並將它們包裝成一個 Promise。所以我們包裝我們的“map”函數。我們已經知道一些處理單一 Promise 的好語法。我們可以‘等待’。 ``` let characterResponse = await fetch('http://swapi.co/api/people/2/') let characterResponseJson = await characterResponse.json() let films = await Promise.all( characterResponseJson.films.map(async filmUrl => { let filmResponse = await fetch(filmUrl) return filmResponse.json() }) ) console.log(films) ``` 為了進行比較,promise 中的等效程式碼如下所示: ``` fetch('http://swapi.co/api/people/2/') .then(characterResponse => characterResponse.json()) .then(characterResponseJson => { Promise.all( characterResponseJson.films.map(filmUrl => fetch(filmUrl).then(filmResponse => filmResponse.json()) ) ).then(films => { console.log(films) }) }) ``` 對我來說,第一組 `.then().then()` 非常語意化,我幾乎可以遵循 async/await 文法。但是一旦我們進入“Promise.all()”,僅使用 Promise 語法事情就開始變得難以遵循。無論我們要對影片執行什麼操作,都將取代“console.log”,並且在“.then”連結語法中,它已經埋藏了 3 級縮排。淺層程式碼是易於理解的程式碼。

40 個免費 HTML 登陸頁面模板

原文出處:https://dev.to/davidepacilio/40-free-html-landing-page-templates-3gfp 這樣你的產品就已經準備好了。您花了幾週的時間尋找要解決的正確問題,幾個月甚至幾年的時間開發出一個出色的解決方案來減輕客戶的痛苦,現在只缺少一件事:您幾乎準備好啟動- 您需要一個登陸頁。 即使您想銷售線上服務、電子書或數位課程,登陸頁面對於幫助您根據特定目標實現預期結果也至關重要,因此值得花一些時間找出您真正需要的東西來把它做好。 不幸的是,建立登陸頁面既困難又耗時:結構、設計、圖像、副本等等。這是極其艱難的。 好訊息是 - 您不必從頭開始設計著陸頁,並且有這麼多**免費著陸頁模板**,您可以立即輕鬆建立一個漂亮的著陸頁,從而節省開發時間您可以投資為您的企業創造正確的資訊. 我花了幾個小時研究並找到最好的免費 HTML 登陸頁面模板,結果,我列出了 **40 個漂亮的模板**,您可以將它們用於多種目的、工具和專案。例如: - 開源專案 - 數位服務、電子書和線上課程 - 時事通訊 - SaaS產品 - 行動應用程式 以下列表中的所有模板都是 100% 免費,由不同團隊和作者精心製作,並使用 HTML5 和 Bootstrap 建立。享受閱讀的樂趣,並隨意將它們用於您想要的任何用途。 ###1。 **Solid** ![實體範本](https://dev-to-uploads.s3.amazonaws.com/i/bqh7szmq6e917xfa1lod.jpg) **[現場示範與下載](https://cruip.com/demos/solid/)** Solid 是一款單頁登陸頁面模板,專為線上工具、SaaS 產品和數位服務而設計。它採用 HTML5 建置,具有現代時尚的深色外觀、明亮的互補調色板、3D 圖標/插圖,並且經過明智的編碼和記錄,因此您不必擔心根據您的特定需求自訂模板,目的。 **附加功能:** - 易於調整的英雄佔位符 - 多功能和多用途的圖像 - 即用型定價選項卡 --- ###2。 **Switch** ![切換模板](https://dev-to-uploads.s3.amazonaws.com/i/019rgq3i9ml7i9uye9kn.jpg) **[現場示範及下載](https://cruip.com/demos/switch/)** Switch 是一款免費的 HTML 登陸頁面模板,以高設計標準和回應效能為理念建置。之所以稱為Switch,是因為它配備了頂級英雄切換開關,可讓您輕鬆在深色和淺色佈局之間切換,因此您可以透過展示真正原創的外觀和感覺來給用戶留下深刻印象,靈感來自於一天中變化的天空。 **附加功能:** - 基於向量的多色形狀 - 透視應用程式設計模型 - 行動優先的內容 --- ###3。 **SaaS 應用程式** ![SaaS 範本](https://dev-to-uploads.s3.amazonaws.com/i/y90d0mmoao3f3r5ooo46.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=saas-app)** SaaS 應用程式是一個免費的 Bootstrap 4 登陸頁面模板,具有有趣且友善的配色方案。它旨在為多種 SaaS 相關軟體和服務提供服務,如果您需要一套現代的現成人類插圖、現代圖像、定價元件和用於捕獲電子郵件地址的文本表單,那麼特別推薦它。 **附加功能:** - 現代與當代繪畫 - 大量現成的部分 - 專為不同用途和產品設計 --- ###4。 **April** ![四月範本](https://dev-to-uploads.s3.amazonaws.com/i/qf0s75h00guuuq0v8w42.jpg) **[現場示範及下載](https://cruip.com/demos/april/)** April 擁有簡單的佈局和強大的自訂選項,適合每個需要美觀、響應式元件用於其專案或時事通訊服務的人。透過流暢的導航和一組有價值的編碼元素,如果您需要顯示應用程式功能、客戶評價以及頂部英雄部分的真實產品預覽,則該模板可以滿足您的需求。 **附加功能:** - 現代與當代繪畫 - 大量現成的部分 - 專為不同用途和產品設計 --- ###5。 **數位服務** ![數位服務範本](https://dev-to-uploads.s3.amazonaws.com/i/0uybxsxod2mrynn8hlfw.jpg) **[現場演示和下載](https://grayic.com/preview/?demo=digital-service)** 數位服務是一種響應式登陸頁面模板,旨在幫助您快速輕鬆地展示您的行動應用程式或線上狀態。它基於 Bootstrap 4,並附帶大量預先設計的部分,例如砌體推薦、定價表、輸入表單、三列功能部分等等。最後但並非最不重要的一點是,其設計的多功能性使該模板成為支援多個利基市場的完美選擇。 **附加功能:** - 測試應用程式原型的理想選擇 - 優雅且開源的圖像 - 適用於產品和線上服務 --- ###6。 **Venus** ![維納斯範本](https://dev-to-uploads.s3.amazonaws.com/i/d56saxazri538scb53y8.jpg) **[現場示範與下載](https://cruip.com/demos/venus/)** Venus 是一個免費的單頁登陸頁面模板,基於預先建置的設計部分和 HTML5。它遵循響應式設計的最新最佳實踐,並且由於存在多個可編輯的行動應用程式佔位符、手工製作的向量插圖、Google 字體、社交媒體圖標和完全可編輯的顏色產品調色板,因此非常容易定制。 **附加功能:** - 頁面恆星動畫 - 靈活的佈局和內容設計 - 在任何裝置上都能完美執行 --- ###7。 **艾莉** ![艾莉模板](https://dev-to-uploads.s3.amazonaws.com/i/jnfdnoel1gbg4qnnkdux.jpg) **[現場示範及下載](https://cruip.com/demos/ellie/)** 使用這個深色、明亮且緊湊的免費模板建立漂亮的登陸頁面並開始收集時事通訊訂閱者。有了 Ellie,只需不到 5 個步驟就可以讓您的產品脫穎而出,並且憑藉頂級英雄動畫和像素完美形狀的組合,您將永遠不會讓存取者在再次滾動到正文部分之前跳動。 **附加功能:** - 動態英雄過渡 - 內建示範功能 - 技術和廣泛的調色板 --- ###8。 **軟體** ![軟體範本](https://dev-to-uploads.s3.amazonaws.com/i/qn4taou9lhy4ona0583l.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=software)** 使用Berlin 輕鬆建立自訂且高度適應性的應用程式網站,這是一個免費的Bootstrap 4 登陸頁面模板,專為SaaS 專案建置,並且希望將更多潛在客戶轉化為客戶,並相信精心設計的登陸頁面可以在一個普通的產品和一個公認的國際軟體品牌。 **附加功能:** - 動態隨機產生的內容 - 智慧餐桌選項 - 影片示範部分 --- ###9。 **勞雷爾** ![月桂模板](https://dev-to-uploads.s3.amazonaws.com/i/42ox85g1o989no5tnkum.jpg) **[現場示範與下載](https://cruip.com/demos/laurel/)** Laurel 是一個基於最新版本 HTML5 的大膽而美麗的登陸模板,它將幫助您美麗而優雅地展示您的行動應用程式。功能方面,此範本附帶了 Sass 檔案、作為建置工具的 NPM 腳本以及 package.json 範例檔案。在設計方面,所有資產都是為了提供無盡而甜蜜的客製化選項而建造的。 **附加功能:** - 記錄良好的程式碼文件 - 即將推出的功能更新 - 完整的利基應用程式模板 --- ###10。 **棱鏡** ![棱鏡模板](https://dev-to-uploads.s3.amazonaws.com/i/0wrh4kf9zyeipjujmlro.jpg) **[現場示範](https://webresourcesdepot.com/demo/prism/)** / **[下載](https://webresourcesdepot.com/freebie/prism/)** Prism 是一款輕量級、現代的免費模板,專為使用 Bootstrap 4、CSS、HTML 和 JavaScript 的開源專案而建置。使用者喜歡 Prism,因為它使用起來非常簡單,修改起來非常直觀,並且它涵蓋了任何人都可以從具有此類設計和功能特徵的登陸頁面中期望的大多數元素。 **附加功能:** - SEO專用設計 - 評論良好的程式碼 - 創意一頁版面 --- ###11。 **Web應用程式** ![Web 應用程式範本](https://dev-to-uploads.s3.amazonaws.com/i/rzuf0mbk2sm0s790as2n.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=web-app)** Web 應用程式是建立簡單HTML 登入頁面的最佳方式,可以幫助您吸引使用者使用下一個出色的Web 應用程式或桌面應用程式- 得益於詳細列出的功能部分、功能性幻燈片客戶以及引人入勝的號召性用語,Web 應用程式應用程式可以輕鬆快速地設定一個有吸引力的獨立頁面,以滿足不同的需求、範圍。 **附加功能:** - 有吸引力的按鈕和表格 - 終極 HTML 功能 - 不同的演示版本 --- ###12。 **騎士** ![騎士模板](https://dev-to-uploads.s3.amazonaws.com/i/ulo57e43t9f6d4uidrgc.jpg) **[現場示範](https://webresourcesdepot.com/demo/knight/)** / **[下載](https://webresourcesdepot.com/freebie/knight/)** 身為黑夜中的勇敢騎士,Knight是一款專為產品登陸頁面設計的深色、現代、優雅的Bootstrap 4登陸頁面模板。它配備了 Feather & Font Awesome 圖示、光滑的滑桿、常見問題清單等福利。 Knight 使用 HTML5、CSS3、jQuery 和 Sass 建置,保證了令人難以置信的響應性能和流暢的自訂選項。 **附加功能:** - 出色的 UI/UX 和資產文件 - 自動調整影像大小 - 免費無限支持 --- ###13。 **開發手冊** ![DevBook 範本](https://dev-to-uploads.s3.amazonaws.com/i/rzp0mc2lqavn08v3bzf7.jpg) **[現場示範](https://themes.3rdwavemedia.com/demo/devbook/)** / **[下載](https://themes.3rdwavemedia.com/bootstrap-templates/product/devbook-free-bootstrap-4-book-ebook-landing-page-template-for-developers/)** DevBook 是一個免費的 Bootstrap 4 書籍登陸頁面模板,專為想要在線上推廣或銷售書籍/電子書的開發人員和程式設計師而設計。它包含吸引好奇的訪客成為潛在讀者的所有基本元素,並且由於它是完全客製化開發的,因此您可以輕鬆地將其與 Gumroad 等平台集成,它將代表您處理購買和付款。 **附加功能:** - 與現代瀏覽器相容 - 免費書籍模型 - 多個可用許可證 --- ###14。 **有** ![Tivo 範本](https://dev-to-uploads.s3.amazonaws.com/i/gojb4bkb00vdk73kjmu3.jpg) **[現場示範](https://inovatik.com/tivo-landing-page/index.html)** / **[下載](https://inovatik.com/tivo-free-saas-app-登陸頁模板.html)** Tivo 是一款使用 HTML5 製作的免費應用程式登陸頁面模板,經過精確建立,可支援建立有吸引力的軟體即服務 (SaaS) 網站和 B2C 應用程式頁面。對於任何尋求簡單下拉導航、文章詳細資訊、視訊框、文字滑桿推薦以及允許製作無限數量的個人和商業專案的易於使用的許可證的人來說,Tivo 是一個智慧解決方案。 **附加功能:** - 功能聯繫表 - 谷歌字體和 Font Awesome - 在每個瀏覽器中經過全面測試 --- ###15。 **光滑** ![光滑模板](https://dev-to-uploads.s3.amazonaws.com/i/vmig768208lulvcgwoxf.jpg) **[現場示範](https://preview.uideck.com/items/slick/business/index.html)** / **[下載](https://uideck.com/templates/slick-free-引導模板/)** Slick 是一個基於 Bootstrap 4 和 HTML5 的免費原創多用途網頁模板。 Slick 提供卓越的設計技術和最佳的 UI/UX 實踐,可在任何類型的數位裝置(例如桌上型電腦、平板電腦和行動裝置)上提供出色的體驗。它為基於網路的軟體產品和行動應用程式提供了兩種不同的主頁變體。 **附加功能:** - CSS 庫和線條圖標 - 超豐富的排版 - 條紋設計靈感 --- ###16。 **線上課程** ![線上課程範本](https://dev-to-uploads.s3.amazonaws.com/i/g0qev01qthm9vxzt9ybn.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=online-course)** 線上課程是一個免費的 HTML 登陸頁面模板,它為您提供建立業務和透過建立和銷售線上課程創造收入所需的一切。使用線上課程模板,您可以快速設定市場上排名第一的使用者體驗設計課程,並且由於有用元素的巨大變體,您可以支援隨時存取線上課程、視訊課程和複習系統。 **附加功能:** - 功能課程課程 - 常見問題部分 - 多種內容呈現 --- ###17。 **聯盟** ![聯合模板](https://dev-to-uploads.s3.amazonaws.com/i/egjsxv0o22v8mmvkgujt.jpg) **[現場示範](https://webresourcesdepot.com/demo/union/)** / **[下載](https://webresourcesdepot.com/freebie/union/)** Union 是一個免費的登陸頁面,可以幫助您更快、更輕鬆地建立漂亮的響應式 HTML 網站。 Union 受到數千名滿意用戶的信賴,提供10 多種不同的配色方案、有序的Sass 檔案(用於快速自訂)、有吸引力的圖像,對於尋求具有多個可編輯元件的高效能一頁的創意團隊來說,它是理想的選擇。 **附加功能:** - 有效且手寫的程式碼 - 漂亮流暢的滾動 - 預先整合和功能形式 --- ###18。 **行動應用2.0** ![行動應用程式 2.0 範本](https://dev-to-uploads.s3.amazonaws.com/i/y9op3tx00u09iiutd2c3.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=mobile-app-2-0)** Mobile App 2.0 是一款免費的行動應用模板,專為行動應用程式、新創公司、產品開發公司和特別注重用戶獲取的企業而設計。行動應用程式 2.0 為您提供了詳細記錄的設置,可讓您在 3 分鐘內完成設置並執行,並且不會影響輕鬆頁面佈局的可能性。 **附加功能:** - 基於向量的氣泡形狀 - 帶有畫廊截圖的滑塊 - 不同的演示類型 --- ###19。 **阿特拉斯** ![圖集範本](https://dev-to-uploads.s3.amazonaws.com/i/wayb6z44rhjris7xo79v.jpg) **[現場示範](https://www.lapa.ninja/lab/atlas/)** / **[下載](https://www.lapa.ninja/freebies/atlas/)** Atlas 是一個響應靈敏且高度可自訂的啟動登陸頁面,提供許多出色的功能,例如推廣 SaaS、聯繫我們部分、優雅的英雄、定價標籤、手工製作的圖標和產品功能。借助 Atlas,您只需點擊幾下即可快速建立任何登陸頁面,並且使用 Bootstrap Framework,您還可以匯入自己喜歡的元件。 **附加功能:** - 版面安全可靠 - 最多 15 個文件 - 清新的佈置配色方案 --- ###20。 **簡單的** ![簡單範本](https://dev-to-uploads.s3.amazonaws.com/i/3el75t5hah5e6uv3oar8.jpg) **[現場示範](https://www.bootstrapdash.com/demo/simple/)** / **[下載](https://www.bootstrapdash.com/product/simple-landing-page/) ** Simple 是一款乾淨友善的單頁登陸頁面模板,旨在以現代且豐富多彩的外觀為您的用戶留下深刻印象。簡單的登陸頁面具有美觀整潔的設計,是展示您的早期新創公司、線上服務和數位產品的理想選擇。此範本具有創意和獨特的設計、人體插圖、像素完美的圖標以及一組預先建立的元素,可輕鬆呈現主要應用程式功能和優點。 **附加功能:** - 定期更新和改進 - 麻省理工學院許可 - 快速和友好的支持 --- ###21。 **幻影** ![幻像範本](https://dev-to-uploads.s3.amazonaws.com/i/c56b1ycw4i87isqaz16e.jpg) **[現場示範](https://webresourcesdepot.com/demo/phantom/)** / **[下載](https://webresourcesdepot.com/freebie/phantom/)** Phantom 是一款富有創意且現代的 Bootstrap 4 登陸頁面模板,專為想要為其副專案和開源工具建立快速、新穎的登陸頁面的開發人員而建置。此範本提供了獨特且利基的佈局,您可以立即適應自己的需求,並提供大量精心製作和組織的高品質編碼元素。 **附加功能:** - 響應靈敏並與現代設備相容 - 設計中包含螢幕截圖 - 可重複使用的元件 --- ###22。 **新的** ![Nova 範本](https://dev-to-uploads.s3.amazonaws.com/i/5hm4f3hxb7bwchtcg7p7.jpg) **[現場示範](https://demo.bootstraptemple.com/app-landing/)** / **[下載](https://bootstrapious.com/p/app-landing-page)** Nova 是由 HTML5 和 Bootstrap 4 提供支援的響應式專業行動應用程式登陸頁面。Nova 是一款功能強大且超級靈活的產品,非常適合各種應用程式登陸頁面。對於每個尋求新鮮的線上形像以及與通常的網站演示不同的東西的人來說,該模板是一個完美的解決方案。 **附加功能:** - 龐大且可變的元素庫 - 無需歸屬 - 可用於個人和商業專案 --- ###23。 **紗羅** ![Leno 範本](https://dev-to-uploads.s3.amazonaws.com/i/s8rmxzr5044hu6d2okm1.jpg) **[現場示範](https://inovatik.com/leno-landing-page/index.html)** / **[下載](https://inovatik.com/leno-free-mobile-app-登陸頁模板.html)** Leno 是一款免費的深色行動應用程式 HTML 登陸頁面,使用 HTML、CSS 和 Javascript 建置。這個引人注目的模板具有一個帶有佔位符大預覽的圖像滑塊、一個用於客戶推薦的動態輪播以及一組附加頁面,其中包括聯繫表單部分,潛在客戶可以在其中輕鬆聯繫您詢問或提出問題。 **附加功能:** - 經過 W3C 有效程式碼測試 - 動畫統計資料和數字 - GDPR 友善設計 --- ###24。 **折斷** ![快照範本](https://dev-to-uploads.s3.amazonaws.com/i/00qaht3st5sdfahj83e7.jpg) **[現場示範](https://webresourcesdepot.com/demo/snap/)** / **[下載](https://webresourcesdepot.com/freebie/snap/)** Snap 是建立展示實用程式應用程式的美觀登陸頁面的最快方法。透過 Snap,您將獲得現代網站以正確方式展示其功能所需的所有必要頁面和部分。其中一些功能包括定價標籤、支援部分、桌面佔位符、福利圖塊以及用於顯示用戶評論的輪播。 **附加功能:** - 企業數位化解決方案 - 靈活的主頁變化 - 在不同的行動裝置上進行測試 --- ###25。 **埃沃洛** ![Evolo 範本](https://dev-to-uploads.s3.amazonaws.com/i/dgkev9n9d0jiu0kri0ha.jpg) **[現場示範](https://inovatik.com/evolo-landing-page/index.html)** / **[下載](https://inovatik.com/evolo-free-startup-landing-頁面模板.html)** Evolo 是一個清新迷人的登陸頁面模板,專為各種線上服務和數位產品而設計。由於 Evolo 建立在靈活的元件之上,因此如果您希望輕鬆建立無限數量的附加頁面,Evolo 是一個很好的伴侶。該模板還提供了一系列支援元素,可以整合這些元素以方便地建立一個全新的網站。 **附加功能:** - 合作夥伴螢幕截圖滑桿 - 支援的專案數量不受限制 - 圖像檔案和文字標誌選項 --- ###26。 **基本的** ![基本範本](https://dev-to-uploads.s3.amazonaws.com/i/h9f4dfaztpi44njq48ek.jpg) **[現場示範](https://preview.uideck.com/items/basic/)** / **[下載](https://uideck.com/templates/basic/)** Basic 是在 Bootstrap 4 和 HTML5 之上開發的軟體和 SaaS 登陸頁面範本。此範本包含新創公司快速展示數位產品所需的所有基本元素。基本它是完全響應式的,透過多個區塊進行編碼,而且它非常靈活,以至於增加 Bootstrap 4 的基本元件變得前所未有的容易。 **附加功能:** - 提供免費和專業功能 - 刪除頁腳積分 - 包含文件 --- ###27。 **快的** ![快速範本](https://dev-to-uploads.s3.amazonaws.com/i/ymv9q8a18jnp2fl2gcaj.jpg) **[現場示範](http://themehunt.com/item/1527640-quick-free-bootstrap-4-theme/preview)** / **[下載](https://themehunt.com/item/1527640-quick-free-bootstrap-4-theme)** Quick 是一個免費的 HTML 登陸頁面,專為行動優先專案、新創公司和數位服務而建置。此範本包含 50 多個預先建置元件、2 個有用的外掛程式和 4 個設計精美的響應式頁面。 Quick 是一個不斷改進的模板,每次新更新都會獲得額外的功能和 UI 元件,以取悅使用者。 **附加功能:** - 提供影片示範 - 智慧程式碼標記 - 包括 NPM 和 Gulp 等熟悉的工具。 --- ###28。 **開發援助** ![devAid 範本](https://dev-to-uploads.s3.amazonaws.com/i/3m3v5mwf9vd3jx0ptfny.jpg) **[現場示範](https://themes.3rdwavemedia.com/demo/devaid/)** / **[下載](https://themes.3rdwavemedia.com/bootstrap-templates/startup/devaid-free-bootstrap-theme-for-developers-side-projects/)** devAid 是一個乾淨的 Bootstrap 模板,非常適合幫助程式設計師展示他們的個人專案和開源產品。此範本配有 4 種不同的配色方案、來源 SCSS 文件,可輕鬆執行樣式自訂,並提供大量額外元素,可根據登陸所包含的工具立即調整內容。 **附加功能:** - 經典的標題版面 - 超快載 - 基於流行的前端框架 --- ###29。 **棉花糖** ![棉花糖模板](https://dev-to-uploads.s3.amazonaws.com/i/it03dy8e17dnco3ya9lo.jpg) **[現場示範](https://www.bootstrapdash.com/demo/marshmallow/)** / **[下載](https://www.bootstrapdash.com/product/marshmallow/)** Marshmallow 是一個 HTML 登陸頁面模板,專為各種類型的 SaaS 產品、新創公司和應用程式展示而設計。該模板 100% 響應,在所有類型的設備上看起來都令人驚嘆。 Marshmallow 具有設計精美的元素和內頁,讓您的網站看起來安全且專業。 **附加功能:** - 提供功能輪播 - 動態網站統計和數字 - 獨立的圖像和插圖 --- ###30。 **阿普蘭** ![Appland 範本](https://dev-to-uploads.s3.amazonaws.com/i/kz7x1n3i7zvlp9jrw3bs.jpg) **[現場示範](https://preview.uideck.com/items/appland/)** / **[下載](https://uideck.com/templates/appland/)** Appland 是一款免費、優雅的登陸頁面模板,專為應用程式登陸、軟體和線上工具而建立。 Appland 功能強大,具有原始設計和新創公司從頭開始建立引人注目的登陸頁面所需的所有必要元素,具有預先建立的佈局視圖和無限的創意可能性。 **附加功能:** - 專為不同目的而設計 - 包括所有基本功能和元素 - 令人驚嘆的動畫英雄部分 --- ###31。 **SEO公司** ![SEO 公司範本](https://dev-to-uploads.s3.amazonaws.com/i/l900p6nf3650bqke632j.jpg) **[現場示範](https://demos.onepagelove.com/html/seo-company/)** / **[下載](https://onepagelove.com/seo-company)** SEO Company 是一款免費且優雅的單頁 HTML 模板,專為數位團隊設計,旨在建立乾淨且令人驚嘆的登陸頁面來展示其線上服務。在功能方面,此模板呈現出獨特的設計、有吸引力的平滑過渡、移動觸控滑桿以及一組充分混合的高級元素,以最大限度地提高轉換率。 **附加功能:** - 包含 PSD 範本和文件 - 直接下載 - 乾淨的鋸齒形特徵 --- ###32。 **炫** ![Dazzle 範本](https://dev-to-uploads.s3.amazonaws.com/i/874afgq81sb9nqmh2p6w.jpg) **[現場示範](https://demos.onepagelove.com/html/dazzle/)** / **[下載](https://onepagelove.com/dazzle)** Dazzle 是一款響應式單頁模板,具有視差滾動佈局,旨在提供平滑的外觀和無限的自訂可能性。 Dazzle 的設計和開發考慮了最新的設計趨勢,並經過最佳化以包含所有基本元素,為幾乎所有可能的場合建立具有視覺吸引力的頁面。 **附加功能:** - 平滑滾動到各個部分 - 應用程式概述部分 - 推薦滑桿和下載按鈕 --- ###33。 **新時代** ![新時代模板](https://dev-to-uploads.s3.amazonaws.com/i/hmdc6gzpcai262bgx65z.jpg) **[現場示範](https://startbootstrap.com/previews/new-age/)** / **[下載](https://startbootstrap.com/themes/new-age/)** New Age 是一個應用程式登陸頁面模板,它將幫助您以絕對的快樂和平靜的心態精美地展示您的新行動應用程式或其他任何內容。該模板具有不同的免費使用 HTML/CSS 設備佔位符、帶有滾動動畫的定制導航以及帶有導航、部分和旁白的語義標記。由於其大膽、多彩且時尚的外觀,New 是您下一個基於應用程式的專案的優秀樣板! **附加功能:** - 帶有滾動過渡的菜單 - 不同的按鈕樣式 - CSS 漸層與紋理和覆蓋 --- ###34。 **登陸頁** ![登陸頁範本](https://dev-to-uploads.s3.amazonaws.com/i/ikvoesq8mv52mxagi37h.jpg) **[現場示範](https://startbootstrap.com/previews/landing-page/)** / **[下載](https://startbootstrap.com/themes/landing-page/)** 登陸頁面是適用於HTML5 和Bootstrap 4 的響應式登陸頁面模板。即使對於非技術用戶來說,登陸頁面也可以輕鬆建立引人注目的網路形象,並且透過其詳細的修飾,您會發現可以更改任何模板部分或元素小菜一碟。在功能方面,此範本包括一個帶有響應式背景圖像的輸入表單選擇標題,以及展示應用程式功能和優點的豐富內容部分。 **附加功能:** - 包括簡單的線條圖標 - 動態內容部分 - 響應式背景圖片 --- ###35。 **即將推出** ![即將推出範本](https://dev-to-uploads.s3.amazonaws.com/i/agexcg1run49mcy1fxum.jpg) **[現場示範](https://startbootstrap.com/previews/coming-soon/)** / **[下載](https://startbootstrap.com/themes/coming-soon/)** 如果您正在開發令人興奮的新產品,並且需要開始圍繞它建立受眾群體,「即將推出」範本將幫助您在幾分鐘內輕鬆建立令人驚嘆的發布前登陸頁面。即將推出是透過優雅的即用型產品吸引早期客戶的理想解決方案,並且由於它是為多種目的而設計的,因此您不需要進行大量的客製化工作來根據您的特定目的和需求調整內容。 **附加功能:** - 移動後備影像 - 電子郵件訂閱者選擇加入輸入 - 實用的影片背景 --- ###36。 **SaaS 訂閱** ![SaaS 訂閱](https://dev-to-uploads.s3.amazonaws.com/i/4m892kjw7iaq57xnq3xv.jpg) **[現場示範及下載](https://grayic.com/preview/?demo=saas-sub)** 您的 SaaS 模型應基於您的產品提供的價值,不同的價值類型需要不同類型的訂閱。 SaaS 訂閱是完美的 HTML 登陸頁面模板,可協助您取得新使用者並將早期訪客轉化為潛在客戶。該模板具有幾個漂亮的圖形和預先建立的部分,並且只需較低的程式碼技能即可輕鬆編輯。 **附加功能:** - 可切換的定價選項 - 工作聯絡表 - 無限的定制可能性 --- ###37。 **開發空間** ![開發空間](https://dev-to-uploads.s3.amazonaws.com/i/yy2bsudpfbor7nlf4xsi.jpg) **[現場示範與下載](https://www.lapa.ninja/lab/dev-space)** Dev Space 是一款非常實用且設計精美的登陸頁面模板,專為應用程式開發顧問服務而製作。此範本建立在可靠的基礎設施之上,其中包括全面的內部工具、幾個免費的流線型圖示、豐富的插圖包以及高效且易於維護的記錄良好的程式碼。 **附加功能:** - 預先設計的標誌雲 - 認識我們的團隊部分 - 互動的現代運用 --- ###38。 **降落** ![登陸](https://dev-to-uploads.s3.amazonaws.com/i/qrtbv9hzuebkbrymw7cl.jpg) **[現場示範](https://www.tailwindtoolbox.com/templates/landing-page-demo.php) / [下載](https://www.tailwindtoolbox.com/templates/landing-page)** Landing 是一個獨特且現代的 SaaS 登陸頁面模板,主要專注於流暢的使用者體驗,使您的網站脫穎而出。 Landing 中的所有圖像均可用於個人或商業專案,並且具有廣泛的預建置內容塊和元件,該模板為您提供了無限數量的自訂可能性。 **附加功能:** - 交易和卡片內容元件 - 多欄英雄部分 - 入門範本已準備好 --- ###39。 **凱羅斯** ![Kairos 範本](https://dev-to-uploads.s3.amazonaws.com/i/o8m08uga9xc4u58wsobz.jpg) **[現場示範](https://templatemag.com/demo/templates/CleanLanding/index.html) / [下載](https://templatemag.com/cleanlanding-bootstrap-landing-template/)** Kairos 是一個很棒的應用程式模板,可讓您以簡單且時尚的方式展示您的應用程式。此範本將讓您的客戶更了解您的應用程式提供的功能,首先是優點部分,然後是聯絡表單,您將獲得他們的興趣和信任,因此他們會嘗試您的應用程式。 Kairos 還使用平滑過渡來為您的著陸提供更自然的外觀和感覺。 **附加功能:** - 持續更新和錯誤修復 - 支援無限網站 - PHP 和 AJAX 聯絡表 --- ###40。 **蝴蝶** ![蝴蝶模](https://dev-to-uploads.s3.amazonaws.com/i/bpyehblbxhdabeupg1ft.jpg) **[現場示範](https://bootstrapmade.com/demo/Butterfly/) / [下載](https://bootstrapmade.com/butterfly-free-bootstrap-theme/)** Butterfly 是一個乾淨且有吸引力的單頁 Bootstrap 模板,專為希望展示與容器元素功能相關的號召性用語的特色部分的公司新創公司而建立。總體而言,此模板是用 HTML 5 開發的,所有支援的元素均支援視網膜,適用於較大的顯示器和小型行動裝置。 Butterfly 將幫助您建立令人驚嘆的登陸頁面,讓您的用戶停下來思考。 **附加功能:** - 透過電子郵件提供高級支援 - 整頁版面 - 以生產品質打造 --- ##結論 登陸頁面將佔您的用戶的大部分,因此他們需要您的全力關注和關注。有了這個免費的 HTML 登陸頁面模板列表,您就沒有理由不能擁有一個能夠正確展示您的產品並且轉換良好的登陸頁面。

我使用了幾款文字編輯器的心得:sublime、vim、emacs、vscode

我將談論我使用每個文字編輯器的經驗,也許這對於評估文字編輯器的人花時間學習可能有用。 ## Sublime Text 我在高中時開始學習編程,編寫一些程式碼並在瀏覽器上查看結果非常有趣。我使用 Sublime Text 是因為我正在透過部落格文章和影片進行學習,而且大部分內容都提到了 Sublime Text。而且,它速度很快,而且我安裝了一些外掛程式來處理 php、html、javascript 和 css。我在工作中也使用了它幾年,直到有一天我加入了一家擁有 vim 和 emacs 用戶的公司。 ## vim vim 使用者從事的專案與我自己的專案更接近,所以我開始檢查他們是如何做事的,然後我開始學習 vim。這並不酷,學習如何使用它太難了,要加載新的配置文件我每次都必須重新啟動 vim 以及許多其他問題,但既然他們這樣做了,我說服自己我也可以學習它,所以我做過。 我使用 vim 幾個月了,我最喜歡 vim 的是它針對編輯檔案而不是建立新檔案進行了最佳化。大多數時候,我們都在編輯檔案並在程式碼行之間移動,這樣才有意義。最後,我對 vim 的使用感到高興並且富有成效。 ## neovim 有一天,我聽說了 neovim,但不確定為什麼我轉而使用它,但請查看這兩個專案的 github 專案: ![](https://thepracticaldev.s3.amazonaws.com/i/mr475vfxouxinfcgxeaa.png) ![](https://thepracticaldev.s3.amazonaws.com/i/oogdvczt4j9mt5v49jxu.png) 等等什麼? vim 儲存庫上只有一位貢獻者?怎麼只有一個人就能做到這一點?檢查拉取請求列表,我發現作者應用了其他人的更改,因為這是他的更改。好吧,我不喜歡這樣,所以我改用 neovim,當人們做出貢獻時,它會給他們帶來榮譽。 更新:我同意我的調查太淺薄。我看到[此評論](https://dev.to/heast/comment/4gk4)。但我還是不喜歡這樣。 ## nvim + tmux 當時人們正在談論“tmux”,它是一個終端多路復用器,因此您可以拆分終端並擁有選項卡並自定義所有內容,從快捷方式到tmux 的外觀,例如向狀態欄加入有用的訊息。我嘗試過並且喜歡。由於我在終端中使用 nvim,它執行得非常好,我甚至找到了一些插件來順利整合它們。它是這樣的: ![](https://thepracticaldev.s3.amazonaws.com/i/u4ncf7ir9rfeb0lx700j.gif) 我不再使用 nvim,但你仍然可以找到我的 [vim](https://github.com/lucasprag/vimlociraptor) 和 [tmux](https://github.com/lucasprag/dotfiles/blob) 設定/ master/tmux.conf)。 我使用這個堆疊三年了,我很高興。 然後我看到自己管理 nvim、tmux、zsh 的配置和插件,以便在工作中的 Debian 上的 terminator 和家裡的 macOS 上的 iTerm 上執行良好。插件之間也不是一切都工作得很好,例如,我在nvim 上有自動完成功能,它工作得很好,除非我使用多個遊標,這是另一個插件的功能,自動完成功能僅適用於第一個遊標,因為它們沒有彼此融為一體。我還遇到了類似的其他令人沮喪的問題,而且我發現自己花了太多時間除錯或嘗試替代插件,所以我傾向於嘗試一些不同的東西,這就是 emacs 引起我注意的地方。 ## emacs Emacs 有一個 GUI 應用程式,它讓我不再需要 terminator 或 iTerm,它有自己的 shell,用 elist 編寫,稱為 eshell,所以我對所有事情都有一個配置和插件。我嘗試像使用 vim 一樣從頭開始寫下每個配置,但要學的東西太多了,而且我必須完成一些工作,所以我嘗試了 spacemacs,在一些幫助下我很快就能提高工作效率。 Spacemacs 有 vim 鍵綁定,所以我在家裡,我只需要學習處理 spacemacs 的不同方面,例如佈局和工作區。做事情更容易,因為它會即時顯示可用的鍵綁定,所以當您鍵入時,您可以看到您的選項,它也是助記符,所以如果您想要佈局,您可以使用“SPC + l” ,如果需要的話要切換某些內容,請使用“SPC + t”,如果需要緩衝區,請使用“SPC + b”,對於專案,請使用“SPC + p”,對於文件,請使用“SPC + f”,並儲存您需要的檔案組合“SPC + fs”(意指“spacemacs -> 檔案 -> save”)和“SPC + pt”等指令來檢視檔案的專案樹。非常簡單吧? 我花了大約一個月的時間才理解一切並順利工作。我對一切都不滿意,我遇到了一些錯誤,這沒關係,我報告了並嘗試修復它們,但我必須再學習一種語言(elisp)才能做到這一點。這是一次很棒的經歷,因為我學習了很多 elisp,在我看來這是一門很棒的語言,所以我玩得很開心,這讓我使用了 spacemacs 6 個月。 然後我看到了這個影片: https://www.youtube.com/watch?v=8kCd4w4kc68 等等什麼?一週的 VSCode 就毀了對 Emacs 10 年的熱愛?我有一些朋友也對 vscode 很滿意,他們強烈推薦它。好吧,讓我們試試看。 ## vscode 我必須在周末做一些編碼,所以我切換到 vscode,總的來說,體驗很棒,但一開始我不得不經常使用滑鼠。 我安裝了vim 鍵綁定,令我驚訝的是,它還支援常見的vim 插件,例如「vim-easymotion」、「vim-surround」、「vim-commentary」以及其他我從未聽說過的插件。 我在 vscode 中發現的殺手級功能: - `cmd + p` 使用模糊搜尋開啟檔案更快、更聰明(它將最近的檔案放在頂部) - 快捷方式與瀏覽器快捷方式更相似,這讓我作為網頁開發人員的生活更輕鬆 - 設定檔是「JSON」文件,不需要太多自訂(我的設定檔有 22 行,僅此而已) - 整合的多終端運作得非常好,並且像 tmux 一樣有分割功能 - vscode 由 Microsoft 維護,我認為有一個團隊致力於它並加入彼此集成良好的功能真是太好了 - 我不需要除錯 vscode 並且到目前為止我沒有發現任何錯誤 - 我不需要安裝太多插件 - 跳到 React 的定義 我安裝的VScode外掛: - [Vim 鍵綁定](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) - [專案管理器](https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager) - 使用「ALT + CMD + p」更輕鬆地在專案之間切換 - [書籤](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks) - `ALT + CMD + k` 加書籤,`ALT + CMD + l` 跳到下一個書籤 - [檔案實用程式](https://marketplace.visualstudio.com/items?itemName=sleistner.vscode-fileutils) - 無需使用滑鼠即可輕鬆複製或重新命名文件 - [對 Ruby 的支持](https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby) - [對 Ruby Haml 的支持](https://marketplace.visualstudio.com/items?itemName=vayan.haml) - [Ruby 自動補全](https://marketplace.visualstudio.com/items?itemName=castwide.solargraph) - [Ruby 的 Linting](https://marketplace.visualstudio.com/items?itemName=misogi.ruby-rubocop) - [Python 支援](https://marketplace.visualstudio.com/items?itemName=ms-python.python) - [Elixir 支援](https://marketplace.visualstudio.com/items?itemName=mjmcloug.vscode-elixir) - [React 支援](https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets) - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - [One Dark Pro Monokai Darker 主題](https://marketplace.visualstudio.com/items?itemName=eserozvataf.one-dark-pro-monokai-darker) - 我最喜歡的配色方案 ![](https://thepracticaldev.s3.amazonaws.com/i/thtlo6h65n8hmfksan23.gif) 這是我使用文字編輯器的經驗。今天,我不必除錯我的編輯器來使簡單的事情發生,而且我仍然可以根據我的需要對其進行自訂。是的,我對 vscode 非常滿意,也許你也應該嘗試一下。我希望它對某人有用。 我也期待聽到您對這些文字編輯器的意見和經驗。謝謝。 --- 原文出處:https://dev.to/lucasprag/my-editor-journey-sublime-vim-emacs-vscode-19k0

🤩 最佳 VS Code 外掛 🛠 2023 年每個開發人員都應該使用

您是否正在為您的 Web 應用程式尋找令人驚嘆的 VS Code 擴充功能?那麼這裡是 2023 年最佳 VS Code 擴充的驚人集合。 [**VS Code 擴充**](https://marketplace.visualstudio.com/VSCode) 在現代 Web 開發中至關重要。它們基本上是用於建立現代 Web 應用程式的原始程式碼編輯器。它是一個免費的開源編輯器。此外,它支援大量可用於 Web 應用程式開發的擴充功能。 **[VS Code](https://code.visualstudio.com/)** 擴充功能可讓您為安裝新增偵錯器、語言和工具,以支援您的開發工作流程。其豐富的可擴充性模型使擴充作者可以直接插入 VS Code UI,並透過 VS Code 使用的相同 API 提供功能。 因此,為了幫助您選擇正確的擴展,這些擴展將比它們從您的系統中獲得的資源增加更多的價值,我們列出了當今可用的最佳趨勢擴展的廣泛列表。雖然其中一些是眾所周知且普遍安裝的,但其他擴充功能是使用 Visual Studio Code 的經驗豐富的開發人員強烈推薦的擴充功能。 現在,在處理任何Web 專案時,我們建議您使用這個令人印象深刻的[Bootstrap 儀表板範本](https://themeselection.com/item/category/bootstrap-admin-templates/),它具有現代且獨特的設計。 [![Sneat Bootstrap 5 HTML 管理範本](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ss73uc2z3h7oueged1jn.png)](https://themeselection.com/products/sneat-bootstrapsneat-bootstrapsneat-bootstrapsneat-bootstrapsneat-bootstrapsneat-bootstrap-html-管理模板/) ##### 1. [GITLENS](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) [![Gitlens](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/docs/gitlens-preview.gif)](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) GitLens 只是幫助您更好地理解程式碼。快速了解一行或程式碼區塊被更改的人、原因和時間。此外,它還可以讓您輕鬆探索程式碼庫的歷史和演變。 GitLens 增強了 Visual Studio Code 中內建的 Git 功能。它還可以幫助您透過 Git 責任註釋和程式碼鏡頭一目了然地視覺化程式碼作者身份,無縫導航和探索 Git 儲存庫,透過強大的比較命令獲得有價值的見解等等。 下載次數:5,972,117 ##### 2. [PRETTIER – 程式碼格式化程式](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) [![Prettier 程式碼格式化程式](https://themeselection.com/wp-content/uploads/2020/08/prettier.png)](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 它是一個固執己見的程式碼格式化程序,透過解析程式碼並使用自己的規則重新列印程式碼(考慮最大行長度)來強制執行一致的樣式,並在必要時包裝程式碼。此外,它支援多種語言。它可以與大多數編輯器整合。 下載次數:7,676,738 ##### 3. [ESLINT](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) [![Eslint](https://themeselection.com/wp-content/uploads/2020/08/12-ESLint-1024x640-2.png)](https://themeselection.com/wp-content/uploads/2020/08/12-ESLint-1024x640-2.png) ESLint 靜態分析您的程式碼以快速發現問題。 ESLint 靜態分析您的程式碼以快速發現問題。它內建於大多數文字編輯器中,您可以將 ESLint 作為持續整合管道的一部分執行。 ESLint 修復是語法感知的,因此您不會遇到傳統尋找和取代演算法引入的錯誤。 下載次數:10,236,293 ##### 4. [QUOKKA.JS](https://marketplace.visualstudio.com/items?itemName=WallabyJs.quokka-vscode) [![Quokkajs](https://quokkajs.com/assets/img/main-video.gif)](https://marketplace.visualstudio.com/items?itemName=WallabyJs.quokka-vscode) Quokka.js 是一款用於快速 JavaScript / TypeScript 原型設計的開發人員生產力工具。當您鍵入時,執行時間值會更新並顯示在 IDE 中程式碼旁邊。它使**原型設計、學習和測試** JavaScript / TypeScript **速度極快**。預設不需要配置,只需開啟一個新的 Quokka 檔案並開始試驗 下載次數:754,978 ##### 5. [路徑智慧](https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense) [![路徑智能](https://i.giphy.com/iaHeUiDeTUZuo.gif)](https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense) 它為檔案名稱加入了智慧感知風格的補全,讓您可以輕鬆輸入長路徑名。如果語句是導入語句,則預設刪除檔案副檔名 下載次數:3,318,156 ##### 6. [路徑自動完成](https://marketplace.visualstudio.com/items?itemName=ionutvmi.path-autocomplete) [![路徑自動完成](https://raw.githubusercontent.com/ionutvmi/path-autocomplete/master/demo/path-autocomplete.gif)](https://marketplace.visualstudio.com/items?itemName=ionutvmi.path-autocomplete) 此擴充功能為 VS Code 提供路徑補全,因此您不必記住那些長路徑。 下載次數:558,868 ##### 7. [VISUAL STUDIO INTELLICODE](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode) [![Visual Studio Intellicode](https://go.microsoft.com/fwlink/?linkid=2006041)](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode) 它旨在幫助開發人員和程式設計師提供智慧程式碼完成建議。此外,它還預設支援 Python、TypeScript/JavaScript、React 和 Java。 IntelliCode 將您最有可能使用的內容放在完成清單的頂部,從而節省您的時間。 IntelliCode 建議基於 GitHub 上的數千個開源專案,每個專案都有超過 100 顆星。當與程式碼的上下文結合時,完成清單將被自訂以促進常見實踐。 下載次數:6,401,943 ##### 8. [導入成本](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) [![導入成本 VS Code](https://themeselection.com/wp-content/uploads/2020/08/Import-Cost-vscode.jpg)](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) 此擴充功能將在編輯器中內聯顯示導入包的大小。此擴充功能使用 webpack 和 babili-webpack-plugin 來偵測導入的大小。 下載次數:710,298 ##### 9. [檔案大小](https://marketplace.visualstudio.com/items?itemName=mkxml.vscode-filesize) [![檔案大小](https://themeselection.com/wp-content/uploads/2020/08/02.jpg)](https://marketplace.visualstudio.com/items?itemName=mkxml.vscode-filesize ) 它在編輯器的狀態列中顯示焦點檔案的大小。 下載次數:198,807 **查看最佳的 [Asp.NET Core 管理範本](https://themeselection.com/item/category/asp-net-dashboard/):** [![Sneat Asp.NET Core 管理範本](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sc9q4e8s0uk0084xw6mr.png)](https://themeselection.com/item/sneat-aspnet-core-admin-模板/) ##### 10. [即時伺服器](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) [![即時伺服器](https://github.com/ritwickdey/vscode-live-server/raw/master/images/Screenshot/vscode-live-server-animated-demo.gif)](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) 點擊即可啟動開發本機伺服器,並透過一些額外功能觀看即時更改 下載次數:6,541,468 ##### 11. [專案經理](https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager) 它可以幫助您輕鬆存取您的專案,無論它們位於何處。不要再錯過那些重要的專案了。 下載次數:1,090,254 ##### 12. [程式碼拼字檢查器](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) [![程式碼拼字檢查器](https://raw.githubusercontent.com/streetsidesoftware/vscode-spell-checker/master/packages/client/images/example.gif)](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) 適用於多種程式語言的簡單原始碼拼字檢查器。 下載次數:1,596,862 ##### 13. [支架對著色器](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer) [![括號對著色器](https://themeselection.com/wp-content/uploads/2020/08/06.jpg)](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer) 此擴充允許用顏色來辨識匹配的括號。使用者可以定義要匹配哪些標記以及要使用哪些顏色。 下載次數:1,154,226 ##### 14. [遠端 — SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) 遠端 – SSH 擴充功能可讓您使用任何具有 SSH 伺服器的遠端電腦作為開發環境。 下載次數:1,605,734 ##### 15. [REST 用戶端](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) [![休息客戶端](https://raw.githubusercontent.com/Huachao/vscode-restclient/master/images/usage.gif)](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) REST 用戶端可讓您傳送 HTTP 請求並直接在 Visual Studio Code 中查看回應。 下載次數:1,025,700 ##### 16. [JAVASCRIPT (ES6) 程式碼片段](https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets) [![Javascript 程式碼片段](https://themeselection.com/wp-content/uploads/2020/08/09.jpg)](https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets) 此擴充包含 Vs Code 編輯器的 ES6 語法中的 JavaScript 程式碼片段(支援 JavaScript 和 TypeScript)。 下載次數:3,789,793 ##### 17. [程式碼執行器](https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner) [![程式碼執行器](https://github.com/formulahendry/vscode-code-runner/raw/master/images/usage.gif)](https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner) Code Runner 是多種語言的執行程式碼片段或程式碼檔案。透過檔案總管的上下文功能表執行目前活動文字編輯器的程式碼檔案非常有用。此外,您還可以在文字編輯器中執行選定的程式碼片段。它透過在整合終端中執行程式碼來支援 REPL 下載次數:4,549,546 --- 建議使用[Next js Dashboard Template](https://themeselection.com/item/category/next-js-admin-template/),因為它附帶預製元件,您可以直接使用,無需任何額外的工作。 例如,您必須查看 [**Sneat MUI React Next js 管理範本**](https://themeselection.com/item/sneat-mui-react-nextjs-admin-template/)。 [![Sneat MUI React Nextjs 管理範本](https://miro.medium.com/max/630/0*wdCaJM9lBKBLTWMa.png)](https://themeselection.com/item/sneat-mui-react-nextjs-管理範本/) [**React 管理儀表板**](https://themeselection.com/item/category/react-admin-templates/) 具有 6 種獨特的佈局:預設、邊框、半暗和暗😎 --- ##### 18. [DOCKER](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) [![Docker](https://themeselection.com/wp-content/uploads/2020/08/docker.png)](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-泊塢窗戶) Docker 擴充功能可讓您輕鬆地從 Visual Studio Code 建置、管理和部署容器化應用程式。它還提供容器內 Node.js、Python 和 .NET Core 的一鍵偵錯。此擴充功能可辨識使用最受歡迎的開發語言(C#、Node.js、Python、Ruby、Go 和 Java)的工作區,並相應地自訂生成的 Docker 檔案。 Docker 擴充功能為 VS Code 提供了 Docker 視圖。 Docker 檢視可讓您檢查和管理 Docker 資產:容器、映像、磁碟區、網路和容器登錄檔 下載次數:5,136,014 ##### 19. [更好的評論](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments) [![更好的評論](https://themeselection.com/wp-content/uploads/2020/08/better-comments.png)](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments) Better Comments 擴充功能將幫助您在程式碼中建立更人性化的註解。您將能夠將註釋分類為警報、查詢、待辦事項、突出顯示等。此外,還可以對註釋掉的程式碼進行樣式設置,以明確該程式碼不應該在那裡,並且您想要的任何其他註釋樣式都可以在設定中指定。 下載次數:960,927 ##### 20. [Chrome 偵錯器](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) [![Chrome 偵錯器](https://themeselection.com/wp-content/uploads/2020/08/debugger-for-chrome.png)](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) 偵錯器是 VS Code 擴展,用於在 Google Chrome 瀏覽器或支援 Chrome DevTools 協議的其他目標中偵錯 JavaScript 程式碼。它有助於除錯 eval 腳本、腳本標籤、動態加入的腳本以及設定斷點,包括在啟用來源映射時在來源檔案中設定斷點。 下載次數:1,617,311 ##### 21. [MARKDOWN 多合一](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) [![chrome 偵錯器](https://github.com/yzhang-gh/vscode-markdown/raw/master/images/gifs/section-numbers.gif)](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) Markdown 所需的一切(鍵盤快速鍵、目錄、自動預覽等)。它支援以下 Markdown 語法: - [CommonMark](https://spec.commonmark.org/) - [表格](https://help.github.com/articles/organizing-information-with-tables/)、[刪除線](https://help.github.com/articles/basic-writing-and-formatting-syntax/#styling-text)和[任務清單](https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax#task-lists)(來自GitHub 風格的 Markdown) - [數學支援](https://github.com/waylonflinn/markdown-it-katex#syntax)(來自 KaTeX) - [前題](https://github.com/ParkSB/markdown-it-front-matter#valid-front-matter) 下載次數:5,136,014 ##### 22. [搜尋節點模組](https://marketplace.visualstudio.com/items?itemName=jasonnutter.search-node-modules) [![搜尋節點模組](https://raw.githubusercontent.com/jasonnutter/vscode-search-node-modules/master/img/demo.gif)](https://marketplace.visualstudio.com/items?itemName=jasonnutter.search-node-modules) 搜尋節點模組是 VS Code 的簡單插件,可讓您快速導航專案的 node_modules 目錄中的檔案。 下載次數:571,040 ##### 23. [設定同步](https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync) 透過設定同步,您可以使用簡單的 Gist 跨電腦同步設定、片段、主題、檔案圖示、鍵綁定、工作區和擴充。它支援 GitHub Enterprise、帶有 @sync 關鍵字的編譯指示:host、os 和 env。一鍵上傳和下載很簡單。它允許您在電腦之間同步任何檔案。 下載次數:1,870,161 ##### 24. [NPM](https://marketplace.visualstudio.com/items?itemName=eg2.vscode-npm-script) 此擴充功能支援執行 package.json 檔案中定義的 npm 腳本,並根據 package.json 中定義的依賴項驗證已安裝的模組。這些腳本可以在整合終端或輸出視窗中執行。 下載次數:2,748,322 #### 結論: 嗯,到 2023 年,Visual Studio Code 的月活躍用戶數為 490 萬。毫無疑問,它是目前最好的程式碼編輯器。最好的功能之一是 [**Market Place**](https://marketplace.visualstudio.com/vscode) 提供大量擴展,可以根據您的需求準確定制它,並幫助您編寫高品質的程式碼。 在本文中,我們將向使用 CSS、HTML、JavaScript 以及 Angular、ReactJS 和 VueJS 等框架的前端工程師推薦這些 VS Code 擴充功能。 我們 [ThemeSelection](https://themeselection.com/) 使用其中一些擴充功能來建立現代且乾淨的 Bootstrap 管理範本。 [Sneat Bootstrap 5 HTML 管理範本](https://themeselection.com/products/sneat-bootstrap-html-admin-template/) [Chameleon 免費 Bootstrap 管理範本](https://themeselection.com/products/chameleon-admin-free-bootstrap-dashboard-template/) 您也可以檢查使用這些擴充功能製作的一些[引導管理範本](https://themeselection.com/products/category/bootstrap-admin-templates/)。 我們想說這個集合不是完整的,擴充不一定是最好的,但我們希望它可以幫助您選擇最好的工具來幫助您編寫高品質的程式碼並成為最好的 Web 開發人員。 如果您認為此列表缺少擴展,請隨時提出建議並透過在評論部分中加入您最喜歡的擴充功能來擴展它。 --- 原文出處:https://dev.to/themeselection/vs-codes-every-developers-should-use-in-2020-2fa3

最漂亮的 50 個 VS Code 主題

原文出處:https://dev.to/softwaredotcom/50-vs-code-themes-for-2020-45cc 如果您正在尋找新的主題來在新的一年裡改變您的程式碼編輯器,我隨時為您提供協助!看看具有獨特調色板的各種時尚主題 - 從時尚到時髦到充滿活力以及介於兩者之間的一切 - 看看什麼最適合您。我甚至加入了一些有趣的圖標包來進一步自訂 VS Code。 我將這些 VS Code 主題分為以下部分: * [熱門](#trending) (1-20) * [深色](#dark) (21-30) * [光](#光) (31-40) * [多彩](#colorful) (41-50) * [獎勵:圖示](#icons) (51-56) 要在 VS Code 中安裝主題,只需存取市場並選擇您想要下載的主題。若要在已安裝的主題之間切換,請使用「CMD/CTRL + SHIFT + P」開啟命令調色板,然後輸入「首選項:顏色主題」。然後您可以在菜單中瀏覽您的主題。 ## 熱門 發現越來越流行的 VS Code 新趨勢主題。 ### 1. [激進](https://marketplace.visualstudio.com/items?itemName=dhedgecock.radical-vscode) ![](https://raw.githubusercontent.com/DHedgecock/radical-vscode/master/assets/editor.jpg) ### 2.[礦箱材質](https://marketplace.visualstudio.com/items?itemName=sainnhe.mine.box-material) ![](https://user-images.githubusercontent.com/37491630/69468770-671d3400-0d85-11ea-85a7-cf7ffedab468.png) ### 3. [Merko](https://marketplace.visualstudio.com/items?itemName=merko.merko-green-theme) ![](https://github.com/merko30/merko-green-theme/raw/master/img/s.png) ### 4. [東京之夜](https://marketplace.visualstudio.com/items?itemName=enkia.tokyo-night) ![](https://raw.githubusercontent.com/enkia/tokyo-night-vscode-theme/master/static/ss_tokyo_night.png) ### 5. [補救措施](https://marketplace.visualstudio.com/items?itemName=robertrossmann.remedy) ![](https://raw.githubusercontent.com/robertrossmann/vscode-remedy/master/resources/screenshots/gui.png) ### 6. [最小](https://marketplace.visualstudio.com/items?itemName=dawranliou.minimal-theme-vscode) ![](https://github.com/dawran6/minimal-theme-vscode/raw/master/screenshot.png) ### 7. [Aurora X](https://marketplace.visualstudio.com/items?itemName=marqu3s.aurora-x) ![](https://github.com/marqu3s10/Aurora-X/raw/master/images/screenshot.png) ### 8. [大西洋之夜](https://marketplace.visualstudio.com/items?itemName=mrpbennett.atlantic-night) ![](https://github.com/mrpbennett/atlantic-night-vscode-theme/raw/master/imgs/first-screen.png) ### 9. [玻璃使用者介面](https://marketplace.visualstudio.com/items?itemName=aregghazaryan.glass-ui) ![](https://user-images.githubusercontent.com/38076644/62824174-54eb0180-bbab-11e9-975e-2baf0e8cf33f.png) ### 10. [淡淡的丁香](https://marketplace.visualstudio.com/items?itemName=alexnho.a-touch-of-lilac-theme) ![](https://raw.githubusercontent.com/alexnho/vscode-a-touch-of-lilac-theme/master/images/workbench.png) ### 11. [FireFly Pro](https://marketplace.visualstudio.com/items?itemName=ankitcode.firefly) ![](https://raw.githubusercontent.com/ankitmlive/firefly-theme/master/assets/second-demo.png) ### 12. [ReUI](https://marketplace.visualstudio.com/items?itemName=barrsan.reui) ![](https://raw.githubusercontent.com/barrsan/react-italic-theme-vscode/master/sc.png) ### 13. [史萊姆](https://marketplace.visualstudio.com/items?itemName=smlombardi.slime) ![](https://github.com/smlombardi/theme-slime/raw/master/screenshots/screenshot.png) ### 14. [Signed Dark Pro](https://marketplace.visualstudio.com/items?itemName=enenumxela.signed-dark-pro) ![](https://raw.githubusercontent.com/alex-munene/vscode-signed-dark-pro/master/images/signed-dark-pro.png) ### 15. [黑暗](https://marketplace.visualstudio.com/items?itemName=wart.ariake-dark) ![](https://github.com/a-wart/ariake-dark/blob/master/misc/screenshot_frag.png?raw=true) ### 16. [時髦燈](https://marketplace.visualstudio.com/items?itemName=loilo.snazzy-light) ![](https://raw.githubusercontent.com/loilo/vscode-snazzy-light/master/screenshots/javascript.png) ### 17. [Spacegray](https://marketplace.visualstudio.com/items?itemName=ionutvmi.spacegray-vscode) ![](https://raw.githubusercontent.com/ionutvmi/spacegray-vscode/master/screenshots/eighties.png) ### 18. [Celestial](https://marketplace.visualstudio.com/items?itemName=apvarun.celestial) ![](https://github.com/apvarun/celestial-theme/raw/master/Preview.png) ### 19. [藍莓黑](https://marketplace.visualstudio.com/items?itemName=peymanslh.blueberry-dark-theme) ![](https://raw.githubusercontent.com/peymanslh/vscode-blueberry-dark-theme/master/screenshot.png) ### 20. [熊](https://marketplace.visualstudio.com/items?itemName=dahong.theme-bear) ![](https://raw.githubusercontent.com/shaodahong/theme-bear/master/bear-theme-snap.png) ## 深色 您喜歡在黑暗中工作嗎?發現 VS Code 的一些最佳深色主題。 您也可以透過安裝我們的[最佳深色主題包](https://marketplace.visualstudio.com/items?itemName=thegeoffstevens.best-dark-themes-pack)來安裝所有這些深色主題。 ### 21. [一暗專業版](https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme) ![](https://raw.githubusercontent.com/Binaryify/OneDark-Pro/master/static/screenshot2.png) ### 22. [德古拉官方](https://marketplace.visualstudio.com/items?itemName=dracula-theme.theme-dracula) ![](https://github.com/dracula/visual-studio-code/raw/master/screenshot.png) ### 23. [Nord](https://marketplace.visualstudio.com/items?itemName=arcticicestudio.nord-visual-studio-code) ![](https://raw.githubusercontent.com/arcticicestudio/nord-docs/develop/assets/images/ports/visual-studio-code/ui-overview-jsx.png) ### 24. [Palenight](https://marketplace.visualstudio.com/items?itemName=whizkydee.material-palenight-theme) ![](https://i.imgur.com/1mYWEP4.png) ### 25. [One Monokai](https://marketplace.visualstudio.com/items?itemName=azemoh.one-monokai) ![](https://github.com/azemoh/vscode-one-monokai/raw/master/screenshot-v0.2.0.png) ### 26. [夜貓子](https://marketplace.visualstudio.com/items?itemName=sdras.night-owl) ![](https://github.com/sdras/night-owl-vscode-theme/raw/master/first-screen.jpg) ### 27. [仙女座](https://marketplace.visualstudio.com/items?itemName=EliverLara.andromeda) ![](https://github.com/EliverLara/Andromeda/raw/master/images/andromeda.png) ### 28. [Darcula](https://marketplace.visualstudio.com/items?itemName=rokoroku.vscode-theme-darcula) ![](https://github.com/rokoroku/vscode-theme-darcula/raw/master/screenshot.png) ### 29. [地平線主題](https://marketplace.visualstudio.com/items?itemName=jolaleye.horizon-theme-vscode) ![](https://i.imgur.com/y0gi1ez.png) ### 30. [Cobalt2](https://marketplace.visualstudio.com/items?itemName=wesbos.theme-cobalt2) ![](https://raw.githubusercontent.com/wesbos/cobalt2-vscode/cobalt2-updates/images/ss.png) ## 光 想要程式碼編輯器更輕一些嗎?看看這些時尚的淺色主題。 您也可以透過安裝我們的[最佳淺色主題包](https://marketplace.visualstudio.com/items?itemName=thegeoffstevens.best-light-themes-pack)來安裝所有這些淺色主題。 ### 31. [原子一燈](https://marketplace.visualstudio.com/items?itemName=akamud.vscode-theme-onelight) ![](https://raw.githubusercontent.com/akamud/vscode-theme-onelight/master/screenshots/preview.png) ### 32. [Bluloco Light](https://marketplace.visualstudio.com/items?itemName=uloco.theme-bluloco-light) ![](https://raw.githubusercontent.com/uloco/theme-bluloco-light/master/screenshots/js.png) ### 33. [Brackets Light Pro](https://marketplace.visualstudio.com/items?itemName=fehey.brackets-light-pro) ![](https://raw.githubusercontent.com/EryouHao/brackets-light-pro/master/static/screenshot.png) ### 34. [作家](https://marketplace.visualstudio.com/items?itemName=xaver.theme-writer) ![](https://github.com/xaverh/theme-yscritwr/raw/master/screenshot.png) ### 35. [NetBeans Light](https://marketplace.visualstudio.com/items?itemName=obrejla.netbeans-light-theme) ![](https://github.com/obrejla/vscode-netbeans-light-theme/raw/master/images/vscode-netbeans-light-theme.png) ### 36. [安靜的燈光](https://marketplace.visualstudio.com/items?itemName=onecrayon.theme-quietlight-vsc) ![](https://github.com/onecrayon/theme-quietlight-vsc/raw/master/images/screenshot.png) ### 37. [跳燈](https://marketplace.visualstudio.com/items?itemName=bubersson.theme-hop-light) ![](https://raw.githubusercontent.com/bubersson/hop-theme-vscode/master/hop-light.png") ### 38. [NotepadPlusPlus Remixed](https://marketplace.visualstudio.com/items?itemName=sh4dow.theme-notepadplusplusremixed) ![](https://raw.githubusercontent.com/s-h-a-d-o-w/NotepadPlusPlus-Remixed-Theme/master/screenshot.png) ### 39. [GitHub Light](https://marketplace.visualstudio.com/items?itemName=Hyzeta.vscode-theme-github-light) ![](https://github.com/Hyzeta/vscode-theme-github-light/raw/master/screenshot/0.png) ### 40. [GitHub Plus](https://marketplace.visualstudio.com/items?itemName=thenikso.github-plus-theme) ![](https://github.com/thenikso/github-plus-theme/raw/master/screenshot.jpg) ## 多彩 厭倦了單色主題和沈悶的調色板?使用這些豐富多彩的主題為您的編輯器加入一些顏色。 您也可以透過安裝我們的[最佳彩色主題包](https://marketplace.visualstudio.com/items?itemName=thegeoffstevens.best-colorful-themes-pack)來安裝所有這些彩色主題。 ### 41. [紫色陰影](https://marketplace.visualstudio.com/items?itemName=ahmadawais.shades-of-purple) ![](https://raw.githubusercontent.com/ahmadawais/shades-of-purple-vscode/master/images/markdown.png) ### 42. [SynthWave](https://marketplace.visualstudio.com/items?itemName=RobbOwen.synthwave-vscode) ![](https://github.com/robb0wen/synthwave-vscode/raw/master/theme.jpg) ### 43. [藍色程式碼](https://marketplace.visualstudio.com/items?itemName=Sujan.code-blue) ![](https://i.imgur.com/JLCnwvi.jpg) ### 44. [賽博龐克](https://marketplace.visualstudio.com/items?itemName=max-SS.cyberpunk) ![](https://github.com/max-SS/cyberpunk/raw/master/assets/preview.png) ### 45. [LaserWave](https://marketplace.visualstudio.com/items?itemName=jaredkent.laserwave) ![](https://github.com/Jaredk3nt/laserwave/raw/master/screenshot.png) ### 46. [Zeonica](https://marketplace.visualstudio.com/items?itemName=andrewvallette.zeonica) ![](https://zeonicacom.files.wordpress.com/2018/09/zeonica_9502.png) ### 47. [潮人](https://marketplace.visualstudio.com/items?itemName=ModoNoob.vscode-hipster-theme) ![](https://github.com/ModoNoob/vscode-hipster-theme/raw/master/screenshot.png) ### 48. [野莓](https://marketplace.visualstudio.com/items?itemName=joebayer1340.wildberry-theme) ![](https://github.com/geoffstevens8/best-colorful-themes-pack/blob/master/images/wildberry.png?raw=true) ### 49. [Qiita](https://marketplace.visualstudio.com/items?itemName=Increments.qiita) ![](https://qiita-image-store.s3.amazonaws.com/0/6598/e054a4bb-cea1-8fc9-e193-fbb8376ed93d.png) ### 50. [軟體時代](https://marketplace.visualstudio.com/items?itemName=soft-aesthetic.soft-era-theme) ![](https://github.com/soft-aesthetic/soft-era-vs-code/raw/master/screenshot.png) ## 下一步是什麼? 想找更多主題?試試[搜尋 VS Code 市場](https://marketplace.visualstudio.com/search?target=VSCode&category=Themes&sortBy=Installs) 並依「主題」排序。開發人員已經建立了超過 2,500 個主題,您可以從中選擇來自訂 VS Code。 ___ 我喜歡建立讓開發人員滿意的工具。如果您喜歡這篇文章,您還應該查看我的其他專案: * [Code Time](https://www.software.com/code-time):自動程式設計指標和時間追蹤的免費擴展,就在您的編輯器中 * [SRC](https://www.software.com/src):在十分鐘或更短的時間內提供開發者世界的策略摘要 - 每週一次,直接發送到您的收件匣

使用 Services 和 RxJS 在 Angular 中進行簡單的狀態管理

原文出處:https://dev.to/avatsaev/simple-state-management-in-angular-with-only-services-and-rxjs-41p8 軟體開發中最具挑戰性的事情之一是狀態管理。目前,Angular 應用程式有幾個狀態管理庫:NGRX、NGXS、Akita...它們都有不同的狀態管理風格,最受歡迎的是 [NGRX](https://ngrx.io/),它幾乎遵循React 世界的FLUX/Redux 原則(基本上使用單向資料流和不可變資料結構,但使用RxJS可觀察流)。 但是,如果您不想學習、設定、使用整個狀態管理庫,也不想處理一個簡單專案的所有樣板文件,如果您只想使用您已經熟悉的工具來**管理狀態,該怎麼辦?Anular 開發人員 * *,並且仍然獲得狀態管理庫提供的效能最佳化和一致性(推送變更偵測,一種不可變資料流)。 **免責聲明:這不是針對狀態管理庫的帖子。我們確實在工作中使用NGRX,它確實幫助我們在非常大和複雜的應用程式中管理非常複雜的狀態,但正如我常說的,NGRX 使簡單應用程式的事情變得複雜,並簡化了複雜應用程式的事情,請記住這一點。* * 在這篇文章中,我將向您展示一種僅使用 **RxJS** 和 **依賴注入** 來管理狀態的簡單方法,我們所有的元件樹都將使用 OnPush 更改檢測策略。 想像我們有一個簡單的 Todo 應用程式,我們想要管理它的狀態,我們已經設定了元件,現在我們需要一個服務來管理狀態,讓我們建立一個簡單的 Angular 服務: ``` // todos-store.service.ts @Injectable({provideIn: 'root'}) export class TodosStoreService { } ``` 所以我們需要的是,一種提供待辦事項清單的方法,一種加入、刪除、過濾和完成待辦事項的方法,我們將使用 getter/setter 和 RxJS 的行為主題來執行此操作: 首先我們建立在 todos 中讀寫的方法: ``` // todos-store.service.ts @Injectable({provideIn: 'root'}) export class TodosStoreService { // - We set the initial state in BehaviorSubject's constructor // - Nobody outside the Store should have access to the BehaviorSubject // because it has the write rights // - Writing to state should be handled by specialized Store methods (ex: addTodo, removeTodo, etc) // - Create one BehaviorSubject per store entity, for example if you have TodoGroups // create a new BehaviorSubject for it, as well as the observable$, and getters/setters private readonly _todos = new BehaviorSubject<Todo[]>([]); // Expose the observable$ part of the _todos subject (read only stream) readonly todos$ = this._todos.asObservable(); // the getter will return the last value emitted in _todos subject get todos(): Todo[] { return this._todos.getValue(); } // assigning a value to this.todos will push it onto the observable // and down to all of its subsribers (ex: this.todos = []) private set todos(val: Todo[]) { this._todos.next(val); } addTodo(title: string) { // we assaign a new copy of todos by adding a new todo to it // with automatically assigned ID ( don't do this at home, use uuid() ) this.todos = [ ...this.todos, {id: this.todos.length + 1, title, isCompleted: false} ]; } removeTodo(id: number) { this.todos = this.todos.filter(todo => todo.id !== id); } } ``` 現在讓我們建立一個方法來設定待辦事項的完成狀態: ``` // todos-store.service.ts setCompleted(id: number, isCompleted: boolean) { let todo = this.todos.find(todo => todo.id === id); if(todo) { // we need to make a new copy of todos array, and the todo as well // remember, our state must always remain immutable // otherwise, on push change detection won't work, and won't update its view const index = this.todos.indexOf(todo); this.todos[index] = { ...todo, isCompleted } this.todos = [...this.todos]; } } ``` 最後是一個可觀察的來源,它將只為我們提供已完成的待辦事項: ``` // todos-store.service.ts // we'll compose the todos$ observable with map operator to create a stream of only completed todos readonly completedTodos$ = this.todos$.pipe( map(todos => todos.filter(todo => todo.isCompleted)) ) ``` 現在,我們的待辦事項商店看起來像這樣: ``` // todos-store.service.ts @Injectable({providedIn: 'root'}) export class TodosStoreService { // - We set the initial state in BehaviorSubject's constructor // - Nobody outside the Store should have access to the BehaviorSubject // because it has the write rights // - Writing to state should be handled by specialized Store methods (ex: addTodo, removeTodo, etc) // - Create one BehaviorSubject per store entity, for example if you have TodoGroups // create a new BehaviorSubject for it, as well as the observable$, and getters/setters private readonly _todos = new BehaviorSubject<Todo[]>([]); // Expose the observable$ part of the _todos subject (read only stream) readonly todos$ = this._todos.asObservable(); // we'll compose the todos$ observable with map operator to create a stream of only completed todos readonly completedTodos$ = this.todos$.pipe( map(todos => todos.filter(todo => todo.isCompleted)) ) // the getter will return the last value emitted in _todos subject get todos(): Todo[] { return this._todos.getValue(); } // assigning a value to this.todos will push it onto the observable // and down to all of its subsribers (ex: this.todos = []) private set todos(val: Todo[]) { this._todos.next(val); } addTodo(title: string) { // we assaign a new copy of todos by adding a new todo to it // with automatically assigned ID ( don't do this at home, use uuid() ) this.todos = [ ...this.todos, {id: this.todos.length + 1, title, isCompleted: false} ]; } removeTodo(id: number) { this.todos = this.todos.filter(todo => todo.id !== id); } setCompleted(id: number, isCompleted: boolean) { let todo = this.todos.find(todo => todo.id === id); if(todo) { // we need to make a new copy of todos array, and the todo as well // remember, our state must always remain immutable // otherwise, on push change detection won't work, and won't update its view const index = this.todos.indexOf(todo); this.todos[index] = { ...todo, isCompleted } this.todos = [...this.todos]; } } } ``` 現在我們的智慧元件可以存取商店並輕鬆操作它: *(PS:我建議使用[ImmutableJS](https://immutable-js.github.io/immutable-js)而不是手動管理不變性)* ``` // app.component.ts export class AppComponent { constructor(public todosStore: TodosStoreService) {} } ``` ``` <!-- app.component.html --> <div class="all-todos"> <p>All todos</p> <app-todo *ngFor="let todo of todosStore.todos$ | async" [todo]="todo" (complete)="todosStore.setCompleted(todo.id, $event)" (remove)="todosStore.removeTodo($event)" ></app-todo> </div> ``` 這是完整的最終結果: [StackBlitz 上具有真實 REST API 的完整範例](https://stackblitz.com/edit/angular-rxjs-store?file=src%2Fapp%2Ftodos-store.service.ts) 這也是一種可擴展的狀態管理方式,您可以使用Angular 強大的DI 系統輕鬆地將其他儲存服務注入彼此,將其可觀察量與管道運算符結合起來以建立更複雜的可觀察量,並注入HttpClient 等服務以從伺服器提取資料例如。無需所有 NGRX 樣板或安裝其他狀態管理庫。盡可能保持簡單和輕鬆。 --- 在 Twitter 上關注我,以了解更多有趣的 Angular 相關內容:https://twitter.com/avatsaev

如何從頭開始為基本網站設定 Webpack 4 或 5

原文出處:https://dev.to/antonmelnyk/how-to-configure-webpack-from-scratch-for-a-basic-website-46a5 #簡介 你好,讀者! 如您所知,設定 [Webpack](https://webpack.js.org/) 可能是一項令人沮喪的任務。儘管有很好的文件,但由於一些原因,這個捆綁器並不是一匹舒服的馬。 Webpack 團隊正在非常努力且相對快速地開發它,這是一件好事。然而,對於新開發人員來說,一次性學習所有內容是難以承受的。教程已經過時,一些插件損壞,發現的範例可能會令人困惑。有時,您可能會陷入一些瑣碎的事情,並透過 Google 進行大量搜尋,以在 GitHub issues 中找到一些最終有幫助的簡訊。 缺乏關於 Webpack 及其工作原理的介紹性文章,人們直接奔向 *create-react-app* 或 *vue-cli* 等工具,但有時需要編寫一些簡單的純 JavaScript 和 SASS,無需框架或任何奇特的東西。 本指南將逐步介紹 ES6、SASS 和圖片/字體的 Webpack 配置,無需任何框架。對於大多數簡單的網站開始使用 Webpack 或將其用作進一步學習的平台應該足夠了。儘管本指南需要一些有關 Web 開發和 JavaScript 的先驗知識,但它可能對某些人有用。至少當我開始使用 Webpack 時,我會很高興遇到這樣的事情! #我們的目標 我們將使用 Webpack 將 JavaScript、樣式、圖像和字體檔案捆綁到一個 *dist* 資料夾中。 ![](https://thepracticaldev.s3.amazonaws.com/i/9n9g3yr1v6lhjfg8roqc.png) Webpack 將產生 1 個捆綁的 JavaScript 檔案和 1 個捆綁的 CSS 檔案。您可以像這樣簡單地將它們加入到 HTML 文件中(當然,如果需要,您應該更改 dist 資料夾的路徑): ``` <link rel="stylesheet" href="dist/bundle.css"> <script src="dist/bundle.js"></script> ``` 你就可以走了:tropical_drink: 您可以查看本指南中完成的範例::link:[link](https://github.com/heyanton/simple_webpack_boilerplate)。 注意:我最近更新了依賴項。本指南適用於最新的 Webpack 5,但設定仍適用於 Webpack 4,以防您需要! #開始 ####1。安裝Webpack 我們使用 [npm](https://www.npmjs.com/): `$ npm init` 命令在專案資料夾中建立一個 *package.json* 文件,我們將在其中放置 JavaScript 依賴項。然後我們可以使用 $ npm i --save-dev webpack webpack-cli 來安裝 Webpack 本身。 ####2。建立入口點文件 Webpack 從單一 JavaScript 檔案開始運作,該檔案稱為入口點。在 *javascript* 資料夾中建立 *index.js*。您可以在這裡編寫一些簡單的程式碼,例如 `console.log('Hi')` 以確保其正常運作。 ![](https://thepracticaldev.s3.amazonaws.com/i/tlt38mn2u385i06y4vzn.png) ####3。建立 webpack.config.js ....在專案資料夾中。這裡是所有:sparkles:魔法發生的地方。 ``` // Webpack uses this to work with directories const path = require('path'); // This is the main configuration object. // Here, you write different options and tell Webpack what to do module.exports = { // Path to your entry point. From this file Webpack will begin its work entry: './src/javascript/index.js', // Path and filename of your result bundle. // Webpack will bundle all JavaScript into this file output: { path: path.resolve(__dirname, 'dist'), publicPath: '', filename: 'bundle.js' }, // Default mode for Webpack is production. // Depending on mode Webpack will apply different things // on the final bundle. For now, we don't need production's JavaScript // minifying and other things, so let's set mode to development mode: 'development' }; ``` ####4。在 *package.json* 中新增 npm 腳本來執行 Webpack 要執行 Webpack,我們必須使用 npm 腳本和簡單的命令「webpack」以及我們的設定檔作為 *config* 選項。我們的 *package.json* 現在應該如下所示: ``` { "scripts": { "build": "webpack --config webpack.config.js" }, "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.2.3" } } ``` ####5。執行Webpack 透過基本設置,您可以執行“$ npm run build”命令。 Webpack 將尋找我們的入口文件,解析其中的所有[*import*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 模組相依性並將其捆綁到*dist* 資料夾中的單一 *.js* 檔案。在控制台中,您應該看到如下內容: ![](https://thepracticaldev.s3.amazonaws.com/i/8j9rn72esbfbvn27duz6.png) 如果您將 `<script src="dist/bundle.js"></script>` 加入到 HTML 檔案中,您應該在瀏覽器控制台中看到「Hi」! #:顯微鏡:裝載機 偉大的!我們捆綁了標準 JavaScript。但是,如果我們想使用 ES6(及更高版本)的所有酷炫功能並保持瀏覽器相容性怎麼辦?我們應該如何告訴 Webpack 將我們的 ES6 程式碼轉換(*transpile*)相容於瀏覽器的程式碼? 這就是 Webpack **loaders** 發揮作用的地方。 Loader 是 Webpack 的主要功能之一。他們對我們的程式碼進行某些轉換。 讓我們在 *webpack.config.js* 檔案中新增選項 *module.rules*。在此選項中,我們將介紹 Webpack 如何準確地轉換不同類型的檔案。 ``` entry: /* ... */, output: /* ... */, module: { rules: [ ] } ``` 對於 JavaScript 文件,我們將使用: ###1。 [babel-loader](https://github.com/babel/babel-loader) [Babel](https://babeljs.io/) 是目前最好的 JavaScript 轉譯器。我們將告訴 Webpack 在捆綁之前使用它將現代 JavaScript 程式碼轉換為與瀏覽器相容的 JavaScript 程式碼。 Babel-loader 正是這樣做的。讓我們安裝它: `$ npm i --save-dev babel-loader @babel/core @babel/preset-env` 現在我們要新增有關 JavaScript 檔案的規則: ``` rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] ``` * `test` 是我們要轉換的檔案副檔名的[正規表示式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)。在我們的例子中,它是 JavaScript 檔案。 * `exclude` 是一個正規表示式,它告訴 Webpack 在轉換模組時應該忽略哪個路徑。這意味著如果我們將來導入它們,我們將不會轉換從 npm 匯入的供應商庫。 * `use` 是主要規則的選項。這裡我們設定 loader,它將應用於與「test」正規表示式相對應的檔案(在本例中為 JavaScript 檔案) * “選項”可能因載入程式而異。在這種情況下,我們為 Babel 設定預設預設,以考慮應該轉換哪些 ES6 功能,哪些不應該轉換。它本身是一個單獨的主題,如果您感興趣,可以深入研究它,但目前保持這樣是安全的。 現在您可以將 ES6 程式碼安全地放置在 JavaScript 模組中! ###2。 [sass-loader](https://github.com/webpack-contrib/sass-loader) 是時候處理樣式了。通常,我們不想寫純 CSS。我們經常使用 [SASS](https://sass-lang.com/) 預處理器。我們將 SASS 轉換為 CSS,然後應用自動前綴和縮小。這是 CSS 的一種「預設」方法。讓我們告訴 Webpack 要這樣做。 假設我們在 *javascripts/index.js* 入口點導入主 SASS 檔案 *sass/styles.scss*。 ``` import '../sass/styles.scss'; ``` 但目前,Webpack 不知道如何處理 *.scss* 檔案或除 *.js* 之外的任何檔案。我們需要新增適當的載入器,以便 Webpack 可以解析這些檔案: `$ npm i --save-dev sass sass-loader postcss-loader css-loader` 我們可以為 SASS 檔案新增規則並告訴 Webpack 如何處理它們: ``` rules: [ { test: /\.js$/, /* ... */ }, { // Apply rule for .sass, .scss or .css files test: /\.(sa|sc|c)ss$/, // Set loaders to transform files. // Loaders are applying from right to left(!) // The first loader will be applied after others use: [ { // This loader resolves url() and @imports inside CSS loader: "css-loader", }, { // Then we apply postCSS fixes like autoprefixer and minifying loader: "postcss-loader" }, { // First we transform SASS to standard CSS loader: "sass-loader" options: { implementation: require("sass") } } ] } ] ``` 請注意此處有關 Webpack 的重要事項。可連結多個裝載機;它們將從“use”陣列中的最後一個到第一個逐個應用。 現在,當 Webpack 在程式碼中遇到“import 'file.scss';”時,它知道該怎麼做! ####PostCSS 我們應該如何告訴 *postcss-loader* 它必須應用哪些轉換?我們建立一個單獨的設定檔 `postcss.config.js` 並使用我們的樣式所需的 postcss 外掛程式。您可能會發現對最基本和有用的插件進行縮小和自動加入前綴,以確保 CSS 為您的真實網站做好準備。 首先,安裝這些 postcss 外掛:`$ npm i --save-dev autoprefixer cssnano`。 其次,將它們加入到 *postcss.config.js* 檔案中,如下所示: ``` module.exports = { plugins: [ require('autoprefixer'), require('cssnano'), // More postCSS modules here if needed ] } ``` 您可以更深入地研究 [PostCSS](https://github.com/postcss/postcss),找到更多適合您的工作流程或專案需求的插件。 在完成所有 CSS 設定之後,只剩下一件事了。 Webpack 將解析您的 *.scss* 導入,對其進行轉換,然後...下一步是什麼?它不會神奇地建立一個與您的樣式捆綁在一起的單一 *.css* 檔案;我們必須告訴 Webpack 要這樣做。但這個任務超出了裝載機的能力範圍。為此,我們必須使用 Webpack 的 **插件**。 #:electric_plug: 插件 他們的目的是做裝載機做不到的任何事。如果我們需要將所有轉換後的 CSS 提取到一個單獨的「捆綁」檔案中,我們必須使用插件。我們的案例有一個特殊的插件:*MiniCssExtractPlugin*: `$ npm i --save-dev mini-css-extract-plugin` 我們可以在 *webpack.config.js* 檔案的開頭單獨導入外掛: ``` const MiniCssExtractPlugin = require("mini-css-extract-plugin"); ``` 在我們設定載入器的“module.rules”陣列之後加入新的“plugins”程式碼,我們用選項啟動我們的插件: ``` module: { rules: [ /* ... */ ] }, plugins: [ new MiniCssExtractPlugin({ filename: "bundle.css" }) ] ``` 現在我們可以將此插件連結到我們的 CSS 載入器中: ``` { test: /\.(sa|sc|c)ss$/, use: [ { // After all CSS loaders, we use a plugin to do its work. // It gets all transformed CSS and extracts it into separate // single bundled file loader: MiniCssExtractPlugin.loader }, { loader: "css-loader", }, /* ... Other loaders ... */ ] } ``` 完畢!如果您按照步驟操作,則可以執行「$ npm run build」命令並在 *dist* 資料夾中找到 *bundle.css* 檔案。現在的一般設定應如下所示: ![](https://thepracticaldev.s3.amazonaws.com/i/ye1wwk5r7rvxcu9n0ylm.png) Webpack 有大量用於不同目的的插件。您可以根據需要在[官方文件](https://webpack.js.org/plugins/)中探索它們。 # 更多載入器:圖像和字體 此時,您應該了解 Webpack 工作原理的基礎知識。但我們還沒完成。大多數網站都需要一些資源:我們透過 CSS 設定的圖像和字體。由於 *css-loader*,Webpack 可以解析 `background-image: url(...)` 行,但它不知道如果將 URL 設為 *.png* 或 *jpg* 檔案該怎麼辦。 我們需要一個新的載入器來處理 CSS 內的檔案或能夠直接在 JavaScript 中匯入它們。這是: ### [檔案載入器](https://github.com/webpack-contrib/file-loader) 使用 `$ npm i --save-dev file-loader` 安裝它,並在我們的 *webpack.config.js* 新增規則: ``` rules: [ { test: /\.js$/, /* ... */ }, { test: /\.(sa|sc|c)ss$/, /* ... */ }, { // Now we apply rule for images test: /\.(png|jpe?g|gif|svg)$/, use: [ { // Using file-loader for these files loader: "file-loader", // In options we can set different things like format // and directory to save options: { outputPath: 'images' } } ] } ] ``` 現在,如果你在 CSS 中使用一些像這樣的圖像: ``` body { background-image: url('../images/cat.jpg'); } ``` Webpack 將成功解決它。您將在 *dist/images* 資料夾中找到帶有雜湊名稱的圖像。在 *bundle.css* 會發現類似這樣的內容: ``` body { background-image: url(images/e1d5874c81ec7d690e1de0cadb0d3b8b.jpg); } ``` 正如你所看到的,Webpack 非常聰明——它正確解析了你的 url 相對於 *dist* 資料夾的路徑! 您也可以為字體加入規則並以與圖像類似的方式解析它們;將 outputPath 變更為 *fonts* 資料夾以保持一致性: ``` rules: [ { test: /\.js$/, /* ... */ }, { test: /\.(sa|sc|c)ss$/, /* ... */ }, { test: /\.(png|jpe?g|gif|svg)$/, /* ... */ }, { // Apply rule for fonts files test: /\.(woff|woff2|ttf|otf|eot)$/, use: [ { // Using file-loader too loader: "file-loader", options: { outputPath: 'fonts' } } ] } ] ``` #總結 就是這樣!經典網站的簡單 Webpack 設定。我們介紹了 **入口點**、**載入器** 和 **插件** 的概念以及 Webpack 如何轉換和捆綁檔案。 當然,這是一個非常簡單的配置,旨在了解 Webpack 的一般概念。如果您需要的話,還有很多事情可以加入:來源映射、熱重載、設定 JavaScript 框架以及 Webpack 可以做的所有其他事情,但我覺得這些事情超出了本指南的範圍。 如果您遇到困難或想了解更多訊息,我鼓勵您查看 Webpack [官方文件](https://webpack.js.org/concepts)。快樂捆綁!

開發人員的十大 Discord 伺服器

原文出處:https://dev.to/htnguy/top-10-discord-servers-for-developers-559o --- ### [Discord](https://discordapp.com/company) 到底是什麼? Discord 是一款基於社群的聊天應用程式,可讓您直接與您所在領域的其他人聯繫。儘管 Discord 最初是為遊戲玩家建置的,但隨著它的發展,各行各業的人們開始使用它來增強連接能力。 這些社區之一顯然是開發者社群。我說 **communities** ,複數,因為有多個子社區。一個人可能專注於網頁開發,而另一個人可能專注於遊戲開發。 ### 這是我加入開發者的 10 個最值得加入的 Discord 伺服器 1. [程式設計師聚會](https://discord.me/coding) 2. [Devcord](https://discord.me/devcord) 3. [編碼巢穴](https://discordapp.com/invite/code) 4. [SpeakJS](https://discord.me/speakjs) 5. [程式碼支援](https://discord.me/codesupport) 6. [Nodeiflux](https://disboard.org/server/425824580918181889) 7. [懶惰開發者](https://discordsl.com/server/4837/lazy-developers) 8. [TensorFlow](https://disboard.org/server/395520812347686912) 9. [程式設計師幽默](https://discordapp.com/invite/ph) 10. [Python](https://discordapp.com/invite/python) ### 我錯過了一個嗎? 伺服器有很多,所以將範圍縮小到少數是一項相當困難的任務,所以如果我錯過了一個非常有信譽的伺服器,請原諒我😥並將其發佈在下面的評論中。 以下是像您這樣的貢獻者提出的其他建議: [Reactiflux](https://www.reactiflux.com/) [Vue 土地](https://vue.land/) [打字稿](https://discordapp.com/invite/typescript) [Laravel](https://discordapp.com/invite/mPZNm7A) [Qovery](https://discordapp.com/invite/Bed5FRa)