🔍 搜尋結果:part

🔍 搜尋結果:part

基本演算法介紹

算法是計算的命脈。它們是電腦解決問題、分析資料和做出決策所遵循的逐步指令。就像食譜一樣,它們將複雜的任務分解為易於管理的程式。理解這些基本演算法是掌握電腦科學和程式設計的基石。 > [什麼是演算法](https://github.com/m-mdy-m/algorithms-data-structures/tree/main/1.IntroductionToAlgorithmsAndProblemSolving) 1. 搜尋演算法: --------- ### 什麼是搜尋? 搜尋是在資料集合中定位特定元素或專案的基本過程。此資料集合可以採用各種形式,例如陣列、列表、樹或其他結構化表示。搜尋的主要目標是確定資料中是否存在所需元素,如果存在,則辨識其精確位置或檢索它。它在各種計算任務和現實應用中發揮重要作用,包括資訊檢索、資料分析、決策過程等。 ### 介紹搜尋演算法 所有搜尋演算法都使用搜尋關鍵字來繼續執行該過程。搜尋演算法預計會返回成功或失敗狀態,通常以布林 true/false 表示。可以使用不同的搜尋演算法,其效能和效率取決於資料及其使用方式。 線性搜尋演算法被認為是所有搜尋演算法中最基本的。最好的也許是二分搜尋。還有其他搜尋演算法,例如深度優先搜尋演算法、廣度優先演算法等。搜尋演算法中使用的符號是 O(n),其中 n 是完成的比較次數。它給出了演算法在給定條件下所需執行時間的漸近上限的概念。 搜尋演算法中的搜尋案例可以分為最佳情況、平均情況和最壞情況。在某些演算法中,所有三種情況可能漸近相同,而在其他一些演算法中可能存在很大差異。搜尋演算法的平均行為有助於確定演算法的有用性。 > 摘要: 搜尋演算法是用於在資料集合中定位特定資料的逐步過程。它被認為是計算中的基本過程。在電腦科學中,搜尋資料時,快速應用程式和較慢應用程式之間的差異通常在於使用正確的搜尋演算法。 ### 術語: - **目標元素:**這就是你要尋找的寶藏!這是您想要在集合中尋找的特定資料。想像搜尋電話簿 - 目標元素是特定人的電話號碼。 - **搜尋空間:**將其視為可能隱藏針的大海撈針。它代表您正在搜尋的整個資料集合。這可以是數字陣列、名稱列表或更複雜的資料結構。 - **複雜度:**這是指搜尋演算法所需的工作量。這就像衡量圖書館員需要做多少工作才能找到你的書。複雜性通常以時間(找到目標需要多長時間)和空間(演算法需要多少額外記憶體)來衡量。 - **確定性與非確定性:**搜尋演算法可以像遵循食譜(確定性)或進行有根據的猜測(非確定性)。確定性演算法始終遵循相同的清晰步驟,例如二分搜尋,它將搜尋空間一分為二。非確定性演算法(例如線性搜尋)可能需要在最壞的情況下檢查整個集合。 ### 實際應用 - **資訊檢索:**想像在網路上搜尋特定的食譜。像Google這樣的搜尋引擎利用複雜的搜尋演算法來篩選大量資料集,為網站和內容建立索引。當您輸入查詢時,這些演算法會辨識與您的搜尋字詞最相關的網頁,並在幾分之一秒內提供您要尋找的資訊。 - **資料庫系統:**資料庫儲存大量訊息,從客戶記錄到金融交易。搜尋演算法是高效資料檢索的支柱。當您在資料庫管理系統中提交查詢時,搜尋演算法會快速找到符合您條件的特定記錄,從而節省您的時間和精力。 - **電子商務:**線上購物因高效搜尋而蓬勃發展。電子商務平台使用搜尋演算法來幫助您找到完美的產品。它們允許您根據價格、品牌或顏色等各種標準進行過濾和搜尋。在幕後,搜尋演算法為這些過濾器提供支持,精確定位滿足您偏好的產品,使您的購物體驗順暢高效。 - **網路:**網路是一個由互連設備組成的複雜網路。搜尋演算法在有效路由資料包方面發揮著至關重要的作用。它們可協助確定訊息在網路上傳輸的最佳路徑,確保您的視訊通話或線上遊戲順利運作。 - **人工智慧(AI):**人工智慧正在為許多領域帶來革命性的變化。搜尋演算法是人工智慧應用的基本工具。它們使人工智慧系統能夠解決問題、做出決策,甚至玩國際象棋等遊戲。透過有效地搜尋大量資料並辨識模式,搜尋演算法有助於人工智慧背後的智慧。 - **模式辨識:**模式辨識允許電腦辨識和理解資料中的模式,例如圖像、語音或手寫。搜尋演算法對於模式辨識很有幫助。它們可協助電腦將新資料與現有模式進行匹配,從而實現照片中的臉部辨識或虛擬助理的語音辨識等應用。 ### 演算法類型: ![](https://www.gatevidyalay.com/wp-content/uploads/2018/07/Searching-Algorithms-Approaches-to-Searching.png) **1. `Linear Search` :** 想像一下,在一個雜亂無章的書架中尋找一本特定的書。線性搜尋模仿了這種方法。這是一種簡單的方法,可以**逐項**檢查集合中的每個專案,直到找到目標元素(您想要的書)。 **它的工作原理如下:** 1. 搜尋從集合中的第一項開始。 2. 此演算法將目標元素與目前專案進行比較。 ``` * If they match, the search is successful, and the algorithm returns the location (index) of the target element. ``` ``` * If they don't match, the algorithm moves on to the next item in the collection. ``` 3. 這種比較和移動的過程將持續下去,直到找到目標元素或耗盡整個集合。 **例子:** 讓我們在未排序清單中搜尋值「25」:\[10, 4, 12, 25, 18, 7\]。 - 搜尋從第一個元素 (10) 開始。由於 10 不是 25,我們繼續。 - 我們將目標 (25) 與下一個元素 (4) 進行比較。沒有匹配項,所以我們繼續。 - 重複此過程,直到到達索引 3 處的元素「25」。 **優勢:** - 易於理解和實施,使其成為初學者的好選擇。 - 適用於未排序的資料,在各種情況下提供靈活性。 **弱點:** - 對於大型資料集來說速度很慢。隨著集合大小的增加,搜尋時間也會成比例增加,導致海量資料集效率低。 > 怎麼運作的 : ![](https://media.geeksforgeeks.org/wp-content/cdn-uploads/Linear-Search.png) **2. `Binary Search` :** 二分搜尋在**排序資料**上蓬勃發展。想像一下在一本精心組織的字典中搜尋特定單字。在這種情況下,二分搜尋比線性搜尋快得多。它透過重複地將已排序的集合分成兩半來進行操作。策略如下: 1. 此演算法首先將目標元素與集合的中間元素進行比較。 2. 如果目標元素等於中間元素,則搜尋成功,演算法會傳回中間元素的索引。 3. 如果目標元素**小於**中間元素,則在剩餘集合的**左半部**(不包括中間元素)繼續搜尋。 4. 如果目標元素**大於**中間元素,則在剩餘集合的**右半部**繼續搜尋。 5. 在集合的縮小範圍的一半上重複步驟 1-4,直到找到目標元素或搜尋空間減少到單一元素(與目標不符)。 **例子:** 讓我們在排序清單中搜尋值「18」:\[4, 7, 10, 12, 18, 25\]。 - 中間的元素是 12。 - 現在,右半部的中間元素是18。目標元素位於索引 4 處。 **優勢:** - 對於大型排序資料集,比線性搜尋快得多。透過在每次比較時消除一半的剩餘元素,二分搜尋可以快速縮小搜尋空間。 **弱點:** - 要求預先對資料進行排序,如果資料尚未組織,則加入額外的步驟。 - 不適合未排序的資料。二分搜尋策略依賴資料的排序性質,以在每次迭代期間有效地消除一半的可能性。 > 怎麼運作的 : ![](https://assets.digitalocean.com/articles/alligator/js/linear-vs-binary-search/binary-search.png) --- 2. 排序演算法: --------- ### 什麼是排序? 排序是指根據元素上的比較運算子對給定陣列或元素列表進行重新排列。比較運算子用於決定對應資料結構中元素的新順序。排序意味著將所有元素按升序或降序重新排序。 ### 介紹 在計算機科學中,排序演算法是將列表中的元素依序排列的演算法。最常用的順序是數字順序和字典順序,以及升序或降序。高效排序對於優化需要輸入資料位於排序清單中的其他演算法(例如搜尋和合併演算法)的效率非常重要。排序對於規範化資料和產生人類可讀的輸出通常也很有用。 形式上,任何排序演算法的輸出必須滿足兩個條件: 輸出是單調順序的(根據所需的順序,每個元素不小於/大於前一個元素)。 1. 輸出是輸入的排列(重新排序,但保留所有原始元素)。 2. 為了獲得最佳效率,輸入資料應儲存在允許隨機存取的資料結構中,而不是僅允許順序存取的資料結構中。 > 摘要:排序演算法是一組指令,它將陣列或列表作為輸入並將專案排列成特定的順序。排序最常見的是數字或字母(或字典)順序,並且可以按升序(AZ,0-9)或降序(ZA,9-0)。 ### 術語: - **就地分類:**想像一下重新整理抽屜裡的衣服而不把所有東西都拿出來。就地排序的工作原理類似。這些演算法透過修改現有清單本身內元素的順序來對資料進行排序,從而需要最少的額外空間。例如選擇排序、冒泡排序和插入排序。 - **內部排序:**這是指完全在電腦主記憶體 (RAM) 內執行的排序演算法。整個資料集可以一次載入到記憶體中,使其適合中小型資料集。堆排序、冒泡排序和合併排序等內部排序演算法通常用於記憶體資料操作。 - **外部排序:**當處理超出主存容量的海量資料集時,外部排序就發揮作用。這些演算法將大型資料集分解為較小的區塊,在磁碟(輔助儲存)上對它們進行排序,然後按特定順序將排序後的區塊合併在一起。合併排序是外部排序演算法的一個流行範例。 - **穩定排序:**想像一下按標題對書籍清單進行排序,但您也希望保留具有相同標題的書籍最初列出的順序。穩定的排序演算法在排序過程中保持具有相等鍵(值)的元素的相對順序。歸併排序和插入排序是穩定排序演算法的範例。 - **不穩定排序:**並非所有排序演算法都優先考慮順序保留。不穩定的排序演算法僅專注於實現所需的排序順序(例如,升序或降序),並且可能會打亂具有相同鍵的元素的相對位置。快速排序和堆排序是不穩定排序演算法的例子。 ### 排序演算法的特徵: - **時間複雜度:**這是指排序演算法完成其工作所需的時間(以步驟或比較而言)。我們通常在三種情況下分析時間複雜度: ``` * **Worst-case:** This represents the maximum amount of time the algorithm could take for a particular data size, considering the worst possible input scenario. ``` ``` * **Average-case:** This reflects the average time the algorithm takes across various random inputs of the same size. ``` ``` * **Best-case:** This represents the minimum amount of time the algorithm could take for a specific data size, considering the most favorable input scenario (e.g., data already partially sorted). ``` - **空間複雜度:**這是指排序演算法在原始資料所佔用的空間之外執行其操作所需的額外記憶體量。就地排序演算法透過在現有記憶體分配中操作資料來使用最少的額外空間,而某些演算法在排序過程中可能需要額外的臨時儲存。 - **穩定性:**此屬性決定排序演算法是否保留排序輸出中具有相等值的元素的原始順序。穩定的排序演算法維護這些元素的相對位置,這在特定應用中可能很重要。例如,如果您按姓名對客戶記錄清單進行排序,並且兩個客戶具有相同的姓名,您可能需要穩定的排序來維護它們最初列出的順序(例如,按帳戶建立日期)。 - **就地排序:**如前所述,就地排序演算法非常節省內存,因為它們透過修改原始列表本身來對資料進行排序,從而需要最少的額外空間。當處理大型資料集或記憶體有限時,這可能是有利的。 - **適應性:**一些排序演算法可以適應它們正在排序的資料的特徵。自適應排序演算法可以利用資料中預先存在的順序來提高其效能。例如,如果資料已經部分排序,自適應演算法可能會調整其方法以利用該部分順序並實現更快的排序。 ### 排序演算法的應用: - **搜尋演算法:**想像在電話簿中搜尋特定聯絡人。排序演算法是二分搜尋等高效搜尋演算法的無聲夥伴。透過確保電話簿條目按字母順序組織(排序),二分搜尋可以比透過未排序清單的線性搜尋更快找到您的聯絡人。 - **資料管理:**資料是現代計算的命脈,但有效管理資料需要組織。排序演算法在資料管理中起著至關重要的作用。當您按名稱、日期或大小對文件清單進行排序時,您可以更輕鬆地找到所需的特定文件。此外,排序的資料有助於更快地檢索和分析,從而節省您的時間和精力。 - **資料庫最佳化:**資料庫儲存大量訊息,從客戶記錄到金融交易。排序演算法顯著提高了資料庫查詢的效能。當您在電子商務資料庫中搜尋特定產品時,資料庫可能會根據您的搜尋條件(例如價格)對產品清單進行排序,以快速提供最相關的結果。 - **機器學習:**機器學習演算法從資料中學習以進行預測或分類。然而,需要準備好資料才能進行有效的學習。排序演算法通常用於資料預處理步驟,以在將資料輸入機器學習模型之前組織和建構資料。這可以顯著提高學習過程的準確性和效率。 - **資料分析:**資料分析就是從資訊中提取見解。排序演算法在這個過程中起著至關重要的作用。透過按各種屬性(例如日期、位置、客戶人口統計)對資料進行排序,您可以辨識在未排序的資料集中可能不易明顯的模式、趨勢和異常值。這使得資料分析師能夠更深入地了解資料,為金融、行銷和科學研究等各個領域的更好決策提供資訊。 - **作業系統:**作業系統管理電腦的資源。排序演算法適用於各種作業系統任務。例如,排序演算法可用於確定 CPU 任務的優先權、有效管理記憶體分配或以目錄結構組織文件,從而確保電腦系統的平穩運作。 > 這些只是排序演算法如何滲透到我們日常生活中的幾個例子。 ### 簡單解釋: 想像一下,你有一個雜亂的書架,裡面擺滿了各種主題的書。您想要按類型來組織它們(對它們進行排序)。以下是應用於此場景的排序演算法背後的主要概念和邏輯: 1. **比較:**您一次拿兩本書,並根據它們的類型進行比較(就像比較清單中的兩個元素)。 2. **交換:**如果類型不符合所需的順序(例如,歷史書放在小說之前),則交換它們在書架上的位置(就像重新排列清單中的元素一樣)。 3. **重複:**您繼續一次比較和交換兩本書,直到所有書籍按類型按所需順序排列(清單中的所有元素都根據所選標準排序)。 ### 排序演算法:: **1. 冒泡排序** **概念:**冒泡排序是一種簡單的排序演算法,它迭代列表,反覆比較相鄰元素,如果順序錯誤則交換它們。想像氣泡上升到液體表面 - 具有較大值的元素每次通過都會「冒泡」到清單中。這個過程一直持續到不需要交換為止,這表示清單已排序。 **解釋:** 想像一下將一堆亂七八糟的玩具進行分類。冒泡排序的工作原理如下: 1. **進行多次遍歷:**您多次瀏覽玩具清單。 2. **比較鄰居:**在每一遍中,您將每個玩具與其鄰居進行比較。如果第一個玩具更大(或排序術語中的“更高”),則交換它們的位置。 3. **泡泡上升:**每次交換時,較大的玩具(如泡泡)往往會移向清單的末端。 4. **繼續直到沒有交換:**您在整個清單中重複這些比較和交換,直到完成一次完整的傳遞而無需交換。這表示列表已排序。 **時間複雜度:**不幸的是,在最壞情況和平均情況下,冒泡排序的時間複雜度都是 O(n^2)。這意味著排序時間隨著元素數量 (n) 的二次方增加。對於大型資料集,冒泡排序變得非常低效。 **2.選擇排序** **概念:**選擇排序也遍歷列表,但它不是直接比較相鄰元素,而是專注於尋找未排序部分中的最小(或降序最大)元素。然後,該元素與未排序部分中的第一個元素交換,有效地將其放置在正確的排序位置。重複此過程,在每次通過時逐漸將未排序部分減少一個位置。 **解釋:** 想像一下根據身高安排學生拍照。選擇排序的工作原理如下: 1. **尋找最矮的(或最高的):**在每次遍歷中,您搜尋該行的整個未排序部分以尋找最矮的學生(或按降序排列最高的學生)。 2. **與第一個學生交換:**一旦找到最矮的學生,就可以與未排序部分中的第一個學生交換他們的位置,有效地將他們放在行開頭的正確排序位置(最矮的在前面) 。 3. **重複並減少未排序部分:**繼續此過程,將交換的元素視為已排序部分的開頭,並在剩餘的未排序部分中搜尋最小元素。 **時間複雜度:**與冒泡排序類似,選擇排序在平均情況和最壞情況下的時間複雜度均為 O(n^2)。這意味著排序時間隨元素數量呈二次方增長,從而導致大型資料集效率低下。 **3.插入排序** **概念:**插入排序的工作原理是維護一個已排序的子列表,並迭代地將未排序部分中的元素插入到子列表中的正確位置。想像一下用塊建造一座塔,但您只能將它們一一加入,並且希望保持塔按高度排序。插入排序就像策略性地將每個新區塊放置在不斷增長的排序塔中的正確位置。 **解釋:** 想像一下按高度對書架上的書籍進行排序。插入排序的工作原理如下: 1. **從單一排序元素開始:**從一個空的排序子清單開始(就像書架上只有一本書)。 2. **從未排序的部分中取出一個元素:**您從未排序的堆中挑選一本書。 3. **移動與插入:**您可以將新書與已排序子清單中的每本書進行比較,從右端開始。如果新書較短(或按降序排列較高),則可以移動現有書籍以騰出空間,並將新書插入到正確的位置以保持排序順序。 4. **重複並增長已排序的子清單:**繼續此過程,從未排序的堆中獲取元素,將它們與已排序的子列表進行比較,並將它們插入到正確的已排序位置。這會逐漸增長排序的子列表,直到合併所有元素。 **時間複雜度:**插入排序適用於部分排序的資料。在一般情況下,對於已經排序的資料,它的時間複雜度為 O(n),因此非常有效率。然而,對於完全隨機的資料(最壞情況),它可以回落到 O(n^2),類似於冒泡排序和選擇排序。 **4. 歸併排序** **概念:**合併排序採用巧妙的「分而治之」策略來有效地對清單進行排序。它將問題分解為更小、更易於管理的子問題,然後按排序順序將解決方案重新組合在一起。 **解釋:** 想像一下,你有一支龐大的軍隊,需要按照身高來組織。歸併排序的工作原理如下: 1. **劃分:**將軍隊(列表)分成越來越小的組(子列表),直到每組只有一名士兵(元素)。這就像將一個大問題分解成更小、更容易解決的部分。 2. **征服:**由於現在每個子清單只有一名士兵(元素),因此它已被視為「已排序」。這是分而治之方法的基本情況。 3. **合併:**現在到了合併部分。您可以策略性地將已排序的子清單重新組合在一起,但以特定的方式。您比較每個子清單中的第一個士兵(元素),並將較短的士兵(較小的元素)放入最終的排序清單中。您不斷重複這種比較和放置,直到兩個子清單中的所有士兵(元素)都合併到最終的排序清單中。 4. **重複:**您繼續遞歸地應用這種分而治之的策略,直到整個原始軍隊(列表)從最短到最高(最小到最大)排序。 **時間複雜度:**合併排序的美妙之處在於它的時間複雜度。在平均情況和最壞情況下,其複雜度為 O(n log n)。這意味著對清單進行排序所需的時間隨著元素數量(n) 呈對數增長,這比冒泡排序、選擇排序或插入排序(其複雜度為O(n^2))要快得多。 **5. 快速排序** **概念:**快速排序是另一種分而治之的排序演算法,但採用不同的方法。它依賴一個被稱為“樞軸”的策略性選擇元素來對列表進行分區並解決排序問題。 **解釋:** 想像一下,你的書架上堆滿了雜亂的書。快速排序的工作原理如下: 1. **選擇樞軸:**您從書架中選擇一本書(樞軸)。可以透過不同的方式選擇該主元,但通常它是清單中的第一個或最後一個元素。 2. **分區:**依樞軸重新排列書架上的書。類型按字母順序排列在樞軸類型之前的書籍位於一側,類型按字母順序排列在樞軸類型之後的書籍位於另一側。樞軸本身尚未放置。這種劃分有效地將較大的排序問題劃分為兩個較小的子問題。 3. **遞歸地征服:**現在,您將兩個子清單(成堆的書)中的每一個都視為單獨的排序問題。您可以遞歸地將快速排序策略套用到這些子列表,為每個子列表選擇一個新的主元並相應地對它們進行分割。 4. **合併:**兩個子清單排序後,將原始樞軸元素放置在兩個子清單之間正確的排序位置。現在,整個書架(清單)按字母順序排序。 **時間複雜度:**平均而言,快速排序的時間複雜度為 O(n log n),這使得它對大型資料集非常有效率。然而,其性能可能會根據所選樞軸元件的不同而有所不同。選擇不當的主元(例如,總是排序或部分排序清單中的第一個或最後一個元素)可能會導致O(n^2) 的最壞情況,類似於冒泡排序、選擇排序和插入排序。 --- 3.樹的遍歷演算法: ---------- ### 什麼是樹遍歷? 樹遍歷是指探索樹資料結構的系統方法。這就像有一個路線圖可以精確地存取鄰裡(樹)中的每個房屋(節點)一次,確保您不會迷路或重新存取同一棟房屋。與可以直接按位置存取元素的簡單資料結構不同,樹需要特定的演算法來導航節點之間的連接。這些遍歷演算法定義了存取每個節點的順序,可讓您對它們包含的資料執行操作,例如搜尋特定值、新增節點或刪除現有節點。遍歷技術有多種,每種技術都有其優點和應用,這使得樹遍歷成為電腦科學中的基本概念。 ### 介紹 樹遍歷,也稱為樹搜尋,是在僅包含樹邊的圖上執行的演算法,該演算法僅存取每個節點一次。此類別中的演算法僅在存取每個節點的順序上有所不同。遍歷樹的兩種經典方法是廣度優先搜尋(bfs),即在進入下一層之前存取同一級別或距根部距離的節點;深度優先搜尋,其中分支中的所有節點或從根到葉的一組路徑在傳遞到下一個分支之前都會被存取。還有其他方法,它們使用啟發式或隨機採樣在樹中移動來加速該過程。 #### 概括: - **目的:**系統探索樹資料結構,確保每個節點僅被存取一次。 - **優點:**支援搜尋特定資料、插入新節點或刪除現有節點等操作。 - **主要區別:**演算法分為兩種主要方法: ``` * **Breadth-First Search (BFS):** Visits nodes level by level, starting from the root and progressing outward. ``` ``` * **Depth-First Search (DFS):** Explores one branch (path) as far as possible before backtracking and exploring another branch. Further variations of DFS exist for specific applications. ``` - **其他技術:**其他方法利用啟發式或隨機取樣來加快遍歷速度。 ### 術語: **1. 樹:**一種分層資料結構,模擬顛倒的樹,其節點(資料點)透過邊(連結)連接。節點可以有零個或多個子節點,形成分支,並最終導致底部的葉節點(沒有子節點的節點)。 **2. 節點:**樹的基本建構塊,包含資料和對其子節點的潛在引用。想像一下附近的一棟房子——它保存資訊(資料)並透過道路(邊緣)連接到其他房子(子節點)。 **3.根節點:**樹中最頂層的節點,作為遍歷演算法的起點。將其視為附近的主屋,從這裡開始探索。 **4.葉子節點:**沒有子節點的節點,代表樹中分支的「末端」。想像位於社區邊緣的房屋,沒有進一步的聯繫。 **5.邊:**樹中兩個節點之間的連接,描述它們之間的關係。想想連接附近房屋的道路。 **6. 遍歷:**僅存取樹中每個節點一次的系統過程。這就像探索整個社區,確保您參觀每棟房屋而不會錯過任何一棟或重新參觀同一棟房屋。 **7.廣度優先搜尋(BFS):**一種從根開始向外逐階存取節點的遍歷方法。想像一下,透過造訪第一條街道(層)上的所有房屋,然後再前往下一條街道(層)上的房屋來探索社區。 **8.深度優先搜尋(DFS):**一種遍歷方法,在回溯和探索另一個分支之前,先沿著一個分支(路徑)探索盡可能遠的地方。想像一下,沿著一條路(分支)探索附近區域,直到到達死胡同(葉節點),然後回溯並嘗試另一條路。 DFS 的常見變體包括前序、中序和後序,每種形式都有存取分支內節點的特定順序。 ### 樹遍歷演算法的特點: **1. 只存取每個節點一次:** - 樹遍歷的核心原則是保證樹中的每個節點都被恰好存取一次。這可以防止冗餘處理並確保對樹結構的完整探索。 **2. 探訪順序:** - 雖然每個節點僅被存取一次,但樹遍歷演算法的定義特徵在於它們存取節點的順序。不同的演算法會優先考慮以特定順序探索節點,從而產生不同的遍歷模式。 **3. 遞歸與迭代實作:** - 樹遍歷演算法可以遞歸或迭代地實現。遞歸方法涉及定義在子樹上呼叫自身的函數,模仿樹的層次結構性質。迭代方法利用迴圈和堆疊來管理遍歷過程。 **4.時間與空間複雜度:** - 與任何演算法一樣,樹遍歷方法具有相關的時間和空間複雜度。時間複雜度是指基於樹中節點數 (n) 執行演算法所需的時間量。常見的複雜度包括 O(n)(線性)和 O(n log n)(對數),BFS 和 DFS 變體根據實現的不同具有不同的複雜度。空間複雜度反映了演算法執行所需的額外記憶體量,通常取決於用於遍歷的資料結構(例如堆疊)。 **5.特定於應用的選擇:** - 樹遍歷演算法的選擇在很大程度上取決於手頭上的具體任務。例如,BFS 可能更適合尋找樹中兩個節點之間的最短路徑,而具有變體的 DFS 可用於搜尋特定資料或探索所有可能的路徑。 **6. 不可修改:** - 一般來說,樹遍歷演算法旨在探索現有的樹結構,而不修改樹本身。它們存取節點,對它們包含的資料執行操作,但通常不會更改樹中的連接或資料。 ### 樹遍歷演算法的應用: **1. 檔案系統導航:** - 作業系統使用樹遍歷演算法來導航電腦上的目錄結構。將您的檔案系統想像成一棵樹,其中資料夾作為節點,子資料夾和檔案作為子節點。廣度優先搜尋 (BFS) 可用於列出目錄及其子目錄中的所有文件,而深度優先搜尋 (DFS) 可用於在層次結構中定位特定文件。 **2. 網頁抓取:** - 像 Google 這樣的搜尋引擎利用 BFS 或 DFS 變體來抓取網路。他們從種子 URL(根節點)開始,有系統地探索連結的網頁(子節點)。 BFS 確保在進入更深層次之前探索特定層級(網站)的所有頁面,而 DFS 可能會在回溯和探索其他網站之前更深入地研究特定網站。 **3.人工智慧(AI):** - 人工智慧中的遊戲演算法通常使用樹遍歷來探索可能的動作及其結果。將西洋棋遊戲想像成一棵樹,當前棋盤狀態作為根節點,潛在的移動作為通往新棋盤狀態(子節點)的分支。具有修剪技術的深度優先搜尋可用於評估潛在的移動並確定最有希望的策略。 **4.社會網絡分析:** - 社群媒體平台利用樹遍歷來推薦聯繫或探索朋友網路。將您的個人資料想像為一個節點,將朋友作為子節點。遍歷演算法可用於建議基於共同朋友(樹中的共同祖先)的連接或探索網路以了解資訊流或影響力。 **5.電腦圖形學:** - 光線追蹤是一種用於在 3D 圖形中實現逼真光照效果的技術,通常採用樹遍歷演算法。虛擬場景可以表示為一棵樹,其中物件作為節點,它們的空間關係作為邊緣。遍歷有助於確定光線與哪些物件交互,從而建立逼真的陰影和反射。 **6. 網路路由:** - 電腦網路中的路由協定使用樹遍歷的變體來找到資料包到達目的地的最佳路徑。將網路想像成一棵樹,路由器作為節點,連接作為邊緣。遍歷演算法有助於確定資料在網路中不同點之間傳輸的最有效路徑。 ### 簡單解釋: 想像一下,您是一名送貨員,您有一堆包裹要在附近送貨。附近的房屋透過道路連接起來,形成樹狀結構。 - **房屋是節點:**每個房屋代表樹中的一個節點,包含地址(資料)等資訊以及可能由道路(邊)連接的相鄰房屋(子節點)的地址。 - **您的遞送路線就是遍歷:**樹遍歷演算法定義您存取每個房屋(節點)以遞送包裹(對資料執行操作)的順序。 **您可以透過兩種主要方式進行交付,這對應於兩種常見的樹遍歷方法:** **1. 廣度優先搜尋(BFS):像一個不斷擴大的圓圈一樣提供:** - 您從清單中的第一個房子(根節點)開始。 - 您將包裹運送到該房屋,然後**存取同一條街道(層)上與其直接相連的所有房屋(鄰居/子節點),**然後繼續前進。 - 一旦您運送到第一條街道(層)上的所有房屋,您就會移動到下一條街道(層)並重複該過程,在前往下一層之前存取該層上的所有房屋。 這就像一個不斷擴大的圓圈——您從中心(根部)開始,逐漸向外工作,確保在移動到下一個之前,您可以運送到街道(層)上的所有房屋。如果您想先優先考慮向附近區域的所有房屋送貨,這種方法很有用,也許是因為它們都在同一個街區,並且最大限度地減少行程時間很重要。 **2. 深度優先搜尋(DFS):深入研究一條街道:** - 您從清單中的第一棟房子(根節點)開始。 - 您將包裹運送到該房屋,然後**選擇一條從該房屋引出的相連道路(分支),並沿著它一直走到終點(葉節點)** ,在原路返回之前運送到該路徑(分支)上的所有房屋。 - 一旦到達該路的盡頭(分支),您就回溯到最後一個路口(父節點)並選擇另一條路(分支)進行探索,將物品運送到該新路徑上的所有房屋,直到到達另一個死胡同(葉節點) 。 這就像探索迷宮一樣——你選擇一條路徑(分支)並沿著它一路走下去,運送到沿途的房屋,直到你到達死胡同(葉節點)。然後你原路返回並嘗試另一條路徑(分支),直到你到達所有房屋。如果您正在快速查找特定地址並希望在前往另一條街道(分支)之前探索整條街道(分支),則此方法可能會很有用。 ### 演算法類型: **1.廣度優先搜尋(BFS):** - **概念:** BFS逐級存取節點,從根節點開始,逐層向外進行。想像一下探索一個家譜; BFS 將在向下移動到其子節點(下一層)之前存取所有兄弟節點(同一層級的節點)。 - **怎麼運作的:** ``` 1. Start at the root node and add it to a queue (a data structure that follows a "first-in, first-out" principle). ``` ``` 2. Remove the first node from the queue and visit it (process its data). ``` ``` 3. Add all the unvisited child nodes of the removed node to the back of the queue. ``` ``` 4. Repeat steps 2 and 3 until the queue is empty. ``` - **例子:** 考慮一棵簡單的樹: ``` A / \ B C / \ / \ D E F G ``` BFS 遍歷將依下列順序存取節點:A、B、C、D、E、F、G。 **2.深度優先搜尋(DFS):** - **概念:** DFS 沿著一個分支(路徑)探索盡可能遠的距離,然後回溯並探索另一個分支。 DFS 還有更多變體,但這裡我們將重點放在基本方法上。 - **怎麼運作的:** ``` 1. Start at the root node. ``` ``` 2. Visit the node (process its data). ``` ``` 3. If there are any unvisited child nodes, choose one and repeat steps 2 and 3, essentially following that branch (path) until you reach a leaf node (a node with no children). ``` ``` 4. Once you reach a leaf node, backtrack to the parent node and repeat step 3, exploring another unvisited child node (if any) of the parent. ``` ``` 5. Continue backtracking and exploring until all nodes have been visited. ``` - **例子:** 使用與之前相同的樹: ``` A / \ B C / \ / \ D E F G ``` DFS 遍歷可以以各種順序存取節點,具體取決於每一步選擇的子節點。可能的順序是:A、B、D、E、C、F、G。 **主要區別:** - BFS強調逐級存取節點,確保在深入之前先進行更廣泛的探索。 - DFS 優先考慮完全探索一個分支(路徑),然後再轉向另一個分支(路徑),可能會更快到達特定節點,但不能保證逐級存取。 結論 : ---- 總而言之,這個基本演算法之旅為您理解基本的搜尋、排序和樹遍歷技術奠定了堅實的基礎。這些解釋使用了清晰的語言和相關的類比,使這些抽象概念更加平易近人、直觀。無論您是經驗豐富的程式設計師還是剛開始涉足電腦科學,這種理解都為建立高效且有效的程式奠定了基石。 隨著您對知識的渴望與日俱增,請更深入研究!我的儲存庫充滿了各種演算法和資料結構,等待您的探索([演算法-資料-結構](https://github.com/m-mdy-m/algorithms-data-structures))。這是一個寶庫,您可以在這裡進行實驗、練習並鞏固您對這些基本建置模組的掌握。 **雖然某些部分仍在建設中,**反映了我自己正在進行的學習旅程(這個旅程可能需要 2-3 年才能完成!),但儲存庫正在不斷發展。 冒險不止於探索!我非常重視您的反饋。在文章中遇到障礙?有建設性的批評要分享嗎?還是只是想引發一場關於演算法的對話?我的門(或者更確切地說,我的收件匣)總是開著的。在 Twitter 上聯絡: [@m\_\_mdy\_\_m](https://twitter.com/m__mdy__m)或 Telegram:@m\_mdy\_m。此外,我的 GitHub 帳戶[m-mdy-m](https://github.com/m-mdy-m)歡迎討論和貢獻。讓我們共同建立一個充滿活力的學習社區,在這裡我們分享知識並突破我們的理解界限。 --- 原文出處:https://dev.to/m__mdy__m/basic-algorithms-5bep

為什麼站長的教材,看起來是教 Vue 2,不是教 Vue 3?

站長收到學員來信詢問,寫篇文章簡單回答如下 --- 在你翻閱「自學網頁の嬰兒教材:JavaScript」教學系列時,應該會發現七、八、九的內容,好像都在用舊版語法? 為什麼站長的教材,看起來是教 Vue 2,不是教 Vue 3? 學過時的東西,不是浪費時間嗎?為什麼不教大家最新的東西? 別擔心,這是站長故意設計的,簡單說明如下: ## 不是 Vue 2 跟 Vue 3 的差別,是 Options API 跟 Composition API 的差別 舊版的語法,現在稱之為 Options API 新版的語法,現在稱之為 Composition API 這兩種寫法都會在 Vue 3 被支援!不用擔心! 課程內容的範例程式碼,導入的版本也都是 Vue 3 ``` <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> ``` 你不會學到過氣的東西,別擔心! ## 但是 Options API 就是舊版寫法呀,早晚會被 Vue 核心團隊拋棄吧?那不就無法用了? 這不是事實!Options API 不會被拋棄,會繼續支援下去: https://stackoverflow.com/questions/68611657/ 官方問答,甚至跟你說小型專案,很適合繼續用 Options API: > Will Options API be deprecated?​ > No, we do not have any plan to do so. Options API is an integral part of Vue and the reason many developers love it. We also realize that many of the benefits of Composition API only manifest in larger-scale projects, and Options API remains a solid choice for many low-to-medium-complexity scenarios. ## 可是站長為什麼不教 Composition API 就好?為什麼要教 Options API? 主要原因有三個 第一,Options API 簡單很多!非常容易吸收! 而且跟站長前面設計的 JS 系列教材,銜接比較順利! 學完一~六的同學,讀 Options API 教材的內容時,一定會覺得很簡單 如果直接教 Composition API,我認為你會學得比較吃力 第二,實務工作上,我認為你很可能會需要接觸一些比較舊的專案,都還是用 Options API 寫的 所以身為 Vue 工程師,兩種 API 寫法你都要會才行 第三,寫過 Options API 之後,你再去學 Composition API 會非常容易,幾乎就是改語法、換順序而已 但你如果先學 Composition API,我認為很多細節,你會覺得很「黑箱」,有一種不安全感 ## 可是站長完全不教 Composition API,我要怎麼上班?工作上都用新語法呀? 的確,課程沒有提到 Composition API,但我認為你學過 Options API 之後,自己翻官網、改寫成 Composition API 會很容易 所以我的確完全不教 Composition API,就交給你自己自學啦! 不過,你如果真的完全不想學 Options API,那在 Vue 官網,把左上角的 API 語法設定成 Composition API 即可 本課程內容依然可以進行,你就通通用 Composition API 寫作業即可 或者,你就把作業通通寫兩次,第一次用 Options API,第二次用 Composition API 即可 以上,簡單說明,不用擔心學 Options API 浪費時間! 剛好相反!我認為先學 Options API 對於你的基本功會更有幫助!

如何讓 AI 融入您的使用者(Next.js、OpenAI、CopilotKit)

長話短說 ---- 在本文中,您將了解如何建立基於 AI 的行銷活動管理應用程式,該應用程式可讓您建立和分析廣告活動,從而使您能夠為您的業務做出正確的決策。 我們將介紹如何: - 使用 Next.js 建立 Web 應用程式, - 使用 CopilotKit 將 AI 助理整合到軟體應用程式中,以及 - 建立特定於操作的人工智慧副駕駛來處理應用程式中的各種任務。 - 建立一名競選經理 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xqpz356qm79t90f1l87.gif) --- CopilotKit:建構應用內人工智慧副駕駛的框架 -------------------------- CopilotKit是一個[開源的AI副駕駛平台](https://github.com/CopilotKit/CopilotKit)。我們可以輕鬆地將強大的人工智慧整合到您的 React 應用程式中。 建造: - ChatBot:上下文感知的應用內聊天機器人,可以在應用程式內執行操作 💬 - CopilotTextArea:人工智慧驅動的文字字段,具有上下文感知自動完成和插入功能📝 - 聯合代理:應用程式內人工智慧代理,可以與您的應用程式和使用者互動🤖 ![https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3 .amazonaws.com%2Fuploads%2Farticles%2Fx3us3vc140aun0dvrdof.gif](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3us3vc140aun0dvrdof.gif) {% cta https://git.new/devtoarticle1 %} Star CopilotKit ⭐️ {% endcta %} --- 先決條件 ---- 要完全理解本教程,您需要對 React 或 Next.js 有基本的了解。 我們還將利用以下內容: - [Radix UI](https://www.radix-ui.com/) - 用於為應用程式建立可存取的 UI 元件。 - [OpenAI API 金鑰](https://platform.openai.com/api-keys)- 使我們能夠使用 GPT 模型執行各種任務。 - [CopilotKit](https://github.com/CopilotKit/CopilotKit) - 一個開源副駕駛框架,用於建立自訂 AI 聊天機器人、應用程式內 AI 代理程式和文字區域。 --- 專案設定和套件安裝 --------- 首先,透過在終端機中執行以下程式碼片段來建立 Next.js 應用程式: ``` npx create-next-app campaign-manager ``` 選擇您首選的配置設定。在本教學中,我們將使用 TypeScript 和 Next.js App Router。 ![Next.js 應用程式安裝](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xboujt60i6lpoaqgjyap.png) 接下來,將[Heroicons](https://www.npmjs.com/package/@heroicons/react) 、 [Radix UI](https://www.radix-ui.com/)及其原始元件安裝到專案中。 ``` npm install @heroicons/react @radix-ui/react-avatar @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-icons @radix-ui/react-label @radix-ui/react-popover @radix-ui/react-select @radix-ui/react-slot @radix-ui/react-tabs ``` 另外,安裝[Recharts 程式庫](https://recharts.org/en-US)(一個用於建立互動式圖表的 React 程式庫)以及以下實用程式套件: ``` npm install recharts class-variance-authority clsx cmdk date-fns lodash react-day-picker tailwind-merge tailwindcss-animate ``` 最後,安裝[CopilotKit 軟體套件](https://docs.copilotkit.ai/getting-started/quickstart-chatbot)。這些套件使 AI copilot 能夠從 React 狀態檢索資料並在應用程式中做出決策。 ``` npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend ``` 恭喜!您現在已準備好建立應用程式。 --- 使用 Next.js 建立 Campaign Manager 應用程式 ----------------------------------- 在本節中,我將引導您建立活動管理器應用程式的使用者介面。 首先,讓我們進行一些初始設定。 在`src`資料夾中建立一個`components`和`lib`資料夾。 ``` cd src mkdir components lib ``` 在**`lib`**資料夾中,我們將聲明應用程式的靜態類型和預設活動。因此,在**`lib`**資料夾中建立**`data.ts`**和**`types.ts`**檔案。 ``` cd lib touch data.ts type.ts ``` 將下面的程式碼片段複製到`type.ts`檔中。它聲明了活動屬性及其資料類型。 ``` export interface Campaign { id: string; objective?: | "brand-awareness" | "lead-generation" | "sales-conversion" | "website-traffic" | "engagement"; title: string; keywords: string; url: string; headline: string; description: string; budget: number; bidStrategy?: "manual-cpc" | "cpa" | "cpm"; bidAmount?: number; segment?: string; } ``` 為應用程式建立預設的行銷活動清單並將其複製到`data.ts`檔案中。 ``` import { Campaign } from "./types"; export let DEFAULT_CAMPAIGNS: Campaign[] = [ { id: "1", title: "CopilotKit", url: "https://www.copilotkit.ai", headline: "Copilot Kit - The Open-Source Copilot Framework", description: "Build, deploy, and operate fully custom AI Copilots. In-app AI chatbots, AI agents, AI Textareas and more.", budget: 10000, keywords: "AI, chatbot, open-source, copilot, framework", }, { id: "2", title: "EcoHome Essentials", url: "https://www.ecohomeessentials.com", headline: "Sustainable Living Made Easy", description: "Discover our eco-friendly products that make sustainable living effortless. Shop now for green alternatives!", budget: 7500, keywords: "eco-friendly, sustainable, green products, home essentials", }, { id: "3", title: "TechGear Solutions", url: "https://www.techgearsolutions.com", headline: "Innovative Tech for the Modern World", description: "Find the latest gadgets and tech solutions. Upgrade your life with smart technology today!", budget: 12000, keywords: "tech, gadgets, innovative, modern, electronics", }, { id: "4", title: "Global Travels", url: "https://www.globaltravels.com", headline: "Travel the World with Confidence", description: "Experience bespoke travel packages tailored to your dreams. Luxury, adventure, relaxation—your journey starts here.", budget: 20000, keywords: "travel, luxury, adventure, tours, global", }, { id: "5", title: "FreshFit Meals", url: "https://www.freshfitmeals.com", headline: "Healthy Eating, Simplified", description: "Nutritious, delicious meals delivered to your door. Eating well has never been easier or tastier.", budget: 5000, keywords: "healthy, meals, nutrition, delivery, fit", }, ]; ``` 由於我們使用 Radix UI 建立可以使用 TailwindCSS 輕鬆自訂的基本 UI 元件,因此請在**`lib`**資料夾中建立一個**`utils.ts`**文件,並將以下程式碼片段複製到該文件中。 ``` //👉🏻 The lib folder now contains 3 files - data.ts, type.ts, util.ts //👇🏻 Copy the code below into the "lib/util.ts" file. import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export function randomId() { return Math.random().toString(36).substring(2, 15); } ``` 導航到`components`資料夾並在其中建立其他三個資料夾。 ``` cd components mkdir app dashboard ui ``` `components/app`資料夾將包含應用程式中使用的各種元件,而儀表板資料夾包含某些元素的 UI 元件。 `ui`資料夾包含使用 Radix UI 建立的多個 UI 元素。將[專案儲存庫中的這些元素](https://github.com/CopilotKit/campaign-manager-demo/tree/main/src/components/ui)複製到該資料夾中。 恭喜! `ui`資料夾應包含必要的 UI 元素。現在,我們可以使用它們來建立應用程式中所需的各種元件。 ### 建立應用程式 UI 元件 在這裡,我將引導您完成為應用程式建立使用者介面的過程。 ![應用程式使用者介面](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9l4lvv4394gg033oatwq.png) 首先,導航至**`app/page.tsx`**檔案並將以下程式碼片段貼到其中。該文件呈現在**`components/app`**資料夾中聲明的 App 元件。 ``` "use client"; import { App } from "@/components/app/App"; export default function DashboardPage() { return <App />; } ``` 在`components/app`資料夾中建立`App.tsx` 、 `CampaignForm.tsx` 、 `MainNav.tsx`和`UserNav.tsx`檔案。 ``` cd components/app touch App.tsx CampaignForm.tsx MainNav.tsx UserNav.tsx ``` 將下面的程式碼片段複製到`App.tsx`檔案中。 ``` "use client"; import { DEFAULT_CAMPAIGNS } from "@/lib/data"; import { Campaign } from "@/lib/types"; import { randomId } from "@/lib/utils"; import { Dashboard } from "../dashboard/Dashboard"; import { CampaignForm } from "./CampaignForm"; import { useState } from "react"; import _ from "lodash"; export function App() { //👇🏻 default segments const [segments, setSegments] = useState<string[]>([ "Millennials/Female/Urban", "Parents/30s/Suburbs", "Seniors/Female/Rural", "Professionals/40s/Midwest", "Gamers/Male", ]); const [campaigns, setCampaigns] = useState<Campaign[]>( _.cloneDeep(DEFAULT_CAMPAIGNS) ); //👇🏻 updates campaign list function saveCampaign(campaign: Campaign) { //👇🏻 newly created campaign if (campaign.id === "") { campaign.id = randomId(); setCampaigns([campaign, ...campaigns]); } else { //👇🏻 existing campaign - search for the campaign and updates the campaign list const index = campaigns.findIndex((c) => c.id === campaign.id); if (index === -1) { setCampaigns([...campaigns, campaign]); } else { campaigns[index] = campaign; setCampaigns([...campaigns]); } } } const [currentCampaign, setCurrentCampaign] = useState<Campaign | undefined>( undefined ); return ( <div className='relative'> <CampaignForm segments={segments} currentCampaign={currentCampaign} setCurrentCampaign={setCurrentCampaign} saveCampaign={(campaign) => { if (campaign) { saveCampaign(campaign); } setCurrentCampaign(undefined); }} /> <Dashboard campaigns={campaigns} setCurrentCampaign={setCurrentCampaign} segments={segments} setSegments={setSegments} /> </div> ); } ``` - 從上面的程式碼片段來看, - 我為行銷活動建立了預設細分列表,並對已定義的行銷活動列表進行了深層複製。 - `saveCampaign`函數接受行銷活動作為參數。如果行銷活動沒有 ID,則表示它是新建立的,因此會將其新增至行銷活動清單。否則,它會找到該活動並更新其屬性。 - `Dashboard`和`CampaignForm`元件接受細分和行銷活動作為 props。 [Dashboard 元件](https://github.com/CopilotKit/campaign-manager-demo/blob/main/src/components/dashboard/Dashboard.tsx)在儀表板上顯示各種 UI 元素,而[CampaignForm 元件](https://github.com/CopilotKit/campaign-manager-demo/blob/main/src/components/app/CampaignForm.tsx)使用戶能夠在應用程式中建立和保存新的行銷活動。 您也可以使用[GitHub 儲存庫](https://github.com/CopilotKit/campaign-manager-demo/tree/main/src/components)中的程式碼片段來更新儀表板和應用程式元件。 恭喜!您應該有一個有效的 Web 應用程式,可讓使用者查看和建立新的行銷活動。 在接下來的部分中,您將了解如何將 CopilotKit 加入到應用程式中,以根據每個行銷活動的目標和預算進行分析和決策。 ![應用概述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4phyaucli8pdcbe625u4.gif) --- 使用 CopilotKit 透過 AI 分析廣告活動 -------------------------- 在這裡,您將學習如何將人工智慧加入到應用程式中,以幫助您分析行銷活動並做出最佳決策。 在繼續之前,請造訪[OpenAI 開發者平台](https://platform.openai.com/api-keys)並建立一個新的金鑰。 ![取得 OpenAI API 金鑰](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02972pt2aj3kf9l5suqq.png) 建立一個`.env.local`檔案並將新建立的金鑰複製到該檔案中。 ``` OPENAI_API_KEY=<YOUR_OPENAI_SECRET_KEY> OPENAI_MODEL=gpt-4-1106-preview ``` 接下來,您需要為 CopilotKit 建立 API 端點。在 Next.js 應用程式資料夾中,建立一個包含`route.ts`檔案的`api/copilotkit`資料夾。 ``` cd app mkdir api && cd api mkdir copilotkit && cd copilotkit touch route.ts ``` 將下面的程式碼片段複製到`route.ts`檔中。 [CopilotKit 後端](https://docs.copilotkit.ai/reference/CopilotBackend)接受使用者的請求並使用 OpenAI 模型做出決策。 ``` import { CopilotBackend, OpenAIAdapter } from "@copilotkit/backend"; export const runtime = "edge"; export async function POST(req: Request): Promise<Response> { const copilotKit = new CopilotBackend({}); const openaiModel = process.env["OPENAI_MODEL"]; return copilotKit.response(req, new OpenAIAdapter({ model: openaiModel })); } ``` 若要將您的應用程式連接到此 API 端點,請更新`app/page.tsx`文件,如下所示: ``` "use client"; import { App } from "@/components/app/App"; import { CopilotKit } from "@copilotkit/react-core"; import { CopilotSidebar } from "@copilotkit/react-ui"; export default function DashboardPage() { return ( <CopilotKit url='/api/copilotkit/'> <CopilotSidebar instructions='Help the user create and manage ad campaigns.' defaultOpen={true} labels={{ title: "Campaign Manager Copilot", initial: "Hello there! I can help you manage your ad campaigns. What campaign would you like to work on?", }} clickOutsideToClose={false} > <App /> </CopilotSidebar> </CopilotKit> ); } ``` `CopilotKit`元件包裝整個應用程式並接受包含 API 端點連結的`url`屬性。 `CopilotSidebar`元件為應用程式加入了一個聊天機器人側邊欄面板,使我們能夠向 CopilotKit 提供各種指令。 ![將 CopilotKit 加入 Next.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzh8ui253ftzmgtrcksd.gif) ### 如何讓AI副駕駛執行各種動作 CopilotKit 提供了兩個鉤子,使我們能夠處理使用者的請求並插入應用程式狀態: [useCopilotAction](https://docs.copilotkit.ai/reference/useCopilotAction)和[useMakeCopilotReadable](https://docs.copilotkit.ai/reference/useMakeCopilotReadable) 。 `useCopilotAction`掛鉤可讓您定義 CopilotKit 執行的動作。它接受包含以下參數的物件: - name - 操作的名稱。 - 描述 - 操作的描述。 - 參數 - 包含所需參數清單的陣列。 - render - 預設的自訂函數或字串。 - handler - 由操作觸發的可執行函數。 ``` useCopilotAction({ name: "sayHello", description: "Say hello to someone.", parameters: [ { name: "name", type: "string", description: "name of the person to say greet", }, ], render: "Process greeting message...", handler: async ({ name }) => { alert(`Hello, ${name}!`); }, }); ``` `useMakeCopilotReadable`掛鉤向 CopilotKit 提供應用程式狀態。 ``` import { useMakeCopilotReadable } from "@copilotkit/react-core"; const appState = ...; useMakeCopilotReadable(JSON.stringify(appState)); ``` CopilotKit 還允許您為使用者提示提供上下文,使其能夠做出充分且準確的決策。 將`guidance.ts`和`script.ts`加入到專案內的`lib`資料夾中,並將此[指導](https://github.com/CopilotKit/campaign-manager-demo/blob/main/src/lib/guideline.ts)和[腳本建議](https://github.com/CopilotKit/campaign-manager-demo/blob/main/src/lib/script.ts)複製到檔案中,以便 CopilotKit 做出決策。 在應用程式元件中,將當前日期、腳本建議和指導傳遞到 CopilotKit。 ``` import { GUIDELINE } from "@/lib/guideline"; import { SCRIPT_SUGGESTION } from "@/lib/script"; import { useCopilotAction, useMakeCopilotReadable, } from "@copilotkit/react-core"; export function App() { //-- 👉🏻 ...other component functions //👇🏻 Ground the Copilot with domain-specific knowledge for this use-case: marketing campaigns. useMakeCopilotReadable(GUIDELINE); useMakeCopilotReadable(SCRIPT_SUGGESTION); //👇🏻 Provide the Copilot with the current date. useMakeCopilotReadable("Today's date is " + new Date().toDateString()); return ( <div className='relative'> <CampaignForm segments={segments} currentCampaign={currentCampaign} setCurrentCampaign={setCurrentCampaign} saveCampaign={(campaign) => { if (campaign) { saveCampaign(campaign); } setCurrentCampaign(undefined); }} /> <Dashboard campaigns={campaigns} setCurrentCampaign={setCurrentCampaign} segments={segments} setSegments={setSegments} /> </div> ); } ``` 在`App`元件中建立一個 CopilotKit 操作,該操作可在使用者提供此類指令時建立新的活動或編輯現有的活動。 ``` useCopilotAction({ name: "updateCurrentCampaign", description: "Edit an existing campaign or create a new one. To update only a part of a campaign, provide the id of the campaign to edit and the new values only.", parameters: [ { name: "id", description: "The id of the campaign to edit. If empty, a new campaign will be created", type: "string", }, { name: "title", description: "The title of the campaign", type: "string", required: false, }, { name: "keywords", description: "Search keywords for the campaign", type: "string", required: false, }, { name: "url", description: "The URL to link the ad to. Most of the time, the user will provide this value, leave it empty unless asked by the user.", type: "string", required: false, }, { name: "headline", description: "The headline displayed in the ad. This should be a 5-10 words", type: "string", required: false, }, { name: "description", description: "The description displayed in the ad. This should be a short text", type: "string", required: false, }, { name: "budget", description: "The budget of the campaign", type: "number", required: false, }, { name: "objective", description: "The objective of the campaign", type: "string", enum: [ "brand-awareness", "lead-generation", "sales-conversion", "website-traffic", "engagement", ], }, { name: "bidStrategy", description: "The bid strategy of the campaign", type: "string", enum: ["manual-cpc", "cpa", "cpm"], required: false, }, { name: "bidAmount", description: "The bid amount of the campaign", type: "number", required: false, }, { name: "segment", description: "The segment of the campaign", type: "string", required: false, enum: segments, }, ], handler: (campaign) => { const newValue = _.assign( _.cloneDeep(currentCampaign), _.omitBy(campaign, _.isUndefined) ) as Campaign; setCurrentCampaign(newValue); }, render: (props) => { if (props.status === "complete") { return "Campaign updated successfully"; } else { return "Updating campaign"; } }, }); ``` {% 嵌入 https://www.youtube.com/watch?v=gCJpH6Tnj5g %} 新增另一個模擬 API 呼叫的操作,以允許 CopilotKit 從先前建立的活動中檢索歷史資料。 ``` // Provide this component's Copilot with the ability to retrieve historical cost data for certain keywords. // Will be called automatically when needed by the Copilot. useCopilotAction({ name: "retrieveHistoricalData", description: "Retrieve historical data for certain keywords", parameters: [ { name: "keywords", description: "The keywords to retrieve data for", type: "string", }, { name: "type", description: "The type of data to retrieve for the keywords.", type: "string", enum: ["CPM", "CPA", "CPC"], }, ], handler: async ({ type }) => { // fake an API call that retrieves historical data for cost for certain keywords based on campaign type (CPM, CPA, CPC) await new Promise((resolve) => setTimeout(resolve, 2000)); function getRandomValue(min: number, max: number) { return (Math.random() * (max - min) + min).toFixed(2); } if (type == "CPM") { return getRandomValue(0.5, 10); } else if (type == "CPA") { return getRandomValue(5, 100); } else if (type == "CPC") { return getRandomValue(0.2, 2); } }, render: (props) => { // Custom in-chat component rendering. Different components can be rendered based on the status of the action. let label = "Retrieving historical data ..."; if (props.args.type) { label = `Retrieving ${props.args.type} for keywords ...`; } if (props.status === "complete") { label = `Done retrieving ${props.args.type} for keywords.`; } const done = props.status === "complete"; return ( <div className=''> <div className=' w-full relative max-w-xs'> <div className='absolute inset-0 h-full w-full bg-gradient-to-r from-blue-500 to-teal-500 transform scale-[0.80] bg-red-500 rounded-full blur-3xl' /> <div className='relative shadow-xl bg-gray-900 border border-gray-800 px-4 py-8 h-full overflow-hidden rounded-2xl flex flex-col justify-end items-start'> <h1 className='font-bold text-sm text-white mb-4 relative z-50'> {label} </h1> <p className='font-normal text-base text-teal-200 mb-2 relative z-50 whitespace-pre'> {props.args.type && `Historical ${props.args.type}: ${props.result || "..."}`} </p> </div> </div> </div> ); }, }); ``` ![應用程式預覽](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kz3xm63ciq5q3kyooz9s.gif) 恭喜!您已完成本教學的專案。 結論 -- [CopilotKit](https://copilotkit.ai/)是一款令人難以置信的工具,可讓您在幾分鐘內將 AI Copilot 加入到您的產品中。無論您是對人工智慧聊天機器人和助理感興趣,還是對複雜任務的自動化感興趣,CopilotKit 都能讓您輕鬆實現。 如果您需要建立 AI 產品或將 AI 工具整合到您的軟體應用程式中,您應該考慮 CopilotKit。 您可以在 GitHub 上找到本教學的源程式碼: <https://github.com/CopilotKit/campaign-manager-demo> 感謝您的閱讀! --- 原文出處:https://dev.to/copilotkit/build-an-ai-powered-campaign-manager-nextjs-openai-copilotkit-59ii

Supabase Storage:現在支持S3協議

Supabase Storage 現已正式成為 S3 相容儲存提供者。這是最受歡迎的功能之一,現已在公共 alpha 版本中提供。可斷點續傳上傳也從測試版過渡到普遍可用。 https://supabase.com/ga-week ⚡️ 了解有關 GA 週的更多資訊 [Supabase 儲存引擎](https://github.com/supabase/storage)是完全開源的,是為數不多的提供 3 種可互通協定來管理檔案的儲存解決方案之一: - [標準上傳](https://supabase.com/docs/guides/storage/uploads/standard-uploads):上手簡單 - [斷點續傳](https://supabase.com/docs/guides/storage/uploads/resumable-uploads):適用於大容量斷點續傳 - [S3 上傳](https://supabase.com/docs/guides/storage/s3/compatibility):相容於多種工具 https://youtu.be/WvvGhcNeSPk S3相容性 ----- 我們始終努力在 Supabase 採用業界標準。支援標準使工作負載可移植,這是一個[關鍵的產品原則](https://supabase.com/docs/guides/getting-started/architecture#everything-is-portable)。 S3 API 無疑是一種儲存標準,我們正在讓各種經驗水平的開發人員都可以使用它。 S3 協定向後相容我們的其他 API。如果您已經透過我們的 REST 或 TUS API 使用存儲,現在您可以使用任何 S3 用戶端與您的儲存桶和檔案進行互動:使用 TUS 上傳、使用 REST 提供服務以及使用 S3 協定管理它們。 該協議適用於雲端、本地開發和自託管。[在我們的文件中](https://supabase.com/docs/guides/storage/s3/compatibility)查看 API 相容性 使用 Supabase S3 進行身份驗證 --------------------- 要使用 Supabase S3 進行身份驗證,您有 2 個選項: 1-**標準**`access_key`**和**`secret_key`憑證。您可以從[儲存設定頁面](https://supabase.com/dashboard/project/_/settings/storage)產生這些。這種身份驗證方法與支援S3協定的工具廣泛相容。它也只能*在伺服器端*使用,因為它提供對儲存資源的完全存取。 我們將在不久的將來加入可以存取特定儲存桶的範圍存取金鑰憑證。 2-**使用 RLS 的使用者範圍憑證**。這利用了所有 Supabase 服務中廣泛採用的概念:[行級安全性](https://supabase.com/docs/guides/auth/auth-deep-dive/auth-row-level-security)。它允許您透過將儲存操作範圍限定到特定的經過身份驗證的使用者或角色來與 S3 協定進行交互,同時尊重您現有的 RLS 策略。此方法是透過使用 S3 協定支援的會話令牌標頭來實現的。您可以在[文件](https://supabase.com/docs/guides/storage/s3/authentication#session-token)中找到有關如何使用會話令牌機制的更多資訊。 S3 相容集成 ------- 在 S3 協定的支援下,您現在可以透過提供一對可以隨時撤銷的憑證將 Supabase Storage 連接到許多第三方工具和服務。 您可以使用流行的工具進行備份和遷移,例如: - [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/s3/) :官方 AWS CLI - [rclone](https://rclone.org/) :用於管理雲端儲存上的檔案的命令列程式。 - [Cyberduck](https://cyberduck.io/) :適用於 Mac 和 Windows 的雲端儲存瀏覽器。 - 以及任何其他 s3 相容工具。 ![工具範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ore6ttqgo77d7jnv8k1d.png) 請[在此處](https://supabase.com/partners/integrations/cyberduck)查看我們的 Cyberduck 指南。 資料工程師的 S3 --------- S3 相容性為資料工程師提供了一個很好的原語。您可以將它與許多流行的工具一起使用: - ClickHouse 等資料倉儲 - 查詢引擎,如 DuckDB、Spark、Trino 和 Snowflake 外部表 - 資料載入器,例如 Fivetran 和 Airbyte 在此範例中,我們出色的資料分析師 Tyler 示範如何將 Parquet 檔案儲存在 Supabase Storage 中並使用 DuckDB 直接查詢它們: https://youtu.be/diL00ZZ-q50 S3 中的分段上傳 --------- 除了標準上傳和斷點續傳之外,我們現在還支援透過 S3 協定進行分段上傳。這使您可以透過並行上傳區塊來最大化上傳吞吐量,然後將區塊連接到最後。 斷點續傳已普遍可用 --------- 隨著[平台 GA 的發布](https://supabase.com/ga),我們也很高興地宣布可斷點上傳也普遍可用。 可斷點上傳由[TUS 協定](https://tus.io/)提供支援。與啟迪團隊的密切合作,到達這裡的旅程非常有意義。大力讚揚 TUS 協議的維護者[@murderlon](https://github.com/Murderlon)和[@acconut](https://github.com/Acconut) ,感謝他們在開源方面的協作方式。 Supabase 貢獻了 TUS Spec 的 Node 實現的[一些高級功能,](https://github.com/tus/tus-node-server/pulls?q=is%3Apr+sort%3Aupdated-desc+author%3Afenos)包括[分散式鎖定](https://github.com/tus/tus-node-server/pull/514)、[最大檔案大小](https://github.com/tus/tus-node-server/pull/517)、[過期擴展](https://github.com/tus/tus-node-server/pull/513)和大量錯誤修復: ![bug修復](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6psgtxi00cv8e2sdfe8.png) 這些功能對於 Supabase 至關重要,並且由於[TUS 節點伺服器](https://github.com/tus/tus-node-server)是開源的,因此它們也可供您使用。這是另一個核心原則:只要有可能,[我們就會使用和支援現有工具,](https://supabase.com/docs/guides/getting-started/architecture#support-existing-tools)而不是從頭開始開發。 - **跨儲存桶傳輸**:我們新增了跨儲存桶複製和移動物件的功能,而以前您只能在同一個 Supabase 儲存桶中執行這些操作。 - **標準化錯誤程式碼**:錯誤程式碼現已在整個儲存伺服器上標準化,現在可以更輕鬆地針對特定錯誤進行邏輯分支。您可以[在此處](https://supabase.com/docs/guides/storage/debugging/error-codes)找到錯誤程式碼清單。 - **多租戶遷移**:我們對所有租戶中執行的遷移進行了重大改進。這減少了整個佇列的遷移錯誤,並使我們能夠以非同步方式執行長時間執行的遷移。請繼續關注包含更多詳細資訊的單獨部落格文章。 - **解耦依賴**:儲存與其他 Supabase 產品完全解耦,這意味著您可以將儲存作為獨立服務執行。開始使用這個[docker-compose 檔案](https://github.com/supabase/storage/blob/master/docker-compose.yml)。 入門 -- - [在我們的文件中](https://supabase.com/docs/guides/storage/s3/compatibility)查看 S3 API 相容性 - 了解[S3 身份驗證](https://supabase.com/docs/guides/storage/s3/authentication) - 嘗試使用 Cyberduck 進行 S3:遵循我們的[整合指南](https://supabase.com/partners/integrations/cyberduck) - 嘗試使用 DuckDB 進行 S3:按照[YouTube 上的](https://www.youtube.com/watch?v=diL00ZZ-q50)指南進行操作 **更多關於 GA 週的訊息** - [斯帕巴斯威夫特](https://supabase.com/blog/supabase-swift) - [AWS Marketplace 上的 Supabase](https://supabase.com/blog/supabase-aws-marketplace) - [Supabase 開源黑客馬拉松 2024](https://supabase.com/blog/supabase-oss-hackathon) - [Supabase引導程式](https://supabase.com/blog/supabase-bootstrap) - [分支現已公開](https://supabase.com/blog/branching-publicly-available) --- 原文出處:https://dev.to/supabase/supabase-storage-now-supports-the-s3-protocol-3479

我又建立了另一個 JavaScript 框架

**現在是 2024 年,您又建立了另一個 JavaScript 框架。說真的,夥計!** 當您談論另一個 Javascript 框架時,您可能會笑並認為有大量經過驗證的選項,為什麼還要建立另一個框架? 🤷‍♂️ 讓我與您分享我的個人故事,它導致了新的 Javascript 框架的發展。但如果你不關心我的可悲的故事,這裡是 TLDR: > **TiniJS**是一個元框架,由基於 Google [Lit](https://lit.dev/)庫的**Web 元件**技術提供支援。 > 您可以先下載[入門範本](https://github.com/tinijs/bare-starter)或使用 CLI 初始化應用程式,執行`npx @tinijs/cli@latest new my-app`並按照說明進行操作。 > 有關更多詳細訊息,請參閱下面的**“入門”**部分或存取<https://tinijs.dev/> 。 故事 -- ![Javascript 框架太多](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwi99az05w9syfgn7833.jpeg) 開個玩笑吧,我不是 Javascript 的專家,但我想稍微介紹一下我的背景,這樣您可能就會知道我關於前端 Web 開發的煩惱故事。我曾經是 Angular 1 版本的開發人員,目前主要使用 Vue,偶爾使用 jQuery。我有這**3 個 PITA** (Pain In The \*beep\*)經歷,這促使我嘗試使用 TiniJS 框架。 > **PITA #1 - 建立良好的 UI/UX 很困難,既乏味又耗時!** 早在 2010 年左右,就有兩種不同類型的網站:一種有大量文本、連結,幾乎沒有一些圖片;另一種有大量文本、連結,幾乎沒有一些圖片。其他的則包含大量的圖形元素,例如 GIF 圖像、Flash 背景、彩虹遊標… 人們往往有**後端難前端容易的**刻板印象。這有點正確,但不完全正確,我的意思是,我該怪誰,對吧?因為只要有一點 HTML 和 CSS 知識,您就可以非常輕鬆地建立靜態網站。但是,為了建立良好的 UI/UX,還有很多事情需要考慮。你必須成為更多事情的大師,而它們一點也不容易! ![前端開發不容易!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77sp9asranurm9xcldl5.png) > **PITA #2 - CSS 框架缺乏功能並且自訂它們不是很有效!** 人們意識到前端開發的困難,開始建立CSS框架來幫助解決這個問題。 Bootstrap是這一趨勢的先驅之一,其他流行的有:Foundation、Semantic UI、Bulma、Skeleton、Pure CSS…這些CSS框架很棒,毫無疑問,它們幫助我們建立一個好的UI/使用者體驗更快,但它們也有自己的限制。 首先,儘管所有框架都提供了一定的方式來定制樣式,但總的來說,我覺得這對我來說不是很容易和可重用。 ![客製化CSS框架效率不是很高](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2lhj75s0sy977e8d5e48.jpg) 二是功能缺失。我的意思是,它們主要是 CSS,它們提供一些功能,通常是作為 JQuery 的插件。大多數情況下,您必須編寫自己的 Javascript 才能使功能正常運作。 > **PITA #3 - JavaScript 框架勢不可擋,而且有點多餘!** 進入現代 Web 開發時代,Javascript 框架已成為新趨勢。但問題是我們有太多的選擇,這既是福也是禍。它們提供了很棒的功能,但互通性不太好,您不能只從一個框架中取出一段程式碼並在另一個框架中使用它。特別是,為特定框架建立一個具有單獨套件的 UI 系統是一項非常艱鉅的任務。 ![前端 Web 開發是碎片化的](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4cx44vzf0guksn2horak.png) 每次我開始一個專案或做某種前端工作時,前端web的碎片化對我來說都有些痛苦。這似乎就是生活方式,無可避免,不是嗎?請不要誤會我的意思,我並不反對任何框架,我傾向於選擇解決方案而不是技術,所以如果它適合你,就用它吧。但我想知道有什麼辦法可以以某種方式統一或縮小前端開發經驗的差距嗎? 🤔 介紹 TiniJS --------- 這就是我嘗試**TiniJS 框架**一段時間的原因。它是基於[Lit](https://lit.dev/)庫、使用本機**Web 元件**技術開發 Web/桌面/行動應用程式的工具集合。感謝[Lit](https://lit.dev/)團隊建立了一個出色的工具,幫助我們更輕鬆地使用標準 Web 元件。 它的目標是盡可能**標準化、小型化和多功能化**。與其他框架和元框架沒有真正的可比性,但總體而言,它具有相似的功能,但另一方面也有關鍵的區別。它既是替代選擇,也是對其他框架的補充。您可以使用 TiniJS 建立各種類型的應用程式:**登陸頁面、SPA、PWA、桌面應用程式、行動應用程式**…就功能而言,任何在 Javascript 中工作的內容都應該在 TiniJS 應用程式中正常運作。 整個系統由幾個部分組成,這是一個快速介紹。 > 請注意,**並非所有部件現在都可用**,該專案還處於早期階段,有些是**之前的實驗**,我盡力盡快將它們投入使用。但我的時間和資源有限,所以請耐心等待,如果可能的話請幫助我。 🙇‍♂️ > V1 的計畫及其路線圖可在<https://github.com/tinijs/tinijs>找到 ### 核 **TiniJS 的**核心提供了傳統的專案結構和簡化的開發流程。有**用於組織內容的資料夾**,**用於開發的本機伺服器**,在分發時,您可以選擇 Vite 或 Parcel 或 Webpack 來**建立產品**。 核心還包括:用於在頁面之間導航的**路由器**、用於全域狀態管理的簡單**儲存**、支援**PWA** ,... 您可以嘗試[Try TiniJS 中的範例專案(範例照片庫應用程式)](https://stackblitz.com/edit/try-tinijs?file=app%2Fapp.ts) ### 使用者介面系統 TiniJS 有一個專用的**UI 庫,我的目標是提供每個常用的元件和區塊甚至整個頁面**。元件以特殊的方式建置,它們是自訂元素,不僅可以與 TiniJS 一起使用,還可以與其他框架或不使用框架一起使用。 ![保持UI概念](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rshg9gol388nf0gsw1jt.png) 使用可重複使用元件很容易,通常是以將 props 傳遞給自訂元素標籤的形式。自訂可以透過 props 或 CSS `::part()`或自訂主題系列或完全克隆元件來源來完成,... 元件還能夠以盡可能小的努力適應幾乎所有設計系統。這是透過主題系統實現的,概念是這樣的: - 元件**只寫一次**,它們是**無頭的**(沒有特定的樣式) - 主題被組織成**系列**(又稱設計系統),系列定義自己的基本特徵,例如:Bootstrap、Material、Fluent、Spectrum,... - 根據基本特徵,一個系列有風格變體,稱為**“皮膚”** ,例如,Bootstrap 系列可能有:淺色皮膚、深色皮膚… 考慮到主題概念,任何應用程式都可以具有以下主題功能: - **一個**主題系列 - 可能是當今存在的所有網路應用程式中的 80% 到 90%,只有 1 種特定風格相當於 TiniJS 1 主題系列。 - **一個**主題系列 + 多個皮膚 - 常見的用例是亮/暗模式,相當於 TiniJS 1 主題系列,具有來自同一系列的多個皮膚。 - **多個**主題系列 - 高度個人化的應用程式可能有多個主題系列,每個系列都有一個或多個皮膚,將根據使用者參考應用某個主題。 UI 使用詳細資訊將在[主頁](https://tinijs.dev/ui)上提供,現在您也可以在<https://ui.tinijs.dev/>查看實驗概念,以查看該概念的實際應用。 ### 網頁、PWA、混合桌面/行動設備 預設的 TiniJS 應用程式是**單頁應用程式**,它小而快速,可以直接部署為 Web 應用程式。但是,您也可以將 TiniJS 轉換為以下一種或所有類型的應用程式: - **漸進式 Web 應用程式**(PWA) - 使用單一 CLI 命令實作(即將推出) - 混合**桌面/行動**應用程式 - 使用 Tauri 2.0 或類似工具(todo:編寫指令) ### 靜態和伺服器 TiniJS 應用程式可以作為靜態網路啟動,無論大小,無需伺服器或資料庫。您始終可以使用 SaaS 服務來滿足幾乎任何後端需求。我還在開發`@tinijs/content` - 一個基於文件的內容管理系統,用於輕鬆管理多種類型的內容。靜態站點產生(SSG)或預渲染也在計劃中。 如果您需要處理後端任務並參考從 Node 伺服器而不是靜態主機為 Web 應用程式提供服務,則可選的 Nitro 伺服器用於**伺服器/API 路由**和其他**伺服器內容**。我的開發能力有限,因此第一個版本可能不支援伺服器端渲染(SSR),相反,我計劃使用半SSR,其中伺服器將為用戶提供單頁服務,並向機器人提供最少的伺服器端渲染內容。 ### 功能和模組 在功能方面,由於 TiniJS 應用程式**基於標準 Web 技術**,因此在瀏覽器中工作的任何程式庫和功能都可能在 TiniJS 應用程式中運作。因此,與其他框架相比,您可能**不必擔心功能**。您甚至可以在 TiniJS 應用程式中使用其他框架,例如初始化 Vue 應用程式並將其安裝到 TiniJS 頁面內的元素。 還有一個模組架構師,以便某些模組可以進入 TiniJS 應用程式的工作流程,建立更簡化的開發管道。 ### 工具 官方 CLI 工具提供了一些使用 TiniJS 應用程式的便利命令。 - `dev` - 啟動本機開發伺服器 - `build` - 建構生產分配 - `preview` - 在部署之前預覽生產版本 - `generate` - 生成元件、頁面、實用程式... 此外,CLI**足夠通用,可以在 TiniJS 應用程式以外的任何類型的專案中使用**。它具有可擴展的架構,您可以在其中擴展更多命令以用於其他自動化任務。一些官方擴展,例如`ui`命令(來自`@tinijs/ui` )用於處理 UI 任務或`content`命令(來自`@tinijs/content` )用於處理內容相關任務,...您可以根據自己的目的建立 CLI 擴展包私人或共享。 好了,說得夠多了,讓我看看一些行動吧! 👌 開始使用 ---- **Tini** (越南語中的“ [Tí nị](https://translate.google.com/?sl=vi&tl=en&text=t%C3%AD%20n%E1%BB%8B&op=translate) ”,意思是非常小、可愛的東西)。 為了快速建立**TiniJS**專案,您可以使用[CLI](https://tinijs.dev/cli)來初始化模板。 ``` npx @tinijs/cli@latest new my-app ``` 上面的命令透過下載**Bare**模板來建立一個應用程式。將來,我想提供幾個入門範本。您還可以建立自己的模板並與社區共享或供您自己私人使用。目前,這些模板可用: - [Bare](https://github.com/tinijs/bare-starter) (預設)- TiniJS 應用程式的最小結構。 - [空白](https://github.com/tinijs/blank-starter)(標誌`-t blank` )- 包含路由器、狀態管理、元標記管理和[Bootstrap 主題系列](https://tinijs.dev/ui)。 現在,在專案內部,您可以執行`npm run dev`來啟動開發伺服器。您可以透過編輯檔案`./app/app.ts`來開始開發,該檔案是應用程式的根元件。有關如何使用 Lit 處理自訂元素,請存取[Lit 元件](https://lit.dev/docs/components/overview/), `LitElement`和`TiniComponent`之間存在一些差異,但現在您可以像平常一樣修改`static styles`和`render()` 。 若要建立發行版,請執行`npm run build`並可選擇執行`npm run preview`以預覽生產版本。現在您可以將`.output`資料夾部署到任何靜態主機。 您可能需要查看這些範例以了解 TiniJS 應用程式如何運作的更多細節。 - 首頁 - <https://github.com/tinijs/tinijs/tree/main/apps/tinijs.dev> - 待辦事項應用程式 - <https://github.com/tinijs/tinijs/tree/main/examples/todo> 這就是**TiniJS 框架**的基本介紹。下次我們將探索**TiniJS 應用程式的結構並使用元件**。 欲了解更多訊息,請存取: <https://tinijs.dev> 謝謝您,編碼愉快! 💖 --- 原文出處:https://dev.to/lamnhan/ive-created-yet-another-javascript-framework-5c5o

加入我們參加下一個前端挑戰:地球日版!

--- 標題:加入我們,迎接下一個前端挑戰:地球日版! 發表:真實 描述: 標籤: frontendchallenge、devchallenge、js、css 封面圖:https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emb626o0mysdsw32ox4k.png 使用 100:42 的比例可獲得最佳效果。 ===================== 發表於: 2024-04-17 14:14 +0000 =========================== --- 幾週前,我們開始了[第一個前端挑戰](https://dev.to/devteam/join-our-first-community-challenge-the-frontend-challenge-8be),並承諾如果社區喜歡參與,我們將定期舉辦這些挑戰 - 您的提交讓我們欣喜若狂,所以現在**我們又回來了**,我們的下一個前端挑戰! 對於下一個前端挑戰,我們將慶祝地球上最大的公民活動:**世界地球日**。 🌍🌏🌎 此挑戰有兩個提示。我們將為此挑戰帶回**CSS Art**和**Glam Up My Markup**提示 - 別擔心,One Byte Expander 將來會再次出現! 和上次一樣,每個提示都會有一個獲勝者,為了慶祝地球日,我們也將為此挑戰設置一個額外的獎品類別。這是贏得炫耀權、DEV 商店禮物和專屬 DEV 徽章的 3 次機會。與往常一樣,提交有效提交的參與者將獲得完成徽章。 *前端不是你的菜嗎?或者您正在尋找更大獎勵的挑戰? [Coze AI 機器人挑戰賽](https://dev.to/devteam/join-us-for-the-coze-ai-bot-challenge-3000-in-prizes-4dp7)現已開始,獎金池為 3,000 美元。* 繼續閱讀以了解每個提示以及如何參與! 提示 -- ### CSS 藝術:地球日 畫出您在地球日時想到的內容。這是我們美麗的星球嗎?它是提高人們對氣候變遷認識的象徵嗎?不管是什麼,告訴我們! 您提交的內容**不應使用任何 JavaScript** ,而應在 CSS 中發揮您的創造力。您提交的內容將包括標記,可能包括 SVG 等,但主要應該*展示*您的 CSS 技能。 **評審標準** - 創造力 - 有效使用CSS - 美感效果 這是供任何想要直接參與的人使用的提交模板,但請在提交之前查看[官方挑戰頁面](https://dev.to/challenges/frontend-2024-04-17)上的所有評審標準和挑戰規則。 {% cta https://dev.to/new?prefill=---%0Atitle%3A%20%0Apublished%3A%20%0Atags%3A%20frontendchallenge%2C%20devchallenge%2C%20css%0A---% 0A%0A\_This%20is%20a%20submission%20for%20%5BFrontend%20Challenge%20v24.04.17%5D(https%3A%2F%2Fdev.to%2Fdevteam%2Fjoin-us-for-the-next-frontend地球日版-52e4)%2C%20CSS%20Art%3A%20Earth%20Day.\_%0A%0A%23%23%20靈感%0A%3C!--%20What%20are%20you%20highlighting%20today% 3F %20--%3E%0A%0A%23%23%20Demo%20%0A%3C!--%20Show%20us%20your%20CSS%20Art!%20You%20can%20directly%20embed%20Art!%20You%20can%20directly%20embed%20an%20editor%20an%20editor%進入%20this%20post%20(參見%20the%20FAQ%20section%20of%20the%20challenge%20頁面)%20或%20you%20can%20share%20an%20image%20of%20your%20can%20share%20an%20image%20of%20your%20project%20% 20a%20public %20link%20到%20%20程式碼。 %2C%20what%20you %20已學會%2C%20anything%20you%20are%20尤其%20proud%20of%2C%20what%20you%20hope%20to%20do%20next%2C%20etc.%20--%3E %0A%0A%3C!-- %20Team%20Submissions%3A%20請%20pick%20one%20member%20to%20publish%20the%20submission%20and%20credit%20teammates%20by%20listing%20their%20DEV%20usernames%20directly %20in%20the%20body%20of%20the %20post.%20--%3E%0A%0A%3C!--%20We%20鼓勵%20you%20to%20考慮%20為%20your%20程式碼新增% 20a%20license%20。 %20想要)。 CSS 藝術挑戰提交模板 {% 結束%} ### 魅力我的標記:地球日慶祝活動登陸頁面 使用 CSS 和 JavaScript 使下面的入門 HTML 標記美觀、互動且有用。 您提交的內容應該比我們提供的 HTML 更有趣、更具互動性,而且還應該可用且易於存取。您不應直接編輯提供的 HTML,除非是透過 JavaScript。我們期待風格和實質。您可以加入基本樣板,包括元標記等以用於演示目的。 ``` <body> <header> <h1>Welcome to Our Earth Day Celebration!</h1> </header> <section> <article class="facts"> <h2>Did You Know?</h2> <p>Earth Day was first celebrated on April 22, 1970, and now includes a wide range of events coordinated globally by EARTHDAY.ORG including 1 billion people in more than 193 countries.</p> </article> <article> <h2>Why Celebrate Earth Day?</h2> <p>Earth Day is more than just a single day — April 22. It's a day to remind us to take action in our communities and beyond, to protect the environment, restore damaged ecosystems, and live a more sustainable life.</p> </article> <article> <h2>How You Can Help</h2> <p>Join us in making a difference! Here are some ways you can contribute to preserving our planet:</p> <ul> <li>Reduce, reuse, and recycle.</li> <li>Volunteer for cleanups in your community.</li> <li>Conserve water and electricity.</li> <li>Plant a tree.</li> <li>Educate others about environmental conservation.</li> </ul> </article> <div class="action-call"> <h2>Take Action Now</h2> <p>Join our Earth Day quiz to test your knowledge and learn more about what you can do to help our planet!</p> <a href="#">Start the Quiz!</a> </div> </section> <div class="testimonial"> <h2>Inspiration Corner</h2> <p>"The Earth does not belong to us: we belong to the Earth." - Marlee Matlin</p> <p>This Earth Day, let's remember we are part of a larger ecosystem and our actions have a profound impact on our planet.</p> </div> <div class="events"> <h2>Join an Event</h2> <p>Participate in local and global Earth Day events to take action towards a sustainable future. Check out <a href="#">events near you</a>.</p> </div> <footer> <p>Happy Earth Day! Together, we can make a difference.</p> </footer> </body> ``` **評審標準**: - 無障礙 - 可用性和使用者體驗 - 創造力 - 程式碼品質 這是供任何想要直接參與的人使用的提交模板,但請在提交之前查看[官方挑戰頁面](https://dev.to/challenges/frontend-2024-04-17)上的所有評審標準和挑戰規則。 {% cta https://dev.to/new?prefill=---%0Atitle%3A%20%0Apublished%3A%20%0Atags%3A%20devchallenge%2C%20frontendchallenge%2C%20css%2C%20javascript%2C%20frontendchallenge%2C%20css%2C%20javascript%0Ajava ---%0A%0A\_This%20is%20a%20submission%20for%20%5BFrontend%20Challenge%20v24.04.17%5D(https%3A%2F%2Fdev.to%2Fdevteam%2Fjoin-us-for-the-next前端挑戰-地球日-版本-52e4)%2C%20Glam%20Up%20My%20Markup%3A%20Earth%20Day%20Celebration%20Landing%20Page\_%0A%0A%23%23%20What%20I%20A0ABuilt% %3C!--%20告訴%20us%20什麼%20you%20built%20和%20what%20you%20是%20期待%20實現%20實現。 23%20示範%0A %3C!--%20Show%20us%20您的%20專案! 20the%20FAQ%20section%20from%20the%20challenge%20page )%20或%20you%20can%20share%20an%20image%20of%20your%20project%20and%20share%20a%20share%200%200%200%20share%程式碼。 20you%20are%20specially%20proud%20of%2C%20what%20you%20hope% 20至%20do%20next%2C%20等%20--%3E%0A%0A%3C!--%20團隊%20Submissions% 3A%20請%20pick%20one%20member%20至%20publish%20the%20submission%20and% 20credit%20teammates%20by%20listing%20their%20DEV%20usernames%20by%20listing%20their%20DEV%20usernames%20directthe%200%0%200inbody%200%200%200% %20--%3E%0A%0A%3C!--%20We%20encourage%20you% 20to%20考慮%20加入%20a%20許可證%20用於%20您的%20程式碼。 -%3E%0A%0A%3C! --%20Don%27t%20忘記%20to%20add%20a%20cover%20image%20to% 20您的%20貼文%20(如果%20您%20) 。 使我的標記提交模板更加迷人 {% 結束%} 附加獎類別 ----- 除了能夠贏得每個提示之外,我們還有一個特殊的獎品類別來紀念我們的星球: - 世界地球日:授予提高人們對氣候變遷認識的最佳提交作品。 除了前端挑戰賽獲勝者徽章和 DEV 商店贈送的禮物之外,我們的地球日獲勝者還將獲得一枚額外的超專屬徽章,用於展示其開發者檔案。 如何參與 ---- 為了參與,您需要使用提示的提交範本發布貼文。**您可以在[官方挑戰頁面](https://dev.to/challenges/frontend-2024-04-17)上找到所有評審標準、挑戰規則和提交範本。** 我們的規則和指南涵蓋了參賽要求、團隊提交、人工智慧的使用等主題,因此請務必仔細閱讀。 重要的日子 ----- 4 月 17 日:前端挑戰 v24.04.17 開始! 4 月 28 日:提交截止時間為太平洋夏令時間晚上 11:59 4 月 30 日:公佈得獎者 ![行星地球插圖](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExaDFxNGZ5Z2wwdGV4YXE5M2o0bGc2cWpuMDFlYnBpMmlwdXV3eWs0aSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l1KVcrdl7rJpFnY2s/giphy.gif) 我們很高興看到您的地球日專案!問題?請在下面詢問他們。 祝你好運,編碼愉快! --- 原文出處:https://dev.to/devteam/join-us-for-the-next-frontend-challenge-earth-day-edition-52e4

在 JavaScript 中建立 CLI 工具的指南

歡迎來到使用 JavaScript 的命令列介面 (CLI) 工具世界的令人興奮的旅程。 在本指南中,我將引導您建立 CLI 工具,該工具設定基本的專案結構,解釋每個步驟和程式碼片段,以確保您可以遵循。 設定您的開發環境 -------- 在我們深入 CLI 工具的世界之前,讓我們先設定我們的環境: 1. **安裝 Node.js 和 npm:**前往[Node.js 的官方網站](https://nodejs.org/en/)並下載建議的版本。安裝 Node.js 也會安裝 npm,這是您將用來處理 JavaScript 套件的套件管理器。 2. **驗證您的安裝:**開啟終端機並執行以下命令,確保所有內容均已正確安裝: ``` node --version npm --version ``` 查看版本號碼即可確認您已全部設定完畢! 建立您的 CLI 工具:專案設定自動化 ------------------- 我們的目標是建立一個能夠自動產生基本專案結構的 CLI 工具,從而省去您每次啟動新專案時手動建立資料夾和檔案的麻煩。 ### 第 1 步:啟動您的專案 1. **建立專案目錄:**這是 CLI 工具程式碼所在的位置。 ``` mkdir my-project-setup cd my-project-setup ``` 2. **初始化您的 npm 套件:**此步驟產生`package.json`文件,這對於管理專案的依賴項和配置至關重要。 ``` npm init -y ``` ### 第 2 步:建立 CLI 應用程式 1. **建立主檔案:**將此檔案命名為`index.js` 。它將包含 CLI 工具的邏輯。 2. **合併 Shebang 行:**在`index.js`的開頭新增: ``` #!/usr/bin/env node ``` ``` This line tells your system to use Node.js to execute this script. ``` 3. **實作邏輯:**下面的程式碼建立一個包含目錄和檔案的預定義專案結構: ``` const fs = require('fs'); // File System module to handle file operations // Define the project structure: directories and their respective files const projectStructure = { 'src': ['index.js'], 'public': ['index.html', 'styles.css'], }; // Iterate over the structure, creating directories and files Object.entries(projectStructure).forEach(([dir, files]) => { fs.mkdirSync(dir, { recursive: true }); // Create directories files.forEach(file => fs.writeFileSync(`${dir}/${file}`, '')); // Create files }); console.log("Project structure created successfully!"); ``` ``` Here's what each part of the code does: ``` - **需要 fs 模組:**這是 Node.js 的檔案系統模組,您將使用它來建立目錄和檔案。 - **定義專案結構:**我們指定要建立哪些目錄以及它們應包含哪些檔案。 - **建立目錄和檔案:**使用`fs.mkdirSync`和`fs.writeFileSync` ,腳本根據定義的結建置立每個目錄和檔案。 **4. 讓您的腳本可執行:**修改`package.json`以包含`"bin"`部分。這告訴 npm 哪個指令應該執行你的腳本: ``` `"bin": { ``` ``` "setup-project": "./index.js" ``` ``` }` ``` ### 第 3 步:在本地測試和連結您的工具 在分享您的工具之前,請先對其進行測試: 1. **連結您的工具:**在專案目錄中執行`npm link` 。此命令建立一個符號連結,允許您從終端中的任何位置執行 CLI 工具。 2. **執行您的工具:**只需在終端機的專案目錄中輸入`setup-project`即可。如果一切設定正確,您將看到“專案結構已成功建立!”訊息,以及提到的專案結構。 > 是的,就這樣! ### 第 4 步:增強功能 您的工具現在可以自動執行專案設置,但仍有改進的空間。考慮新增更多功能或處理使用者輸入來自訂專案結構。探索像`yargs`這樣的套件來解析命令列參數。您可以透過[此處的官方文件了解有關 yargs 的更多資訊。](https://yargs.js.org/) ### 第 5 步:在 npm 上分享您的工具 準備好與世界分享您的 CLI 工具了嗎?就是這樣: 1. **在 npm 上註冊:**如果您沒有帳戶,請在<https://www.npmjs.com/signup>建立帳戶。 2. **透過終端登入:**執行`npm login`並輸入您的 npm 憑證。 3. **發佈您的套件:**在您的專案目錄中,執行`npm publish` 。 恭喜!您的 CLI 工具現已在 npm 上提供,供所有人使用。 > 如果您已經關注我到這裡,請查看我用 Javascript 製作並發佈在 npm 上的第一個 CLI 工具:Naturalshell。 Naturalshell在您的終端中提供AI,現在無需記住shell命令! > 您可以[在此處查看 npm 套件](https://www.npmjs.com/package/naturalshell),[在此處查看 github 儲存庫](https://github.com/shreshthgoyal/naturalshell)。 > 請打 ⭐,並隨時加入新功能並與我一起在 naturalshell 上建置! > 該工具利用人工智慧來解析自然語言,並根據使用者的需求向使用者提供 shell 命令。除了提供命令之外,它還提供簡潔的解釋,確保使用者不僅知道要做什麼,而且了解其工作原理。憑藉直接在工具內編輯命令的便利性以及立即執行命令的能力,NaturalShell 提供了用戶友好、直觀的體驗 包起來 --- 您已經向使用 JavaScript 進行 CLI 工具開發的領域邁出了重要的第一步。透過建立一個簡單但實用的工具,您已經了解了建立、測試和發布 CLI 應用程式的基本知識。不斷嘗試、學習和建構。命令列是一個強大的盟友,現在,由您來指揮。快樂編碼! > 如果您發現本指南很有幫助並建立了您自己的出色 CLI 工具,我很樂意看到它!請在下面的評論部分分享您的 GitHub 儲存庫。讓我們一起建造吧! --- 原文出處:https://dev.to/shreshthgoyal/a-guide-to-building-cli-tools-in-javascript-28nn

從頭開始寫簡單的程式語言 - 第 1 部分

如果您是開發人員,您就使用過程式語言。它們是讓電腦做你想做的事的好方法。也許您甚至已經深入研究過彙編或機器碼程式設計。許多人再也不想回來。但有些人想知道,我怎麼能透過做更多的低階程式來更折磨自己呢?我想更多地了解程式語言是如何製作的!開個玩笑,寫一門新語言並不像聽起來那麼糟糕,所以如果你有一點好奇心,我建議你留下來看看它是關於什麼的。 這篇文章的目的是簡單地介紹如何建立程式語言,以及如何建立自己的特殊語言。甚至可以用自己的名字命名。誰知道。 我還敢打賭,這似乎是一項令人難以置信的艱鉅任務。別擔心,因為我已經考慮過這一點。我盡力相對簡單地解釋了一切,沒有講*太多*離題的話。在這篇文章結束時,您將能夠建立自己的程式語言(將有幾個部分),但還有更多。了解幕後發生的事情會讓你更好地進行除錯。您將更能理解新的程式語言以及為何它們做出這樣的決定。如果我之前沒有提到的話,你可以擁有一種以你自己的名字命名的程式語言。而且,這真的很有趣。至少對我來說。 編譯器和解釋器 ------- 程式語言通常是高階的。也就是說,你看的不是0和1,也不是暫存器和組合程式碼。但是,您的電腦只能辨識 0 和 1,因此它需要一種方法來從您輕鬆讀取的內容轉變為機器可以輕鬆讀取的內容。該翻譯可以透過編譯或解釋來完成。 編譯是將原始語言的整個原始檔轉換為目標語言的過程。出於我們的目的,我們將考慮從全新的、最先進的語言一直編譯到可執行的機器碼。 ![簡單編譯圖](https://thepracticaldev.s3.amazonaws.com/i/c5difsv2s2rvk1iwzcih.png) 我的目標是讓「魔法」消失 解釋是或多或少直接執行原始檔中的程式碼的過程。我會讓你覺得這很神奇。 那麼,如何從易於閱讀的原始語言變成難以理解的目標語言呢? 編譯器的階段 ------ 編譯器可以透過多種方式分為多個階段,但有一種方法是最常見的。當你第一次看到它時,它的意義不大,但它是這樣的: ![編譯器的虛假階段](https://thepracticaldev.s3.amazonaws.com/i/cq1uo0x1gp7k73jldp7o.png) 哎呀,我選錯了圖,但這樣就可以了。基本上,您獲取原始文件,將其設置為計算機想要的格式(刪除空格之類的內容),將其更改為計算機可以很好地移動的格式,然後從中生成程式碼。還有更多。那是下次,或者如果你的好奇心快要殺死你的話,也可以供你自己研究。 詞法分析 ---- ### 又名“讓原始碼變得漂亮” 考慮以下完全虛構的語言,它基本上只是一個帶有分號的計算器: ``` // source.ect 3 + 3.2; 5.0 / 1.9; 6 * 2; ``` 計算機不需要所有這些。空間只適合我們狹隘的頭腦。還有新線?沒有人需要那些。電腦將您看到的程式碼轉換為可以使用的標記流,而不是原始檔案。基本上,它知道`3`是整數, `3.2`是浮點數, `+`是對這兩個值進行運算的東西。這就是計算機*真正*需要完成的全部工作。**詞法分析器**的工作是提供這些標記而不是原始程式。 它的實現方式非常簡單:給詞法分析器(詞法分析器的一種聽起來不那麼自命不凡的說法)一些期望的東西,然後告訴它當它看到這些東西時要做什麼。這些稱為*規則*。這是一個例子: ``` int cout << "I see an integer!" << endl; ``` 當 int 通過詞法分析器並執行此規則時,您將看到一個非常明顯的“我看到一個整數!”感嘆。這不是我們使用詞法分析器的方式,但看到程式碼執行是任意的很有用:沒有規則要求您必須建立某個物件並傳回它,它只是常規的舊程式碼。甚至可以用大括號括起來來使用多條線。 順便說一句,我們將使用稱為[FLEX 的](https://github.com/westes/flex)東西來進行詞法分析。它使事情變得非常簡單,但是沒有什麼可以阻止您自己編寫一個程式來執行此操作。 為了了解我們如何使用 flex,請看這個例子: ``` // scanner.lex /* Definitions */ %{ #include <iostream> using namespace std; extern "C" int yylex(); %} /* Rules next */ %% [0-9]+.[0-9]+ cout << "FLOAT: (" << yytext << ")" << endl; [0-9]+ cout << "INT: (" << yytext << ")" << endl; "+" cout << "PLUS" << endl; "-" cout << "MINUS" << endl; "*" cout << "TIMES" << endl; "/" cout << "DIVIDED BY" << endl; ";" cout << "SEMICOLON" << endl; [\t\r\n\f] ; /* ignore whitespace */ %% /* Code */ int main() { yylex(); } ``` 這引入了一些新概念,讓我們回顧一下它們: `%%`用於分隔 .lex 檔案的各個部分。第一部分是聲明 - 基本上是使詞法分析器更具可讀性的變數。這也是您導入的位置,由`%{`和`%}`包圍。 第二部分是規則,我們之前已經看過。這些基本上是一個大的`if` `else if`塊。它將執行*最長的匹配*行。因此,即使您更改 float 和 int 的順序,float 仍然會匹配,因為匹配`3.2`的 3 個字元比`3`的 1 個字元多。請注意,如果這些規則均不匹配,則會採用預設規則,只需將字元列印到標準輸出即可。然後,您可以使用`yytext`來引用它看到的與該規則相符的內容。 第三部分是程式碼,它只是在執行時執行的 C 或 C++ 原始碼。 `yylex();`是執行詞法分析器的函數呼叫。您也可以讓它從檔案中讀取輸入,但預設情況下它從標準輸入中讀取。 假設您將這兩個檔案建立為`source.ect`和`scanner.lex` 。我們可以使用`flex`命令建立一個 C++ 程式(假設您已經安裝了`flex` ),然後編譯它並輸入我們的原始程式碼以達到我們很棒的列印語句。讓我們將其付諸行動吧! ``` evan:ectlang/ $ flex scanner.lex evan:ectlang/ $ g++ lex.yy.c -lfl evan:ectlang/ $ ./a.out < source.ect INT: (3) PLUS FLOAT: (3.2) SEMICOLON FLOAT: (5.0) DIVIDED BY FLOAT: (1.9) SEMICOLON INT: (6) TIMES INT: (2) SEMICOLON evan:ectlang/ $ ``` 嘿,酷!您只需編寫將輸入與規則相符的 C++ 程式碼,以便執行某些操作。 現在,編譯器如何使用它?一般來說,每個規則都會返回一些東西,而不是打印一些東西——一個令牌!這些標記可以在編譯器的下一部分中定義... 語法分析器 ----- ### 又名“使漂亮的源程式碼可用” 是時候玩得開心了!一旦我們到達這裡,我們就開始定義程式的結構。解析器只是獲得一個標記流,它必須匹配該流中的元素,以使原始程式碼具有可用的結構。為了做到這一點,它使用了[語法](https://en.wikipedia.org/wiki/Formal_grammar),你可能在理論課上看到或聽到你奇怪的朋友閒聊的東西。它們非常強大,並且有很多東西需要了解,但我只會提供您需要了解的關於我們有點愚蠢的解析器的資訊。 基本上,語法將非終結符與終結符和非終結符的某種組合相匹配。終端是樹的葉子;非終端有孩子。如果這沒有意義,請不要擔心,程式碼可能會更容易理解。 我們將使用一個名為[Bison 的](https://www.gnu.org/software/bison/)解析器產生器。這次,為了解釋目的,我將把文件分成幾個部分。首先,聲明: ``` // parser.y %{ #include <iostream> using namespace std; extern "C" void yyerror(char *s); extern "C" int yyparse(); %} %union{ int intVal; float floatVal; } %start program %token <intVal> INTEGER_LITERAL %token <floatVal> FLOAT_LITERAL %token SEMI %type <floatVal> exp %type <floatVal> statement %left PLUS MINUS %left MULT DIV ``` 第一部分應該看起來很熟悉:我們正在導入我們想要使用的東西。之後就變得有點棘手了。 聯合是「真正的」C++ 類型到我們將在整個程式中呼叫它的類型的映射。因此,當我們看到`intVal`時,您可以將頭腦中的值替換為`int` ,而當我們看到`floatVal`時,您可以將頭腦中的值替換為`float` 。稍後你就會明白為什麼。 接下來我們來看看符號。您可以在腦海中將它們分為終結符和非終結符,就像我們之前討論的語法一樣。大寫字母表示終端,因此它們不會繼續擴展。小寫意味著非終結符,因此它們繼續擴展。這只是慣例。 每個聲明(以`%`開頭)聲明一些符號。首先,我們看到我們從一個非終端`program`開始。然後,我們定義一些標記。 `<>`括號定義回傳類型:因此`INTEGER_LITERAL`終端機回傳`intVal` 。 `SEMI`終端不會回傳任何內容。使用`type`可以對非終結符完成類似的操作,如將`exp`定義為傳回`floatVal`的非終結符時所見。 最後我們取得了優先權。我們知道 PEMDAS,或者您可能已經學過的任何其他縮寫詞,它告訴您一些簡單的優先規則:乘法在加法之前,等等。現在,我們以一種奇怪的方式在這裡聲明這一點。首先,清單中的位置越低意味著優先順序越高。其次,您可能想知道`left`是什麼意思。這就是關聯性:差不多,如果我們有`a op b op c` , `a`和`b`會在一起,還是`b`和`c`會在一起?我們的大多數運算子都執行前者,即`a`和`b`首先結合在一起:這稱為左結合性。某些運算子(例如求冪)會執行相反的操作: `a^b^c`期望您先提高`b^c`然後再提高`a^(b^c)` 。不過,我們不會處理這個問題。如果您想了解更多詳細訊息,請查看 Bison 頁面。 好吧,我可能已經厭倦了聲明,這是語法規則: ``` // parser.y %% program: /* empty */ | program statement { cout << "Result: " << $2 << endl; } ; statement: exp SEMI exp: INTEGER_LITERAL { $$ = $1; } | FLOAT_LITERAL { $$ = $1; } | exp PLUS exp { $$ = $1 + $3; } | exp MINUS exp { $$ = $1 - $3; } | exp MULT exp { $$ = $1 * $3; } | exp DIV exp { $$ = $1 / $3; } ; ``` 這就是我們之前講的文法。如果您不熟悉語法,這非常簡單:左側可以變成右側的任何內容,並用`|`分隔。 (邏輯`or` )。如果它可以走多條路徑,那就是不行的,我們稱之為*歧義語法*。由於我們的優先聲明,這並不含糊 - 如果我們更改它,使 plus 不再保持關聯,而是聲明為像`SEMI`這樣的`token` ,我們會看到發生移位/歸約衝突。想知道更多?看看Bison是如何運作的,提示,它使用LR解析演算法。 好的,所以`exp`可以是以下情況之一: `INTEGER_LITERAL` 、 `FLOAT_LITERAL`等。請注意,它也是遞歸的,因此`exp`可以變成兩個`exp` 。這允許我們使用複雜的表達式,例如`1 + 2 / 3 * 5` 。請記住,每個`exp`都會傳回一個 float 類型。 括號內的內容與我們在詞法分析器中看到的相同:任意 C++ 程式碼,但帶有更奇怪的語法糖。在這種情況下,我們有一些以`$`開頭的特殊變數。變數`$$`基本上就是回傳的內容。 `$1`是第一個參數傳回的內容, `$2`第二個參數傳回的內容,等等。我所說的「參數」是指語法規則的一部分:因此規則`exp PLUS exp`有參數 1 `exp` 、參數 2 `PLUS`和參數 3 `exp` 。因此,在程式碼執行中,我們將第一個表達式的結果加到第三個表達式中。 最後,一旦它回到`program`非終端,它將列印語句的結果。在這種情況下,程式是一堆語句,其中語句是一個表達式,後面跟著一個分號。 現在我們來編寫程式碼部分。這是當我們通過解析器時實際執行的內容: ``` // parser.y %% int main(int argc, char **argv) { if (argc < 2) { cout << "Provide a filename to parse!" << endl; exit(1); } FILE *sourceFile = fopen(argv[1], "r"); if (!sourceFile) { cout << "Could not open source file " << argv[1] << endl; exit(1); } // Sets input for flex to the file instead of standard in yyin = sourceFile; // Now let's parse it! yyparse(); } // Called on error with message s void yyerror(char *s) { cerr << s << endl; } ``` 好吧,這開始變得有趣了。我們的主函數現在從第一個參數提供的檔案而不是標準輸入中讀取,並且我們加入了一些錯誤程式碼。這是非常不言自明的,並且註釋很好地解釋了正在發生的事情,所以我將把它作為練習留給讀者來弄清楚這一點。您需要知道的是現在我們回到詞法分析器以向解析器提供標記!這是我們的新詞法分析器: ``` // scanner.lex %{ extern "C" int yylex(); #include "parser.tab.c" // Defines the tokens %} %% [0-9]+ { yylval.intVal = atoi(yytext); return INTEGER_LITERAL; } [0-9]+.[0-9]+ { yylval.floatVal = atof(yytext); return FLOAT_LITERAL; } "+" { return PLUS; } "-" { return MINUS; } "*" { return MULT; } "/" { return DIV; } ";" { return SEMI; } [ \t\r\n\f] ; /* ignore whitespace */ ``` 嘿嘿,現在確實變小了!我們看到的是,我們回傳的是終端符號,而不是列印。其中一些,例如整數和浮點數,我們首先在繼續之前設定值( `yylval`是終端符號的回傳值)。除此之外,它只是為解析器提供了一個終端標記流以供其自行決定使用。 酷,那麼讓我們執行吧! ``` evan:ectlang/ $ bison parser.y evan:ectlang/ $ flex scanner.lex evan:ectlang/ $ g++ lex.yy.c -lfl evan:ectlang/ $ ./a.out source.ect Result: 6.2 Result: 2.63158 Result: 12 ``` 我們開始了 - 我們的解析器列印出正確的值!但這並不是真正的編譯器,它只是執行 C++ 程式碼來執行我們想要的內容。為了製作編譯器,我們希望將其轉換為機器碼。為此,我們需要加入一點... 直到下次... ------- 我現在意識到這篇文章會比我想像的要長很多,所以我想我應該在這裡結束這篇文章。我們基本上有一個可用的詞法分析器和解析器,所以這是一個很好的停止點。 如果您對最終產品感到好奇,我已將原始程式碼放在[我的 Github](https://github.com/evantypanski/ectlang)上。隨著更多帖子的發布,該存儲庫將出現更多活動。 有了我們的詞法分析器和解析器,我們現在可以產生程式碼的中間表示,該中間表示最終可以轉換為真實的機器碼,我將向您展示具體的操作方法。 [第 2 部分來了!](https://dev.to/evantypanski/writing-a-simple-programming-language-from-scratch-part-2-2522) ### 其他資源 如果您想了解有關此處介紹的任何內容的更多訊息,我已經連結了一些內容以供您開始使用。我已經講了很多,所以這是我向您展示如何深入研究這些主題的機會。 - Flex 程式碼庫: <https://github.com/westes/flex> - 我們使用的詞法分析工具。 - Bison 文件: <https://www.gnu.org/software/bison/> - 我們使用的解析器產生器。這裡有很棒的文件。 - LALR 解析: <https://web.cs.dal.ca/~sjackson/lalr1.html> - 關於 LALR(1) 解析器(如 Bison 產生的解析器!)如何運作的精心解釋。 - 解決解析衝突: <http://www.cs.ecu.edu/karl/5220/spr16/Notes/Bottom-up/conflict.html> - 如何解決shift/reduce或reduce/reduce衝突,就像我們之前看到的那樣。 - Chomsky 層次結構: [https://en.wikipedia.org/wiki/Chomsky\_hierarchy](https://en.wikipedia.org/wiki/Chomsky_hierarchy) - 對此沒有詳細介紹,但我們使用了上下文無關語法,以便 Bison 可以編譯它。如果您需要上下文敏感性,那是為了後面的階段。 - 符號表: [https://www.tutorialspoint.com/compiler\_design/compiler\_design\_symbol\_table.htm](https://www.tutorialspoint.com/compiler_design/compiler_design_symbol_table.htm) - 使用符號表,編譯器如何處理變數。 哦,順便說一句,如果您不喜歡我的編譯器階段,這裡有一個實際的圖表。我仍然保留了符號表和錯誤處理程序。另請注意,許多圖表與此不同,但這最好地說明了我們所關心的內容。 ![編譯器的實際階段](https://thepracticaldev.s3.amazonaws.com/i/4bgv9blwleizrmcfw2fq.png) --- 原文出處:https://dev.to/evantypanski/writing-a-simple-programming-language-from-scratch-part-1-54a2

React 元件設計模式 - 第 1 部分

React 設計模式是開發人員用來解決使用 React 建立應用程式時遇到的常見問題和挑戰的既定實踐和解決方案。這些模式封裝了針對重複出現的設計問題的可重複使用解決方案,從而提高了可維護性、可擴展性和效率。它們提供了一種結構化的方法來組織元件、管理狀態、處理資料流和最佳化效能。 **請考慮以下 6 種 React 設計模式:** 1. 容器和呈現模式 2. HOC(高階元件)模式 3. 複合元件模式 4. 提供者模式(使用提供者進行資料管理) 5. 狀態減速器模式 6. 元件組成模式 1. 容器和呈現模式 ---------- 在此模式中,容器元件負責管理資料和狀態邏輯。它們從外部來源獲取資料,在必要時對其進行操作,並將其作為 props 傳遞給展示元件。它們通常連接到外部服務、Redux 儲存或上下文提供者。 另一方面,展示元件僅專注於 UI 元素的展示。他們透過 props 從容器元件接收資料,並以視覺上吸引人的方式呈現它。展示元件通常是無狀態的功能元件或純元件,這使得它們更容易測試和重複使用。 **讓我們考慮一個複雜的範例來說明這些模式:** 假設我們正在建立一個社群媒體儀表板,用戶可以在其中查看朋友的貼文並與他們互動。以下是我們建立元件的方式: **容器元件(FriendFeedContainer):** 該元件將負責從 API 獲取有關好友貼文的資料、處理任何必要的資料轉換以及管理提要的狀態。它將相關資料傳遞給展示元件。 ``` import React, { useState, useEffect } from 'react'; import FriendFeed from './FriendFeed'; const FriendFeedContainer = () => { const [friendPosts, setFriendPosts] = useState([]); useEffect(() => { // Fetch friend posts from API const fetchFriendPosts = async () => { const posts = await fetch('https://api.example.com/friend-posts'); const data = await posts.json(); setFriendPosts(data); }; fetchFriendPosts(); }, []); return <FriendFeed posts={friendPosts} />; }; export default FriendFeedContainer; ``` **展示元件(FriendFeed):** 該元件將從其父容器元件 (FriendFeedContainer) 接收好友貼文資料作為 props,並以視覺上吸引人的方式呈現它們。 ``` import React from 'react'; const FriendFeed = ({ posts }) => { return ( <div> <h2>Friend Feed</h2> <ul> {posts.map(post => ( <li key={post.id}> <p>{post.content}</p> <p>Posted by: {post.author}</p> </li> ))} </ul> </div> ); }; export default FriendFeed; ``` 透過以這種方式建立我們的元件,我們將獲取資料和管理狀態的問題與 UI 渲染邏輯分開。這種分離使得我們的 React 應用程式在擴充時可以更輕鬆地進行測試、重複使用和維護。 2.HOC(高階元件)模式 ------------- 高階元件 (HOC) 是 React 中的一種模式,可讓您跨多個元件重複使用元件邏輯。它們是接受元件並傳回具有附加功能的新元件的函數。 為了示範 HOC 在具有 React hook 的社群媒體儀表板範例中的使用,讓我們考慮一個場景,其中您有多個元件需要從 API 取得使用者資料。您可以建立一個 HOC 來處理資料獲取並將獲取的資料作為 props 傳遞給包裝的元件,而不是在每個元件中重複取得邏輯。 這是一個基本範例: ``` import React, { useState, useEffect } from 'react'; // Define a higher-order component for fetching user data const withUserData = (WrappedComponent) => { return (props) => { const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { // Simulate fetching user data from an API const fetchData = async () => { try { const response = await fetch('https://api.example.com/user'); const data = await response.json(); setUserData(data); setLoading(false); } catch (error) { console.error('Error fetching user data:', error); setLoading(false); } }; fetchData(); }, []); return ( <div> {loading ? ( <p>Loading...</p> ) : ( <WrappedComponent {...props} userData={userData} /> )} </div> ); }; }; // Create a component to display user data const UserProfile = ({ userData }) => { return ( <div> <h2>User Profile</h2> {userData && ( <div> <p>Name: {userData.name}</p> <p>Email: {userData.email}</p> {/* Additional user data fields */} </div> )} </div> ); }; // Wrap the UserProfile component with the withUserData HOC const UserProfileWithUserData = withUserData(UserProfile); // Main component where you can render the wrapped component const SocialMediaDashboard = () => { return ( <div> <h1>Social Media Dashboard</h1> <UserProfileWithUserData /> </div> ); }; export default SocialMediaDashboard; ``` 在這個例子中: - `withUserData`是一個高階元件,用於處理從 API 取得使用者資料。它包裝傳遞的元件 ( `WrappedComponent` ) 並將取得的使用者資料作為 prop ( `userData` ) 提供給它。 - `UserProfile`是一個功能元件,它接收`userData`屬性並顯示使用者設定檔資訊。 - `UserProfileWithUserData`是透過使用`withUserData`包裝`UserProfile`傳回的元件。 - `SocialMediaDashboard`是主要元件,您可以在其中呈現`UserProfileWithUserData`或任何其他需要使用者資料的元件。 使用此模式,您可以輕鬆地跨社交媒體儀表板應用程式中的多個元件重複使用資料取得邏輯,而無需重複程式碼。 3. 複合元件模式 --------- React 中的複合元件模式是一種設計模式,可讓您建立協同工作以形成有凝聚力的 UI 的元件,同時仍保持明確的關注點分離並提供自訂元件行為和外觀的靈活性。 在此模式中,父元件充當一個或多個子元件(稱為「複合元件」)的容器。這些子元件協同工作以實現特定的功能或行為。複合元件的關鍵特徵是它們透過父元件彼此共享狀態和功能。 以下是使用 hooks 在 React 中實作複合元件模式的簡單範例: ``` import React, { useState } from 'react'; // Parent component that holds the compound components const Toggle = ({ children }) => { const [isOn, setIsOn] = useState(false); // Function to toggle the state const toggle = () => { setIsOn((prevIsOn) => !prevIsOn); }; // Clone the children and pass the toggle function and state to them const childrenWithProps = React.Children.map(children, (child) => { if (React.isValidElement(child)) { return React.cloneElement(child, { isOn, toggle }); } return child; }); return <div>{childrenWithProps}</div>; }; // Child component for the toggle button const ToggleButton = ({ isOn, toggle }) => { return ( <button onClick={toggle}> {isOn ? 'Turn Off' : 'Turn On'} </button> ); }; // Child component for the toggle status const ToggleStatus = ({ isOn }) => { return <p>The toggle is {isOn ? 'on' : 'off'}.</p>; }; // Main component where you use the compound components const App = () => { return ( <Toggle> <ToggleStatus /> <ToggleButton /> </Toggle> ); }; export default App; ``` 在這個例子中: - `Toggle`是保存複合元件( `ToggleButton`和`ToggleStatus` )的父元件。 - `ToggleButton`是負責渲染切換按鈕的子元件。 - `ToggleStatus`是另一個負責顯示切換狀態的子元件。 - `Toggle`元件管理狀態 ( `isOn` ) 並提供`toggle`功能來控制狀態。它克隆其子級並將`isOn`狀態和`toggle`函數作為 props 傳遞給它們。 透過使用複合元件模式,您可以建立可重複使用和可組合的元件,封裝複雜的 UI 邏輯,同時仍允許自訂和靈活性。 ### 4. Provider Pattern(使用Provider進行資料管理) React 中的提供者模式是一種設計模式,用於跨多個元件管理和共用應用程式狀態或資料。它涉及建立一個封裝狀態或資料的提供者元件,並透過 React 的上下文 API 將其提供給其後代元件。 讓我們透過一個範例來說明 React 中用於管理使用者身份驗證資料的 Provider 模式: ``` // UserContext.js import React, { createContext, useState } from 'react'; // Create a context for user data const UserContext = createContext(); // Provider component export const UserProvider = ({ children }) => { const [user, setUser] = useState(null); // Function to login user const login = (userData) => { setUser(userData); }; // Function to logout user const logout = () => { setUser(null); }; return ( <UserContext.Provider value={{ user, login, logout }}> {children} </UserContext.Provider> ); }; export default UserContext; ``` 在這個例子中: - 我們使用 React 的`createContext`函數來建立一個名為`UserContext`上下文。此上下文將用於跨元件共享用戶資料和與身份驗證相關的功能。 - 我們定義一個`UserProvider`元件作為`UserContext`的提供者。該元件使用`useState`鉤子管理使用者狀態,並提供`login`和`logout`等方法來更新使用者狀態。 - 在`UserProvider`內部,我們用`UserContext.Provider`包裝`children` ,並將`user`狀態以及`login`和`logout`函數作為提供者的值傳遞。 - 現在,任何需要存取使用者資料或驗證相關功能的元件都可以使用`useContext`掛鉤來使用`UserContext` 。 讓我們建立一個使用上下文中的使用者資料的元件: ``` // UserProfile.js import React, { useContext } from 'react'; import UserContext from './UserContext'; const UserProfile = () => { const { user, logout } = useContext(UserContext); return ( <div> {user ? ( <div> <h2>Welcome, {user.username}!</h2> <button onClick={logout}>Logout</button> </div> ) : ( <div> <h2>Please log in</h2> </div> )} </div> ); }; export default UserProfile; ``` 在此元件中: 我們匯入`UserContext`並使用`useContext`鉤子來存取`UserProvider`提供的使用者資料和`logout`功能。 根據使用者是否登錄,我們呈現不同的 UI 元素。 最後,我們用`UserProvider`包裝我們的應用程式,以使用戶資料和身份驗證相關的功能可供所有元件使用: ``` // App.js import React from 'react'; import { UserProvider } from './UserContext'; import UserProfile from './UserProfile'; const App = () => { return ( <UserProvider> <div> <h1>My App</h1> <UserProfile /> </div> </UserProvider> ); }; export default App; ``` 透過這種方式,Provider 模式允許我們跨多個元件管理和共享應用程式狀態或資料,而無需進行 prop 鑽取,從而使我們的程式碼更乾淨、更易於維護。 --- 原文出處:https://dev.to/fpaghar/react-component-design-patterns-part-1-5f0g

歡迎串 Part.16 👋👋👋

歡迎最近新註冊的用戶 @id3p6688 @katiewilson8210 @linejkf552 @tws9666 @8945689 @swotoutty @leemotseng @bear645 @jf76501 @assdee @kk00336 @Adjutle @imxp978 @jeff858484 @ytb0315 @Iven10252158 @AppleLily @wmw588ss52 @Weboai @1891722 @react @cian1223 @dd2d2d41 @vincentchen0606 @slinexm594 @sunmoonsky @reelvetut @4000889 @HansChang83 @wsx1688 @zc235475 @jensen @idziq888 @s1s2s4s5h @Imardaxia @vidmateApk @Skylar0628 @z2837777 @pikashowsapk1 @instaproapps @Vidmateapp @pon0620 @PikaShow23 @Excarrath @tenkai0812 @ScottX8 @Hannahdrorn @Learn20240303 @simon5537 @arielh1004 @hhkwe1 @line771554 @kinemasters @kgi-design-team @doochulk @pdmod @Cartinson @futemaxapk @av0988 @kinemastersapk @Taulinip @rectvindir @rhwang @Chengyi-Lin @nfc851026 @lanni0619 @max002215 @lline966757 @Luckypatcher1s @gary880306 @zzliou @hdstreamzapk03 @PinoyLambingan @aaronzhan0906 @lu4707xeop @Mia1314echooo @TsuPeiHo @roynitisha29 @ovallyfef @qun18009 @nozomi @Aaron @Filmy4WapAPK1 @lu10510 @joeyoldbar @prochini @Yutai @sherry860722 @hzf9Nf5P @yunie0216st @pikashowshd @Kuo-TingKai @alexwu @chelsea22222 @zoetanghk852 @MagicBenson @xd890872680 @Nehaambore01 @Note @shabazsayyed @1084926 @kenni1107 @0711kps @Shuntw6096 @capcutt @TopFollowAPK @YICHI1018 @kaede1904 @HxVfHNMpK @universitykart0101 @pikashowapks @ishad1 @GustavoFringgg @skrbomb @Luckypatcherapk @wwertytrd3 @av9397 @Asn66 @MarioooooLin @snapptube891 歡迎在留言處簡單自介一下~ 可以說一下從哪邊發現這論壇的~最近在學什麼東西~或者是關於你的一件小趣事~ 也請大家多多回覆別人的留言,問別人小問題,或者回個「嗨嗨 👋」都很好~ **歡迎各位加入這個技術成長社群!**

每個 Web 開發人員都需要的資源

我編制了一份工具列表,這些工具多年來幫助我建立了供個人使用和客戶使用的網站。讓我們開始吧,與網路上許多其他可用於增強網站外觀的資源相比,這是一個很小的清單。 **學習 Web 開發的網站** - 程式碼學院 - 奧丁計劃 - 前端導師 - JavaScript30 - Coursera - 可汗學院 - 免費程式碼訓練營 - MDN 網路文件 - W3學校 - 斯林巴 **學習 Web 開發的 YouTube 頻道** - 簡化網頁開發 - 編碼器 編碼器 - 編碼列車 - 自由程式設計營 - 穿越媒體 - 網路忍者 - 與哈利一起編程(印地語) \*\*HTML/CSS 範本網站 \*\* - HTML5UP - HTML修訂版 - 自由CSS - 模板化 - 免費HTML5 - 啟動引導程式 - 引導製作 - 靴子手錶 - 引導品味 - 克魯普 - 車頂板 - HTML5xCSS3 **使用的程式碼編輯器** - Visual Studio 程式碼(VS 程式碼) - 崇高的文字 - 括號 - 為什麼 **JavaScript 動畫庫** - Anime.js:輕量級 JavaScript 動畫函式庫。 - ScrollReveal.js:當元素進入視窗時輕鬆顯示元素。 - Popmotion:功能齊全、靈活的 JavaScript 運動庫。 - AniJS:CSS 動畫的聲明性處理庫。 - Wow.js:向下捲動頁面時顯示 CSS 動畫。 - Typed.js:一個類型的 JavaScript 函式庫。 - Velocity.js:加速 JavaScript 動畫。 - GSAP:現代網路的專業級動畫。 **向量、圖像和插圖網站** - Freepik:發現免費向量、照片、PSD 和圖示。 - Vecteezy:尋找高品質的向量藝術、圖形和插圖。 - Unsplash:存取超過一百萬張免費高解析度照片。 - Pixabay:探索龐大的免費圖片和影片庫。 - Flaticon:下載免費圖示、SVG、PSD、PNG、EPS 格式或 ICON FONT。 - Openclipart:分享和使用免費的剪貼畫和圖像。 - SVGRepo:免費下載 SVG。 - Vectorportal:免費向量、剪貼畫和圖示。 - SVGBackgrounds:可自訂的 SVG 圖案和背景。 - FreeDesignFile:高品質的圖形設計資源。 - Pexels:尋找才華橫溢的創作者分享的免費庫存照片和影片。 - Vectorian:下載免版稅向量藝術、庫存照片和素材。 我希望這些資源能幫助您建立網站。 謝謝閱讀。我們下一篇部落格見!😊🙌 --- 原文出處:https://dev.to/monica_w/resources-every-web-developer-needs-18m0

JavaScript 的 25 個不明顯的特性

通常,作為開發人員,我們會編寫類似類型的程式碼,陷入雖然舒適但有時感覺很平凡的模式。 然而,JavaScript 的世界是廣闊的,充滿了高級功能,當發現和使用這些功能時,可以將我們的開發工作變得更加令人興奮和充實。 在本指南中,我們將揭曉 25 個高級 JavaScript 功能,這些功能不僅能揭示這些隱藏的瑰寶,還能將您對 JavaScript 的掌握提升到前所未有的水平。 讓我們一起踏上這段發現之旅,將 JavaScript 的高級功能整合到我們的編碼庫中,以建立更有效率、更優雅、更強大的應用程式。是時候為我們的開發任務注入新的樂趣和創造力了。 1 — 迴圈和區塊語句的標籤 -------------- JavaScript 允許標記迴圈和區塊語句,從而可以使用`break`和`continue`進行精確控制。 ``` outerLoop: for (let i = 0; i < 5; i++) { innerLoop: for (let j = 0; j < 5; j++) { if (i === 2 && j === 2) break outerLoop; console.log(`i=${i}, j=${j}`); } } ``` 2 - 逗號運算符 --------- 逗號運算子允許按序列計算多個表達式,並傳回最後一個表達式的結果。 ``` let a = (1, 2, 3); // a = 3 ``` 3 — 字串格式化以外的標記範本文字 ------------------ 除了建立字串之外,標記範本還可用於 DSL(域特定語言)、清理使用者輸入或本地化。 ``` function htmlEscape(strings, ...values) { // Example implementation } ``` 4 — 區塊內的函數聲明 ------------ 儘管不推薦,但 JavaScript 允許在區塊內聲明函數,這可能會導致非嚴格模式下的不同行為。 ``` if (true) { function test() { return "Yes"; } } else { function test() { return "No"; } } test(); // Behavior varies depending on the environment ``` 5 — 無效運算符 --------- `void`運算子計算任何表達式,然後傳回未定義的值,這對於 JavaScript 的超連結很有用。 ``` void (0); // returns undefined ``` 6 — 用於快速數學的位元運算符 ---------------- 位運算符,例如`|`和`&` ,可以更快地執行一些數學運算,但會犧牲可讀性。 ``` let floor = 5.95 | 0; // Fast way to do Math.floor(5.95) ``` 7 — with 處理物件的語句 ---------------- `with`語句擴展了區塊的作用域鏈,讓您可以編寫更短的程式碼。但是,出於可讀性和效能方面的考慮,不建議這樣做。 ``` with(document.getElementById("myDiv").style) { background = "black"; color = "white"; } ``` [![Christian Heilmann 的 JavaScript 開發技能分享課程](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h758ymfla96s5md0vyf3.png)](https://skillshare.eqcm.net/QyLx6z) 與 Christian Heilmann 一起提高您的 JavaScript 技能 - 從今天開始編寫更乾淨、更快、更好的程式碼! 8 — 自動分號插入 (ASI) ---------------- JavaScript 嘗試修復缺少的分號,但依賴它可能會導致意外結果。 ``` let x = 1 let y = 2 [x, y] = [y, x] // Without proper semicolons, this could fail ``` 9 — 在屬性檢查操作員中 ------------- 檢查物件是否具有屬性,而無需直接存取其值。 ``` "toString" in {}; // true ``` 10——instanceof 與 typeof ----------------------- `instanceof`檢查原型鏈,而`typeof`傳回一個字串,指示未計算的操作數的類型。 ``` function Person() {} let person = new Person(); console.log(person instanceof Person); // true console.log(typeof person); // "object" ``` 11 — ES6 中的區塊級函數 ---------------- [ES6](https://www.webdevstory.com/mastering-es6-for-react/)允許函數具有區塊作用域,類似`let`和`const` 。 ``` { function test() { return "block scoped"; } } console.log(typeof test); // "function" in non-strict mode, "undefined" in strict mode ``` 12 — 偵錯器語句 ---------- 使用`debugger`語句暫停執行並開啟偵錯器。 ``` function problematicFunction() { debugger; // Execution pauses here if the developer tools are open } ``` **13 —**用於動態程式碼執行的`eval()` -------------------------- `eval`將字串作為 JavaScript 程式碼執行,但會帶來重大的安全性和效能影響。 ``` eval("let a = 1; console.log(a);"); // 1 ``` [![InMotion Hosting 促銷虛擬主機計劃](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vevir51wi5coo4gtq3db.png)](https://partners.inmotionhosting.com/k0EPWv) 利用 InMotion Hosting 的一系列計劃(從共享伺服器到 VPS 以及專用伺服器)為您的專案找到合適的託管解決方案。 **14 — 非標準**`__proto__`屬性 ------------------------- 雖然`__proto__`被廣泛支持用於設定物件的原型,但它是非標準的。請改用`Object.getPrototypeOf()`和`Object.setPrototypeOf()` 。 ``` let obj = {}; obj.__proto__ = Array.prototype; // Not recommended ``` 15 — document.write() 用於直接文件編輯 ------------------------------ `document.write()`直接寫入 HTML 文件,但使用它可能會產生負面影響,特別是同步載入外部腳本。 ``` document.write("<h1>Hello World!</h1>"); ``` 16 — 鍊式分配 --------- JavaScript 允許鍊式賦值,它可以在一個語句中將單一值指派給多個變數。 ``` let a, b, c; a = b = c = 5; // Sets all three variables to the value of 5 ``` 17 — 屬性存在的 in 運算符 ----------------- `in`運算子檢查屬性是否存在於物件中,而不存取屬性值。 ``` const car = { make: 'Toyota', model: 'Corolla' }; console.log('make' in car); // true ``` 18 — 物件屬性簡寫 ----------- 為物件指派屬性時,如果屬性名稱與變數名稱相同,則可以使用簡寫。 ``` const name = 'Alice'; const age = 25; const person = { name, age }; ``` 19 — 預設參數值和解構組合 --------------- 您可以將預設參數值與函數參數中的解構結合起來,以獲得更具可讀性和更靈活的函數定義。 ``` function createPerson({ name = 'Anonymous', age = 0 } = {}) { console.log(`Name: ${name}, Age: ${age}`); } createPerson({ name: 'Alice' }); // Name: Alice, Age: 0 createPerson(); // Name: Anonymous, Age: 0 ``` **20 — 使用**`Array.fill()`初始化陣列 ------------------------------ 使用`fill()`方法使用特定值快速初始化陣列。 ``` const initialArray = new Array(5).fill(0); // Creates an array [0, 0, 0, 0, 0] ``` [![符合人體工學的工作空間設置,辦公桌上放置一台筆記型電腦,由 LED 檯燈照明,35.4 英寸的亮度為 500 勒克斯,周圍擺滿了書籍和辦公用品。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ly6b54e50pprze3p6lc9.jpg)](https://amzn.to/3VhQFae) 利用可調式 LED 照明優化您的工作空間,提高工作效率和舒適度,為專注的工作會議創造理想的環境。 **21 —** `Array.includes()`用於存在檢查 --------------------------------- 使用`includes()`方法可以輕鬆檢查陣列中是否存在元素,這比使用`indexOf()`更具可讀性。 ``` const fruits = ['apple', 'banana', 'mango']; console.log(fruits.includes('banana')); // true ``` 22 — 解構別名 --------- `destructuring`物件時,可以使用別名將屬性指派給具有不同名稱的變數。 ``` const obj = { x: 1, y: 2 }; const { x: newX, y: newY } = obj; console.log(newX); // 1 ``` 23 — 預設值的空合併運算符 --------------- 使用`??`僅在處理`null`或`undefined`時提供預設值,而不是其他`falsy`值,例如 ``` const count = 0; console.log(count ?? 10); // 0, because count is not null or undefined ``` 24 — 動態函數名稱 ----------- 使用物件字面量中的計算屬性名稱建立具有動態名稱的函數。 ``` const dynamicName = 'func'; const obj = { [dynamicName]() { return 'Dynamic Function Name!'; } }; console.log(obj.func()); // "Dynamic Function Name!" ``` 25 — 私有類別字段 ----------- 使用 hash `#`前綴定義類別中的私有字段,該字段無法從類別外部存取。 ``` class Counter { #count = 0; increment() { this.#count++; } getCount() { return this.#count; } } ``` 簡單總結 --- 當我們結束 25 個 JavaScript 高階功能的探索時,JavaScript 的函式庫既龐大又強大。 我們深入研究的每個功能都為解決編碼挑戰開闢了新途徑,類似於在我們的工具包中加入創新工具。 這不僅增強了我們創造性、有效率地制定解決方案的能力,而且還強調了 JavaScript 的動態多功能性。 這些進階功能凸顯了持續學習在 Web 開發領域的關鍵角色。 接受這些細微差別並將它們整合到我們的日常編碼實踐中,使我們能夠提高我們的技能並為[網路技術的發展](https://www.simplilearn.com/what-is-web-1-0-web-2-0-and-web-3-0-with-their-difference-article)做出貢獻。 請記住,[掌握 JavaScript 的](https://www.webdevstory.com/javascript-essential-terms/)道路是一個持續的旅程,每一行程式碼都提供了發現非凡事物的機會。 讓我們不斷突破 JavaScript 所能實現的極限,保持好奇心並對未來的無限可能性保持開放。 ***支持我們的技術見解*** [![請我喝杯咖啡](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsm9uucbnw7x9iw0loxr.png)](https://www.buymeacoffee.com/mmainulhasan) [![透過 PayPal 按鈕捐贈](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipnhbim2ba56kt32zhn3.png)](https://www.paypal.com/donate/?hosted_button_id=GDUQRAJZM3UR8) 注意:此頁面上的某些連結可能是附屬連結。如果您透過這些連結進行購買,我可能會賺取少量佣金,而您無需支付額外費用。感謝您的支持! --- 原文出處:https://dev.to/mmainulhasan/25-unnoticeable-features-of-javascript-15l1

在各種部落格平台,放進自己的的程式碼

在本文中,我將概述各種[部落格平台及其程式碼注入功能](https://bloggingplatforms.app/blog/blogging-platforms-with-the-best-code-injection)。 想像自己是個博主,無論您是經驗豐富還是剛起步;你不僅僅是一個文字大師。您擁有 Web 開發技能,並且渴望控制線上主頁的外觀、感覺和功能。這就是部落格平台上的程式碼注入可以發揮作用的地方! 我將指導您使用[最好的部落格平台](https://bloggingplatforms.app/)來注入自訂程式碼。 **為什麼程式碼注入很重要以及它如何使您受益** 讓我們分解一下:程式碼注入意味著在您的部落格中加入自訂程式碼(如 HTML、CSS 或 JavaScript)。此功能很有用,因為: - **獨特的設計:**擺脫模板並個性化您部落格的視覺效果。 - **特殊功能:**考慮互動式元素、自訂小工具或獨特的動畫。 - **資料追蹤:**整合您喜歡的分析工具以獲得深入的見解。 - **功能改進:**優化頁面載入速度或實現複雜功能。 ![為部落格平台注入程式碼的好處](https://bloggingplatforms.app/media/posts/blogging-platforms-with-the-best-code-injection/benefits-of-code-injection.webp) **具有最佳程式碼注入選項的 Bogging 平台** --------------------------- ### **WordPress:程式碼注入動力源** 作為內容管理系統 (CMS) 領域無可爭議的皇帝, [WordPress](https://wordpress.com/)擁有無與倫比的程式碼注入能力。 其**主題自訂**功能可讓您直接存取主題文件,使您能夠透過主題編輯器無縫整合自訂程式碼片段。 此外,**程式碼片段**和**插入頁首和頁尾等**[WordPress 外掛](https://wordpress.com/plugins)提供了使用者友善的介面,用於將程式碼注入部落格佈局的特定部分,例如頁首、頁尾或側邊欄。 #### **探索 WordPress 的程式碼注入功能:** - **主題編輯:**對於技術嫻熟的人來說,WordPress 透過內建主題編輯器授予對主題文件的完全存取權。這種精細的控制使您能夠將程式碼直接注入到模板檔案(header.php、footer.php 等)中,從而對部落格的佈局和功能進行深度自訂。 - **外掛程式:**廣泛的 WordPress 外掛程式生態系統提供了大量的程式碼注入工具。程式碼片段和插入頁首和頁尾等插件提供了用戶友好的介面,用於將程式碼片段注入部落格的特定區域,滿足所有技術背景的用戶的需求。這些插件通常配備程式碼語法突出顯示和錯誤檢查等功能,從而簡化程式碼注入過程。 #### **重要考慮因素:** - **主題相容性:**直接注入主題檔案的程式碼本質上連結到您正在使用的特定主題。如果切換主題,您可能需要修改或重新註入程式碼片段。 - **插件依賴:**雖然插件簡化了程式碼注入,但它們引入了外部依賴項。確保您選擇的外掛程式得到積極維護並與您的 WordPress 版本相容。 --- ### **Webflow:透過視覺編碼為精通設計的部落客提供支持** [Webflow](https://webflow.grsm.io/gr15bvbdlvfo)提出了一個獨特的主張:一個**視覺化開發平台**,讓您無需編寫任何程式碼即可建立自訂網站。正因為如此,Webflow 是[藝術家最好的部落格平台](https://bloggingplatforms.app/blog/best-blog-platforms-for-artists)之一。 然而,對於那些尋求更精細控制的人來說, [Webflow](https://webflow.grsm.io/gr15bvbdlvfo)提供了一個程式碼注入功能,恰當地命名為**「自訂程式碼」** 。此功能可讓您將程式碼片段直接注入網站的專案設定中,從而可以微調特定的設計元素和功能。 #### **釋放 Webflow 的程式碼注入潛力:** - **自訂程式碼:** Webflow 的自訂程式碼功能可讓您存取專案設定中指定的程式碼注入區域。在這裡,您可以注入針對網站設計和功能的特定方面的程式碼片段,例如加入自訂動畫、整合第三方服務或修改現有元素的行為。 - **Visual Canvas:** Webflow 的主要優點在於其視覺化開發介面。您無需直接操作程式碼即可實現顯著程度的自訂。然而,自訂程式碼為那些尋求更深入研究並釋放程式碼注入全部威力的人提供了一個逃生口。 #### **重要考慮因素:** - **學習曲線:**雖然 Webflow 提供了視覺化開發介面,但了解基本的 HTML、CSS 和 JavaScript 對於有效的程式碼注入非常有益。 - **範圍有限:**與 WordPress 等成熟的 CMS 相比,Webflow 的程式碼注入功能旨在微調設計元素和功能,而不是進行廣泛的結構修改。 --- ### **WebWave:為新手部落客簡化程式碼注入** [WebWave](https://webwave.me/ref/14072231082)是另一個視覺化網站建立器,它迎合了那些在用戶友好的拖放功能和注入自訂程式碼的能力之間尋求平衡的人。其**程式碼編輯器**可讓您將程式碼片段注入網站佈局的特定部分,從而提供個人化的途徑,而無需廣泛的編碼專業知識。 **探索 WebWave 的程式碼注入功能:** - **程式碼編輯器:** WebWave 的程式碼編輯器可讓您將程式碼片段直接注入網站頁面的指定部分。這種有針對性的方法可讓您自訂特定元素,例如新增自訂按鈕、整合小工具或修改現有功能。 - **初學者友善的介面:**程式碼編輯器提供了一個相對用戶友好的介面,具有語法突出顯示和基本的錯誤檢查功能,即使對 Web 開發有基本了解的部落客也可以使用它。 #### **重要考慮因素:** - **範圍有限:** WebWave 的程式碼注入功能著重於在地化修改,而不是對網站進行全面的結構變更。 - **外部依賴:**依賴 WebWave 的程式碼編輯器意味著一定程度的平台鎖定。如果您要遷移網站,自訂程式碼片段可能需要修改。 --- ### **Typedream:無程式碼環境中的程式碼注入** [Typedream](https://typedream.com/?via=blogplatforms)將自己定位為無程式碼網站建立器,在建立精美網站時優先考慮易用性。然而,它提供了有針對性的程式碼注入功能來擴展其功能並允許超出預定模板的自訂。 #### **Typedream 的程式碼注入潛力:** - **嵌入元素:**透過嵌入程式碼片段,將第三方服務、小工具或自訂元素整合到 Typedream 網站的特定區域。 - **功能增強:**透過自訂互動、動畫或獨特的視覺效果擴展 Typedream 的內建功能。 #### **重要考慮因素:** - **無程式碼優先:** Typedream 的優勢在於其簡單性和使用者友善性。程式碼注入可作為高級使用者工具使用,而不是其主要的客製化方法。 --- ### **Unicorn:程式碼注入以實現靈活性** [Unicorn Platform](https://unicornplatform.com/?via=bloggingplatforms)透過其拖放介面簡化了網站開發流程。此外,它還提供**程式碼注入**功能,可讓您透過將程式碼片段直接插入特定部分來進一步自訂您的網站。 #### **Unicorn 的程式碼注入潛力:** - **有針對性的客製化:** Unicorn 的程式碼注入功能可讓您修改單一元件或為您的網站加入新功能。整合第三方服務、建立自訂動畫或根據需要調整樣式。 - **使用者友善的介面:** Unicorn 對簡單性的關注延伸到了其程式碼注入功能,使其對於具有基本 Web 開發知識的使用者來說易於使用。 #### 重要考慮因素: - **平台依賴:**請記住,注入 Unicorn 的自訂程式碼片段依賴於平台。如果您決定遷移站點,程式碼相容性可能會成為一個問題。 --- ### **Dorik:無程式碼,但具有客製化潛力** [Dorik](https://dorik.com/?ref=can-burak72)將自己定位為專注於無程式碼原則的未來網站建立者。它的目的是擺脫傳統的頁面建立器。雖然 Dorik 並沒有專注於程式碼注入,但它在其新穎的框架中提供了客製化潛力。 #### **Dorik 的客製化潛力:** - **程式碼探索:**期望在 Dorik 上採用更具實驗性的客製化方法,可能涉及獨特的視覺化工具,而不是直接程式碼注入。 #### **重要考慮因素:** - **不斷發展的平台:** Dorik 相對較新,因此其客製化理念可能會隨著時間的推移而不斷發展。隨時了解他們的發展路線圖以獲取更多見解。 --- ### **Wix:用於擴展功能的程式碼注入** [Wix](https://www.wix.com/)是一種流行的拖放式網站建立器,透過其開發人員工具集(稱為**Corvid by Wix(以前稱為 Velo))**提供程式碼注入功能。這個先進的平台使您能夠透過編寫自訂 JavaScript 程式碼和整合外部 API 來擴展 Wix 網站的功能。 #### **利用 Corvid(以前稱為 Velo)的力量:** - **以 JavaScript 為中心:** Corvid 主要依賴 JavaScript 來實作自訂功能。對 JavaScript 的深入理解對於充分發揮其潛力至關重要。 - **API 整合:** Corvid 可以整合第三方服務和 API,從而增強您網站的功能,超越 Wix 內建工具的限制。 - **專用開發環境:**存取 Corvid 需要轉向專用開發環境,這可能會為非程式設計師帶來更陡峭的學習曲線。 #### **重要考慮因素:** - **陡峭的學習曲線:**有效利用 Corvid 需要 JavaScript 的應用知識和熟悉程式設計概念。 - **Wix 生態系內的有限控制:雖然很靈活,但 Corvid 的功能僅限於 Wix 生態系的範圍內。**廣泛的網站結構修改可能會受到限制。 --- ### **Squarespace:在易用性和客製化之間取得平衡** [Squarespace](https://www.squarespace.com/)以其用戶友好的介面和拖放功能而聞名,提供了一種經過衡量的程式碼注入方法。其**程式碼注入**功能可讓您將程式碼片段插入網站的指定區域,從而無需具備豐富的編碼知識即可實現有針對性的自訂。 #### **釋放 Squarespace 的程式碼注入潛力:** - **有針對性的客製化:** Squarespace 的程式碼注入功能適合特定的修改。嵌入自訂字體、整合社交媒體來源或加入自訂按鈕來增強部落格的外觀和感覺。 - **初學者友善的介面:** Squarespace 的程式碼注入功能優先考慮可存取性。即使具有最少編碼經驗的使用者也可以導航介面並實現基本的程式碼修改。 #### **重要考慮因素:** - **範圍有限:** Squarespace 的程式碼注入主要專注於在地化自訂,而不是對網站進行徹底的結構變更。 - **平台依賴:**注入 Squarespace 的自訂程式碼片段與平台相關。如果您要遷移網站,程式碼相容性可能會成為問題。 --- ### **Pixpa:攝影師和創意人員的程式碼注入** [Pixpa](https://www.pixpa.com/signup?refcode=BURAK10)專門為尋求展示作品平台的攝影師和創意專業人士提供服務。雖然其核心功能圍繞著令人驚嘆的視覺效果,但 Pixpa 為尋求額外控制的人提供了**程式碼注入**功能。 #### **Pixpa 的程式碼注入功能:** - **增強的功能:**注入程式碼片段以整合第三方服務,例如分析工具、支付網關或自訂聯絡表單,從而擴展部落格的功能。 - **易於使用:** Pixpa 的程式碼注入功能提供了使用者友善的介面和清晰的說明,使得非程式設計師也可以輕鬆使用。 #### **重要考慮因素:** - **有限的客製化:** Pixpa 的程式碼注入主要針對功能增強而不是廣泛的設計修改。 - **專注於利基市場:**雖然 Pixpa 非常適合創意人士,但它可能無法像更通用的平台那樣有效地迎合所有部落格利基市場。 --- ### **PageCloud:用於有針對性的增強的程式碼注入** [PageCloud](https://www.pagecloud.com/home-partners?gspk=Y2FuYnVyYWtzb2Z5YWxpb2dsdTMwNTQ&gsxid=Is2tDreeXpFm&utm_campaign=affiliate-referral-program&utm_medium=partner&utm_source=canburaksofyalioglu3054)將自己定位為具有程式碼注入功能的用戶友好的網站建立器。其**自訂程式碼**功能可讓您將程式碼片段注入網站的特定部分,從而實現有針對性的自訂。 #### **探索 PageCloud 的程式碼注入選項:** - **特定部分:** PageCloud 的程式碼注入著重於修改網站的指定部分,例如頁首、頁尾或側邊欄。這種方法非常適合整合小部件、加入自訂按鈕或微調佈局。 - **初學者的介面:** PageCloud 透過其程式碼注入功能優先考慮易用性。清晰的說明和用戶友好的介面使得那些具有最少編碼經驗的人也可以使用它。 #### **重要考慮因素:** - **範圍有限:**與 Squarespace 類似,PageCloud 的程式碼注入功能面向局部增強,而不是廣泛的結構修改。 - **平台依賴性:**自訂程式碼片段依賴PageCloud。從平台遷移可能需要程式碼調整或重新註入。然而,這可以被視為 PageCloud 提供的易用性和用戶友好介面的權衡。 --- ### **Blogger:具有基本程式碼注入功能的免費平台** [Blogger](https://www.blogger.com/)上提供了基本等級的程式碼注入功能,Blogger 是 Google 的免費部落格平台。透過其**HTML/CSS 編輯**功能,您可以存取和修改部落格範本的底層程式碼。 #### **Blogger 的程式碼注入潛力:** - **直接模板編輯:**對於熟悉程式碼的人,Blogger 授予對其模板文件的存取權限,從而可以直接修改控制部落格佈局和樣式的 HTML 和 CSS 程式碼。 - **功能有限:** Blogger 的程式碼注入功能是針對基本自訂,主要涉及編輯現有程式碼,而不是注入全新的功能。 #### **重要考慮因素:** - **所需技術專長:**直接模板編輯需要對 HTML 和 CSS 有深入的了解。對於初學者來說,這種方法可以呈現更陡峭的學習曲線。 - **控制有限:**與付費平台相比,Blogger 的免費方案對客製化施加了限制。 --- ### **Framer:為 Web 開發人員提供程式碼驅動的客製化** [Framer](https://www.framer.com/)是一個強大的視覺設計平台,整合了 React 編碼功能,為 Web 開發人員提供了一個遊樂場。雖然 Framer 主要針對應用程式開發,但其以程式碼為中心的方法為在其生態系統中建立部落格的人員提供了廣泛的客製化可能性。 #### **探索 Framer 的程式碼注入功能:** - **以開發人員為中心的客製化:**使用 Framer,您基本上可以使用 React 元件建立博客,從而可以對從佈局到互動的各個方面進行細粒度控制。 - **無限的可能性:** Framer 的程式碼注入幾乎是無限的,使您能夠製作一個突破傳統設計和功能界限的部落格。 #### **重要考慮因素:** - **陡峭的學習曲線:**有效利用 Framer 需要對 React 和 Web 開發原理有深入的了解。 - **資源密集:**與程式碼密集度較低的平台相比,在 Framer 中開發部落格可能需要更多時間和開發資源。 --- ### **Ghost:為精通程式碼的用戶提供開源靈活性** [Ghost](https://ghost.org/)以專注於純粹的部落格而聞名,提供了對開發人員有吸引力的開源靈活性。其主題結構利用 Handlebars 模板語言,允許全面的程式碼注入和客製化。 #### **Ghost的程式碼注入潛力:** - **完整的主題自訂:** Ghost 的開源特性可讓您直接修改主題文件,從而對部落格的結構和設計提供高階的控制。 - **社區驅動的支持:** Ghost 受益於蓬勃發展的開發者社區,該社區共享主題、程式碼片段和知識,培育了豐富的生態系統。 #### **重要考慮因素:** - **技術專長:** Ghost 中的有效程式碼注入需要熟悉 Handlebars 範本和 Web 開發原則。 - **自架:**為了實現完全客製化的潛力,自架您的 Ghost 部落格可能是必要的,這會帶來額外的技術開銷。 --- ### **曝光:攝影師的程式碼注入** [Exposure](https://exposure.co/)專注於視覺敘事,並擁有時尚的介面。它使攝影師和那些有視覺傾向的人能夠建立令人驚嘆的部落格來展示他們的作品。 Exposure 提供了注入自訂程式碼的能力,為進一步個性化您的線上形像打開了大門。 #### **了解 Exposure 的程式碼注入功能:** - **有針對性的調整:**將自訂程式碼注入您的 Exposure 部落格的特定區域,例如頁首、頁尾或單一頁面。 - **功能增強:**透過整合第三方工具或自訂元素來增強您的博客,擴展 Exposure 的內建功能。 #### **重要考慮因素:** - **社群支援:**考慮專門為 Exposure 上的程式碼注入量身定制的資源的可用性,因為其社群可能比某些其他平台小。 --- ### **Hashnode:為開發者部落客客製化的程式碼注入解決方案** [Hashnode](https://hashnode.com/)是一個為開發人員量身定制的社群驅動的部落格平台。它是[**最好的開發者部落格平台**](https://bloggingplatforms.app/blog/best-blogging-platforms-for-developers)之一。 它優先考慮技術討論和知識共享。使用 Hashnode,您可以注入自訂程式碼來客製化部落格的外觀和功能。 #### **探索Hashnode的程式碼注入功能:** - **自訂樣式:**使用自訂 CSS 程式碼修改 Hashnode 部落格的外觀,創造與您的品牌相符的獨特外觀。 - **嵌入和小部件:**整合程式碼片段以顯示程式碼區塊、可嵌入內容和動態小部件,豐富您的技術寫作。 #### **重要考慮因素:** - **利基焦點:** Hashnode 最適合開發人員和技術部落客。它可能不是一般興趣部落格的理想平台。 --- ### **Odoo:模組化 CMS 中的程式碼注入** [Odoo](https://www.odoo.com/)是一個龐大的開源業務管理套件,包括網站建立器和部落格模組。它提供了注入程式碼的能力,允許在 Odoo 生態系統中進行客製化。 #### **Odoo 的程式碼注入潛力:** - **深度整合:**自訂 Odoo 模組(包括您的部落格)以與特定業務工作流程或流程保持一致。 - **框架熟悉度:** Odoo 中的有效程式碼注入需要了解其基於 Python 的開發框架。 **重要考慮因素:** - **以企業為中心:** Odoo 的複雜性使其最適合擁有大量 IT 資源的企業,而不是個人部落客。 --- ### **Jimdo:有限的程式碼注入** [Jimdo](https://www.jimdo.com/)是一個用戶友好的網站建立器,提供有限程度的程式碼注入。您可以將基本 HTML 片段新增至網站的某些區域。 #### **重要考慮因素:** - **簡單性:** Jimdo 優先考慮易用性而不是廣泛的客製化選項。 - **最適合初學者:** Jimdo 上的程式碼注入可能適合簡單的修改,但無法滿足需要高級自訂的用戶的需求。 --- ### **Weebly:用於基本客製化的定向程式碼注入** [Weebly](https://www.weebly.com/)是另一個受歡迎的拖放網站建立器,提供有針對性的程式碼注入功能。其**嵌入程式碼**功能可讓您將 HTML 和 JavaScript 片段插入網站的特定部分,從而實現基本的修改和整合。 #### **Weebly 的程式碼注入潛力:** - **具體增強功能:**利用 Weebly 的程式碼注入來整合第三方小工具,例如社群媒體來源或即時聊天工具。 - **適合初學者:** Weebly 的嵌入程式碼功能為基本程式碼插入提供了一個簡單的介面。 #### **重要考慮因素:** - **Weebly 的程式碼注入範圍:**與 Squarespace 和 PageCloud 一樣,Weebly 的程式碼注入專注於局部增強而不是廣泛的結構變化。 - **平台相容性:**注入 Weebly 的自訂程式碼與平台綁定。遷移可能需要程式碼調整或重新註入。 --- ### **Tumblr:主題客製化的程式碼注入** [Tumblr](https://www.tumblr.com/)是一個以其強大的社群和社交功能而聞名的微博平台,允許透過其主題自訂選項進行一定程度的程式碼注入。 #### **Tumblr 的程式碼注入潛力:** - **主題修改:**直接存取主題檔案可讓您注入 HTML、CSS 和 JavaScript 程式碼。自訂 Tumblr 部落格的佈局、樣式和功能。 - **創意人員的靈活性:** Tumblr 的開放主題結構和對視覺表達的重視使其成為渴望獨特線上形象的創意人士的熱門選擇。 #### **重要考慮因素:** - **熟悉語法:**修改 Tumblr 主題需要對 HTML、CSS 和 JavaScript 語法有基本的了解。 - **社群驅動:** Tumblr 擁有龐大的主題開發人員社區,他們經常分享程式碼片段和自訂技巧。 --- ### **Tilda:帶有程式碼注入選項的視覺設計** [Tilda](https://tilda.cc/)是一個強調設計的模組化網站建立器,在其**零區塊**功能中提供程式碼注入功能。此功能可讓您插入自訂程式碼,提供超越其視覺化建構器工具的靈活性。 #### **Tilda 的程式碼注入潛力:** - **視覺 + 程式碼:**在 Tilda 直覺的拖放介面和程式碼注入的強大功能之間取得平衡。建立獨特的元素或修改現有區塊以進行細粒度控制。 - **有針對性的增強:**透過整合、自訂動畫或獨特的設計元素擴展 Tilda 的功能。 #### **重要考慮因素:** - **零塊特定:** Tilda 上的程式碼注入主要側重於零塊功能內的自定義,與直接主題編輯相比,提供了更結構化的方法。 - **設計優先:Tilda 專注於將拖放建構器作為主要設計工具。**程式碼注入是擴展定制的一個有價值的補充功能。 --- ### **SmugMug:針對以視覺為中心的攝影師的有限程式碼注入** [SmugMug](https://www.smugmug.com/)是一個專門為攝影師展示和銷售作品而設計的平台。它提供有限的程式碼注入功能,主要用於基本修改。 #### **SmugMug 的程式碼注入潛力:** - **有限制的自訂:**雖然 SmugMug 允許進行一些 HTML 和 CSS 自訂,但其主要重點是視覺驅動的模板和主題。 - **適合攝影師:** SmugMug 的核心受眾可能會發現程式碼注入不再那麼重要,因為該平台優先考慮易用性來展示令人驚嘆的視覺效果。 #### **重要考慮因素:** - **特定領域:** SmugMug 非常注重影像展示和銷售功能,為攝影師提供了良好的服務。對於 SmugMug 的核心用戶來說,大量的程式碼注入可能不是優先考慮的事情。 --- ### **Cargo:創意組合的程式碼注入** [Cargo](https://cargo.site/)以其極簡設計和強調展示創意作品而聞名,允許一定程度的程式碼注入。此功能使用戶能夠在預先建立的模板之外個性化他們的線上作品集。 #### **Cargo 的程式碼注入潛力:** - **有針對性的自訂:**注入程式碼片段可以進行自訂,例如自訂字體、獨特的佈局或互動式元素,以增強您的創意作品的呈現。 #### **重要考慮因素:** - **範圍有限:** Cargo 上的程式碼注入最適合在地化增強,而不是對您的產品組合進行徹底的結構性變更。 --- ### **Adobe Portfolio:為視覺藝術家提供有限的程式碼注入** [Adobe Portfolio](https://portfolio.adobe.com/)與 Adobe 的 Creative Cloud 套件集成,主要專注於視覺作品集的無縫展示。它提供有限的程式碼注入功能,通常用於較小的樣式調整或嵌入第三方內容。 #### **Adobe Portfolio 的程式碼注入潛力:** - **增強視覺效果:**利用程式碼注入來自訂工作的呈現、加入微妙的動畫或嵌入互動元素。 - **專注於集成:** Adobe Portfolio 優先考慮與其他 Adobe Creative Cloud 服務的集成,而不是廣泛的程式碼驅動自訂。 #### **重要考慮因素:** - **視覺第一:** Adobe Portfolio 的核心優勢在於其易用性和專注於令人驚嘆的視覺呈現。程式碼注入能力是次要的。 --- ### **Vev:平衡視覺設計與程式碼注入** [Vev](https://www.vev.design/?gspk=Y2FuYnVyYWtzb2Z5YWxpb2dsdTMwNTQ&gsxid=6ql2Ybh49e9T&utm_campaign=affiliate&utm_id=VevPartners&utm_medium=partnerstack&utm_source=partnerstack)在直覺的視覺化設計工具和程式碼注入的靈活性之間提供了平衡。其專用的程式碼注入區域可實現有針對性的客製化和整合。 #### **Vev 的程式碼注入潛力:** - **自訂元素:**在 Vev 視覺建構器的框架內建立獨特的元素或修改現有元素,以獲得客製化的外觀和感覺。 - **第三方整合:**透過使用程式碼無縫整合外部服務和小部件來擴展部落格的功能。 #### **重要考慮因素:** - **混合方法:** Vev 迎合了既尋求視覺化建構器的便利性又尋求目標程式碼注入修改的強大功能的使用者。 --- ### **Voog:高級客製化的程式碼注入** [Voog](https://www.voog.com/)是一個多語言網站建立器,為開發人員和尋求對其網站結構和設計進行廣泛控制的人員提供強大的程式碼注入功能。 #### **Voog 的程式碼注入潛力:** - **完整的主題控制:** Voog 的開放式結構允許直接修改主題文件,從而實現廣泛的客製化可能性。 - **API 整合:**利用 Voog 的 API 整合自訂功能並簡化工作流程。 #### **重要考慮因素:** - **技術專長:**在 Voog 中進行有效的程式碼注入需要熟悉 Web 開發原理。 - **多語言聚焦:** Voog 的優勢在於其多語言能力。如果這是一個核心需求,那麼它的程式碼注入潛力就變得特別有利。 --- ### 格式:投資組合網站的有限程式碼注入 [Format](https://format.grsm.io/vdptwrg0w0q9)迎合了尋求透過令人驚嘆的作品集網站展示其作品的創意人士。它提供一定程度的程式碼注入,但優先考慮簡單性和視覺化設計工具。 #### Format 的程式碼注入潛力: - **整合與增強:**注入程式碼以嵌入外部內容、追蹤分析或為您的產品組合加入微妙的自訂。 #### 重要考慮因素: - **視覺焦點:**與 Ghost 或 WordPress 等平台相比,Format 的體驗不太以程式碼為中心。程式碼注入主要作為補充選項。 --- ### Carbonmade:用於組合定制的程式碼注入 [Carbonmade](https://carbonmade.com/)與 Format 和 Adobe Portfolio 類似,專為建立線上作品集的創意人員量身定制。它提供了適合基本修改和增強的程式碼注入等級。 #### Carbonmade 的程式碼注入潛力: - **微調示範:**透過 CSS 調整和基於 JavaScript 的互動來自訂 Carbonmade 產品組合的外觀和感覺。 #### 重要考慮因素: - **產品組合特定:** Carbonmade 的程式碼注入功能可能無法提供與 WordPress 等成熟的 CMS 相同的深度。 ### **結論** 選擇正確的具有程式碼注入的部落格平台可以歸結為在客製化和易用性之間取得平衡。請記住,您理想的部落格平台取決於您的具體需求、技術專長以及您對線上空間的創意願景。 --- 原文出處:https://dev.to/elastoplastique/code-injection-options-for-blogging-platforms-2g0a

建立文字到 PowerPoint 應用程式(LangChain、Next.js 和 CopilotKit)

長話短說 ==== 在本文中,您將學習如何建立由 AI 驅動的 PowerPoint 應用程式,該應用程式可以搜尋網路以自動製作有關任何主題的簡報。 我們將介紹使用: - 用於應用程式框架的 Next.js 🖥️ - 法學碩士 OpenAI 🧠 - LangChain 和 Tavily 的網路搜尋人工智慧代理🤖 - 使用 CopilotKit 將 AI 整合到您的應用程式中 🪁 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztokmi3hdcthmxkj2gny.gif) --- CopilotKit:為您的應用程式建立人工智慧副駕駛 --------------------------- CopilotKit 是[開源人工智慧副駕駛平台。](https://github.com/CopilotKit/CopilotKit)我們可以輕鬆地將強大的人工智慧整合到您的 React 應用程式中。 建造: - ChatBot:上下文感知的應用內聊天機器人,可以在應用程式內執行操作 💬 - CopilotTextArea:人工智慧驅動的文字字段,具有上下文感知自動完成和插入功能📝 - 聯合代理:應用程式內人工智慧代理,可以與您的應用程式和使用者互動🤖 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h69i50cxsyvcknlcs6q0.gif) {% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %} 現在回到文章。 (本文是我們三週前發表的一篇文章的進展,但您無需閱讀該文章即可理解這一點)。 --- **先決條件** -------- 在開始建立應用程式之前,讓我們先查看建置應用程式所需的依賴項或套件 `copilotkit/react-core` :CopilotKit 前端包,帶有 React hooks,用於向副駕駛提供應用程式狀態和操作(AI 功能) `copilotkit/react-ui` :聊天機器人側邊欄 UI 的 CopilotKit 前端包 `copilotkit/react-textarea` :CopilotKit 前端包,用於在演講者筆記中進行人工智慧輔助文字編輯。 `LangChainJS` :一個用於開發由語言模型支援的應用程式的框架。 `Tavily Search API` :幫助將法學碩士和人工智慧應用程式連接到可信賴的即時知識的 API。 安裝所有專案包和依賴項 ----------- 在安裝所有專案包和依賴項之前,我們首先在終端機上執行以下命令來建立 Nextjs 專案。 ``` npx create-next-app@latest ``` 然後系統會提示您選擇一些選項。請隨意標記它們,如下所示。 ![建立 Nextjs 專案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n668ixcvu5lnrsm9jpmq.png) 之後,使用您選擇的文字編輯器開啟新建立的 Nextjs 專案。然後在命令列中執行以下命令來安裝所有專案包和依賴項。 ``` npm i @copilotkit/backend @copilotkit/shared @langchain/langgraph @copilotkit/react-core @copilotkit/react-ui @copilotkit/react-textarea @heroicons/react ``` **建立 PowerPoint 應用程式前端** ------------------------ 讓我們先建立一個名為`Slide.tsx`的檔案。該文件將包含顯示和編輯投影片內容的程式碼,包括其`title` 、 `body text` 、 `background image`和`spoken narration text` 。 要建立該文件,請前往`/[root]/src/app`並建立一個名為`components`的資料夾。在 Components 資料夾中,建立`Slide.tsx`檔案。 之後,在文件頂部加入以下程式碼。程式碼定義了兩個名為`SlideModel`和`SlideProps`的 TypeScript 介面。 ``` "use client"; // Define an interface for the model of a slide, specifying the expected structure of a slide object. export interface SlideModel { title: string; content: string; backgroundImageDescription: string; spokenNarration: string; } // Define an interface for the properties of a component or function that manages slides. export interface SlideProps { slide: SlideModel; partialUpdateSlide: (partialSlide: Partial<SlideModel>) => void; } ``` 接下來,在上面的程式碼下面加入以下程式碼。程式碼定義了一個名為`Slide`功能元件,它接受`SlideProps`類型的 props。 ``` // Define a functional component named Slide that accepts props of type SlideProps. export const Slide = (props: SlideProps) => { // Define a constant for the height of the area reserved for speaker notes. const heightOfSpeakerNotes = 150; // Construct a URL for the background image using the description from slide properties, dynamically fetching an image from Unsplash. const backgroundImage = 'url("https://source.unsplash.com/featured/?' + encodeURIComponent(props.slide.backgroundImageDescription) + '")'; // Return JSX for the slide component. return ( <> {/* Slide content container with dynamic height calculation to account for speaker notes area. */} <div className="w-full relative bg-slate-200" style={{ height: `calc(100vh - ${heightOfSpeakerNotes}px)`, // Calculate height to leave space for speaker notes. }} > {/* Container for the slide title with centered alignment and styling. */} <div className="h-1/5 flex items-center justify-center text-5xl text-white text-center z-10" > {/* Textarea for slide title input, allowing dynamic updates. */} <textarea className="text-2xl bg-transparent text-black p-4 text-center font-bold uppercase italic line-clamp-2 resize-none flex items-center" style={{ border: "none", outline: "none", }} value={props.slide.title} placeholder="Title" onChange={(e) => { props.partialUpdateSlide({ title: e.target.value }); }} /> </div> {/* Container for the slide content with background image. */} <div className="h-4/5 flex" style={{ backgroundImage, backgroundSize: "cover", backgroundPosition: "center", }} > {/* Textarea for slide content input, allowing dynamic updates and styled for readability. */} <textarea className="w-full text-3xl text-black font-medium p-10 resize-none bg-red mx-40 my-8 rounded-xl text-center" style={{ lineHeight: "1.5", }} value={props.slide.content} placeholder="Body" onChange={(e) => { props.partialUpdateSlide({ content: e.target.value }); }} /> </div> </div> {/* Textarea for entering spoken narration with specified height and styling for consistency. */} <textarea className=" w-9/12 h-full bg-transparent text-5xl p-10 resize-none bg-gray-500 pr-36" style={{ height: `${heightOfSpeakerNotes}px`, background: "none", border: "none", outline: "none", fontFamily: "inherit", fontSize: "inherit", lineHeight: "inherit", }} value={props.slide.spokenNarration} onChange={(e) => { props.partialUpdateSlide({ spokenNarration: e.target.value }); }} /> </> ); }; ``` 之後,我們現在會建立一個名為`Presentation.tsx`的檔案。 該文件將包含初始化和更新投影片狀態、渲染目前投影片以及根據目前狀態動態啟用或停用按鈕實現導覽和投影片管理操作的程式碼。 要建立該文件,請將另一個文件新增至元件資料夾中,並將其命名為`Presentation.tsx` ,然後使用下列程式碼在檔案頂部匯入`React hooks` 、 `icons` 、 `SlideModel`和`Slide`元件。 ``` "use client"; import { useCallback, useMemo, useState } from "react"; import { BackwardIcon, ForwardIcon, PlusIcon, SparklesIcon, TrashIcon } from "@heroicons/react/24/outline"; import { SlideModel, Slide } from "./Slide"; ``` 之後,在上面的程式碼下面加入以下程式碼。程式碼定義了一個`ActionButton`功能元件,它將呈現具有可自訂屬性的按鈕元素。 ``` export const ActionButton = ({ disabled, onClick, className, children, }: { disabled: boolean; onClick: () => void; className?: string; children: React.ReactNode; }) => { return ( <button disabled={disabled} className={`bg-blue-500 text-white font-bold py-2 px-4 rounded ${disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-700"} ${className}`} onClick={onClick} > {children} </button> ); }; ``` 然後在上面的程式碼下面加入下面的程式碼。程式碼定義了一個名為「Presentation」的功能元件,用於初始化投影片的狀態並定義一個用於更新目前投影片的函數。 ``` // Define the Presentation component as a functional component. export const Presentation = () => { // Initialize state for slides with a default first slide and a state to track the current slide index. const [slides, setSlides] = useState<SlideModel[]>([ { title: `Welcome to our presentation!`, // Title of the first slide. content: 'This is the first slide.', // Content of the first slide. backgroundImageDescription: "hello", // Description for background image retrieval. spokenNarration: "This is the first slide. Welcome to our presentation!", // Spoken narration text for the first slide. }, ]); const [currentSlideIndex, setCurrentSlideIndex] = useState(0); // Current slide index, starting at 0. // Use useMemo to memoize the current slide object to avoid unnecessary recalculations. const currentSlide = useMemo(() => slides[currentSlideIndex], [slides, currentSlideIndex]); // Define a function to update the current slide. This function uses useCallback to memoize itself to prevent unnecessary re-creations. const updateCurrentSlide = useCallback( (partialSlide: Partial<SlideModel>) => { // Update the slides state by creating a new array with the updated current slide. setSlides((slides) => [ ...slides.slice(0, currentSlideIndex), // Copy all slides before the current one. { ...slides[currentSlideIndex], ...partialSlide }, // Merge the current slide with the updates. ...slides.slice(currentSlideIndex + 1), // Copy all slides after the current one. ]); }, [currentSlideIndex, setSlides] // Dependencies for useCallback. ); // The JSX structure for the Presentation component. return ( <div className="relative"> {/* Render the current slide by passing the currentSlide and updateCurrentSlide function as props. */} <Slide slide={currentSlide} partialUpdateSlide={updateCurrentSlide} /> {/* Container for action buttons located at the top-left corner of the screen. */} <div className="absolute top-0 left-0 mt-6 ml-4 z-30"> {/* Action button to add a new slide. Disabled state is hardcoded to true for demonstration. */} <ActionButton disabled={true} onClick={() => { // Define a new slide object. const newSlide: SlideModel = { title: "Title", content: "Body", backgroundImageDescription: "random", spokenNarration: "The speaker's notes for this slide.", }; // Update the slides array to include the new slide. setSlides((slides) => [ ...slides.slice(0, currentSlideIndex + 1), newSlide, ...slides.slice(currentSlideIndex + 1), ]); // Move to the new slide by updating the currentSlideIndex. setCurrentSlideIndex((i) => i + 1); }} className="rounded-r-none" > <PlusIcon className="h-6 w-6" /> {/* Icon for the button. */} </ActionButton> {/* Another action button, currently disabled and without functionality. */} <ActionButton disabled={true} onClick={async () => { }} // Placeholder async function. className="rounded-l-none ml-[1px]" > <SparklesIcon className="h-6 w-6" /> {/* Icon for the button. */} </ActionButton> </div> {/* Container for action buttons at the top-right corner for deleting slides, etc. */} <div className="absolute top-0 right-0 mt-6 mr-24"> <ActionButton disabled={slides.length === 1} // Disable button if there's only one slide. onClick={() => {}} // Placeholder function for the button action. className="ml-5 rounded-r-none" > <TrashIcon className="h-6 w-6" /> {/* Icon for the button. */} </ActionButton> </div> {/* Display current slide number and total slides at the bottom-right corner. */} <div className="absolute bottom-0 right-0 mb-20 mx-24 text-xl" style={{ textShadow: "1px 1px 0 #ddd, -1px -1px 0 #ddd, 1px -1px 0 #ddd, -1px 1px 0 #ddd", }} > Slide {currentSlideIndex + 1} of {slides.length} {/* Current slide and total slides. */} </div> {/* Container for navigation buttons (previous and next) at the bottom-right corner. */} <div className="absolute bottom-0 right-0 mb-6 mx-24"> {/* Button to navigate to the previous slide. */} <ActionButton className="rounded-r-none" disabled={ currentSlideIndex === 0 || true} // Example condition to disable button; 'true' is just for demonstration. onClick={() => { setCurrentSlideIndex((i) => i - 1); // Update currentSlideIndex to move to the previous slide. }} > <BackwardIcon className="h-6 w-6" /> {/* Icon for the button. */} </ActionButton> {/* Button to navigate to the next slide. */} <ActionButton className="mr-[1px] rounded-l-none" disabled={ true || currentSlideIndex + 1 === slides.length} // Example condition to disable button; 'true' is just for demonstration. onClick={async () => { setCurrentSlideIndex((i) => i + 1); // Update currentSlideIndex to move to the next slide. }} > <ForwardIcon className="h-6 w-6" /> {/* Icon for the button. */} </ActionButton> </div> </div> ); }; ``` 要在瀏覽器上呈現 PowerPoint 應用程式,請前往`/[root]/src/app/page.tsx`檔案並新增以下程式碼。 ``` "use client"; import "./style.css"; import { Presentation } from "./components/Presentation"; export default function AIPresentation() { return ( <Presentation /> ); } ``` 如果您想要在 Powerpoint 應用程式前端新增樣式,請在`/[root]/src/app`資料夾中建立名為`style.css`的檔案。 然後導航[到此 gist 文件](https://gist.github.com/TheGreatBonnie/e7c0b790a2e2af3e669810539ba54fed),複製 CSS 程式碼,並將其新增至 style.css 檔案。 最後,在命令列上執行命令`npm run dev` ,然後導航到 http://localhost:3000/。 現在您應該在瀏覽器上查看 PowerPoint 應用程式,如下所示。 ![PowerPoint應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqcjqqmo1b2oow6y4p76.png) **將 PowerPoint 應用程式與 CopilotKit 後端集成** -------------------------------------- 讓我們先在根目錄中建立一個名為`.env.local`的檔案。然後在保存 ChatGPT 和 Tavily Search API 金鑰的檔案中加入下面的環境變數。 ``` OPENAI_API_KEY="Your ChatGPT API key" TAVILY_API_KEY="Your Tavily Search API key" ``` 若要取得 ChatGPT API 金鑰,請導覽至 https://platform.openai.com/api-keys。 ![ChatGPT API 金鑰](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u65aswytyym0zpoh5wx.png) 若要取得 Tavilly Search API 金鑰,請導覽至 https://app.tavily.com/home ![泰維利搜尋 API 金鑰](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ugx1oqifnk24l69jjkf.png) 之後,轉到`/[root]/src/app`並建立一個名為`api`的資料夾。在`api`資料夾中,建立一個名為`copilotkit`的資料夾。 在`copilotkit`資料夾中,建立一個名為`research.ts`的檔案。然後導航到[該 Research.ts gist 文件](https://gist.github.com/TheGreatBonnie/58dc21ebbeeb8cbb08df665db762738c),複製程式碼,並將其新增至**`research.ts`**檔案中 接下來,在`/[root]/src/app/api/copilotkit`資料夾中建立一個名為`route.ts`的檔案。該文件將包含設定後端功能來處理 POST 請求的程式碼。它有條件地包括對給定主題進行研究的“研究”操作。 現在在文件頂部導入以下模組。 ``` import { CopilotBackend, OpenAIAdapter } from "@copilotkit/backend"; // For backend functionality with CopilotKit. import { researchWithLangGraph } from "./research"; // Import a custom function for conducting research. import { AnnotatedFunction } from "@copilotkit/shared"; // For annotating functions with metadata. ``` 在上面的程式碼下面,定義一個執行時環境變數和一個註解的函數,以便使用下面的程式碼進行研究。 ``` // Define a runtime environment variable, indicating the environment where the code is expected to run. export const runtime = "edge"; // Define an annotated function for research. This object includes metadata and an implementation for the function. const researchAction: AnnotatedFunction<any> = { name: "research", // Function name. description: "Call this function to conduct research on a certain topic. Respect other notes about when to call this function", // Function description. argumentAnnotations: [ // Annotations for arguments that the function accepts. { name: "topic", // Argument name. type: "string", // Argument type. description: "The topic to research. 5 characters or longer.", // Argument description. required: true, // Indicates that the argument is required. }, ], implementation: async (topic) => { // The actual function implementation. console.log("Researching topic: ", topic); // Log the research topic. return await researchWithLangGraph(topic); // Call the research function and return its result. }, }; ``` 然後在上面的程式碼下加入下面的程式碼來定義處理POST請求的非同步函數。 ``` // Define an asynchronous function that handles POST requests. export async function POST(req: Request): Promise<Response> { const actions: AnnotatedFunction<any>[] = []; // Initialize an array to hold actions. // Check if a specific environment variable is set, indicating access to certain functionality. if (process.env["TAVILY_API_KEY"]) { actions.push(researchAction); // Add the research action to the actions array if the condition is true. } // Instantiate CopilotBackend with the actions defined above. const copilotKit = new CopilotBackend({ actions: actions, }); // Use the CopilotBackend instance to generate a response for the incoming request using an OpenAIAdapter. return copilotKit.response(req, new OpenAIAdapter()); } ``` **將 PowerPoint 應用程式與 CopilotKit 前端集成** -------------------------------------- 讓我們先導入`/[root]/src/app/components/Slide.tsx`檔案頂部的`useMakeCopilotActionable`掛鉤。 ``` import { useMakeCopilotActionable } from "@copilotkit/react-core"; ``` 在 Slide 函數中,新增以下程式碼,該程式碼使用`useMakeCopilotActionable`掛鉤來設定一個名為`updateSlide`的操作,該操作具有特定參數以及根據提供的值更新投影片的實作。 ``` useMakeCopilotActionable({ // Defines the action name. This is a unique identifier for the action within the application. name: "updateSlide", // Describes what the action does. In this case, it updates the current slide. description: "Update the current slide.", // Details the arguments that the action accepts. Each argument has a name, type, description, and a flag indicating if it's required. argumentAnnotations: [ { name: "title", // The argument name. type: "string", // The data type of the argument. description: "The title of the slide. Should be a few words long.", // Description of the argument. required: true, // Indicates that this argument must be provided for the action to execute. }, { name: "content", type: "string", description: "The content of the slide. Should generally consists of a few bullet points.", required: true, }, { name: "backgroundImageDescription", type: "string", description: "What to display in the background of the slide. For example, 'dog', 'house', etc.", required: true, }, { name: "spokenNarration", type: "string", description: "The spoken narration for the slide. This is what the user will hear when the slide is shown.", required: true, }, ], // The implementation of the action. This is a function that will be called when the action is executed. implementation: async (title, content, backgroundImageDescription, spokenNarration) => { // Calls a function passed in through props to partially update the slide with new values for the specified properties. props.partialUpdateSlide({ title, content, backgroundImageDescription, spokenNarration, }); }, }, [props.partialUpdateSlide]); // Dependencies array for the custom hook or function. This ensures that the action is re-initialized only when `props.partialUpdateSlide` changes. ``` 之後,請前往`/[root]/src/app/components/Presentation.tsx`檔案並使用下面的程式碼匯入頂部的 CopilotKit 前端套件。 ``` import { useCopilotContext } from "@copilotkit/react-core"; import { CopilotTask } from "@copilotkit/react-core"; import { useMakeCopilotActionable, useMakeCopilotReadable } from "@copilotkit/react-core"; ``` 在演示函數中,加入以下程式碼,該程式碼使用`useMakeCopilotReadable`掛鉤加入`Slides`和`currentSlide`幻燈片陣列作為應用程式內聊天機器人的上下文。掛鉤使副駕駛可以讀取簡報中的整個幻燈片集合以及當前幻燈片的資料。 ``` useMakeCopilotReadable("These are all the slides: " + JSON.stringify(slides)); useMakeCopilotReadable( "This is the current slide: " + JSON.stringify(currentSlide) ); ``` 在`useMakeCopilotReadable`掛鉤下方,新增以下程式碼,該程式碼使用`useCopilotActionable`掛鉤來設定名為`appendSlide`的操作,其中包含說明和加入多張幻燈片的實作函數。 ``` useMakeCopilotActionable( { // Defines the action's metadata. name: "appendSlide", // Action identifier. description: "Add a slide after all the existing slides. Call this function multiple times to add multiple slides.", // Specifies the arguments that the action takes, including their types, descriptions, and if they are required. argumentAnnotations: [ { name: "title", // The title of the new slide. type: "string", description: "The title of the slide. Should be a few words long.", required: true, }, { name: "content", // The main content or body of the new slide. type: "string", description: "The content of the slide. Should generally consist of a few bullet points.", required: true, }, { name: "backgroundImageDescription", // Description for fetching or generating the background image of the new slide. type: "string", description: "What to display in the background of the slide. For example, 'dog', 'house', etc.", required: true, }, { name: "spokenNarration", // Narration text that will be read aloud during the presentation of the slide. type: "string", description: "The text to read while presenting the slide. Should be distinct from the slide's content, and can include additional context, references, etc. Will be read aloud as-is. Should be a few sentences long, clear, and smooth to read.", required: true, }, ], // The function to execute when the action is triggered. It creates a new slide with the provided details and appends it to the existing slides array. implementation: async (title, content, backgroundImageDescription, spokenNarration) => { const newSlide: SlideModel = { // Constructs the new slide object. title, content, backgroundImageDescription, spokenNarration, }; // Updates the slides state by appending the new slide to the end of the current slides array. setSlides((slides) => [...slides, newSlide]); }, }, [setSlides] // Dependency array for the hook. This action is dependent on the `setSlides` function, ensuring it reinitializes if `setSlides` changes. ); ``` 在上面的程式碼下方,定義一個名為`context`的變數,該變數使用名為`useCopilotContext`的自訂掛鉤從 copilot 上下文中檢索當前上下文。 ``` const context = useCopilotContext(); ``` 之後,定義一個名為`generateSlideTask`的函數,它包含一個名為`CopilotTask`的類別。 `CopilotTask`類別定義用於產生與簡報的整體主題相關的新投影片的指令 ``` const generateSlideTask = new CopilotTask({ instructions: "Make the next slide related to the overall topic of the presentation. It will be inserted after the current slide. Do NOT carry any research", }); ``` 然後在上面的程式碼下面初始化一個名為`generateSlideTaskRunning`的狀態變數,預設值為false。 ``` const [generateSlideTaskRunning, **setGenerateSlideTaskRunning**] = useState(false); ``` 之後,使用下面的程式碼更新簡報元件中的操作按鈕,以透過新增、刪除和導覽投影片來新增動態互動。 ``` // The JSX structure for the Presentation component. return ( <div className="relative"> {/* Renders the current slide using a Slide component with props for the slide data and a method to update it. */} <Slide slide={currentSlide} partialUpdateSlide={updateCurrentSlide} /> {/* Container for action buttons positioned at the top left corner of the relative parent */} <div className="absolute top-0 left-0 mt-6 ml-4 z-30"> {/* ActionButton to add a new slide. It is disabled when a generateSlideTask is running to prevent concurrent modifications. */} <ActionButton disabled={generateSlideTaskRunning} onClick={() => { const newSlide: SlideModel = { title: "Title", content: "Body", backgroundImageDescription: "random", spokenNarration: "The speaker's notes for this slide.", }; // Inserts the new slide immediately after the current slide and updates the slide index to point to the new slide. setSlides((slides) => [ ...slides.slice(0, currentSlideIndex + 1), newSlide, ...slides.slice(currentSlideIndex + 1), ]); setCurrentSlideIndex((i) => i + 1); }} className="rounded-r-none" > <PlusIcon className="h-6 w-6" /> </ActionButton> {/* ActionButton to generate a new slide based on the current context, also disabled during task running. */} <ActionButton disabled={generateSlideTaskRunning} onClick={async () => { setGenerateSlideTaskRunning(true); // Indicates the task is starting. await generateSlideTask.run(context); // Executes the task with the current context. setGenerateSlideTaskRunning(false); // Resets the flag when the task is complete. }} className="rounded-l-none ml-[1px]" > <SparklesIcon className="h-6 w-6" /> </ActionButton> </div> {/* Container for action buttons at the top right, including deleting the current slide and potentially other actions. */} <div className="absolute top-0 right-0 mt-6 mr-24"> {/* ActionButton for deleting the current slide, disabled if a task is running or only one slide remains. */} <ActionButton disabled={generateSlideTaskRunning || slides.length === 1} onClick={() => { console.log("delete slide"); // Removes the current slide and resets the index to the beginning as a simple handling strategy. setSlides((slides) => [ ...slides.slice(0, currentSlideIndex), ...slides.slice(currentSlideIndex + 1), ]); setCurrentSlideIndex((i) => 0); }} className="ml-5 rounded-r-none" > <TrashIcon className="h-6 w-6" /> </ActionButton> </div> {/* Display showing the current slide index and the total number of slides. */} <div className="absolute bottom-0 right-0 mb-20 mx-24 text-xl" style={{ textShadow: "1px 1px 0 #ddd, -1px -1px 0 #ddd, 1px -1px 0 #ddd, -1px 1px 0 #ddd", }} > Slide {currentSlideIndex + 1} of {slides.length} </div> {/* Navigation buttons to move between slides, disabled based on the slide index or if a task is running. */} <div className="absolute bottom-0 right-0 mb-6 mx-24"> {/* Button to move to the previous slide, disabled if on the first slide or a task is running. */} <ActionButton className="rounded-r-none" disabled={generateSlideTaskRunning || currentSlideIndex === 0} onClick={() => { setCurrentSlideIndex((i) => i - 1); }} > <BackwardIcon className="h-6 w-6" /> </ActionButton> {/* Button to move to the next slide, disabled if on the last slide or a task is running. */} <ActionButton className="mr-[1px] rounded-l-none" disabled={generateSlideTaskRunning || currentSlideIndex + 1 === slides.length} onClick={async () => { setCurrentSlideIndex((i) => i + 1); }} > <ForwardIcon className="h-6 w-6" /> </ActionButton> </div> </div> ); ``` 現在讓我們轉到`/[root]/src/app/page.tsx`文件,使用下面的程式碼匯入 CopilotKit 前端包和文件頂部的樣式。 ``` import { CopilotKit, } from "@copilotkit/react-core"; import { CopilotSidebar } from "@copilotkit/react-ui"; import "@copilotkit/react-ui/styles.css"; import "@copilotkit/react-textarea/styles.css"; ``` 然後使用`CopilotKit`和`CopilotSidebar`來包裝Presentation元件,如下所示。 ``` export default function AIPresentation() { return ( <CopilotKit url="/api/copilotkit/"> <CopilotSidebar instructions="Help the user create and edit a powerpoint-style presentation. IMPORTANT NOTE: SOMETIMES you may want to research a topic, before taking further action. BUT FIRST ASK THE USER if they would like you to research it. If they answer 'no', do your best WITHOUT researching the topic first." defaultOpen={true} labels={{ title: "Presentation Copilot", initial: "Hi you! 👋 I can help you create a presentation on any topic.", }} clickOutsideToClose={false} > <Presentation /> </CopilotSidebar> </CopilotKit> ); } ``` 之後,執行開發伺服器並導航到 http://localhost:3000/。您應該會看到應用程式內聊天機器人已整合到 PowerPoint Web 應用中。 ![應用程式內聊天機器人](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rb2g54qslrrfxyx2pbcy.png) 最後,給右側的聊天機器人一個提示,例如“在 JavaScript 上建立 PowerPoint 簡報”,聊天機器人將開始產生回應,完成後,使用底部的前進按鈕瀏覽產生的幻燈片。 注意:如果聊天機器人沒有立即產生投影片,請根據其回應給予適當的後續提示。 ![PowerPoint簡報](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6dl4av0asoeokzopwra.png) 結論 -- 總而言之,您可以使用 CopilotKit 建立應用內 AI 聊天機器人,該機器人可以查看當前應用程式狀態並在應用程式內執行操作。 AI 聊天機器人可以與您的應用程式前端、後端和第三方服務對話。 完整的原始碼:https://github.com/TheGreatBonnie/aipoweredpowerpointapp --- 原文出處:https://dev.to/copilotkit/how-to-build-an-ai-powered-powerpoint-generator-langchain-copilotkit-openai-nextjs-4c76

100 多個專案創意

**編輯**:大家好!在對本文做出驚人反應後,我建立了一個名為「每週專案俱樂部」的專案。每週您的收件匣都會收到需要解決的問題。你可以努力解決問題,並且你將得到整個俱樂部的幫助,讓你走上正軌。了解更多並[在這裡](https://weeklyproject.club)註冊! 有一天我注意到一個模式。我注意到很多人都在努力 學習編程,但他們心中沒有特定的目標。我已經討論過如何了解您想要學習程式設計的原因可以幫助您選擇要學習的語言[!](https://pickaframework.com/articles/why/) ,以及如何實際做出決定([在這裡!](https://pickaframework.com/feature_fishing/) )但是專案有什麼幫助呢? 當我指導程式設計師時,我發現有一個專案可以幫助排除其他一些幹擾,例如想知道你是否使用了正確的語言。透過專注於一個特定的目標,你就不用那麼費力去擔心*這*是否正是你應該使用的語言。結果是你建立了一些簡潔的東西,並且一路上你學到了一些東西! 2隻鳥,1塊石頭。 這就是為什麼我為初學者程式設計師策劃了這個專案清單。許多人列出了大量的專案來學習編程,但很少按照難度進行組織。我瀏覽了幾個流行的程式設計專案想法清單。如果您想查看完整列表,可以在頁面底部找到來源。 我將其分為教程和想法。教程包含資源連結,而想法只是專案的一般描述。我還列出了我最喜歡的初學者清單。 看看,看看是否有什麼啟發你! 教學 == 我的最愛 ---- - [透過 30 個教學在 30 天內建立 30 個東西](https://javascript30.com) - [在 30 分鐘內建立一個簡單的搜尋機器人](https://medium.freecodecamp.org/how-to-build-a-simple-search-bot-in-30-minutes-eb56fcedcdb1) - [使用 Xamarin 和 Visual Studio 建立 iOS 照片庫應用程式](https://www.raywenderlich.com/134049/building-ios-apps-with-xamarin-and-visual-studio) - [建立 Android 手電筒應用程式](https://www.youtube.com/watch?v=dhWL4DC7Krs)(影片) - [製作聊天應用程式](https://medium.freecodecamp.org/how-to-build-a-chat-application-using-react-redux-redux-saga-and-web-sockets-47423e4bc21a) - [使用 React Native 建立 ToDo 應用程式](https://blog.hasura.io/tutorial-fullstack-react-native-with-graphql-and-authentication-18183d13373a) 簡單的 --- - [使用 C# 和 Xamarin 建立空白應用程式(正在進行中)](https://www.intertech.com/Blog/xamarin-tutorial-part-1-create-a-blank-app/) - [使用 Xamarin 和 Visual Studio 建立 iOS 照片庫應用程式](https://www.raywenderlich.com/134049/building-ios-apps-with-xamarin-and-visual-studio) - [建立加載畫面](https://medium.freecodecamp.org/how-to-build-a-delightful-loading-screen-in-5-minutes-847991da509f) - [使用 JS 建立 HTML 計算器](https://medium.freecodecamp.org/how-to-build-an-html-calculator-app-from-scratch-using-javascript-4454b8714b98) - [建立 React Native Todo 應用程式](https://egghead.io/courses/build-a-react-native-todo-application) - 使用 Node.js 編寫 Twitter 機器人 ``` - [Part 1](https://codeburst.io/build-a-simple-twitter-bot-with-node-js-in-just-38-lines-of-code-ed92db9eb078) ``` ``` - [Part 2](https://codeburst.io/build-a-simple-twitter-bot-with-node-js-part-2-do-more-2ef1e039715d) ``` - [建立一個簡單的 RESTFUL Web 應用程式](https://closebrace.com/tutorials/2017-03-02/creating-a-simple-restful-web-app-with-nodejs-express-and-mongodb) - [在 30 分鐘內建立一個簡單的搜尋機器人](https://medium.freecodecamp.org/how-to-build-a-simple-search-bot-in-30-minutes-eb56fcedcdb1) - [建立一個工作抓取 Web 應用程式](https://medium.freecodecamp.org/how-i-built-a-job-scraping-web-app-using-node-js-and-indreed-7fbba124bbdc) - [使用 Python 挖掘 Twitter 資料](https://marcobonzanini.com/2015/03/02/mining-twitter-data-with-python-part-1/) - [使用 Scrapy 和 MongoDB 抓取網站](https://realpython.com/blog/python/web-scraping-with-scrapy-and-mongodb/) - [如何使用 Python 和 Selenium WebDriver 進行抓取](http://www.byperth.com/2018/04/25/guide-web-scraping-101-what-you-need-to-know-and-how-to-scrape-with-python-selenium-webdriver/) - [我應該使用 BeautifulSoup 觀看哪部電影](https://medium.com/@nishantsahoo.in/which-movie-should-i-watch-5c83a3c0f5b1) - [使用 Flask 建立微博](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) - 在 Django 中建立部落格 Web 應用程式 ``` - [Part I : Introduction](https://tutorial.djangogirls.org/en/) ``` ``` - [Part II : Extension To Add More Features](https://legacy.gitbook.com/book/djangogirls/django-girls-tutorial-extensions/details) ``` - [選擇您自己的冒險演示](https://www.twilio.com/blog/2015/03/choose-your-own-adventures-presentations-wizard-mode-part-1-of-3.html) - [使用 Flask 和 RethinkDB 建立待辦事項列表](https://realpython.com/blog/python/rethink-flask-a-simple-todo-list-powered-by-flask-and-rethinkdb/) 中等的 --- - [透過建立簡單的 RPG 遊戲來學習 C#](http://scottlilly.com/learn-c-by-building-a-simple-rpg-index/) - [用 C# 創作 Rogue-like 遊戲](https://roguesharp.wordpress.com/) - [使用 Clojure 建構 Twitter 機器人](http://howistart.org/posts/clojure/1/index.html) - [建立拼字檢查器](https://bernhardwenzel.com/articles/clojure-spellchecker/) - [使用 Java 建立簡單的 HTTP 伺服器](http://javarevisited.blogspot.com/2015/06/how-to-create-http-server-in-java-serversocket-example.html) - [建立 Android 手電筒應用程式](https://www.youtube.com/watch?v=dhWL4DC7Krs)(影片) - [建立具有使用者身份驗證的 Spring Boot 應用程式](https://scotch.io/tutorials/build-a-spring-boot-app-with-user-authentication) - [透過 30 個教學在 30 天內建立 30 個東西](https://javascript30.com) - [使用純 JS 建立應用程式](https://medium.com/codingthesmartway-com-blog/pure-javascript-building-a-real-world-application-from-scratch-5213591cfcd6) - [建立無伺服器 React.js 應用程式](http://serverless-stack.com/) - [建立 Trello 克隆](http://codeloveandboards.com/blog/2016/01/04/trello-tribute-with-phoenix-and-react-pt-1/) - [使用 React、Node、MongoDB 和 SocketIO 建立角色投票應用程式](http://sahatyalkabov.com/create-a-character-voting-app-using-react-nodejs-mongodb-and-socketio/) - [React 教學:克隆 Yelp](https://www.fullstackreact.com/articles/react-tutorial-cloning-yelp/) - [使用 React.js 和 Node.js 建立簡單的中型克隆](https://codeburst.io/build-simple-medium-com-on-node-js-and-react-js-a278c5192f47) - [在 JS 中整合 MailChimp](https://medium.freecodecamp.org/how-to-integrate-mailchimp-in-a-javascript-web-app-2a889fb43f6f) - [使用 React Native 建立 ToDo 應用程式](https://blog.hasura.io/tutorial-fullstack-react-native-with-graphql-and-authentication-18183d13373a) - [製作聊天應用程式](https://medium.freecodecamp.org/how-to-build-a-chat-application-using-react-redux-redux-saga-and-web-sockets-47423e4bc21a) - [使用 React Native 建立新聞應用程式](https://medium.freecodecamp.org/create-a-news-app-using-react-native-ced249263627) - [學習 React 的 Webpack](https://medium.freecodecamp.org/learn-webpack-for-react-a36d4cac5060) - [建立您自己的 React 樣板](https://medium.freecodecamp.org/how-to-build-your-own-react-boilerplate-2f8cbbeb9b3f) - [基本 React+Redux 入門教學](https://hackernoon.com/a-basic-react-redux-introductory-tutorial-adcc681eeb5e) - [建立一個預約安排程序](https://hackernoon.com/build-an-appointment-scheduler-using-react-twilio-and-cosmic-js-95377f6d1040) - 使用 Angular 2+ 建立具有離線功能的 Hacker News 用戶端 ``` - [Part 1](https://houssein.me/angular2-hacker-news) ``` ``` - [Part 2](https://houssein.me/progressive-angular-applications) ``` - 帶有 Angular 5 的 ToDo 應用程式 ``` - [Introduction to Angular](http://www.discoversdk.com/blog/intro-to-angular-and-the-evolution-of-the-web) ``` ``` - [Part 1](http://www.discoversdk.com/blog/angular-5-to-do-list-app-part-1) ``` - 帶有 Angular 5 的 ToDo 應用程式 ``` - [Introduction to Angular](http://www.discoversdk.com/blog/intro-to-angular-and-the-evolution-of-the-web) ``` ``` - [Part 1](http://www.discoversdk.com/blog/angular-5-to-do-list-app-part-1) ``` 難的 -- - [建構一個解釋器](http://www.craftinginterpreters.com/)(第 14 章是用 C 寫的) - [用 C 語言寫一個 Shell](https://brennan.io/2015/01/16/write-a-shell-in-c/) - [編寫 FUSE 文件系統](https://www.cs.nmsu.edu/~pfeiffer/fuse-tutorial/) - [建立您自己的文字編輯器](http://viewsourcecode.org/snaptoken/kilo/) - [建立自己的 Lisp](http://www.buildyourownlisp.com/) - [建構 CoreWiki](https://www.youtube.com/playlist?list=PLVMqA0_8O85yC78I4Xj7z48ES48IQBa7p)這是一個 Wiki 風格的內容管理系統,完全用 C# 使用 ASP.NET Core 和 Razor Pages 編寫。您可以[在這裡](https://github.com/csharpfritz/CoreWiki)找到原始程式碼。 - [建構 JIRA 與 Clojure 和 Atlassian Connect 的集成](https://hackernoon.com/building-a-jira-integration-with-clojure-atlassian-connect-506ebd112807) - [建構一個解釋器](http://www.craftinginterpreters.com/)(第 4-13 章是用 Java 寫的) - [使用 Mocha、React、Redux 和 Immutable 透過測試優先開發來建立全端電影投票應用程式](https://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html) - [使用 React 和 Node 建立 Twitter Stream](https://scotch.io/tutorials/build-a-real-time-twitter-stream-with-node-and-react-js) - 使用 Webtask.io 建立無伺服器 MERN Story 應用程式 ``` - [Part 1](https://scotch.io/tutorials/build-a-serverless-mern-story-app-with-webtask-io-zero-to-deploy-1) ``` ``` - [Part 2](https://scotch.io/tutorials/build-a-serverless-mern-story-app-with-webtask-io-zero-to-deploy-2) ``` - [使用 React + Parcel 建立 Chrome 擴充功能](https://medium.freecodecamp.org/building-chrome-extensions-in-react-parcel-79d0240dd58f) ``` [Testing React App With Pupepeteer and Jest](https://blog.bitsrc.io/testing-your-react-app-with-puppeteer-and-jest-c72b3dfcde59) ``` - [用 React 編寫生命遊戲](https://medium.freecodecamp.org/create-gameoflife-with-react-in-one-hour-8e686a410174) - [建立帶有情感分析的聊天應用程式](https://codeburst.io/build-a-chat-app-with-sentiment-analysis-using-next-js-c43ebf3ea643) - [建立全端 Web 應用程式設置](https://hackernoon.com/full-stack-web-application-using-react-node-js-express-and-webpack-97dbd5b9d708) - 建立隨機報價機 ``` - [Part 1](https://www.youtube.com/watch?v=3QngsWA9IEE) ``` ``` - [Part 2](https://www.youtube.com/watch?v=XnoTmO06OYo) ``` ``` - [Part 3](https://www.youtube.com/watch?v=us51Jne67_I) ``` ``` - [Part 4](https://www.youtube.com/watch?v=iZx7hqHb5MU) ``` ``` - [Part 5](https://www.youtube.com/watch?v=lpba9vBqXl0) ``` ``` - [Part 6](https://www.youtube.com/watch?v=Jvp8j6zrFHE) ``` ``` - [Part 7](https://www.youtube.com/watch?v=M_hFfrN8_PQ) ``` - 使用 Angular 6 建立美麗的現實世界應用程式: ``` - [Part I](https://medium.com/@hamedbaatour/build-a-real-world-beautiful-web-app-with-angular-6-a-to-z-ultimate-guide-2018-part-i-e121dd1d55e) ``` - [使用 BootStrap 4 和 Angular 6 建立響應式佈局](https://medium.com/@tomastrajan/how-to-build-responsive-layouts-with-bootstrap-4-and-angular-6-cfbb108d797b) - [使用 Django 和測試驅動開發建立待辦事項列表](http://www.obeythetestinggoat.com/) - [使用 Python 建立 RESTful 微服務](http://www.skybert.net/python/developing-a-restful-micro-service-in-python/) - [使用 Docker、Flask 和 React 的微服務](https://testdriven.io/) - [使用 Flask 建立簡單的 Web 應用程式](https://pythonspot.com/flask-web-app-with-python/) - [使用 Flask 建立 RESTful API – TDD 方式](https://scotch.io/tutorials/build-a-restful-api-with-flask-the-tdd-way) - [在 20 分鐘內建立 Django API](https://codeburst.io/create-a-django-api-in-under-20-minutes-2a082a60f6f3) 想法 == 簡單的 --- ### 99 瓶 - 建立一個程序,列印歌曲“牆上的 99 瓶啤酒”的每一行。 - 不要使用所有數字的列表,也不要手動輸入所有數字。請改用內建函數。 - 除了短語“取下一個”之外,您不得直接在歌詞中輸入任何數字/數字名稱。 - 請記住,當您還剩下 1 瓶時,「瓶子」一詞將變為單數。 ### 魔術8球 - 模擬神奇的 8 球。 - 允許使用者輸入他們的問題。 - 顯示正在進行的訊息(即“思考”)。 - 建立 20 個回應,並顯示隨機回應。 - 允許用戶提出另一個問題或退出。 - 獎金: ``` - Add a gui. ``` ``` - It must have a box for users to enter the question. ``` ``` - It must have at least 4 buttons: ``` ``` - ask ``` ``` - clear (the text box) ``` ``` - play again ``` ``` - quit (this must close the window) ``` ### 石頭剪刀布遊戲 - 建立一個石頭剪刀布遊戲。 - 讓玩家選擇石頭、剪刀或布。 - 讓計算機選擇它的移動方式。 - 比較選擇並決定誰獲勝。 - 列印結果。 - 子目標: ``` - Give the player the option to play again. ``` ``` - Keep a record of the score (e.g. Player: 3 / Computer: 6). ``` ### 倒數時鐘 - 建立一個程序,允許使用者選擇時間和日期,然後以給定的時間間隔(例如每秒)列印一條訊息,告訴使用者距離所選時間還有多長時間。 - 子目標: ``` - If the selected time has already passed, have the program tell the user to start over. ``` ``` - If your program asks for the year, month, day, hour, etc. separately, allow the user to be able to type in either the month name or its number. ``` ``` - TIP: Making use of built in modules such as time and datetime can change this project from a nightmare into a much simpler task. ``` 中等的 --- ### 番茄計時器 建立一個番茄計時器。 番茄計時器是一種時間管理方法。該技術使用計時器將工作分解為多個時間間隔,通常長度為 25 分鐘,中間間隔短暫的休息。這些間隔被命名為“pomodoros”,是意大利語單字“pomodoro”(番茄)的英文複數形式,以西里洛在大學時使用的番茄形狀的廚房計時器命名。 原始技巧有六個步驟: 決定要完成的任務。 設定番茄計時器(傳統上為 25 分鐘)。 完成任務。 當計時器響起時結束工作並在一張紙上畫上複選標記。 如果您的複選標記少於四個,請短暫休息(3-5 分鐘),然後轉到步驟 2。 四個番茄鐘後,休息較長時間(15-30 分鐘),將複選標記計數重設為零,然後轉到步驟 1。 要了解有關番茄計時器的更多訊息[,請單擊此處](https://en.wikipedia.org/wiki/Pomodoro_Technique) ### 谷歌案例 - 這是一個可以讓你玩英文句子的遊戲。 - 使用者將以任何格式輸入一個句子。(大寫或小寫或兩者的混合) - 程式必須將給定的句子轉換為Google大小寫。什麼是Google大小寫句子風格?\[know\_about\_it\_here:\](這是一種寫作風格,我們將所有小寫字母替換為大寫字母,留下所有單字的首字母)。 - 子目標: ``` - Program must then convert the given sentence in camel case.To know more about camel case ``` ``` [click_here](https://en.wikipedia.org/wiki/Camel_case) ``` ``` - Sentence can be entered with any number of spaces. ``` ### 擲骰子模擬器 - 允許使用者輸入骰子的面數以及應擲骰子的次數。 - 您的程式應該模擬擲骰子並追蹤每個數字出現的次數(這不必顯示)。 - 最後,列印出每個數字出現的次數。 - 子目標: ``` - Adjust your program so that if the user does not type in a number when they need to, the program will keep prompting them to type in a real number until they do so. ``` ``` - Put the program into a loop so that the user can continue to simulate dice rolls without having to restart the entire program. ``` ``` - In addition to printing out how many times each side appeared, also print out the percentage it appeared. If you can, round the percentage to 4 digits total OR two decimal places. ``` - 獎金: ``` - You are about to play a board game, but you realize you don't have any dice. Fortunately you have this program. ``` ``` - 1. Create a program that opens a new window and draws 2 six-sided dice ``` ``` - 2. Allow the user to quit, or roll again ``` ``` - Allow the user to select the number of dice to be drawn on screen(1-4) 2. Add up the total of the dice and display it ``` ### 計算並修復綠雞蛋和火腿 你們有些人可能還記得蘇博士的故事「綠雞蛋和火腿」。對於那些不記得或從未聽說過的人,[這](http://pastebin.com/XMY48CnN)是這個故事。然而,我給你的故事有一個問題——每次使用「我」這個詞時,它都是小寫的。 由於此問題,您的工作是執行以下操作: - 將我給您的故事複製到常規文字檔案中。 - 建立一個程式來通讀故事並在任何時候將字母 i 變為大寫。 (當它也用在 sam-I-am 的名字中時,請務必更改它。) - 讓你的程式建立一個新文件,並讓它正確地寫出故事。 - 印出有多少錯誤被修正。 - 完成後,您應該已經糾正了[這麼多](https://i.imgur.com/GRkj3yz.jpg)錯誤。 難的 -- ### 隨機維基百科文章 如果您曾造訪維基百科,您可能已經注意到螢幕左側有一個指向隨機文章的連結。雖然看到您被帶到哪篇文章可能很有趣,但有時看到文章的名稱會很好,這樣您就可以在聽起來很無聊時跳過它。幸運的是,維基百科有一個 API,允許我們這樣做[點擊這裡](https://en.wikipedia.org/w/api.php?action=query&list=random&rnnamespace=0&rnlimit=10&format=json)。 然而,有一個困境。由於維基百科擁有有關世界各地主題的文章,其中一些文章的標題中包含特殊字元。例如,關於西班牙畫家[埃拉斯托·科爾特斯·華雷斯 (Erasto Cortés Juárez)](https://en.wikipedia.org/wiki/Erasto_Cort%C3%A9s_Ju%C3%A1rez)的文章中就有 é 和 á。如果您查看這篇特定文章的[API](https://en.wikipedia.org/w/api.php?action=query&prop=info&pageids=39608394&inprop=url&format=json) ,您將看到標題是“Erasto Cort\\u00e9s Ju\\u00e1rez”,並且 \\u00e9 和 \\u00e1 正在替換前面提到的兩個字母。 (有關這是什麼的訊息,請首先查看文件中[本頁](https://docs.python.org/2/howto/unicode.html)的前半部分)。為了讓你的程式正常運作,你必須以某種方式處理這個問題。 - 建立一個程序,從官方維基百科 API 中提取標題,然後一一詢問用戶是否願意閱讀該文章。 - 例子: ``` - If the first title is Reddit, then the program should ask something along the lines of "Would you like to read about Reddit?" If the user says yes, then the program should open up the article for the user to read. ``` ``` - HINT: Click [here](https://en.wikipedia.org/wiki?curid=39608394) to see how the article's ID can be used to access the actual article. ``` - 子目標: ``` - As mentioned before, do something about the possibility of unicode appearing in the title. ``` ``` - Whether you want your program to simply filter out these articles or you want to actually turn the codes into readable characters, that's up to you. ``` ``` - Make the program pause once the user has selected an article to read, and allow him or her to continue browsing different article titles once finished reading. ``` ``` - Allow the user to simply press ENTER to be asked about a new article. ``` ### 天氣如何? 如果您想了解 API 的基礎知識,請查看 iamapizza 的[這篇](http://www.reddit.com/r/explainlikeimfive/comments/qowts/eli5_what_is_api/c3z9kok)文章。 - 建立一個程序,從 OpenWeatherMap.org 提取資料並列印有關當前天氣的訊息,例如您居住的地方的最高氣溫、最低氣溫和雨量。 - 子目標: ``` - Print out data for the next 5-7 days so you have a 5 day/week long forecast. ``` ``` - Print the data to another file that you can open up and view at, instead of viewing the information in the command line. ``` ``` - If you know html, write a file that you can print information to so that your project is more interesting. ``` - 尖端: ``` - APIs that are in Json are essentially lists and dictionaries. Remember that to reference something in a list, you must refer to it by what number element it is in the list, and to reference a key in a dictionary, you must refer to it by its name. ``` ``` - Don't like Celsius? Add &units=imperial to the end of the URL of the API to receive your data in Fahrenheit. ``` ### 來源 - https://github.com/tuvtran/project-based-learning - https://github.com/jorgegonzalez/beginner-projects - https://github.com/MunGell/awesome-for-beginners/blob/master/README.md - https://github.com/sarahbohr/AbsoluteBeginnerProjects --- 你怎麼認為?您喜歡透過特定專案進行學習還是不喜歡透過特定專案進行學習? --- 原文出處:https://dev.to/samborick/100-project-ideas-oda

軟體工程師面試學習指南

[本·羅戈揚](https://www.linkedin.com/in/benjaminrogojan/) 軟體工程面試與其他技術面試一樣,需要充分的準備。有許多主題需要涵蓋,以確保您準備好應對有關演算法、資料結構、設計、最佳化以及不斷增長的主題的連續問題。 因此,我在最後一輪面試中建立了一個清單,涵蓋了許多熱門話題。 為了幫助您追蹤進度,我們針對下面列出的相同問題編制了一份全面的清單; [該列表可以在這裡找到](https://docs.google.com/spreadsheets/d/19hSRrL4l3gRiJ5ucH9q4iwFo2QHgic9gGMNUrcn1mm0/edit?usp=sharing)。 ### **聆聽經典熱身** 1. [fizzbuzz](https://www.hackerrank.com/challenges/fizzbuzz/problem) 2. [子陣列和等於 K](https://leetcode.com/problems/subarray-sum-equals-k/) 3. [陣列:左旋轉](https://www.hackerrank.com/challenges/ctci-array-left-rotation/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&playlist_slugs%5B%5D=arrays) 4. [字串:製作字謎詞](https://www.hackerrank.com/challenges/ctci-making-anagrams/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&playlist_slugs%5B%5D=strings) 5. [第 N 次斐波那契數](https://www.algoexpert.io/questions/Nth%20Fibonacci) 你怎麼做的?花點時間對這些經典作品進行評價。我們在面試過程中的某個時刻被問到了其中大部分問題,而且通常是在早期就被問到的淘汰式問題。它們通常與演算法和資料結構關係不大,但仍然需要對循環和陣列有很好的理解(是的,陣列是一種資料結構)。 ### **演算法和資料結構** #### **預習問題** 在觀看有關資料結構和演算法的影片內容之前,請考慮嘗試以下這些問題。看看你能否回答他們。這將幫助您知道要關注什麼。 1. [985. 查詢後偶數之和](https://leetcode.com/problems/sum-of-even-numbers-after-queries/) 2. [657. 機器人回歸原點](https://leetcode.com/problems/robot-return-to-origin/) 3. [961. 2N 陣列中的 N 重複元素](https://leetcode.com/problems/n-repeated-element-in-size-2n-array/) 4. [110.平衡二元樹](https://leetcode.com/problems/balanced-binary-tree/) 5. [3. 最長無重複字元的子字串](https://leetcode.com/problems/longest-substring-without-repeating-characters/) 6. [19. 從清單結尾刪除第 N 個節點](https://leetcode.com/problems/remove-nth-node-from-end-of-list/) 7. [23. 合併 k 個排序列表](https://leetcode.com/problems/merge-k-sorted-lists/) 8. [31. 下一個排列](https://leetcode.com/problems/next-permutation/) ### 演算法和資料結構影片 #### 資料結構 ![](https://cdn-images-1.medium.com/max/1600/1*Dyu63sMUVL-gYEZISOE2BQ.jpeg) 1. [資料結構與演算法 #1 --- 什麼是資料結構?](https://www.youtube.com/watch?v=bum_19loj9A) --- 影片 2. [多調光](https://archive.org/details/0102WhatYouShouldKnow/02_05-multidimensionalArrays.mp4)--- 影片 3. [動態陣列](https://www.coursera.org/learn/data-structures/lecture/EwbnV/dynamic-arrays)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 4. [調整陣列大小](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 5. [資料結構:鍊錶](https://youtu.be/njTh_OwMljA)--- 影片 6. [核心鍊錶與陣列](https://www.coursera.org/learn/data-structures-optimizing-performance/lecture/rjBs9/core-linked-lists-vs-arrays)--- 影片 7. [指針到指針](https://www.eskimo.com/~scs/cclass/int/sx8.html)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 8. [資料結構:樹](https://youtu.be/oSWTXtMglKE)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 9. [資料結構:堆](https://youtu.be/t0Cq6tVNRBA)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 10. [資料結構:哈希表](https://youtu.be/shs0KM3wKv8)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 11. [電話簿問題](https://www.coursera.org/learn/data-structures/lecture/NYZZP/phone-book-problem)---影片 12. [資料結構:堆疊和佇列](https://youtu.be/wjI1WNcIntg)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 13. [使用堆疊後進先出](https://archive.org/details/0102WhatYouShouldKnow/05_01-usingStacksForLast-inFirst-out.mp4)--- 影片 14. [資料結構:計算機科學速成課程#14](https://youtu.be/DuDz6B4cqVc)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 15. [資料結構:嘗試](https://www.youtube.com/watch?v=zIjfhVPRZCg)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 #### 演算法 ![](https://cdn-images-1.medium.com/max/1600/1*bPpvELo9_QqQsDz7CSbwXQ.gif) 1. [演算法:圖搜尋、DFS 和 BFS](https://www.youtube.com/watch?v=zaBhtODEL0w&list=PLX6IKgS15Ue02WDPRCmYKuZicQHit9kFt)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 2. [BFS(廣度優先搜尋)和DFS(深度優先搜尋)](https://www.youtube.com/watch?v=uWL6FJhq5fM) --- 影片 3. [演算法:二分查找](https://youtu.be/P3YID7liBug)---影片 4. [二元搜尋樹回顧](https://www.youtube.com/watch?v=x6At0nzX92o&index=1&list=PLA5Lqm4uh9Bbq-E0ZnqTIa8LRaL77ica6)--- 影片 5. [用於面試的 Python 演算法](https://www.youtube.com/watch?v=p65AHm9MX80)[ ](https://archive.org/details/0102WhatYouShouldKnow/03_01-resizableArrays.mp4)--- 影片 6. [演算法:遞歸](https://youtu.be/KEEKn7Me-ms)---影片 7. [演算法:冒泡排序](https://youtu.be/6Gv8vg0kcHc)--- 影片 8. [演算法:歸併排序](https://youtu.be/KF2j-9iSf4Q)--- 影片 9. [演算法:快速排序](https://youtu.be/SLauY6PpjW4)---影片 ### **大 O 表示法** 1. [大 O 表示法與時間複雜度簡介(資料結構與演算法 #7)](https://www.youtube.com/watch?v=D6xkbGLQesk) --- 影片 2. [哈佛 CS50 --- 漸近符號](https://www.youtube.com/watch?v=iOq5kSKqeR4)--- 影片 3. [演算法複雜度分析的簡單介紹](http://discrete.gr/complexity/)--- Post 4. [備忘錄](http://bigocheatsheet.com/)--- 帖子 ### **動態規劃** 1. [動態規劃(像程式設計師一樣思考)---影片](https://www.youtube.com/watch?v=iv_yHjmkv4I) 2. [演算法:記憶化與動態規劃 --- 影片](https://www.youtube.com/watch?v=P8Xa2BitN3I&t=13s) 3. 6 [.006:動態規劃 I:斐波那契、最短路徑 --- 影片](https://www.youtube.com/watch?v=OQ5jsbhAv_M&t=7s) 4. [6.006:動態規劃 II:文字對齊、二十一點 --- 影片](https://www.youtube.com/watch?v=ENyox7kNKeY&t=4s) 5. [動態規劃 --- 帖子](https://www.topcoder.com/community/competitive-programming/tutorials/dynamic-programming-from-novice-to-advanced/) ### **字串操作** 1. [編碼面試問答:最長的連續字元](https://www.youtube.com/watch?v=qRNB8CV3_LU)--- 影片 2. [Sedgewick --- 子字串搜尋](https://www.coursera.org/learn/algorithms-part2/home/week/4)--- 影片 #### 面試問題演練 1. [谷歌編碼面試 --- 普世價值樹問題](https://www.youtube.com/watch?v=7HgsS8bRvjo)--- 影片 2. [Google 編碼面試問題和答案 #1:第一個重複出現的字元](https://www.youtube.com/watch?v=GJdiM-muYqc)--- 影片 3. [在二元搜尋樹中找到最小和最大元素](https://www.youtube.com/watch?v=Ut90klNN264&index=30&list=PL2_aWCzGMAwI3W_JlcBbtYTwiQSsOTa6P)--- 影片 4. [求二元樹的高度](https://www.youtube.com/watch?v=_pnqMz5nrRs&list=PL2_aWCzGMAwI3W_JlcBbtYTwiQSsOTa6P&index=31)--- 影片 5. [檢查二元樹是否為二元搜尋樹](https://www.youtube.com/watch?v=yEwSGhSsT0U&index=35&list=PL2_aWCzGMAwI3W_JlcBbtYTwiQSsOTa6P)--- 影片 6. [什麼是尾遞歸?為什麼這麼糟?](https://www.quora.com/What-is-tail-recursion-Why-is-it-so-bad) --- 影片 ### **學後問題** 現在您已經學習了一些,並觀看了一些影片,讓我們嘗試更多問題! 1. [越大越好](https://www.hackerrank.com/challenges/bigger-is-greater/problem) 2. [6.之字折線轉換](https://leetcode.com/problems/zigzag-conversion/) 3. [7. 反轉整數](https://leetcode.com/problems/reverse-integer/) 4. [40. 組合和 II](https://leetcode.com/problems/combination-sum-ii/) 5. [43. 字串相乘](https://leetcode.com/problems/multiply-strings/) 6. [拉里的陣列](https://www.hackerrank.com/challenges/larrys-array/problem) 7. [短回文](https://www.hackerrank.com/challenges/short-palindrome/problem) 8. [65. 有效號碼](https://leetcode.com/problems/valid-number/) 9. [越大越好](https://www.hackerrank.com/challenges/bigger-is-greater/problem) 10. [完整計數排序](https://www.hackerrank.com/challenges/countingsort4/problem) 11. [莉莉的家庭作業](https://www.hackerrank.com/challenges/lilys-homework/problem) 12. [普通孩子](https://www.hackerrank.com/challenges/common-child/problem) 13. [459. 重複子字串模式](https://leetcode.com/problems/repeated-substring-pattern/) 14. [27. 刪除元素](https://leetcode.com/problems/remove-element/) 15. [450. 刪除 BST 中的節點](https://leetcode.com/problems/delete-node-in-a-bst/) 16. [659. 將陣列分割成連續的子序列](https://leetcode.com/problems/split-array-into-consecutive-subsequences/) 17. [最大值有界的子陣列數](https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum) 18. [組合和 IV](https://leetcode.com/problems/combination-sum-iv) 19. [買賣股票的最佳時機(有冷卻時間)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown) 20. [最長重複字元替換](https://leetcode.com/problems/longest-repeating-character-replacement) 21. [成對交換節點](https://leetcode.com/problems/swap-nodes-in-pairs) 22. [二元樹右側視圖](https://leetcode.com/problems/binary-tree-right-side-view) 23. [展平嵌套列表迭代器](https://leetcode.com/problems/flatten-nested-list-iterator) 24. [二元樹層次順序遍歷](https://leetcode.com/problems/binary-tree-level-order-traversal) 25. [二元搜尋樹迭代器](https://leetcode.com/problems/binary-search-tree-iterator) 26. [對鏈最大長度](https://leetcode.com/problems/maximum-length-of-pair-chain) 27. [將鍊錶拆分為多個部分](https://leetcode.com/problems/split-linked-list-in-parts) ### **操作性程式設計問題** 有些公司不會問你演算法問題。相反,他們可能會更專注於實施和營運問題。這些通常更小眾,涉及實際問題,例如循環資料和執行某種任務。這些類型的問題通常不需要太多練習,因為更多的是了解陣列和 HashMap 等基本概念並追蹤您對它們所做的事情。 1. [袋鼠問題](https://www.hackerrank.com/challenges/kangaroo/problem) 2. [打破記錄](https://www.hackerrank.com/challenges/breaking-best-and-worst-records/problem) 3. [查找字串](https://www.hackerrank.com/challenges/find-a-string/problem)[迭代器](https://www.hackerrank.com/challenges/itertools-permutations/problem) 4. [不知道!](https://www.hackerrank.com/challenges/no-idea/problem) 5. [程式設計師的日子](https://www.hackerrank.com/challenges/day-of-the-programmer/problem) 6. [排行榜](https://www.hackerrank.com/challenges/climbing-the-leaderboard/problem) 7. [詞序](https://www.hackerrank.com/challenges/word-order/problem) 8. [夏洛克和方塊](https://www.hackerrank.com/challenges/sherlock-and-squares/problem) 9. [均衡陣列](https://www.hackerrank.com/challenges/equality-in-a-array/problem) 10. [蘋果和橘子](https://www.hackerrank.com/challenges/apple-and-orange/problem) 11. [更多操作風格問題](https://www.hackerrank.com/domains/python) ### **系統設計影片** 系統設計問題是至關重要的問題,表明您不僅僅是一名編碼員。身為工程師,您需要能夠思考大局。某些服務屬於哪裡,您需要什麼樣的伺服器,您將如何管理流量等等。所有這些想法都表明您能夠設計軟體,而不僅僅是編寫人們告訴您編寫的程式碼。 1. [停車場系統](https://youtu.be/DSGsa0pu8-k)---影片 2. [什麼是應用程式](https://www.youtube.com/watch?v=vvhC64hQZMk)--- 影片 3. [優步設計](https://youtu.be/umWABit-wbk)---影片 4. [Instagram](https://www.youtube.com/watch?v=QmX2NPkJTKg) --- 影片 5. [Tinder 服務](https://www.youtube.com/watch?v=xQnIN9bW0og)--- 影片 ### **作業系統** 作業系統問題比較少見,但是對執行緒、調度、記憶體等概念有紮實的理解是有好處的,即使只是基本的理解。當被問到進程和線程有什麼區別而不知道答案時,這是非常尷尬的。 1. [常見作業系統面試問題](https://www.geeksforgeeks.org/commonly-asked-operating-systems-interview-questions-set-1/) 2. [什麼是翻譯後備緩衝區?](https://www.geeksforgeeks.org/operating-system-translation-lookaside-buffer-tlb/) 3. [為什麼循環法可以避免優先反轉問題?](https://leetcode.com/discuss/interview-question/operating-system/220604/Why-does-Round-Robin-avoid-the-Priority-Inversion-Problem) 4. [中斷與系統呼叫---檔案系統中的「inode」是什麼?](https://leetcode.com/discuss/interview-question/operating-system/124838/Interrupt-Vs-System-Call) 5. [作業系統面試題目及答案 --- 第一部分](https://www.youtube.com/watch?v=b18X4uOKjHs) 6. [什麼是內核 --- Gary 解釋](https://www.youtube.com/watch?v=mycVSMyShk8) 7. [循環演算法教程(CPU調度)](https://www.youtube.com/watch?v=aWlQYllBZDs) 8. [LRU 快取的魔力(Google 開發 100 天)](https://www.youtube.com/watch?v=R5ON3iwx78M) --- 影片 9. [MIT 6.004 L15:記憶體層次結構](https://www.youtube.com/watch?v=vjYF_fAZI5E&list=PLrRW1w6CGAcXbMtDFj205vALOGmiRc82-&index=24)--- 影片 10. [中斷](https://www.youtube.com/watch?v=uFKi2-J-6II&list=PLCiOXwirraUCBE9i_ukL8_Kfg6XNv7Se8&index=3)---影片 11. [調度](https://www.youtube.com/watch?v=-Gu5mYdKbu4&index=4&list=PLCiOXwirraUCBE9i_ukL8_Kfg6XNv7Se8)---影片 ### **執行緒數** ![](https://cdn-images-1.medium.com/max/1600/1*uSHd3KSxg363C1frMv2KwQ.png) 1. [用戶級線程與內核級線程](https://leetcode.com/discuss/interview-question/operating-system/124631/User-Level-thread-Vs-Kernel-Level-thread) 2. [行程和線程簡介](https://www.youtube.com/watch?v=exbKr6fnoUw)--- 影片 3. [進程和線程之間的區別 --- 佐治亞理工學院 --- 高級操作系統](https://www.youtube.com/watch?v=O3EyzlZxx3g&t=11s)--- 影片 4. [分叉和多線程之間的區別](https://leetcode.com/discuss/interview-question/operating-system/125024/Difference-between-forking-and-multithreading) ### **物件導向** ![](https://cdn-images-1.medium.com/max/1600/1*8LHiDwWhrU4iegYiNnKX_A.png) 與作業系統類似,並不是每次面試都會問你有關物件導向程式設計的問題,但你永遠不知道。您需要確保記住計算機 162 課程中的基礎知識。 1. [Java 程式設計教學 --- 49 --- 繼承](https://www.youtube.com/watch?v=9JpNY-XAseg)--- 影片 2. [Java 程式設計教學 --- 55 --- 多態性簡介](https://www.youtube.com/watch?v=0xw06loTm1k)--- 影片 3. [Java 程式設計教學 --- 58 --- 抽象類別與具體類別](https://www.youtube.com/watch?v=TyPNvt6Zg8c)--- 影片 4. [Java 程式設計教學 --- 57 --- 重寫規則](https://www.youtube.com/watch?v=zN9pKULyoj4&t=3s)--- 影片 5. [Java 程式設計教學 --- 59 --- 保存物件的類](https://www.youtube.com/watch?v=slY5Ag7IjM0) 6. [物件導向程式設計](https://www.youtube.com/watch?v=lbXsrHGhBAU)---影片 ### **設計模式** 如果您像我們一樣,我們不會被教導所有各種設計模式。因此,最好了解它們的工作原理以及使用它們的原因。一些面試問題可以很簡單,例如“為什麼要使用工廠類?” 1. [工廠設計模式](https://www.youtube.com/watch?v=ub0DXaeV6hA)---影片 2. [觀察者設計模式](https://youtu.be/wiQdrH2YpT4)---影片 3. [適配器設計模式](https://www.youtube.com/watch?v=qG286LQM6BU&list=PLF206E906175C7E07&index=13)---影片 4. [立面設計模式](https://www.youtube.com/watch?v=B1Y8fcYrz5o&list=PLF206E906175C7E07&index=14)---影片 5. [責任鏈設計模式](https://www.youtube.com/watch?v=jDX6x8qmjbA&list=PLF206E906175C7E07&index=22)---影片 6. [解譯器設計模式](https://www.youtube.com/watch?v=6CVymSJQuJE&list=PLF206E906175C7E07&index=23)---影片 7. [單例設計模式教學](https://www.youtube.com/watch?v=NZaXM67fxbs&list=PLF206E906175C7E07&index=7)--- 影片 8. [第 6 章(第 1 部分)--- 模式(影片)](https://youtu.be/LAP2A80Ajrg?list=PLJ9pm_Rc9HesnkwKlal_buSIHA-jTZMpO&t=3344) --- 影片 9. [Head First 設計模式](https://www.amazon.com/Head-First-Design-Patterns-Freeman/dp/0596007124)--- 影片 ### SQL 這是最後一節。你們中的許多人可能不會被問到那麼多 SQL 問題。不過,我始終認為放在後口袋是件好事。 #### SQL --- 問題 1. [262. 行程和用戶](https://leetcode.com/problems/trips-and-users/) 2. [601. 體育場人流量](https://leetcode.com/problems/human-traffic-of-stadium/) 3. [185. 部門前三薪資](https://leetcode.com/problems/department-top-three-salaries/) 4. [626.交換席位](https://leetcode.com/problems/exchange-seats/) 5. [駭客排名報告](https://www.hackerrank.com/challenges/the-report/problem) 6. [177.第N最高薪水](https://leetcode.com/problems/nth-highest-salary/) 7. [對稱對](https://www.hackerrank.com/challenges/symmetric-pairs/problem) 8. [職業](https://www.hackerrank.com/challenges/placements/problem)[安置](https://www.hackerrank.com/challenges/occupations/problem) 9. [奧利凡德的庫存](https://www.hackerrank.com/challenges/harry-potter-and-wands/problem) #### SQL --- 影片 1. [IQ15:6 個 SQL 查詢面試問題](https://www.youtube.com/watch?v=uAWWhEA57bE)--- 影片 2. [了解 ROW\_NUMBER 和分析函數](https://www.youtube.com/watch?v=QFj-hZi8MKk)--- 影片 3. [分析函數的高級實現](https://www.youtube.com/watch?v=G3kYPzLWtpo&t=4s)--- 影片 4. [分析函數的高級實現第 2 部分](https://www.youtube.com/watch?v=XecU6Ieyu-4&t=54s)--- 影片 5. [聰明的貓頭鷹 SQL 影片](https://www.youtube.com/watch?v=2-1XQHAgDsM&list=PL6EDEB03D20332309)--- 影片 #### SQL 後問題 1. [二元樹節點](https://www.hackerrank.com/challenges/binary-search-tree-1/problem) 2. [氣象觀測站 18](https://www.hackerrank.com/challenges/weather-observation-station-18/problem) 3. [挑戰](https://www.hackerrank.com/challenges/challenges/problem)[列印素數](https://www.hackerrank.com/challenges/print-prime-numbers/problem) 4. [595.大國](https://leetcode.com/problems/big-countries/) 5. [626.交換席位](https://leetcode.com/problems/exchange-seats/) 6. [SQL 面試問題:3 個技術篩選練習(針對資料分析師)](https://data36.com/sql-interview-questions-tech-screening-data-analysts/) 面試可能會很困難,因為你會覺得自己沒有任何進展。擁有本學習指南將幫助您追蹤您的進度並讓您更好地了解自己的表現! 祝你好運! 另外,如果您想閱讀/觀看更多精彩的貼文或影片: [在 SaturnCloud 上使用 Jupyter Notebook 連接到大查詢第 2 部分](https://www.youtube.com/watch?v=O1cBN5gJtdw&t=15s) [身為資料科學家你應該閱讀的三本書](https://www.coriers.com/three-books-you-must-read-if-you-want-a-career-as-a-data-scientist/) [Hadoop 與關聯式資料庫](http://www.acheronanalytics.com/acheron-blog/hadoop-vs-relational-databases) [算法如何變得不道德和有偏見](http://www.acheronanalytics.com/acheron-blog/how-do-machines-learn-bias-data-science) [如何改善資料驅動策略](http://www.acheronanalytics.com/acheron-blog/how-to-improve-your-data-driven-strategy) [如何開發穩健演算法](https://medium.com/better-programming/how-to-develop-a-robust-algorithm-c38e08f32201) [資料科學家必須具備的 4 項技能](https://www.theseattledataguy.com/4-skills-data-scientist-must-have/) [SQL 最佳實踐 - 設計 ETL 影片](http://www.acheronanalytics.com/acheron-blog/sql-best-practices-designing-an-etl-video) --- 原文出處:https://dev.to/seattledataguy/the-interview-study-guide-for-software-engineers-764

Next.js 14 使用瀏覽器爬蟲,進行即時資料抓取的預訂應用程式

目錄 == - [介紹](#introduction) - [技術堆疊](#tech-stack) - [特徵](#features) - [設定 Next.js 應用程式](#step-1-setting-up-the-nextjs-application) - [安裝所需的套件](#step-2-installing-required-packages) - [設定 Redis 連接](#step-3-setting-up-redis-connection) - [配置 BullMQ 佇列](#step-4-configuring-bullmq-queue) - [Next.js 儀器設置](#step-5-nextjs-instrumentation-setup) - [設定 Bright Data 的抓取瀏覽器](#step-6-setting-up-bright-datas-scraping-browser) - [Bright Data 的抓取瀏覽器是什麼?](#what-is-bright-datas-scraping-browser) - [設定 Bright Data 抓取瀏覽器的步驟](#steps-to-set-up-bright-datas-scraping-browser) - [使用 Puppeteer 實作抓取邏輯](#implementing-the-scraping-logic-with-puppeteer) - [航班搜尋功能](#flight-search-feature) - [顯示航班搜尋結果](#displaying-flight-search-results) - [探索完整的指南和程式碼庫](#discover-the-complete-guide-and-codebase) - [在 YouTube 上觀看詳細說明](#watch-the-detailed-explanation-on-youtube) - [在 GitHub 上探索完整程式碼](#explore-the-full-code-on-github) - [結論](#conclusion) 介紹 == 在不斷發展的 Web 開發領域,有效收集、處理和顯示外部來源資料的能力變得越來越有價值。無論是市場研究、競爭分析或客戶洞察,網路抓取在釋放網路資料的巨大潛力方面都發揮著至關重要的作用。 這篇部落格文章介紹了建立強大的 Next.js 應用程式的綜合指南,該應用程式旨在從領先的旅行搜尋引擎之一 Kayak 抓取航班資料。透過利用 Next.js 的強大功能以及 BullMQ、Redis 和 Puppeteer 等現代技術。 技術堆疊 ==== - [Next.js](https://nextjs.org/docs) - [順風CSS](https://tailwindcss.com/docs) - [下一個介面](https://nextui.org/docs) - [健康)狀況](https://zustand.surge.sh/) - [條紋](https://stripe.com/docs) - [Bright Data 的抓取瀏覽器](https://brdta.com/kishansheth21) - [打字稿](https://www.typescriptlang.org/docs) - [雷迪斯](https://redis.io/documentation) - [BullMQ](https://docs.bullmq.io/) - [傀儡師](https://pptr.dev/) - [智威湯遜](https://jwt.io/introduction) - [阿克西奧斯](https://axios-http.com/docs/intro) - [PostgreSQL](https://www.postgresql.org/docs) - [棱鏡](https://www.prisma.io/docs) 特徵 == - 🚀 帶有 Tailwind CSS 的 Next.js 14 應用程式目錄 - 體驗由最新 Next.js 14 提供支援的時尚現代的 UI,並使用 Tailwind CSS 進行設計,以實現完美的外觀和感覺。 - 🔗 API 路由和伺服器操作 - 深入研究與 Next.js 14 的 API 路由和伺服器操作的無縫後端集成,確保高效的資料處理和伺服器端邏輯執行。 - 🕷 使用 Puppeteer Redis 和 BullMQ 進行抓取 - 利用 Puppeteer 的強大功能進行進階 Web 抓取,並使用 Redis 和 BullMQ 管理佇列和作業以實現強大的後端操作。 - 🔑 用於身份驗證和授權的 JWT 令牌 - 使用 JWT 令牌保護您的應用程式,為整個平台提供可靠的身份驗證和授權方法。 - 💳 支付網關 Stripe - 整合 Stripe 進行無縫支付處理,為預訂旅行、航班和飯店提供安全、輕鬆的交易。 - ✈️ 使用 Stripe 支付網關預訂旅行、航班和飯店 - 使用我們的 Stripe 支援的支付系統,讓您的旅遊預訂體驗變得輕鬆。 - 📊 從多個網站抓取即時資料 - 從多個來源抓取即時資料,保持領先,讓您的應用程式更新最新資訊。 - 💾 使用 Prisma 將抓取的資料儲存在 PostgreSQL 中 - 利用 PostgreSQL 和 Prisma 高效儲存和管理抓取的資料,確保可靠性和速度。 - 🔄 用於狀態管理的 Zustand - 透過 Zustand 簡化狀態邏輯並增強效能,在您的應用程式中享受流暢且可管理的狀態管理。 - 😈 該應用程式的最佳功能 - 使用 Bright Data 的抓取瀏覽器抓取不可抓取的資料。 ![抓取瀏覽器迷因](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0ytki1wpsbvluk1qkpo.jpg) Bright Data的抓取瀏覽器為我們提供了自動驗證碼解決功能,可以幫助我們抓取不可抓取的資料。 第 1 步:設定 Next.js 應用程式 --------------------- 1. **建立 Next.js 應用程式**:首先建立一個新的 Next.js 應用程式(如果您還沒有)。您可以透過在終端機中執行以下命令來完成此操作: ``` npx create-next-app@latest booking-app ``` 2. **導航到您的應用程式目錄**:變更為您新建立的應用程式目錄: ``` cd booking-app ``` 步驟2:安裝所需的軟體包 ------------ 您需要安裝多個軟體包,包括 Redis、BullMQ 和 Puppeteer Core。執行以下命令來安裝它們: ``` npm install ioredis bullmq puppeteer-core ``` - `ioredis`是 Node.js 的強大 Redis 用戶端,支援與 Redis 進行通訊。 - `bullmq`以 Redis 作為後端來管理作業和訊息佇列。 - `puppeteer-core`可讓您控制外部瀏覽器以進行抓取。 步驟3:設定Redis連接 ------------- 在適當的目錄(例如`lib/` )中建立一個檔案(例如`redis.js` )來配置 Redis 連線: ``` // lib/redis.js import Redis from 'ioredis'; // Use REDIS_URL from environment or fallback to localhost const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379'; const connection = new Redis(REDIS_URL); export { connection }; ``` 步驟4:配置BullMQ佇列 -------------- 透過在 Redis 配置所在的相同目錄中建立另一個檔案(例如, `queue.js` )來設定 BullMQ 佇列: ``` // lib/queue.js import { Queue } from 'bullmq'; import { connection } from './redis'; export const importQueue = new Queue('importQueue', { connection, defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000, }, }, }); ``` 第 5 步:Next.js 儀器設置 ------------------ Next.js 允許偵測,可以在 Next.js 配置中啟用。您還需要建立一個用於作業處理的工作文件。 1.**在 Next.js 中啟用 Instrumentation** :將以下內容新增至`next.config.js`以啟用 Instrumentation: ``` // next.config.js module.exports = { experimental: { instrumentationHook: true, }, }; ``` 2.**建立用於作業處理的 Worker** :在您的應用程式中,建立一個檔案 ( `instrumentation.js` ) 來處理作業處理。該工作人員將使用 Puppeteer 來執行抓取任務: ``` // instrumentation.js export const register = async () => { if (process.env.NEXT_RUNTIME === 'nodejs') { const { Worker } = await import('bullmq'); const puppeteer = await import('puppeteer-core'); const { connection } = await import('./lib/redis'); const { importQueue } = await import('./lib/queue'); new Worker('importQueue', async (job) => { // Job processing logic with Puppeteer goes here }, { connection, concurrency: 10, removeOnComplete: { count: 1000 }, removeOnFail: { count: 5000 }, }); } }; ``` 第 6 步:設定 Bright Data 的抓取瀏覽器 --------------------------- 在設定 Bright 資料抓取瀏覽器之前,我們先來談談什麼是抓取瀏覽器。 ### Bright Data 的抓取瀏覽器是什麼? Bright Data 的抓取瀏覽器是一款用於自動網頁抓取的尖端工具,旨在與 Puppeteer、Playwright 和 Selenium 無縫整合。它提供了一套網站解鎖功能,包括代理輪換、驗證碼解決等,以提高抓取效率。它非常適合需要互動的複雜網頁抓取,透過在 Bright Data 基礎架構上託管無限的瀏覽器會話來實現可擴展性。如欲了解更多詳情,請造訪[光明資料](https://brdta.com/kishansheth21)。 ![明亮的資料抓取瀏覽器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c70ochb1lgdrusgicwz4.png) <a id="steps-to-set-up-bright-datas-scraping-browser"></a> #### 第 1 步:導覽至 Bright Data 網站 首先造訪[Brightdata.com](https://brdta.com/kishansheth21) 。這是您存取 Bright Data 提供的豐富網頁抓取資源和工具的入口。 ![光明資料首頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afgkpgytcytoqfuq0h8w.png) #### 第 2 步:建立帳戶 造訪 Bright Data 網站後,註冊並建立一個新帳戶。系統將提示您輸入基本資訊以啟動並執行您的帳戶。 ![登入/註冊 Bright Data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffha75szs9tubh8kra9j.png) #### 第 3 步:選擇您的產品 在產品選擇頁面上,尋找代理商和抓取基礎設施產品。本產品專為滿足您的網路抓取需求而設計,提供強大的資料擷取工具和功能。 ![光明資料產品](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b058azlmjv30po6289fh.png) #### 第 4 步:新增代理 在「代理程式和抓取基礎設施」頁面中,您會找到一個「新增按鈕」。點擊此按鈕開始將新的抓取瀏覽器新增到您的工具包的過程。 ![新代理](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jscxsyt9yess1nvzi4z.png) #### 第五步:選擇抓取瀏覽器 將出現一個下拉列表,您應該從中選擇抓取瀏覽器選項。這告訴 Bright Data 您打算設定一個新的抓取瀏覽器環境。 ![選擇抓取瀏覽器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i483ipx7spwne65c6tep.png) #### 第 6 步:為您的抓取瀏覽器命名 為您的新抓取瀏覽器指定一個唯一的名稱。這有助於稍後辨識和管理它,特別是如果您計劃對不同的抓取專案使用多個瀏覽器。 ![抓取瀏覽器名稱](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0qnitec7s7gki7t3826.png) #### 步驟7:新增瀏覽器 命名您的瀏覽器後,按一下「新增」按鈕。此操作完成了新的抓取瀏覽器的建立。 ![新增抓取瀏覽器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao6n1edyyx2no621nh01.png) #### 第 8 步:查看您的抓取瀏覽器詳細訊息 新增抓取瀏覽器後,您將被導向到一個頁面,您可以在其中查看新建立的抓取瀏覽器的所有詳細資訊。這些資訊對於整合和使用至關重要。 ![抓取瀏覽器詳細訊息](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ev33mbgznevn6h9p60g6.png) #### 第 9 步:存取程式碼和整合範例 尋找“查看程式碼和整合範例”按鈕。點擊此按鈕將為您提供如何跨多種程式語言和程式庫整合和使用抓取瀏覽器的全面視圖。對於希望自訂抓取設定的開發人員來說,此資源非常寶貴。 ![程式碼和整合範例按鈕](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30araqzko585yzmhrumd.png) #### 第 10 步:整合您的抓取瀏覽器 最後,複製 SRS\_WS\_ENDPOINT 變數。這是一條關鍵訊息,您需要將其整合到原始程式碼中,以便您的應用程式能夠與您剛剛設定的抓取瀏覽器進行通訊。 ![抓取瀏覽器端點](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqhpglz1lngobguk2nu4.png) 透過遵循這些詳細步驟,您已在 Bright Data 平台中成功建立了一個抓取瀏覽器,準備好處理您的網頁抓取任務。請記住,Bright Data 提供廣泛的文件和支持,幫助您最大限度地提高抓取專案的效率和效果。無論您是在收集市場情報、進行研究還是監控競爭格局,新設定的抓取瀏覽器都是資料收集庫中的強大工具。 ### 第 7 步:使用 Puppeteer 實作抓取邏輯 從我們上次設定用於抓取航班資料的 Next.js 應用程式的地方開始,下一個關鍵步驟是實現實際的抓取邏輯。此過程涉及利用 Puppeteer 連接到瀏覽器實例、導航到目標 URL(在我們的範例中為 Kayak)並抓取必要的飛行資料。提供的程式碼片段概述了實現此目標的複雜方法,與我們先前建立的 BullMQ 工作設定無縫整合。讓我們分解這個抓取邏輯的元件,並了解它如何適合我們的應用程式。 #### 建立與瀏覽器的連接 我們抓取過程的第一步是透過 Puppeteer 建立與瀏覽器的連線。這是透過利用`puppeteer.connect`方法來完成的,該方法使用 WebSocket 端點 ( `SBR_WS_ENDPOINT` ) 連接到現有的瀏覽器實例。此環境變數應設定為您正在使用的抓取瀏覽器服務的 WebSocket URL,例如 Bright Data: ``` const browser = await puppeteer.connect({ browserWSEndpoint: SBR_WS_ENDPOINT, }); ``` #### 開啟新頁面並導航到目標 URL 連線後,我們在瀏覽器中建立一個新頁面並導航到作業資料中指定的目標 URL。此 URL 是我們打算從中抓取航班資料的特定 Kayak 搜尋結果頁面: ``` const page = await browser.newPage(); await page.goto(job.data.url); ``` #### 抓取航班資料 我們邏輯的核心在於從頁面中抓取航班資料。我們透過使用`page.evaluate`來實現這一點,這是一種 Puppeteer 方法,允許我們在瀏覽器上下文中執行腳本。在此腳本中,我們等待必要的元素加載,然後繼續收集航班資訊: - **Flight Selector** :我們以`.nrc6-wrapper`類別為目標元素,其中包含航班詳細資訊。 - **資料擷取**:對於每個航班元素,我們提取詳細訊息,例如航空公司徽標、出發和到達時間、航班持續時間、航空公司名稱和價格。出發和到達時間經過清理,以刪除最後不必要的數值,確保我們準確地捕捉時間。 - **價格處理**:價格在刪除所有非數字字元後提取為整數,確保其可用於數值運算或比較。 擷取的資料被建構成飛行物件陣列,每個物件都包含上述詳細資訊: ``` const scrappedFlights = await page.evaluate(async () => { // Data extraction logic const flights = []; // Process each flight element // ... return flights; }); ``` #### 錯誤處理和清理 我們的抓取邏輯被包裝在一個 try-catch 區塊中,以在抓取過程中優雅地處理任何潛在的錯誤。無論結果如何,我們都會確保瀏覽器在finally區塊中正確關閉,從而保持資源效率並防止潛在的記憶體洩漏: ``` try { // Scraping logic } catch (error) { console.log({ error }); } finally { await browser.close(); console.log("Browser closed successfully."); } ``` #### 整個程式碼 ``` const SBR_WS_ENDPOINT = process.env.SBR_WS_ENDPOINT; export const register = async () => { if (process.env.NEXT_RUNTIME === "nodejs") { const { Worker } = await import("bullmq"); const puppeteer = await import("puppeteer"); const { connection } = await import("./lib/redis"); const { importQueue } = await import("./lib/queue"); new Worker( "importQueue", async (job) => { const browser = await puppeteer.connect({ browserWSEndpoint: SBR_WS_ENDPOINT, }); try { const page = await browser.newPage(); console.log("in flight scraping"); console.log("Connected! Navigating to " + job.data.url); await page.goto(job.data.url); console.log("Navigated! Scraping page content..."); const scrappedFlights = await page.evaluate(async () => { await new Promise((resolve) => setTimeout(resolve, 5000)); const flights = []; const flightSelectors = document.querySelectorAll(".nrc6-wrapper"); flightSelectors.forEach((flightElement) => { const airlineLogo = flightElement.querySelector("img")?.src || ""; const [rawDepartureTime, rawArrivalTime] = ( flightElement.querySelector(".vmXl")?.innerText || "" ).split(" – "); // Function to extract time and remove numeric values at the end const extractTime = (rawTime: string): string => { const timeWithoutNumbers = rawTime .replace(/[0-9+\s]+$/, "") .trim(); return timeWithoutNumbers; }; const departureTime = extractTime(rawDepartureTime); const arrivalTime = extractTime(rawArrivalTime); const flightDuration = ( flightElement.querySelector(".xdW8")?.children[0]?.innerText || "" ).trim(); const airlineName = ( flightElement.querySelector(".VY2U")?.children[1]?.innerText || "" ).trim(); // Extract price const price = parseInt( ( flightElement.querySelector(".f8F1-price-text")?.innerText || "" ) .replace(/[^\d]/g, "") .trim(), 10 ); flights.push({ airlineLogo, departureTime, arrivalTime, flightDuration, airlineName, price, }); }); return flights; }); } catch (error) { console.log({ error }); } finally { await browser.close(); console.log("Browser closed successfully."); } }, { connection, concurrency: 10, removeOnComplete: { count: 1000 }, removeOnFail: { count: 5000 }, } ); } }; ``` ### 步驟8:航班搜尋功能 基於我們的航班資料抓取功能,讓我們將全面的航班搜尋功能整合到我們的 Next.js 應用程式中。此功能將為使用者提供一個動態介面,透過指定出發地、目的地和日期來搜尋航班。利用強大的 Next.js 框架以及現代 UI 庫和狀態管理,我們建立了引人入勝且響應迅速的航班搜尋體驗。 #### 航班搜尋功能的關鍵組成部分 1. **動態城市選擇**:此功能包括來源和目的地輸入的自動完成功能,由預先定義的城市機場程式碼清單提供支援。當使用者輸入時,應用程式會過濾並顯示匹配的城市,透過更輕鬆地尋找和選擇機場來增強用戶體驗。 2. **日期選擇**:使用者可以透過日期輸入選擇預期的航班日期,為規劃旅行提供彈性。 3. **抓取狀態監控**:啟動抓取作業後,應用程式透過定期 API 呼叫來監控作業的狀態。這種非同步檢查允許應用程式使用抓取過程的狀態更新 UI,確保使用者了解進度和結果。 #### 航班搜尋元件的完整程式碼 ``` "use client"; import { useAppStore } from "@/store"; import { USER_API_ROUTES } from "@/utils/api-routes"; import { cityAirportCode } from "@/utils/city-airport-codes"; import { Button, Input, Listbox, ListboxItem } from "@nextui-org/react"; import axios from "axios"; import Image from "next/image"; import { useRouter } from "next/navigation"; import React, { useEffect, useRef, useState } from "react"; import { FaCalendarAlt, FaSearch } from "react-icons/fa"; const SearchFlights = () => { const router = useRouter(); const { setScraping, setScrapingType, setScrappedFlights } = useAppStore(); const [loadingJobId, setLoadingJobId] = useState<number | undefined>(undefined); const [source, setSource] = useState(""); const [sourceOptions, setSourceOptions] = useState< { city: string; code: string; }[] >([]); const [destination, setDestination] = useState(""); const [destinationOptions, setDestinationOptions] = useState< { city: string; code: string; }[] >([]); const [flightDate, setFlightDate] = useState(() => { const today = new Date(); return today.toISOString().split("T")[0]; }); const handleSourceChange = (query: string) => { const matchingCities = Object.entries(cityAirportCode) .filter(([, city]) => city.toLowerCase().includes(query.toLowerCase())) .map(([code, city]) => ({ code, city })) .splice(0, 5); setSourceOptions(matchingCities); }; const destinationChange = (query: string) => { const matchingCities = Object.entries(cityAirportCode) .filter(([, city]) => city.toLowerCase().includes(query.toLowerCase())) .map(([code, city]) => ({ code, city })) .splice(0, 5); setDestinationOptions(matchingCities); }; const startScraping = async () => { if (source && destination && flightDate) { const data = await axios.get(`${USER_API_ROUTES.FLIGHT_SCRAPE}?source=${source}&destination=${destination}&date=${flightDate}`); if (data.data.id) { setLoadingJobId(data.data.id); setScraping(true); setScrapingType("flight"); } } }; useEffect(() => { if (loadingJobId) { const checkIfJobCompleted = async () => { try { const response = await axios.get(`${USER_API_ROUTES.FLIGHT_SCRAPE_STATUS}?jobId=${loadingJobId}`); if (response.data.status) { set ScrappedFlights(response.data.flights); clearInterval(jobIntervalRef.current); setScraping(false); setScrapingType(undefined); router.push(`/flights?data=${flightDate}`); } } catch (error) { console.log(error); } }; jobIntervalRef.current = setInterval(checkIfJobCompleted, 3000); } return () => clearInterval(jobIntervalRef.current); }, [loadingJobId]); return ( <div className="h-[90vh] flex items-center justify-center"> <div className="absolute left-0 top-0 h-[100vh] w-[100vw] max-w-[100vw] overflow-hidden overflow-x-hidden"> <Image src="/flight-search.png" fill alt="Search" /> </div> <div className="absolute h-[50vh] w-[60vw] flex flex-col gap-5"> {/* UI and functionality for flight search */} </div> </div> ); }; export default SearchFlights; ``` ### 步驟9:航班搜尋頁面UI ![航班搜尋頁面](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54bkhpea27fkzg4vlur1.png) ### 顯示航班搜尋結果 成功抓取飛行資料後,下一個關鍵步驟是以使用者友善的方式將這些結果呈現給使用者。 Next.js 應用程式中的 Flights 元件就是為此目的而設計的。 ``` "use client"; import { useAppStore } from "@/store"; import { USER_API_ROUTES } from "@/utils/api-routes"; import { Button } from "@nextui-org/react"; import axios from "axios"; import Image from "next/image"; import { useRouter, useSearchParams } from "next/navigation"; import React from "react"; import { FaChevronLeft } from "react-icons/fa"; import { MdOutlineFlight } from "react-icons/md"; const Flights = () => { const router = useRouter(); const searchParams = useSearchParams(); const date = searchParams.get("date"); const { scrappedFlights, userInfo } = useAppStore(); const getRandomNumber = () => Math.floor(Math.random() * 41); const bookFLight = async (flightId: number) => {}; return ( <div className="m-10 px-[20vw] min-h-[80vh]"> <Button className="my-5" variant="shadow" color="primary" size="lg" onClick={() => router.push("/search-flights")} > <FaChevronLeft /> Go Back </Button> <div className="flex-col flex gap-5"> {scrappedFlights.length === 0 && ( <div className="flex items-center justify-center py-5 px-10 mt-10 rounded-lg text-red-500 bg-red-100 font-medium"> No Flights found. </div> )} {scrappedFlights.map((flight: any) => { const seatsLeft = getRandomNumber(); return ( <div key={flight.id} className="grid grid-cols-12 border bg-gray-200 rounded-xl font-medium drop-shadow-md" > <div className="col-span-9 bg-white rounded-l-xl p-10 flex flex-col gap-5"> <div className="grid grid-cols-4 gap-4"> <div className="flex flex-col gap-3 font-medium"> <div> <div className="relative w-20 h-16"> <Image src={flight.logo} alt="airline name" fill /> </div> </div> <div>{flight.name}</div> </div> <div className="col-span-3 flex justify-between"> <div className="flex flex-col gap-2"> <div className="text-blue-600">From</div> <div> <span className="text-3xl"> <strong>{flight.departureTime}</strong> </span> </div> <div>{flight.from}</div> </div> <div className="flex flex-col items-center justify-center gap-2"> <div className="bg-violet-100 w-max p-3 text-4xl text-blue-600 rounded-full"> <MdOutlineFlight /> </div> <div> <span className="text-lg"> <strong>Non-stop</strong> </span> </div> <div>{flight.duration}</div> </div> <div className="flex flex-col gap-2"> <div className="text-blue-600">To</div> <div> <span className="text-3xl"> <strong>{flight.arrivalTime}</strong> </span> </div> <div>{flight.to}</div> </div> </div> </div> <div className="flex justify-center gap-10 bg-violet-100 p-3 rounded-lg"> <div className="flex"> <span>Airplane  </span> <span className="text-blue-600 font-semibold"> Boeing 787 </span> </div> <div className="flex"> <span>Travel Class:  </span> <span className="text-blue-600 font-semibold">Economy</span> </div> </div> <div className="flex justify-between font-medium"> <div> Refundable <span className="text-blue-600"> $5 ecash</span> </div> <div className={`${ seatsLeft > 20 ? "text-green-500" : "text-red-500" }`} > Only {seatsLeft} Seats Left </div> <div className="cursor-pointer">Flight Details</div> </div> </div> <div className="col-span-3 bg-violet-100 rounded-r-xl h-full flex flex-col items-center justify-center gap-5"> <div> <div> <span className="line-through font-light"> ${flight.price + 140} </span> </div> <div className="flex items-center gap-2"> <span className="text-5xl font-bold">${flight.price}</span> <span className="text-blue-600">20% OFF</span> </div> </div> <Button variant="ghost" radius="full" size="lg" color="primary" onClick={() => { if (userInfo) bookFLight(flight.id); }} > {userInfo ? "Book Now" : "Login to Book"} </Button> </div> </div> ); })} </div> </div> ); }; export default Flights; ``` #### 航班搜尋結果 ![航班搜尋結果](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxufusoyrf6hgtrrcmsw.png) ### 探索完整的指南和程式碼庫 上面共享的部分和程式碼片段僅代表使用 Next.js 建立強大的航班資料抓取和搜尋應用程式所需的完整功能和程式碼的一小部分。為了掌握這個專案的全部內容,包括高級功能、優化和最佳實踐,我邀請您更深入地研究我的線上綜合資源。 #### 在 YouTube 上觀看詳細說明 有關引導您完成此應用程式的開發過程、編碼細微差別和功能的逐步影片指南,請觀看我的 YouTube 影片。本教程旨在讓您更深入地了解這些概念,讓您按照自己的步調進行操作並獲得對 Next.js 應用程式開發的寶貴見解。 https://www.youtube.com/watch?v=ZWVhk0fxHM0 #### 在 GitHub 上探索完整程式碼 如果您渴望探索完整的程式碼,請造訪我的 GitHub 儲存庫。在那裡,您將找到完整的程式碼庫,包括讓該應用程式在您自己的電腦上執行所需的所有元件、實用程式和設定說明。 https://github.com/koolkishan/nextjs-travel-planner ### 結論 使用 Next.js 建立飛行資料抓取和搜尋工具等綜合應用程式展示了現代 Web 開發工具和框架的強大功能和多功能性。無論您是希望提高技能的經驗豐富的開發人員,還是渴望深入 Web 開發的初學者,這些資源都是為您的旅程量身定制的。在 YouTube 上觀看詳細教程,在 GitHub 上探索完整程式碼,並加入對話以增強您的開發專業知識並為充滿活力的開發者社群做出貢獻。 --- 原文出處:https://dev.to/kishansheth/nextjs-14-booking-app-with-live-data-scraping-using-scraping-browser-610

為初學者到專家提供的 101 個 Bash 指令和提示

> **2019 年 9 月 25 日更新:**感謝[ラナ・kuaru](https://twitter.com/rana_kualu)的辛勤工作,本文現已提供日文版。請點擊下面的連結查看他們的工作。如果您知道本文被翻譯成其他語言,請告訴我,我會將其發佈在這裡。 [🇯🇵 閱讀日語](https://qiita.com/rana_kualu/items/7b62898d373901466f5c) > **2019 年 7 月 8 日更新:**我最近發現大約兩年前發佈在法語留言板上的[這篇非常相似的文章](https://bookmarks.ecyseo.net/?EAWvDw)。如果您有興趣學習一些 shell 命令——並且您*會說 français* ,那麼它是對我下面的文章的一個很好的補充。 直到大約一年前,我幾乎只在 macOS 和 Ubuntu 作業系統中工作。在這兩個作業系統上, `bash`是我的預設 shell。在過去的六、七年裡,我對`bash`工作原理有了大致的了解,並想為那些剛入門的人概述一些更常見/有用的命令。如果您認為您了解有關`bash`所有訊息,請無論如何看看下面的內容 - 我已經提供了一些提示和您可能忘記的標誌的提醒,這可以讓您的工作更輕鬆一些。 下面的命令或多或少以敘述風格排列,因此如果您剛開始使用`bash` ,您可以從頭到尾完成操作。事情到最後通常會變得不那麼常見並且變得更加困難。 <a name="toc"></a> 目錄 -- - [基礎](#the-basics) ``` - [First Commands, Navigating the Filesystem](#first-commands) ``` ``` - [`pwd / ls / cd`](#pwd-ls-cd) ``` ``` - [`; / && / &`](#semicolon-andand-and) ``` ``` - [Getting Help](#getting-help) ``` ``` - [`-h`](#minus-h) ``` ``` - [`man`](#man) ``` ``` - [Viewing and Editing Files](#viewing-and-editing-files) ``` ``` - [`head / tail / cat / less`](#head-tail-cat-less) ``` ``` - [`nano / nedit`](#nano-nedit) ``` ``` - [Creating and Deleting Files and Directories](#creating-and-deleting-files) ``` ``` - [`touch`](#touch) ``` ``` - [`mkdir / rm / rmdir`](#mkdir-rm-rmdir) ``` ``` - [Moving and Copying Files, Making Links, Command History](#moving-and-copying-files) ``` ``` - [`mv / cp / ln`](#mv-cp-ln) ``` ``` - [Command History](#command-history) ``` ``` - [Directory Trees, Disk Usage, and Processes](#directory-trees-disk-usage-processes) ``` ``` - [`mkdir –p / tree`](#mkdir--p-tree) ``` ``` - [`df / du / ps`](#df-du-ps) ``` ``` - [Miscellaneous](#basic-misc) ``` ``` - [`passwd / logout / exit`](#passwd-logout-exit) ``` ``` - [`clear / *`](#clear-glob) ``` - [中間的](#intermediate) ``` - [Disk, Memory, and Processor Usage](#disk-memory-processor) ``` ``` - [`ncdu`](#ncdu) ``` ``` - [`top / htop`](#top-htop) ``` ``` - [REPLs and Software Versions](#REPLs-software-versions) ``` ``` - [REPLs](#REPLs) ``` ``` - [`-version / --version / -v`](#version) ``` ``` - [Environment Variables and Aliases](#env-vars-aliases) ``` ``` - [Environment Variables](#env-vars) ``` ``` - [Aliases](#aliases) ``` ``` - [Basic `bash` Scripting](#basic-bash-scripting) ``` ``` - [`bash` Scripts](#bash-scripts) ``` ``` - [Custom Prompt and `ls`](#custom-prompt-ls) ``` ``` - [Config Files](#config-files) ``` ``` - [Config Files / `.bashrc`](#config-bashrc) ``` ``` - [Types of Shells](#types-of-shells) ``` ``` - [Finding Things](#finding-things) ``` ``` - [`whereis / which / whatis`](#whereis-which-whatis) ``` ``` - [`locate / find`](#locate-find) ``` ``` - [Downloading Things](#downloading-things) ``` ``` - [`ping / wget / curl`](#ping-wget-curl) ``` ``` - [`apt / gunzip / tar / gzip`](#apt-gunzip-tar-gzip) ``` ``` - [Redirecting Input and Output](#redirecting-io) ``` ``` - [`| / > / < / echo / printf`](#pipe-gt-lt-echo-printf) ``` ``` - [`0 / 1 / 2 / tee`](#std-tee) ``` - [先進的](#advanced) ``` - [Superuser](#superuser) ``` ``` - [`sudo / su`](#sudo-su) ``` ``` - [`!!`](#click-click) ``` ``` - [File Permissions](#file-permissions) ``` ``` - [File Permissions](#file-permissions-sub) ``` ``` - [`chmod / chown`](#chmod-chown) ``` ``` - [User and Group Management](#users-groups) ``` ``` - [Users](#users) ``` ``` - [Groups](#groups) ``` ``` - [Text Processing](#text-processing) ``` ``` - [`uniq / sort / diff / cmp`](#uniq-sort-diff-cmp) ``` ``` - [`cut / sed`](#cut-sed) ``` ``` - [Pattern Matching](#pattern-matching) ``` ``` - [`grep`](#grep) ``` ``` - [`awk`](#awk) ``` ``` - [Copying Files Over `ssh`](#ssh) ``` ``` - [`ssh / scp`](#ssh-scp) ``` ``` - [`rsync`](#rsync) ``` ``` - [Long-Running Processes](#long-running-processes) ``` ``` - [`yes / nohup / ps / kill`](#yes-nohup-ps-kill) ``` ``` - [`cron / crontab / >>`](#cron) ``` ``` - [Miscellaneous](#advanced-misc) ``` ``` - [`pushd / popd`](#pushd-popd) ``` ``` - [`xdg-open`](#xdg-open) ``` ``` - [`xargs`](#xargs) ``` - [獎勵:有趣但大多無用的東西](#bonus) ``` - [`w / write / wall / lynx`](#w-write-wall-lynx) ``` ``` - [`nautilus / date / cal / bc`](#nautilus-date-cal-bc) ``` --- <a name="the-basics"></a> 基礎 == <a name="first-commands"></a> 第一個指令,瀏覽檔案系統 ------------ 現代檔案系統具有目錄(資料夾)樹,其中目錄要么是*根目錄*(沒有父目錄),要么是*子目錄*(包含在單一其他目錄中,我們稱之為“父目錄”)。向後遍歷檔案樹(從子目錄到父目錄)將始終到達根目錄。有些檔案系統有多個根目錄(如 Windows 的磁碟機: `C:\` 、 `A:\`等),但 Unix 和類別 Unix 系統只有一個名為`\`的根目錄。 <a name="pwd-ls-cd"></a> ### `pwd / ls / cd` [\[ 返回目錄 \]](#toc) 在檔案系統中工作時,使用者始終*在*某個目錄中工作,我們稱之為當前目錄或*工作目錄*。使用`pwd`列印使用者的工作目錄: ``` [ andrew@pc01 ~ ]$ pwd /home/andrew ``` 使用`ls`列出該目錄的內容(檔案和/或子目錄等): ``` [ andrew@pc01 ~ ]$ ls Git TEST jdoc test test.file ``` > **獎金:** > > 使用`ls -a`顯示隱藏(“點”)文件 > > 使用`ls -l`顯示文件詳細訊息 > > 組合多個標誌,如`ls -l -a` > > 有時您可以連結諸如`ls -la`之類的標誌,而不是`ls -l -a` 使用`cd`更改到不同的目錄(更改目錄): ``` [ andrew@pc01 ~ ]$ cd TEST/ [ andrew@pc01 TEST ]$ pwd /home/andrew/TEST [ andrew@pc01 TEST ]$ cd A [ andrew@pc01 A ]$ pwd /home/andrew/TEST/A ``` `cd ..`是「 `cd`到父目錄」的簡寫: ``` [ andrew@pc01 A ]$ cd .. [ andrew@pc01 TEST ]$ pwd /home/andrew/TEST ``` `cd ~`或只是`cd`是「 `cd`到我的主目錄」的簡寫(通常`/home/username`或類似的東西): ``` [ andrew@pc01 TEST ]$ cd [ andrew@pc01 ~ ]$ pwd /home/andrew ``` > **獎金:** > > `cd ~user`表示「 `cd`到`user`的主目錄 > > 您可以使用`cd ../..`等跳轉多個目錄等級。 > > 使用`cd -`返回到最近的目錄 > > `.`是「此目錄」的簡寫,因此`cd .`不會做太多事情 <a name="semicolon-andand-and"></a> ### `; / && / &` [\[ 返回目錄 \]](#toc) 我們在命令列中輸入的內容稱為*命令*,它們總是執行儲存在電腦上某處的一些機器碼。有時這個機器碼是一個內建的Linux命令,有時它是一個應用程式,有時它是你自己寫的一些程式碼。有時,我們會想依序執行一個指令。為此,我們可以使用`;` (分號): ``` [ andrew@pc01 ~ ]$ ls; pwd Git TEST jdoc test test.file /home/andrew ``` 上面的分號表示我首先 ( `ls` ) 列出工作目錄的內容,然後 ( `pwd` ) 列印其位置。連結命令的另一個有用工具是`&&` 。使用`&&`時,如果左側命令失敗,則右側命令將不會執行。 `;`和`&&`都可以在同一行中多次使用: ``` # whoops! I made a typo here! [ andrew@pc01 ~ ]$ cd /Giit/Parser && pwd && ls && cd -bash: cd: /Giit/Parser: No such file or directory # the first command passes now, so the following commands are run [ andrew@pc01 ~ ]$ cd Git/Parser/ && pwd && ls && cd /home/andrew/Git/Parser README.md doc.sh pom.xml resource run.sh shell.sh source src target ``` ....但是與`;` ,即使第一個命令失敗,第二個命令也會執行: ``` # pwd and ls still run, even though the cd command failed [ andrew@pc01 ~ ]$ cd /Giit/Parser ; pwd ; ls -bash: cd: /Giit/Parser: No such file or directory /home/andrew Git TEST jdoc test test.file ``` `&`看起來與`&&`類似,但實際上實現了完全不同的功能。通常,當您執行長時間執行的命令時,命令列將等待該命令完成,然後才允許您輸入另一個命令。在命令後面加上`&`可以防止這種情況發生,並允許您在舊命令仍在執行時執行新命令: ``` [ andrew@pc01 ~ ]$ cd Git/Parser && mvn package & cd [1] 9263 ``` > **額外的好處:**當我們在命令後使用`&`來「隱藏」它時,我們說該作業(或「進程」;這些術語或多或少可以互換)是「後台的」。若要查看目前正在執行的背景作業,請使用`jobs`指令: > ````bash \[ andrew@pc01 ~ \]$ 職位 \[1\]+ 執行 cd Git/Parser/ && mvn package & ``` <a name="getting-help"></a> ## Getting Help <a name="minus-h"></a> ### `-h` [[ Back to Table of Contents ]](#toc) Type `-h` or `--help` after almost any command to bring up a help menu for that command: ``` \[ andrew@pc01 ~ \]$ du --help 用法:你\[選項\]...\[檔案\]... 或: du \[選項\]... --files0-from=F 對目錄遞歸地總結文件集的磁碟使用情況。 長期權的強制性參數對於短期權也是強制性的。 -0, --null 以 NUL 結束每個輸出行,而不是換行符 -a, --all 計算所有檔案的寫入計數,而不僅僅是目錄 ``` --apparent-size print apparent sizes, rather than disk usage; although ``` ``` the apparent size is usually smaller, it may be ``` ``` larger due to holes in ('sparse') files, internal ``` ``` fragmentation, indirect blocks, and the like ``` -B, --block-size=SIZE 在列印前按 SIZE 縮放大小;例如, ``` '-BM' prints sizes in units of 1,048,576 bytes; ``` ``` see SIZE format below ``` … ``` <a name="man"></a> ### `man` [[ Back to Table of Contents ]](#toc) Type `man` before almost any command to bring up a manual for that command (quit `man` with `q`): ``` LS(1) 使用者指令 LS(1) 姓名 ``` ls - list directory contents ``` 概要 ``` ls [OPTION]... [FILE]... ``` 描述 ``` List information about the FILEs (the current directory by default). ``` ``` Sort entries alphabetically if none of -cftuvSUX nor --sort is speci- ``` ``` fied. ``` ``` Mandatory arguments to long options are mandatory for short options ``` ``` too. ``` … ``` <a name="viewing-and-editing-files"></a> ## Viewing and Editing Files <a name="head-tail-cat-less"></a> ### `head / tail / cat / less` [[ Back to Table of Contents ]](#toc) `head` outputs the first few lines of a file. The `-n` flag specifies the number of lines to show (the default is 10): ``` 列印前三行 ===== \[ andrew@pc01 ~ \]$ 頭 -n 3 c 這 文件 有 ``` `tail` outputs the last few lines of a file. You can get the last `n` lines (like above), or you can get the end of the file beginning from the `N`-th line with `tail -n +N`: ``` 從第 4 行開始列印文件末尾 ============== \[ andrew@pc01 ~ \]$ tail -n +4 c 確切地 六 線 ``` `cat` concatenates a list of files and sends them to the standard output stream (usually the terminal). `cat` can be used with just a single file, or multiple files, and is often used to quickly view them. (**Be warned**: if you use `cat` in this way, you may be accused of a [_Useless Use of Cat_ (UUOC)](http://bit.ly/2SPHE4V), but it's not that big of a deal, so don't worry too much about it.) ``` \[ andrew@pc01 ~ \]$ 貓 a 歸檔一個 \[ andrew@pc01 ~ \]$ 貓 ab 歸檔一個 文件b ``` `less` is another tool for quickly viewing a file -- it opens up a `vim`-like read-only window. (Yes, there is a command called `more`, but `less` -- unintuitively -- offers a superset of the functionality of `more` and is recommended over it.) Learn more (or less?) about [less](http://man7.org/linux/man-pages/man1/less.1.html) and [more](http://man7.org/linux/man-pages/man1/more.1.html) at their `man` pages. <a name="nano-nedit"></a> ### `nano / nedit` [[ Back to Table of Contents ]](#toc) `nano` is a minimalistic command-line text editor. It's a great editor for beginners or people who don't want to learn a million shortcuts. It was more than sufficient for me for the first few years of my coding career (I'm only now starting to look into more powerful editors, mainly because defining your own syntax highlighting in `nano` can be a bit of a pain.) `nedit` is a small graphical editor, it opens up an X Window and allows point-and-click editing, drag-and-drop, syntax highlighting and more. I use `nedit` sometimes when I want to make small changes to a script and re-run it over and over. Other common CLI (command-line interface) / GUI (graphical user interface) editors include `emacs`, `vi`, `vim`, `gedit`, Notepad++, Atom, and lots more. Some cool ones that I've played around with (and can endorse) include Micro, Light Table, and VS Code. All modern editors offer basic conveniences like search and replace, syntax highlighting, and so on. `vi(m)` and `emacs` have more features than `nano` and `nedit`, but they have a much steeper learning curve. Try a few different editors out and find one that works for you! <a name="creating-and-deleting-files"></a> ## Creating and Deleting Files and Directories <a name="touch"></a> ### `touch` [[ Back to Table of Contents ]](#toc) `touch` was created to modify file timestamps, but it can also be used to quickly create an empty file. You can create a new file by opening it with a text editor, like `nano`: ``` \[ andrew@pc01 前 \]$ ls \[ andrew@pc01 ex \]$ 奈米 a ``` _...editing file..._ ``` \[ andrew@pc01 前 \]$ ls A ``` ...or by simply using `touch`: ``` \[ andrew@pc01 ex \]$ touch b && ls ab ``` > **Bonus**: > > Background a process with \^z (Ctrl+z) > > ```bash > [ andrew@pc01 ex ]$ nano a > ``` > > _...editing file, then hit \^z..._ > > ```bash > Use fg to return to nano > > [1]+ Stopped nano a > [ andrew@pc01 ex ]$ fg > ``` > > _...editing file again..._ --- > **Double Bonus:** > > Kill the current (foreground) process by pressing \^c (Ctrl+c) while it’s running > > Kill a background process with `kill %N` where `N` is the job index shown by the `jobs` command <a name="mkdir-rm-rmdir"></a> ### `mkdir / rm / rmdir` [[ Back to Table of Contents ]](#toc) `mkdir` is used to create new, empty directories: ``` \[ andrew@pc01 ex \]$ ls && mkdir c && ls ab ABC ``` You can remove any file with `rm` -- but be careful, this is non-recoverable! ``` \[ andrew@pc01 ex \]$ rm a && ls 西元前 ``` You can add an _"are you sure?"_ prompt with the `-i` flag: ``` \[ andrew@pc01 前 \]$ rm -ib rm:刪除常規空文件“b”? y ``` Remove an empty directory with `rmdir`. If you `ls -a` in an empty directory, you should only see a reference to the directory itself (`.`) and a reference to its parent directory (`..`): ``` \[ andrew@pc01 ex \]$ rmdir c && ls -a 。 .. ``` `rmdir` removes empty directories only: ``` \[ andrew@pc01 ex \]$ cd .. && ls 測試/ \*.txt 0.txt 1.txt a a.txt bc \[ andrew@pc01 ~ \]$ rmdir 測試/ rmdir:無法刪除“test/”:目錄不為空 ``` ...but you can remove a directory -- and all of its contents -- with `rm -rf` (`-r` = recursive, `-f` = force): ``` \[ andrew@pc01 ~ \]$ rm –rf 測試 ``` <a name="moving-and-copying-files"></a> ## Moving and Copying Files, Making Links, Command History <a name="mv-cp-ln"></a> ### `mv / cp / ln` [[ Back to Table of Contents ]](#toc) `mv` moves / renames a file. You can `mv` a file to a new directory and keep the same file name or `mv` a file to a "new file" (rename it): ``` \[ andrew@pc01 ex \]$ ls && mv ae && ls A B C D BCDE ``` `cp` copies a file: ``` \[ andrew@pc01 ex \]$ cp e e2 && ls BCDE E2 ``` `ln` creates a hard link to a file: ``` ln 的第一個參數是 TARGET,第二個參數是 NEW LINK ================================= \[ andrew@pc01 ex \]$ ln bf && ls bcde e2 f ``` `ln -s` creates a soft link to a file: ``` \[ andrew@pc01 ex \]$ ln -sbg && ls BCDE E2 FG ``` Hard links reference the same actual bytes in memory which contain a file, while soft links refer to the original file name, which itself points to those bytes. [You can read more about soft vs. hard links here.](http://bit.ly/2D0W8cN) <a name="command-history"></a> ### Command History [[ Back to Table of Contents ]](#toc) `bash` has two big features to help you complete and re-run commands, the first is _tab completion_. Simply type the first part of a command, hit the \<tab\> key, and let the terminal guess what you're trying to do: ``` \[ andrew@pc01 目錄 \]$ ls 另一個長檔名 這是一個長檔名 一個新檔名 \[ andrew@pc01 目錄 \]$ ls t ``` _...hit the TAB key after typing `ls t` and the command is completed..._ ``` \[ andrew@pc01 dir \]$ ls 這是檔名 這是長檔名 ``` You may have to hit \<TAB\> multiple times if there's an ambiguity: ``` \[ andrew@pc01 目錄 \]$ ls a \[ andrew@pc01 目錄 \]$ ls an 一個新檔名另一個長檔名 ``` `bash` keeps a short history of the commands you've typed previously and lets you search through those commands by typing \^r (Ctrl+r): ``` \[ andrew@pc01 目錄 \] ``` _...hit \^r (Ctrl+r) to search the command history..._ ``` (反向搜尋)``: ``` _...type 'anew' and the last command containing this is found..._ ``` (reverse-i-search)`anew': 觸碰新檔名 ``` <a name="directory-trees-disk-usage-processes"></a> ## Directory Trees, Disk Usage, and Processes <a name="mkdir--p-tree"></a> ### `mkdir –p / tree` [[ Back to Table of Contents ]](#toc) `mkdir`, by default, only makes a single directory. This means that if, for instance, directory `d/e` doesn't exist, then `d/e/f` can't be made with `mkdir` by itself: ``` \[ andrew@pc01 ex \]$ ls && mkdir d/e/f ABC mkdir:無法建立目錄「d/e/f」:沒有這樣的檔案或目錄 ``` But if we pass the `-p` flag to `mkdir`, it will make all directories in the path if they don't already exist: ``` \[ andrew@pc01 ex \]$ mkdir -pd/e/f && ls A B C D ``` `tree` can help you better visualise a directory's structure by printing a nicely-formatted directory tree. By default, it prints the entire tree structure (beginning with the specified directory), but you can restrict it to a certain number of levels with the `-L` flag: ``` \[ andrew@pc01 前 \]$ 樹 -L 2 。 |-- 一個 |-- b |-- c `--d ``` `--e ``` 3個目錄,2個文件 ``` You can hide empty directories in `tree`'s output with `--prune`. Note that this also removes "recursively empty" directories, or directories which aren't empty _per se_, but which contain only other empty directories, or other recursively empty directories: ``` \[ andrew@pc01 ex \]$ 樹 --prune 。 |-- 一個 `--b ``` <a name="df-du-ps"></a> ### `df / du / ps` [[ Back to Table of Contents ]](#toc) `df` is used to show how much space is taken up by files for the disks or your system (hard drives, etc.). ``` \[ andrew@pc01 前 \]$ df -h 已使用的檔案系統大小 可用 使用% 安裝於 udev 126G 0 126G 0% /dev tmpfs 26G 2.0G 24G 8% /執行 /dev/mapper/ubuntu--vg-root 1.6T 1.3T 252G 84% / … ``` In the above command, `-h` doesn't mean "help", but "human-readable". Some commands use this convention to display file / disk sizes with `K` for kilobytes, `G` for gigabytes, and so on, instead of writing out a gigantic integer number of bytes. `du` shows file space usage for a particular directory and its subdirectories. If you want to know how much space is free on a given hard drive, use `df`; if you want to know how much space a directory is taking up, use `du`: ``` \[ andrew@pc01 ex \]$ 你 4 ./d/e/f 8./d/e 12 ./天 4./c 20 . ``` `du` takes a `--max-depth=N` flag, which only shows directories `N` levels down (or fewer) from the specified directory: ``` \[ andrew@pc01 ex \]$ du -h --max-深度=1 12K./天 4.0K./c 20K。 ``` `ps` shows all of the user's currently-running processes (aka. jobs): ``` \[ andrew@pc01 前 \]$ ps PID TTY 時間 CMD 16642 分/15 00:00:00 ps 25409 點/15 00:00:00 重擊 ``` <a name="basic-misc"></a> ## Miscellaneous <a name="passwd-logout-exit"></a> ### `passwd / logout / exit` [[ Back to Table of Contents ]](#toc) Change your account password with `passwd`. It will ask for your current password for verification, then ask you to enter the new password twice, so you don't make any typos: ``` \[ andrew@pc01 目錄 \]$ 密碼 更改安德魯的密碼。 (目前)UNIX 密碼: 輸入新的 UNIX 密碼: 重新輸入新的 UNIX 密碼: passwd:密碼更新成功 ``` `logout` exits a shell you’ve logged in to (where you have a user account): ``` \[ andrew@pc01 目錄 \]$ 註銷 ────────────────────────────────────────────────── ── ────────────────────────────── 會話已停止 ``` - Press <return> to exit tab ``` ``` - Press R to restart session ``` ``` - Press S to save terminal output to file ``` ``` `exit` exits any kind of shell: ``` \[ andrew@pc01 ~ \]$ 退出 登出 ────────────────────────────────────────────────── ── ────────────────────────────── 會話已停止 ``` - Press <return> to exit tab ``` ``` - Press R to restart session ``` ``` - Press S to save terminal output to file ``` ``` <a name="clear-glob"></a> ### `clear / *` [[ Back to Table of Contents ]](#toc) Run `clear` to move the current terminal line to the top of the screen. This command just adds blank lines below the current prompt line. It's good for clearing your workspace. Use the glob (`*`, aka. Kleene Star, aka. wildcard) when looking for files. Notice the difference between the following two commands: ``` \[ andrew@pc01 ~ \]$ ls Git/Parser/source/ PArrayUtils.java PFile.java PSQLFile.java PWatchman.java PDateTimeUtils.java PFixedWidthFile.java PStringUtils.java PXSVFile.java PDelimitedFile.java PNode.java PTextFile.java Parser.java \[ andrew@pc01 ~ \]$ ls Git/Parser/source/PD\* Git/Parser/source/PDateTimeUtils.java Git/Parser/source/PDelimitedFile.java ``` The glob can be used multiple times in a command and matches zero or more characers: ``` \[ andrew@pc01 ~ \]$ ls Git/Parser/source/P *D* m\* Git/Parser/source/PDateTimeUtils.java Git/Parser/source/PDelimitedFile.java ``` <a name="intermediate"></a> # Intermediate <a name="disk-memory-processor"></a> ## Disk, Memory, and Processor Usage <a name="ncdu"></a> ### `ncdu` [[ Back to Table of Contents ]](#toc) `ncdu` (NCurses Disk Usage) provides a navigable overview of file space usage, like an improved `du`. It opens a read-only `vim`-like window (press `q` to quit): ``` \[ andrew@pc01 ~ \]$ ncdu ncdu 1.11 ~ 使用箭頭鍵導航,按 ?求助 \--- /home/安德魯 ------------------------------------------- ------------------ 148.2 MiB \[##########\] /.m2 91.5 MiB \[######\] /.sbt 79.8 MiB \[######\] /.cache 64.9 MiB \[####\] /.ivy2 40.6 MiB \[##\] /.sdkman 30.2 MiB \[##\] /.local 27.4 MiB \[#\] /.mozilla 24.4 MiB \[#\] /.nanobackups 10.2 MiB \[ \] .confout3.txt ``` 8.4 MiB [ ] /.config ``` ``` 5.9 MiB [ ] /.nbi ``` ``` 5.8 MiB [ ] /.oh-my-zsh ``` ``` 4.3 MiB [ ] /Git ``` ``` 3.7 MiB [ ] /.myshell ``` ``` 1.7 MiB [ ] /jdoc ``` ``` 1.5 MiB [ ] .confout2.txt ``` ``` 1.5 MiB [ ] /.netbeans ``` ``` 1.1 MiB [ ] /.jenv ``` 564.0 KiB \[ \] /.rstudio-desktop 磁碟使用總量:552.7 MiB 表觀大小:523.6 MiB 專案:14618 ``` <a name="top-htop"></a> ### `top / htop` [[ Back to Table of Contents ]](#toc) `top` displays all currently-running processes and their owners, memory usage, and more. `htop` is an improved, interactive `top`. (Note: you can pass the `-u username` flag to restrict the displayed processes to only those owner by `username`.) ``` \[ andrew@pc01 ~ \]$ htop 1 \[ 0.0%\] 9 \[ 0.0%\] 17 \[ 0.0%\] 25 \[ 0.0%\] 2 \[ 0.0%\] 10 \[ 0.0%\] 18 \[ 0.0%\] 26 \[ 0.0%\] 3 \[ 0.0%\] 11 \[ 0.0%\] 19 \[ 0.0%\] 27 \[ 0.0%\] 4 \[ 0.0%\] 12 \[ 0.0%\] 20 \[ 0.0%\] 28 \[ 0.0%\] 5 \[ 0.0%\] 13 \[ 0.0%\] 21 \[| 1.3%\] 29 \[ 0.0%\] 6 \[ 0.0%\] 14 \[ 0.0%\] 22 \[ 0.0%\] 30 \[| 0.6%\] 7 \[ 0.0%\] 15 \[ 0.0%\] 23 \[ 0.0%\] 31 \[ 0.0%\] 8 \[ 0.0%\] 16 \[ 0.0%\] 24 \[ 0.0%\] 32 \[ 0.0%\] Mem\[|||||||||||||||||||1.42G/252G\] 任務:188、366 個; 1 執行 交換電壓\[| 2.47G/256G\]平均負載:0.00 0.00 0.00 ``` Uptime: 432 days(!), 00:03:55 ``` PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ 指令 9389 安德魯 20 0 23344 3848 2848 R 1.3 0.0 0:00.10 htop 10103 根 20 0 3216M 17896 2444 S 0.7 0.0 5h48:56 /usr/bin/dockerd ``` 1 root 20 0 181M 4604 2972 S 0.0 0.0 15:29.66 /lib/systemd/syst ``` 533 根 20 0 44676 6908 6716 S 0.0 0.0 11:19.77 /lib/systemd/syst 546 根 20 0 244M 0 0 S 0.0 0.0 0:01.39 /sbin/lvmetad -f 1526 根 20 0 329M 2252 1916 S 0.0 0.0 0:00.00 /usr/sbin/ModemMa 1544 根 20 0 329M 2252 1916 S 0.0 0.0 0:00.06 /usr/sbin/ModemMa F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit ``` <a name="REPLs-software-versions"></a> ## REPLs and Software Versions <a name="REPLs"></a> ### REPLs [[ Back to Table of Contents ]](#toc) A **REPL** is a Read-Evaluate-Print Loop, similar to the command line, but usually used for particular programming languages. You can open the Python REPL with the `python` command (and quit with the `quit()` function): ``` \[ andrew@pc01 ~ \]$ python Python 3.5.2(默認,2018 年 11 月 12 日,13:43:14)... > > > 辭職() ``` Open the R REPL with the `R` command (and quit with the `q()` function): ``` \[ andrew@pc01 ~ \]$ R R版3.5.2(2018-12-20)--「蛋殼冰屋」... > q() 儲存工作區影像? \[是/否/c\]: 否 ``` Open the Scala REPL with the `scala` command (and quit with the `:quit` command): ``` \[ andrew@pc01 ~ \]$ scala 歡迎使用 Scala 2.11.12 ... 斯卡拉>:退出 ``` Open the Java REPL with the `jshell` command (and quit with the `/exit` command): ``` \[ andrew@pc01 ~ \]$ jshell |歡迎使用 JShell——版本 11.0.1 ... jshell> /退出 ``` Alternatively, you can exit any of these REPLs with \^d (Ctrl+d). \^d is the EOF (end of file) marker on Unix and signifies the end of input. <a name="version"></a> ### `-version / --version / -v` [[ Back to Table of Contents ]](#toc) Most commands and programs have a `-version` or `--version` flag which gives the software version of that command or program. Most applications make this information easily available: ``` \[ andrew@pc01 ~ \]$ ls --version ls (GNU coreutils) 8.25 ... \[ andrew@pc01 ~ \]$ ncdu -版本 NCDU 1.11 \[ andrew@pc01 ~ \]$ python --version Python 3.5.2 ``` ...but some are less intuitive: ``` \[ andrew@pc01 ~ \]$ sbt scalaVersion … \[資訊\]2.12.4 ``` Note that some programs use `-v` as a version flag, while others use `-v` to mean "verbose", which will run the application while printing lots of diagnostic or debugging information: ``` SCP(1) BSD 通用指令手冊 SCP(1) 姓名 ``` scp -- secure copy (remote file copy program) ``` … -v 詳細模式。導致 scp 和 ssh(1) 列印偵錯訊息 ``` about their progress. This is helpful in debugging connection, ``` ``` authentication, and configuration problems. ``` … ``` <a name="env-vars-aliases"></a> ## Environment Variables and Aliases <a name="env-vars"></a> ### Environment Variables [[ Back to Table of Contents ]](#toc) **Environment variables** (sometimes shortened to "env vars") are persistent variables that can be created and used within your `bash` shell. They are defined with an equals sign (`=`) and used with a dollar sign (`$`). You can see all currently-defined env vars with `printenv`: ``` \[ andrew@pc01 ~ \]$ printenv SPARK\_HOME=/usr/local/spark 術語=xterm … ``` Set a new environment variable with an `=` sign (don't put any spaces before or after the `=`, though!): ``` \[ andrew@pc01 ~ \]$ myvar=你好 ``` Print a specific env var to the terminal with `echo` and a preceding `$` sign: ``` \[ andrew@pc01 ~ \]$ echo $myvar 你好 ``` Environment variables which contain spaces or other whitespace should be surrounded by quotes (`"..."`). Note that reassigning a value to an env var overwrites it without warning: ``` \[ andrew@pc01 ~ \]$ myvar="你好,世界!" && 回顯 $myvar 你好世界! ``` Env vars can also be defined using the `export` command. When defined this way, they will also be available to sub-processes (commands called from this shell): ``` \[ andrew@pc01 ~ \]$ export myvar="另一" && echo $myvar 另一個 ``` You can unset an environment variable by leaving the right-hand side of the `=` blank or by using the `unset` command: ``` \[ andrew@pc01 ~ \]$ 取消設定 mynewvar \[ andrew@pc01 ~ \]$ echo $mynewvar ``` <a name="aliases"></a> ### Aliases [[ Back to Table of Contents ]](#toc) **Aliases** are similar to environment variables but are usually used in a different way -- to replace long commands with shorter ones: ``` \[ andrew@pc01 apidocs \]$ ls -l -a -h -t 總計 220K drwxr-xr-x 5 安德魯 安德魯 4.0K 12 月 21 日 12:37 。 -rw-r--r-- 1 安德魯 安德魯 9.9K 十二月 21 12:37 help-doc.html -rw-r--r-- 1 安德魯 安德魯 4.5K 12 月 21 日 12:37 script.js … \[ andrew@pc01 apidocs \]$ 別名 lc="ls -l -a -h -t" \[ andrew@pc01 apidocs \]$ lc 總計 220K drwxr-xr-x 5 安德魯 安德魯 4.0K 12 月 21 日 12:37 。 -rw-r--r-- 1 安德魯 安德魯 9.9K 十二月 21 12:37 help-doc.html -rw-r--r-- 1 安德魯 安德魯 4.5K 12 月 21 日 12:37 script.js … ``` You can remove an alias with `unalias`: ``` \[ andrew@pc01 apidocs \]$ unalias lc \[ andrew@pc01 apidocs \]$ lc 目前未安裝程式“lc”。 … ``` > **Bonus:** > > [Read about the subtle differences between environment variables and aliases here.](http://bit.ly/2TDG8Tx) > > [Some programs, like **git**, allow you to define aliases specifically for that software.](http://bit.ly/2TG8X1A) <a name="basic-bash-scripting"></a> ## Basic `bash` Scripting <a name="bash-scripts"></a> ### `bash` Scripts [[ Back to Table of Contents ]](#toc) `bash` scripts (usually ending in `.sh`) allow you to automate complicated processes, packaging them into reusable functions. A `bash` script can contain any number of normal shell commands: ``` \[ andrew@pc01 ~ \]$ echo "ls && touch file && ls" > ex.sh ``` A shell script can be executed with the `source` command or the `sh` command: ``` \[ andrew@pc01 ~ \]$ 源 ex.sh 桌面 Git TEST c ex.sh 專案測試 桌面 Git TEST c ex.sh 檔案專案測試 ``` Shell scripts can be made executable with the `chmod` command (more on this later): ``` \[ andrew@pc01 ~ \]$ echo "ls && touch file2 && ls" > ex2.sh \[ andrew@pc01 ~ \]$ chmod +x ex2.sh ``` An executable shell script can be run by preceding it with `./`: ``` \[ andrew@pc01 ~ \]$ ./ex2.sh 桌面 Git TEST c ex.sh ex2.sh 檔案專案測試 桌面 Git TEST c ex.sh ex2.sh 檔案 file2 專案測試 ``` Long lines of code can be split by ending a command with `\`: ``` \[ andrew@pc01 ~ \]$ echo "for i in {1..3}; do echo \\ > \\"歡迎\\$i次\\";完成” > ex3.sh ``` Bash scripts can contain loops, functions, and more! ``` \[ andrew@pc01 ~ \]$ 源 ex3.sh 歡迎1次 歡迎2次 歡迎3次 ``` <a name="custom-prompt-ls"></a> ### Custom Prompt and `ls` [[ Back to Table of Contents ]](#toc) Bash scripting can make your life a whole lot easier and more colourful. [Check out this great bash scripting cheat sheet.](https://devhints.io/bash) `$PS1` (Prompt String 1) is the environment variable that defines your main shell prompt ([learn about the other prompts here](http://bit.ly/2SPgsmT)): ``` \[ andrew@pc01 ~ \]$ printf "%q" $PS1 $'\\n\\\[\\E\[1m\\\]\\\[\\E\[30m\\\]\\A'$'\\\[\\E\[37m\\\]|\\\[\\E\[36m\\\]\\u\\\[\\E\[37m \\\]@\\\[\\E\[34m\\\]\\h'$'\\\[\\E\[32m\\\]\\W\\\[\\E\[37m\\\]|'$'\\\[\\E(B\\E\[m\\\] ' ``` You can change your default prompt with the `export` command: ``` \[ andrew@pc01 ~ \]$ export PS1="\\n此處指令> " 此處指令> echo $PS1 \\n此處指令> ``` ...[you can add colours, too!](http://bit.ly/2TMbEit): ``` 此處指令> export PS1="\\e\[1;31m\\n程式碼: \\e\[39m" (這應該是紅色的,但在 Markdown 中可能不會這樣顯示) =============================== 程式碼:回顯$PS1 \\e\[1;31m\\n程式碼: \\e\[39m ``` You can also change the colours shown by `ls` by editing the `$LS_COLORS` environment variable: ``` (同樣,這些顏色可能不會出現在 Markdown 中) =========================== 程式碼:ls 桌面 Git TEST c ex.sh ex2.sh ex3.sh 檔案 file2 專案測試 程式碼:匯出 LS\_COLORS='di=31:fi=0:ln=96:or=31:mi=31:ex=92' 程式碼:ls 桌面 Git TEST c ex.sh ex2.sh ex3.sh 檔案 file2 專案測試 ``` <a name="config-files"></a> ## Config Files <a name="config-bashrc"></a> ### Config Files / `.bashrc` [[ Back to Table of Contents ]](#toc) If you tried the commands in the last section and logged out and back in, you may have noticed that your changes disappeared. _config_ (configuration) files let you maintain settings for your shell or for a particular program every time you log in (or run that program). The main configuration file for a `bash` shell is the `~/.bashrc` file. Aliases, environment variables, and functions added to `~/.bashrc` will be available every time you log in. Commands in `~/.bashrc` will be run every time you log in. If you edit your `~/.bashrc` file, you can reload it without logging out by using the `source` command: ``` \[ andrew@pc01 ~ \]$ nano ~/.bashrc ``` _...add the line `echo “~/.bashrc loaded!”` to the top of the file_... ``` \[ andrew@pc01 ~ \]$ 源 ~/.bashrc ~/.bashrc 已載入! ``` _...log out and log back in..._ ``` 最後登入:2019 年 1 月 11 日星期五 10:29:07 從 111.11.11.111 ~/.bashrc 已加載! \[ 安德魯@pc01 ~ \] ``` <a name="types-of-shells"></a> ### Types of Shells [[ Back to Table of Contents ]](#toc) _Login_ shells are shells you log in to (where you have a username). _Interactive_ shells are shells which accept commands. Shells can be login and interactive, non-login and non-interactive, or any other combination. In addition to `~/.bashrc`, there are a few other scripts which are `sourced` by the shell automatically when you log in or log out. These are: - `/etc/profile` - `~/.bash_profile` - `~/.bash_login` - `~/.profile` - `~/.bash_logout` - `/etc/bash.bash_logout` Which of these scripts are sourced, and the order in which they're sourced, depend on the type of shell opened. See [the bash man page](https://linux.die.net/man/1/bash) and [these](http://bit.ly/2TGCwA8) Stack Overflow [posts](http://bit.ly/2TFHFsf) for more information. Note that `bash` scripts can `source` other scripts. For instance, in your `~/.bashrc`, you could include the line: ``` 來源~/.bashrc\_addl ``` ...which would also `source` that `.bashrc_addl` script. This file can contain its own aliases, functions, environment variables, and so on. It could, in turn, `source` other scripts, as well. (Be careful to avoid infinite loops of script-sourcing!) It may be helpful to split commands into different shell scripts based on functionality or machine type (Ubuntu vs. Red Hat vs. macOS), for example: - `~/.bash_ubuntu` -- configuration specific to Ubuntu-based machines - `~/.bashrc_styles` -- aesthetic settings, like `PS1` and `LS_COLORS` - `~/.bash_java` -- configuration specific to the Java language I try to keep separate `bash` files for aesthetic configurations and OS- or machine-specific code, and then I have one big `bash` file containing shortcuts, etc. that I use on every machine and every OS. Note that there are also _different shells_. `bash` is just one kind of shell (the "Bourne Again Shell"). Other common ones include `zsh`, `csh`, `fish`, and more. Play around with different shells and find one that's right for you, but be aware that this tutorial contains `bash` shell commands only and not everything listed here (maybe none of it) will be applicable to shells other than `bash`. <a name="finding-things"></a> ## Finding Things <a name="whereis-which-whatis"></a> ### `whereis / which / whatis` [[ Back to Table of Contents ]](#toc) `whereis` searches for "possibly useful" files related to a particular command. It will attempt to return the location of the binary (executable machine code), source (code source files), and `man` page for that command: ``` \[ andrew@pc01 ~ \]$ whereis ls ls: /bin/ls /usr/share/man/man1/ls.1.gz ``` `which` will only return the location of the binary (the command itself): ``` \[ andrew@pc01 ~ \]$ 其中 ls /bin/ls ``` `whatis` prints out the one-line description of a command from its `man` page: ``` \[ andrew@pc01 ~ \]$ 什麼是哪裡是哪個什麼是 whereis (1) - 尋找指令的二進位、原始檔和手冊頁文件 which (1) - 定位指令 Whatis (1) - 顯示一行手冊頁描述 ``` `which` is useful for finding the "original version" of a command which may be hidden by an alias: ``` \[ andrew@pc01 ~ \]$ 別名 ls="ls -l" “original” ls 已被上面定義的別名“隱藏” =========================== \[ andrew@pc01 ~ \]$ ls 總計 36 drwxr-xr-x 2 安德魯 andrew 4096 Jan 9 14:47 桌面 drwxr-xr-x 4 安德魯 安德魯 4096 十二月 6 10:43 Git … 但我們仍然可以使用返回的位置來呼叫「原始」ls ======================= \[ andrew@pc01 ~ \]$ /bin/ls 桌面 Git TEST c ex.sh ex2.sh ex3.sh 檔案 file2 專案測試 ``` <a name="locate-find"></a> ### `locate / find` [[ Back to Table of Contents ]](#toc) `locate` finds a file anywhere on the system by referring to a semi-regularly-updated cached list of files: ``` \[ andrew@pc01 ~ \]$ 找到 README.md /home/andrew/.config/micro/plugins/gotham-colors/README.md /home/andrew/.jenv/README.md /home/andrew/.myshell/README.md … ``` Because it's just searching a list, `locate` is usually faster than the alternative, `find`. `find` iterates through the file system to find the file you're looking for. Because it's actually looking at the files which _currently_ exist on the system, though, it will always return an up-to-date list of files, which is not necessarily true with `locate`. ``` \[ andrew@pc01 ~ \]$ find ~/ -iname "README.md" /home/andrew/.jenv/README.md /home/andrew/.config/micro/plugins/gotham-colors/README.md /home/andrew/.oh-my-zsh/plugins/ant/README.md … ``` `find` was written for the very first version of Unix in 1971, and is therefore much more widely available than `locate`, which was added to GNU in 1994. `find` has many more features than `locate`, and can search by file age, size, ownership, type, timestamp, permissions, depth within the file system; `find` can search using regular expressions, execute commands on files it finds, and more. When you need a fast (but possibly outdated) list of files, or you’re not sure what directory a particular file is in, use `locate`. When you need an accurate file list, maybe based on something other than the files’ names, and you need to do something with those files, use `find`. <a name="downloading-things"></a> ## Downloading Things <a name="ping-wget-curl"></a> ### `ping / wget / curl` [[ Back to Table of Contents ]](#toc) `ping` attempts to open a line of communication with a network host. Mainly, it's used to check whether or not your Internet connection is down: ``` \[ andrew@pc01 ~ \]$ ping google.com PING google.com (74.125.193.100) 56(84) 位元組資料。 使用 32 位元組資料 Ping 74.125.193.100: 來自 74.125.193.100 的回覆:位元組=32 時間<1ms TTL=64 … ``` `wget` is used to easily download a file from the Internet: ``` \[ andrew@pc01 ~ \]$ wget \\ > http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso ``` `curl` can be used just like `wget` (don’t forget the `--output` flag): ``` \[ andrew@pc01 ~ \]$ 捲曲 \\ > http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso \\ > \--輸出ubuntu.iso ``` `curl` and `wget` have their own strengths and weaknesses. `curl` supports many more protocols and is more widely available than `wget`; `curl` can also send data, while `wget` can only receive data. `wget` can download files recursively, while `curl` cannot. In general, I use `wget` when I need to download things from the Internet. I don’t often need to send data using `curl`, but it’s good to be aware of it for the rare occasion that you do. <a name="apt-gunzip-tar-gzip"></a> ### `apt / gunzip / tar / gzip` [[ Back to Table of Contents ]](#toc) Debian-descended Linux distributions have a fantastic package management tool called `apt`. It can be used to install, upgrade, or delete software on your machine. To search `apt` for a particular piece of software, use `apt search`, and install it with `apt install`: ``` \[ andrew@pc01 ~ \]$ apt 搜尋漂白位 ...bleachbit/bionic、bionic 2.0-2 全部 從系統中刪除不需要的文件 您需要“sudo”來安裝軟體 ============== \[ andrew@pc01 ~ \]$ sudo apt installbleachbit ``` Linux software often comes packaged in `.tar.gz` ("tarball") files: ``` \[ andrew@pc01 ~ \]$ wget \\ > https://github.com/atom/atom/releases/download/v1.35.0-beta0/atom-amd64.tar.gz ``` ...these types of files can be unzipped with `gunzip`: ``` \[ andrew@pc01 ~ \]$gunzipatom-amd64.tar.gz && ls 原子 amd64.tar ``` A `.tar.gz` file will be `gunzip`-ped to a `.tar` file, which can be extracted to a directory of files using `tar -xf` (`-x` for "extract", `-f` to specify the file to "untar"): ``` \[ andrew@pc01 ~ \]$ tar -xfatom-amd64.tar && mv \\ 原子-beta-1.35.0-beta0-amd64 原子 && ls 原子atom-amd64.tar ``` To go in the reverse direction, you can create (`-c`) a tar file from a directory and zip it (or unzip it, as appropriate) with `-z`: ``` \[ andrew@pc01 ~ \]$ tar -zcf 壓縮.tar.gz 原子 && ls 原子atom-amd64.tar壓縮.tar.gz ``` `.tar` files can also be zipped with `gzip`: ``` \[ andrew@pc01 ~ \]$ gzipatom-amd64.tar && ls 原子 原子-amd64.tar.gz 壓縮.tar.gz ``` <a name="redirecting-io"></a> ## Redirecting Input and Output <a name="pipe-gt-lt-echo-printf"></a> ### `| / > / < / echo / printf` [[ Back to Table of Contents ]](#toc) By default, shell commands read their input from the standard input stream (aka. stdin or 0) and write to the standard output stream (aka. stdout or 1), unless there’s an error, which is written to the standard error stream (aka. stderr or 2). `echo` writes text to stdout by default, which in most cases will simply print it to the terminal: ``` \[ andrew@pc01 ~ \]$ 回顯“你好” 你好 ``` The pipe operator, `|`, redirects the output of the first command to the input of the second command: ``` 'wc'(字數)傳回檔案中的行數、字數、位元組數 ======================== \[ andrew@pc01 ~ \]$ echo "範例文件" |廁所 ``` 1 2 17 ``` ``` `>` redirects output from stdout to a particular location ``` \[ andrew@pc01 ~ \]$ echo "test" > 文件 && 頭文件 測試 ``` `printf` is an improved `echo`, allowing formatting and escape sequences: ``` \[ andrew@pc01 ~ \]$ printf "1\\n3\\n2" 1 3 2 ``` `<` gets input from a particular location, rather than stdin: ``` 'sort' 依字母/數字順序對檔案的行進行排序 ======================== \[ andrew@pc01 ~ \]$ sort <(printf "1\\n3\\n2") 1 2 3 ``` Rather than a [UUOC](#viewing-and-editing-files), the recommended way to send the contents of a file to a command is to use `<`. Note that this causes data to "flow" right-to-left on the command line, rather than (the perhaps more natural, for English-speakers) left-to-right: ``` \[ andrew@pc01 ~ \]$ printf "1\\n3\\n2" > 文件 && 排序 < 文件 1 2 3 ``` <a name="std-tee"></a> ### `0 / 1 / 2 / tee` [[ Back to Table of Contents ]](#toc) 0, 1, and 2 are the standard input, output, and error streams, respectively. Input and output streams can be redirected with the `|`, `>`, and `<` operators mentioned previously, but stdin, stdout, and stderr can also be manipulated directly using their numeric identifiers: Write to stdout or stderr with `>&1` or `>&2`: ``` \[ andrew@pc01 ~ \]$ 貓測試 回顯“標準輸出”>&1 回顯“標準錯誤”>&2 ``` By default, stdout and stderr both print output to the terminal: ``` \[ andrew@pc01 ~ \]$ ./測試 標準錯誤 標準輸出 ``` Redirect stdout to `/dev/null` (only print output sent to stderr): ``` \[ andrew@pc01 ~ \]$ ./test 1>/dev/null 標準錯誤 ``` Redirect stderr to `/dev/null` (only print output sent to stdout): ``` \[ andrew@pc01 ~ \]$ ./test 2>/dev/null 標準輸出 ``` Redirect all output to `/dev/null` (print nothing): ``` \[ andrew@pc01 ~ \]$ ./test &>/dev/null ``` Send output to stdout and any number of additional locations with `tee`: ``` \[ andrew@pc01 ~ \]$ ls && echo "測試" | tee 文件1 文件2 文件3 && ls 文件0 測試 文件0 文件1 文件2 文件3 ``` <a name="advanced"></a> # Advanced <a name="superuser"></a> ## Superuser <a name="sudo-su"></a> ### `sudo / su` [[ Back to Table of Contents ]](#toc) You can check what your username is with `whoami`: ``` \[ andrew@pc01 abc \]$ whoami 安德魯 ``` ...and run a command as another user with `sudo -u username` (you will need that user's password): ``` \[ andrew@pc01 abc \]$ sudo -u 測試觸摸 def && ls -l 總計 0 -rw-r--r-- 1 次測試 0 Jan 11 20:05 def ``` If `–u` is not provided, the default user is the superuser (usually called "root"), with unlimited permissions: ``` \[ andrew@pc01 abc \]$ sudo touch ghi && ls -l 總計 0 -rw-r--r-- 1 次測試 0 Jan 11 20:05 def -rw-r--r-- 1 root root 0 Jan 11 20:14 ghi ``` Use `su` to become another user temporarily (and `exit` to switch back): ``` \[ andrew@pc01 abc \]$ su 測試 密碼: test@pc01:/home/andrew/abc$ whoami 測試 test@pc01:/home/andrew/abc$ 退出 出口 \[ andrew@pc01 abc \]$ whoami 安德魯 ``` [Learn more about the differences between `sudo` and `su` here.](http://bit.ly/2SKQH77) <a name="click-click"></a> ### `!!` [[ Back to Table of Contents ]](#toc) The superuser (usually "root") is the only person who can install software, create users, and so on. Sometimes it's easy to forget that, and you may get an error: ``` \[ andrew@pc01 ~ \]$ apt 安裝 ruby E:無法開啟鎖定檔案 /var/lib/dpkg/lock-frontend - 開啟(13:權限被拒絕) E: 無法取得 dpkg 前端鎖定 (/var/lib/dpkg/lock-frontend),您是 root 嗎? ``` You could retype the command and add `sudo` at the front of it (run it as the superuser): ``` \[ andrew@pc01 ~ \]$ sudo apt install ruby 正在閱讀包裝清單... ``` Or, you could use the `!!` shortcut, which retains the previous command: ``` \[ andrew@pc01 ~ \]$ apt 安裝 ruby E:無法開啟鎖定檔案 /var/lib/dpkg/lock-frontend - 開啟(13:權限被拒絕) E: 無法取得 dpkg 前端鎖定 (/var/lib/dpkg/lock-frontend),您是 root 嗎? \[ andrew@pc01 ~ \]$ sudo !! sudo apt 安裝 ruby 正在閱讀包裝清單... ``` By default, running a command with `sudo` (and correctly entering the password) allows the user to run superuser commands for the next 15 minutes. Once those 15 minutes are up, the user will again be prompted to enter the superuser password if they try to run a restricted command. <a name="file-permissions"></a> ## File Permissions <a name="file-permissions-sub"></a> ### File Permissions [[ Back to Table of Contents ]](#toc) Files may be able to be read (`r`), written to (`w`), and/or executed (`x`) by different users or groups of users, or not at all. File permissions can be seen with the `ls -l` command and are represented by 10 characters: ``` \[ andrew@pc01 ~ \]$ ls -lh 總計 8 drwxr-xr-x 4 安德魯 安德魯 4.0K 1 月 4 日 19:37 品嚐 -rwxr-xr-x 1 安德魯 安德魯 40 Jan 11 16:16 測試 -rw-r--r-- 1 安德魯 安德魯 0 一月 11 16:34 tist ``` The first character of each line represents the type of file, (`d` = directory, `l` = link, `-` = regular file, and so on); then there are three groups of three characters which represent the permissions held by the user (u) who owns the file, the permissions held by the group (g) which owns the file, and the permissions held any other (o) users. (The number which follows this string of characters is the number of links in the file system to that file (4 or 1 above).) `r` means that person / those people have read permission, `w` is write permission, `x` is execute permission. If a directory is “executable”, that means it can be opened and its contents can be listed. These three permissions are often represented with a single three-digit number, where, if `x` is enabled, the number is incremented by 1, if `w` is enabled, the number is incremented by 2, and if `r` is enabled, the number is incremented by 4. Note that these are equivalent to binary digits (`r-x` -> `101` -> `5`, for example). So the above three files have permissions of 755, 755, and 644, respectively. The next two strings in each list are the name of the owner (`andrew`, in this case) and the group of the owner (also `andrew`, in this case). Then comes the size of the file, its most recent modification time, and its name. The `–h` flag makes the output human readable (i.e. printing `4.0K` instead of `4096` bytes). <a name="chmod-chown"></a> ### `chmod / chown` [[ Back to Table of Contents ]](#toc) File permissions can be modified with `chmod` by setting the access bits: ``` \[ andrew@pc01 ~ \]$ chmod 777 測試 && chmod 000 tit && ls -lh 總計 8.0K drwxr-xr-x 4 安德魯 安德魯 4.0K 1 月 4 日 19:37 品嚐 -rwxrwxrwx 1 安德魯 安德魯 40 Jan 11 16:16 測試 \---------- 1 安德魯 安德魯 0 一月 11 16:34 tist ``` ...or by adding (`+`) or removing (`-`) `r`, `w`, and `x` permissions with flags: ``` \[ andrew@pc01 ~ \]$ chmod +rwx Tist && chmod -w 測試 && ls -lh chmod:測試:新權限是 r-xrwxrwx,而不是 r-xr-xr-x 總計 8.0K drwxr-xr-x 4 安德魯 安德魯 4.0K 1 月 4 日 19:37 品嚐 -r-xrwxrwx 1 安德魯 安德魯 40 Jan 11 16:16 測試 -rwxr-xr-x 1 安德魯 安德魯 0 一月 11 16:34 tist ``` The user who owns a file can be changed with `chown`: ``` \[ andrew@pc01 ~ \]$ sudo chown 碼頭測試 ``` The group which owns a file can be changed with `chgrp`: ``` \[ andrew@pc01 ~ \]$ sudo chgrp hadoop tit && ls -lh 總計 8.0K drwxr-xr-x 4 安德魯 安德魯 4.0K 1 月 4 日 19:37 品嚐 \-----w--w- 1 瑪麗娜·安德魯 2011 年 1 月 40 日 16:16 測試 -rwxr-xr-x 1 安德魯 hadoop 0 一月 11 16:34 tist ``` <a name="users-groups"></a> ## User and Group Management <a name="users"></a> ### Users [[ Back to Table of Contents ]](#toc) `users` shows all users currently logged in. Note that a user can be logged in multiple times if -- for instance -- they're connected via multiple `ssh` sessions. ``` \[ andrew@pc01 ~ \]$ 用戶 安德魯·科林·科林·科林·科林·科林·克里希納·克里希納 ``` To see all users (even those not logged in), check `/etc/passwd`. (**WARNING**: do not modify this file! You can corrupt your user accounts and make it impossible to log in to your system.) ``` \[ andrew@pc01 ~ \]$ alias au="cut -d: -f1 /etc/passwd \\ > |排序| uniq”&& au \_易於 一個廣告 安德魯... ``` Add a user with `useradd`: ``` \[ andrew@pc01 ~ \]$ sudo useradd aardvark && au \_易於 土豚 一個廣告... ``` Delete a user with `userdel`: ``` \[ andrew@pc01 ~ \]$ sudo userdel aardvark && au \_易於 一個廣告 安德魯... ``` [Change a user’s default shell, username, password, or group membership with `usermod`.](http://bit.ly/2D4upIg) <a name="groups"></a> ### Groups [[ Back to Table of Contents ]](#toc) `groups` shows all of the groups of which the current user is a member: ``` \[ andrew@pc01 ~ \]$ 組 andrew adm cdrom sudo dial plugdev lpadmin sambashare hadoop ``` To see all groups on the system, check `/etc/group`. (**DO NOT MODIFY** this file unless you know what you are doing.) ``` \[ andrew@pc01 ~ \]$ alias ag=“cut -d: -f1 /etc/group \\ > |排序”&& ag 管理員 一個廣告 安德魯... ``` Add a group with `groupadd`: ``` \[ andrew@pc01 ~ \]$ sudo groupadd aardvark && ag 土豚 管理員 一個廣告... ``` Delete a group with `groupdel`: ``` \[ andrew@pc01 ~ \]$ sudo groupdel aardvark && ag 管理員 一個廣告 安德魯... ``` [Change a group’s name, ID number, or password with `groupmod`.](https://linux.die.net/man/8/groupmod) <a name="text-processing"></a> ## Text Processing <a name="uniq-sort-diff-cmp"></a> ### `uniq / sort / diff / cmp` [[ Back to Table of Contents ]](#toc) `uniq` can print unique lines (default) or repeated lines: ``` \[ andrew@pc01 man \]$ printf "1\\n2\\n2" > a && \\ > printf "1\\n3\\n2" > b \[ andrew@pc01 人 \]$ uniq a 1 2 ``` `sort` will sort lines alphabetically / numerically: ``` \[ andrew@pc01 man \]$ 排序 b 1 2 3 ``` `diff` will report which lines differ between two files: ``` \[ andrew@pc01 人 \]$ diff ab 2c2 < 2 --- > 3 ``` `cmp` reports which bytes differ between two files: ``` \[andrew@pc01 人\]$ cmp ab ab 不同:字元 3,第 2 行 ``` <a name="cut-sed"></a> ### `cut / sed` [[ Back to Table of Contents ]](#toc) `cut` is usually used to cut a line into sections on some delimiter (good for CSV processing). `-d` specifies the delimiter and `-f` specifies the field index to print (starting with 1 for the first field): ``` \[ andrew@pc01 人 \]$ printf "137.99.234.23" > c \[ andrew@pc01 man \]$ cut -d'.' c-f1 137 ``` `sed` is commonly used to replace a string with another string in a file: ``` \[ andrew@pc01 man \]$ echo "舊" | sed s/舊/新/ 新的 ``` ...but `sed` is an extremely powerful utility, and cannot be properly summarised here. It’s actually Turing-complete, so it can do anything that any other programming language can do. `sed` can find and replace based on regular expressions, selectively print lines of a file which match or contain a certain pattern, edit text files in-place and non-interactively, and much more. A few good tutorials on `sed` include: - [https://www.tutorialspoint.com/sed/](https://www.tutorialspoint.com/sed/) - [http://www.grymoire.com/Unix/Sed.html](http://www.grymoire.com/Unix/Sed.html) - [https://www.computerhope.com/unix/used.htm](https://www.computerhope.com/unix/used.htm) <a name="pattern-matching"></a> ## Pattern Matching <a name="grep"></a> ### `grep` [[ Back to Table of Contents ]](#toc) The name `grep` comes from `g`/`re`/`p` (search `g`lobally for a `r`egular `e`xpression and `p`rint it); it’s used for finding text in files. `grep` is used to find lines of a file which match some pattern: ``` \[ andrew@pc01 ~ \]$ grep -e " *.fi.* " /etc/profile /etc/profile:Bourne shell 的系統範圍 .profile 檔案 (sh(1)) =================================================== ``` # The file bash.bashrc already sets the default PS1. ``` ``` fi ``` ``` fi ``` … ``` ...or contain some word: ``` \[ andrew@pc01 ~ \]$ grep "andrew" /etc/passwd 安德魯:x:1000:1000:安德魯,,,:/home/andrew:/bin/bash ``` `grep` is usually the go-to choice for simply finding matching lines in a file, if you’re planning on allowing some other program to handle those lines (or if you just want to view them). `grep` allows for (`-E`) use of extended regular expressions, (`-F`) matching any one of multiple strings at once, and (`-r`) recursively searching files within a directory. These flags used to be implemented as separate commands (`egrep`, `fgrep`, and `rgrep`, respectively), but those commands are now deprecated. > **Bonus**: [see the origins of the names of a few famous `bash` commands](https://kb.iu.edu/d/abnd) <a name="awk"></a> ### `awk` [[ Back to Table of Contents ]](#toc) `awk` is a pattern-matching language built around reading and manipulating delimited data files, like CSV files. As a rule of thumb, `grep` is good for finding strings and patterns in files, `sed` is good for one-to-one replacement of strings in files, and `awk` is good for extracting strings and patterns from files and analysing them. As an example of what `awk` can do, here’s a file containing two columns of data: ``` \[ andrew@pc01 ~ \]$ printf "A 10\\nB 20\\nC 60" > 文件 ``` Loop over the lines, add the number to sum, increment count, print the average: ``` \[ andrew@pc01 ~ \]$ awk 'BEGIN {sum=0;計數=0; OFS=“”} {sum+=$2; count++} END {print "平均值:", sum/count}' 文件 平均:30 ``` `sed` and `awk` are both Turing-complete languages. There have been multiple books written about each of them. They can be extremely useful with pattern matching and text processing. I really don’t have enough space here to do either of them justice. Go read more about them! > **Bonus**: [learn about some of the differences between `sed`, `grep`, and `awk`](http://bit.ly/2AI3IaN) <a name="ssh"></a> ## Copying Files Over `ssh` <a name="ssh-scp"></a> ### `ssh / scp` [[ Back to Table of Contents ]](#toc) `ssh` is how Unix-based machines connect to each other over a network: ``` \[ andrew@pc01 ~ \]$ ssh –p安德魯@137.xxx.xxx.89 上次登入:2019 年 1 月 11 日星期五 12:30:52,來自 137.xxx.xxx.199 ``` Notice that my prompt has changed as I’m now on a different machine: ``` \[ andrew@pc02 ~ \]$ 退出 登出 與 137.xxx.xxx.89 的連線已關閉。 ``` Create a file on machine 1: ``` \[ andrew@pc01 ~ \]$ echo "你好" > 你好 ``` Copy it to machine 2 using `scp` (secure copy; note that `scp` uses `–P` for a port #, `ssh` uses `–p`) ``` \[ andrew@pc01 ~ \]$ scp –P你好安德魯@137.xxx.xxx.89:~ 你好 100% 0 0.0KB/秒 00:00 ``` `ssh` into machine 2: ``` \[ andrew@pc02 ~ \]$ ssh –p安德魯@137.xxx.xxx.89 上次登入:2019 年 1 月 11 日星期五 22:47:37,來自 137.xxx.xxx.79 ``` The file’s there! ``` \[ andrew@pc02 ~ \]$ ls 你好多xargs \[ andrew@pc02 ~ \]$ 貓你好 你好 ``` <a name="rsync"></a> ### `rsync` [[ Back to Table of Contents ]](#toc) `rsync` is a file-copying tool which minimises the amount of data copied by looking for deltas (changes) between files. Suppose we have two directories: `d`, with one file, and `s`, with two files: ``` \[ andrew@pc01 d \]$ ls && ls ../s f0 f0 f1 ``` Sync the directories (copying only missing data) with `rsync`: ``` \[ andrew@pc01 d \]$ rsync -off ../s/\* . 正在發送增量文件列表... ``` `d` now contains all files that `s` contains: ``` \[ andrew@pc01 d \]$ ls f0 f1 ``` `rsync` can be performed over `ssh` as well: ``` \[ andrew@pc02 r \]$ ls \[ andrew@pc02 r \]$ rsync -avz -e "ssh -p “ [email protected]:~/s/\* 。 接收增量檔案列表 f0 f1 發送 62 位元組 接收 150 位元組 141.33 位元組/秒 總大小為 0 加速率為 0.00 \[ andrew@pc02 r \]$ ls f0 f1 ``` <a name="long-running-processes"></a> ## Long-Running Processes <a name="yes-nohup-ps-kill"></a> ### `yes / nohup / ps / kill` [[ Back to Table of Contents ]](#toc) Sometimes, `ssh` connections can disconnect due to network or hardware problems. Any processes initialized through that connection will be “hung up” and terminate. Running a command with `nohup` insures that the command will not be hung up if the shell is closed or if the network connection fails. Run `yes` (continually outputs "y" until it’s killed) with `nohup`: ``` \[ andrew@pc01 ~ \]$ nohup 是 & \[1\]13173 ``` `ps` shows a list of the current user’s processes (note PID number 13173): ``` \[ andrew@pc01 ~ \]$ ps | sed -n '/是/p' 13173 分/10 00:00:12 是 ``` _...log out and log back into this shell..._ The process has disappeared from `ps`! ``` \[ andrew@pc01 ~ \]$ ps | sed -n '/是/p' ``` But it still appears in `top` and `htop` output: ``` \[ andrew@pc01 ~ \]$ 頂部 -bn 1 | sed -n '/是/p' 13173 安德魯 20 0 4372 704 636 D 25.0 0.0 0:35.99 是 ``` Kill this process with `-9` followed by its process ID (PID) number: ``` \[ andrew@pc01 ~ \]$ 殺死 -9 13173 ``` It no longer appears in `top`, because it’s been killed: ``` \[ andrew@pc01 ~ \]$ 頂部 -bn 1 | sed -n '/是/p' ``` <a name="cron"></a> ### `cron / crontab / >>` [[ Back to Table of Contents ]](#toc) `cron` provides an easy way of automating regular, scheduled tasks. You can edit your `cron` jobs with `crontab –e` (opens a text editor). Append the line: ``` - - - - - 日期 >> ~/datefile.txt ``` This will run the `date` command every minute, appending (with the `>>` operator) the output to a file: ``` \[ andrew@pc02 ~ \]$ head ~/datefile.txt 2019 年 1 月 12 日星期六 14:37:01 GMT 2019 年 1 月 12 日星期六

使用 VS Code 在 JavaScript 專案中設定 ESLINT

*ESLINT* :你有沒有想過ESLINT 是什麼,當我第一次聽說ESLINT 時,我很好奇它到底是怎麼回事,從那時起我就一直在我的專案中使用它,儘管一開始我錯誤地使用了它,那就是為什麼我發布這篇文章是為了讓人們能夠正確理解。但在深入探討之前,讓我先快速解釋一下什麼是 ESLINT 和 VS Code。 **ESLINT**是用於 Javascript 和 JSX 的可插入 linting 實用程序,它有助於發現可能的錯誤。 **VS Code**是頂級的開發編輯器之一,它由 Microsoft 開發和維護,它有助於提高生產力,並且還具有許多功能,我要強調的功能之一是擴充。擴充功能是 VS Code 中的外部套件,可讓您擴展編輯器的功能 你可以從他們的官方網站下載 VS Code [VS Code Download](https://code.visualstudio.com/) **注意:***我不會深入研究 VS Code。本文中有關 VS Code 的所有內容都只與 ESLINT 相關*。 **腳步**: - 建立一個 JavaScript 專案 - 在 VS Code 編輯器中安裝 eslint 作為擴展 - 使用 npm 將 eslint 安裝為全域包 - 在您的 javascript 專案中初始化 eslint - 修改專案中的 eslint 設定檔。 讓我們使用`npm init --yes`建立一個簡單的 javascript 專案 ![alt text](https://thepracticaldev.s3.amazonaws.com/i/tebfsgkfr3k3h4bl7zvr.PNG "簡單的專案") 操作成功後,它將建立一個*package.json*文件,該文件將管理我們專案的所有配置。 讓我們嘗試在 VS Code 編輯器上安裝 ESLINT 擴充 ![alt text](https://thepracticaldev.s3.amazonaws.com/i/9rmkgbk7nio6ravjm0rx.PNG "ESLINT安裝過程") 一旦我們在 VS Code 編輯器上安裝了 eslint 擴展,然後使用下面的程式碼透過 npm 將 eslint 安裝為全域包 ``` npm install eslint -g ``` 您需要在專案中初始化 eslint,以便可以利用 eslint 的強大功能。從您的根專案輸入以下程式碼來初始化 eslint ``` eslint --init ``` 在初始化期間 eslint 會問你一些問題,更像是設定你的設定檔。 - **您想如何使用 ESLint?** ``` * __To check syntax only__ => it helps you correct your syntax and make sure it conform to standard. ``` ``` * __To check syntax and find problems__ => to help you check for syntax correctness and also help to find any problems in your code base ``` ``` * __To check syntax, find problems, and enforce code style___ => to help you check for syntax, find problem and enforce style, enforcing style means to conforms to a particular coding standard such as Airbnb, Google and other Standard coding style. But I always go for the last option the one with syntax, find problems and enforce code style ``` - **您的專案使用什麼類型的模組?** ``` * __Javascript module (import/export)__ => if your project has babel installed then you definitely need to choose this option. If you are working on a project such as React, Vue, Angular e.t.c they all use babel so you need choose this option. ``` ``` * __CommonJS (require/exports)__ => this option is meant for commonJS that has nothing to do with babel, maybe your nodejs project and any other javascript project ``` - **您的專案使用哪個框架?** ``` * __React__ => if you are using react in/for your project then this option is for you ``` ``` * __Vue__ => if you are using Vue in/for your project then this option is for you ``` ``` * __None of these__ => if you are using neither React or Vue in your project choose this option ``` - **你的程式碼在哪裡執行?** ``` * __Browser__ => if your project runs on browser e.g React, Angular, Vue e.t.c then go for this option ``` ``` * __Node__ => if your project is a node based then gladly choose this option ``` - **您希望如何為您的專案定義風格?** ``` * __Use a popular style guide__ => This allows you to choose from set of popular style such as Airbnb,Standard and Google style guide, it is advisable to choose this option in order for you to follow popular and most used style guide and i will be choosen this option in this post. ``` ``` * Answer questions about your style: _This is for custom style guide_ ``` ``` * Inspect your JavaScript file(s).: _custom style guide_ ``` - **您希望設定檔採用什麼格式?** ``` * __Javascript__ => whether you want your eslint config file to be in *.js* file ``` ``` * __YAML__ => whether you want your eslint config file to be in *.yaml* file ``` ``` * __JSON__ => whether you want your eslint config file to be in *.json* file ``` 您可以選擇此部分中的任何選項 選擇首選設定檔類型後,它將提示您安裝所有必要的依賴項。成功安裝所有必要的依賴項後,它將產生一個帶有“.eslintrc”.“js/json/yaml”的設定檔。 **如下所示的設定檔範例** ![alt text](https://thepracticaldev.s3.amazonaws.com/i/sqyim5m8qoet5lx4bu8o.PNG "eslint設定檔鏡像") 下面是一個小動畫圖像,顯示 VS Code 如何與 eslint 配合使用來通知您 javascript 專案中的錯誤 ![alt text](https://cdn-images-1.medium.com/max/800/1*udUEME0YgHCXqD4pjMxpUA.gif "eslint設定檔鏡像") **在專案中設定 ESLINT 規則** 在專案中定義 ESLINT 規則會告知 eslint 您要新增或刪除的規則類型。您可以在設定檔的規則部分修改/設定規則 要設定的規則範例是 ``` "rules" : { no-console: 0; no-empty: 0; no-irregular-whitespace:0; } ``` 您可以定義盡可能多的規則,您可以在其官方文件[ESLINT Rules Documentation](https://eslint.org/docs/rules/)上閱讀有關 ESLINT 規則的更多訊息 最後,我將向您展示如何將 eslint 連結到 javascript 專案編譯器/轉譯器 以下步驟 - 前往`package.json`文件,在文件的腳本段中加入以下內容 ``` script:{ "lint":"eslint" } ``` **注意:** *“lint”只是一個普通單詞,您可以使用任何您喜歡的單詞* 然後在你的根專案中你可以執行你的 linting 腳本 ``` npm run lint ``` > ESLINT 有助於提高工作效率,根據標準編寫程式碼,並在您的程式碼庫違反樣式指南規則時標記錯誤。透過這篇文章,您應該能夠將 ESLINT 整合到您的 Javascript 專案中。 --- 原文出處:https://dev.to/devdammak/setting-up-eslint-in-your-javascript-project-with-vs-code-2amf