🔍 搜尋結果:研

🔍 搜尋結果:研

讓我們將函數式程式設計引入 OOP 程式碼庫

時間越長,我就越成為函數式程式設計的愛好者。即使當我在 OOP 程式碼庫中工作時,我也會嘗試應用旨在簡化程式碼並更輕鬆地預測結果的小概念。身為 Ruby 專家,我也喜歡使用功能程式碼編寫單元測試是多麼簡單。 本文的目的是分享我對函數式程式設計概念的看法,並提出一種可以在已編寫的 OOP 程式碼中使用函數式概念的方法。希望我們能夠停止爭論哪種範式更好,並開始編寫好的想法,每次都能產生更好的程式碼! 目錄 -- - [一開始,有函數式編程](#in-the-beginning-there-was-functional-programming) - [進入OOP,這個範式是什麼?](#entering-oop-what-is-this-paradigm) - [什麼是類別以及我們如何以不同的方式思考它](#what-is-a-class-and-how-can-we-think-about-it-differently) - [變異還是不變異:什麼是不變性](#to-mutate-or-not-to-mutate-what-is-immutability) - [床下的怪物:有什麼副作用](#the-monster-under-the-bed-what-are-side-effects) - [隔離一切:什麼是純函數](#isolate-everything-what-are-pure-functions) - [使用函數式模式而不需要完整的 haskell](#using-functional-patterns-without-going-full-haskell) - [結論](#conclusion) 一開始,有函數式編程 ---------- ![功能性](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hpg7b5lx6czu67jv9e07.png) 函數式程式設計範式於 1958 年隨著第一個 Lisp 語言的出現而出現(美好的時光)。它的根源可以追溯到 Alonzo Church 的 lambda 演算。函數式程式設計的核心原則圍繞著最小化對程式碼庫中狀態的依賴。 與允許狀態但強調封裝的物件導向程式設計 (OOP) 不同,函數式程式設計師努力優先編寫無狀態元件。這種方法鼓勵建立獨立於外部狀態變數的程式碼。 此外,即使引入了狀態,在編寫狀態時也必須考慮不變性、函數的純度,甚至避免副作用。隨著本文的深入,所有這些概念都將進一步介紹。 進入OOP,這個範式是什麼? -------------- ![打開](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/em6b2ejnoml80awxyrhw.png) OOP,或更廣為人知的名稱為“物件導向程式設計”,是一種可以追溯到 1967 年的範式。其最偉大的代表是 Simula、Smalltalk 和 Java。背後的思想過程是透過強制封裝實踐來將這些狀態以及修改它們的任何行為分組到公共「實體」或「物件」下,從而減少「全局」狀態的數量。 事實上,「物件導向程式設計」這個名字多年來一直被廣泛討論。 OOP 的建立者之一 Alan Key 實際上希望更多地關注該範例的訊息傳遞方面。這意味著我們應該強調封裝並允許物件之間進行狀態和行為的通訊。也許在不同的宇宙中,我們可以擁有「面向訊息的程式」。然而,OOP 這個名字已經經久不衰,而我們就在這裡! 我不知道你怎麼想,但是這個考慮範式的另一個可能名稱的簡單過程讓我的思維變得瘋狂,重新思考了一些概念,並實際上簡化了我建置軟體的方式。 什麼是類別以及我們如何以不同的方式思考它 -------------------- ![什麼是類](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/70x9cp26qi2cvx13dm3q.png) 我想每個人都聽過那個經典的講座,其中我們學到了“動物”類,其中包括“狗”類,對嗎?你可能聽過同樣的話(至少我聽過)。 > 類別是現實世界中實體的藍圖,描述其特徵和操作。 雖然不正確,但我想建議稍微改變一下單字的用法,以幫助澄清封裝,以便更好地理解,就像它對我所做的那樣。讓我們考慮以下新引文: > 類別是一種封裝狀態和在該狀態上操作的行為的方法。 這個簡單的字的改變確實讓我的思想改變了。我不再嘗試將類別視為現實世界的實體,而是開始簡單地將其視為將具有相似上下文的狀態分組在一起並公開對這些狀態進行操作的函數的另一種方式。我希望這個小小的改變也能幫助你回顧自己的概念! 以這種方式抽象化這個概念的重要性是,在從具有不同結構(例如模組)的語言中讀取程式碼時變得熟練。我們可以觀察到這段 OOP 程式碼是用 typescript 寫的: ``` class Github { private _url: string; privale _repo: string; private _username: string; constructor(url: string, repo: string, username: string) { this._repo = repo this._username = username this._url = url } public createRepo(name: string): void { // TODO: do stuff here using the provided state in _url, _repo and _username } } ``` 與此 Elixir 程式碼完全等效,即使 Elixir 程式碼使用“模組”而不是“類別”: ``` defmodule Github do @url "" @repo "" @username "" defstruct url: @url, repo: @repo, username: @username def new(url, repo, username) do %Github{url: url, repo: repo, username: username} end def create_repo(%Github{repo: repo, username: username}, name) do # TODO: do stuff here using the provided state in url, repo, and username end end ``` 接下來我們將研究一些功能概念並進一步討論這些範例的合併,讓我們開始吧! 變異還是不變異:什麼是不變性 -------------- ![不變性](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fotff1cbifqi2c5ubtb.png) 現在我們正在達到第一個真正的功能概念,而且是一個非常重要的概念,我可以補充一下(一切都在這裡計劃)!為了正確理解不變性,讓我們回顧一下在程式設計中處理值的方式: 通常,我們將值綁定到變數,以便稍後可以對它們進行操作,對嗎?就像簡單的事情 ``` # Bounding values to variables name = 'Cherry' age = 23 # Operating on it and bounding to another variable year_born = Time.now.year - age # Printing it puts "#{name} has #{age} years old and was born at #{year_born}" ``` 對於這些變數,透過更新其值來更改原始變數是很常見的,但這裡缺少的是:修改變數是一種**破壞性**操作。 但為什麼?好吧,讓我們想像一下多個操作(函數或程式碼區塊)在不同時刻和頻率修改相同變數。在這種情況下,我們會產生很多問題,例如: - **1. 無法對操作重新排序或根本無法更改它們:**當我們有如此多的依賴程式碼時,甚至很難對程式碼進行重新排序或更改,因為所有內容都綁定到特定的更改順序。 - **2. 理解程式碼在做什麼的心理負擔:**雖然這是個人觀點,但我認為這是一個廣受認可的觀點。高度可變的程式碼很容易變得混亂且難以理解資料流,需要除錯器等工具來逐步完成轉換。 - **3. 測試時的困難:**模擬函數轉換的特定狀態確實很困難,這將逐漸擴展您的單元測試,直到它們不再是單元。 不變性可以定義為避免更改(或變異)程式內任何變數的做法。儘管根據語言的不同,我們可能需要做出讓步並改變一些控制變數,但這裡要學到的總體教訓是: > 我們應該不惜一切代價避免改變沒有定義範圍的變數。 透過這句話,我的意思是可以在函數內建立作用域變數並在那裡對其進行變異。然而,一旦您將這個可變變數傳遞給另一個函數,您就會增加改變相同變數的目標數量,並且您將慢慢失去控制。這正是我們想要避免的情況! 床底下的怪物:有什麼副作用 ------------- ![副作用](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yb1osy6ziacyc2l12h6v.png) 每當有人提出這個話題來討論時,這個話題就會引起很大的熱度。我可能不會涵蓋這個主題的每一個細微差別,但我一定會向您解釋它們是什麼以及我如何在我自己的軟體中管理副作用,好嗎? 那麼,副作用就是透過呼叫協定(HTTP、WebSocket、GraphQL 等)甚至操作 stdin/stdout 與外部資源(或「外部世界」)互動的每一次計算。是的,我知道,即使是我們無害的`print`也會在這裡受到指責。 😔 但與變異性不同的是,我們不應該盡可能避免使用它,而應該將其隔離在單獨處理副作用的特定函數中。這樣,我們將程式碼分為「不執行任何副作用的函數」和「執行副作用的函數」。但為什麼要擔心這種分離呢? 每次我們觸發對「外部世界」的任何操作時,我們都會失去對這個特定計算部分可能發生的情況的控制(例如在執行 HTTP 呼叫時,伺服器可能會關閉或可能根本不存在)。其他問題包括測試困難和程式碼可預測性降低。 由於我們無法編寫沒有副作用的任何現實世界軟體,因此一般建議是將其聚集成小函數,透過對錯誤的適當抽象來單獨處理它。這樣,就可以只測試我們 100% 控制的函數,並模擬所有執行副作用的函數。 例如,請考慮以下執行 HTTP 請求的函數以及轉換從該請求傳回的資料的小函數。 ``` require 'faraday' module MyServiceModule # This function perform side effects def perform_http_request conn = Faraday.new(url: "fakeapi.com") begin response = conn.get {ok: true, data: response.body} rescue => e {ok: false, error: e} end end # These functions doens't perform any side effects def upcase_name(name) return '' unless name.is_a?(String) name.upcase end def retrieve_born_year(age) return 0 unless age.is_a?(Integer) Time.now.year - age end end ``` > 看到我怎麼說「錯誤周圍的抽象」了嗎?這正是上面的程式碼範例中實現的,而不是讓異常在我們針對雜湊抽象的程式碼中冒泡。 在使用「副作用」和「無副作用」之間的明確定義來定義這些函數之後,很容易預測程式碼中會發生什麼,也更容易測試,如下所示: ``` require 'minitest/autorun' class TestingStuff < Minitest::Test def test_upcase_name assert_equal MyServiceModule.upcase_name "cherry", "CHERRY" assert_equal MyServiceModule.upcase_name "kalane", "KALANE" assert_equal MyServiceModule.upcase_name "Thales", "THALES" end def test_retrieve_born_year Time.stub :now, Time.new(2024, 3, 5) do assert_equal MyServiceModule.retrieve_born_year 23, 2001 assert_equal MyServiceModule.retrieve_born_year 20, 2004 assert_equal MyServiceModule.retrieve_born_year 14, 2010 end end end ``` 這個策略真的很棒,因為您甚至不需要在測試時擔心副作用部分,只需為實際執行某些操作的程式碼轉換部分編寫斷言,您最終會得到更好的測試,真正驗證重要的內容您的程式碼庫的一部分!整齊吧? 隔離一切:什麼是純函數 ----------- ![純函數](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgnpfyig36sssuio9qpy.png) 現在是時候總結到目前為止所獲得的所有知識了。在前面的範例中,我們觀察到程式碼分為「副作用」和「無副作用」。我們還看到了這些功能如何更容易測試,並且我們的主要轉換業務邏輯應該保持隔離。您想知道這些函數叫什麼嗎?它們是**純函數**! 讓我們檢查純函數的正確形式定義並逐步探索這個概念。 > 純函數是尊重不變性、不執行任何副作用並且在給定相同參數的情況下傳回相同輸出的函數。 基本上,純函數遵循我們之前提到的所有原則,而且它們總是為相同的參數產生相同的返回。讓我們來看看之前的函數。 ``` def upcase_name(name) return '' unless name.is_a?(String) name.upcase end upcase_name('cherry') # => Will be *always* CHERRY ``` 使用純函數,我們可以輕鬆定義多個斷言,因為我們不受任何需要大量模擬的上下文的約束。我們只需用靜態值傳遞所需的參數,就這樣! 由於純函數非常小且可組合,因此它們的數量增加得非常快。為了解決這個問題,像 Elixir 這樣的函數式語言提供了像管道這樣的組合運算符,這使得按順序執行多個純函數變得非常容易。 ``` "cherry " |> trim |> upcase # => "CHERRY" ``` > 管道運算子源自 Bash 等函數。您可以在這裡閱讀更多相關資訊:\[ <https://dev.to/cherryramatis/linux-filters-how-to-streamline-text-like-a-boss-2dp4#what-is-a-pipeline> \] 使用函數式模式而不需要完整的 haskell ---------------------- ![都是功能啊](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/42ndx4tuvrs35q2woepg.png) 我一直害怕學習函數範式,因為社群透過使用現成的句子和大概念讓每個試圖學習一些小技巧的人變得非常複雜。在掌握了許多函數式語言並嘗試盡可能多地學習之後,我的目標是簡化這些概念,最重要的是,提倡在 OOP 程式碼中使用函數式概念。 應用純函數(或純方法,如果您願意)、不變性和副作用分離可以使您的 OOP 程式碼看起來更乾淨和解耦。你不需要知道什麼是 monad 或如何在 Haskell 中手動編寫編譯器;您可以使用簡單而有效的函數概念來堅持使用 Ruby on Rails! 我希望透過這篇小文章(以及本系列中的文章),無論您選擇哪種語言和框架,您都可以透過可組合性和簡單性來改進您的程式碼庫。 結論 -- 這篇文章是我嘗試使函數範式的知識民主化(在我的能力和專業知識範圍內)。需要強調的是,我不是函數式專家,本文針對的是了解 OOP 並對函數式程式設計感興趣的初學者。我希望它有用,並且我願意提供任何需要的幫助。願原力與你同在🍒 --- 原文出處:https://dev.to/cherryramatis/ending-the-war-or-continuing-it-lets-bring-functional-programming-to-oop-codebases-3mhd

2024 年你需要了解的 Python 函式庫

長話短說 ---- 你正在學習Python嗎?這是一片叢林! 您可以擁有任何您能想到的庫 - 從建立遊戲到建立 Web 應用程式。 透過此列表,無論您是剛剛入門還是想要深化 Python 遊戲,都可以快速了解 50 個標準 Python 庫及其用途。 ![介紹](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/at6swte9i2adqyeni03w.gif) --- 1.**Taipy** -------- Taipy 旨在加快應用程式開發,從最初的原型到生產就緒的應用程式。 這個開源 Python 函式庫專為輕鬆開發前端 (GUI) 和 ML/資料管道而設計。 它程式碼量低,專為任何 pythonista 設計。 ![口服](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scksaelbdhxsyzkv0bdq.png) https://github.com/Avaiga/taipy Star ⭐ Taipy 儲存庫 您的支持意義重大🌱,並在許多方面幫助我們,例如寫文章! 🙏 --- **2.NumPy** ----------- 對於數值計算至關重要,支援大型多維陣列和矩陣。該函式庫是 Python 版稅的一部分。 https://github.com/numpy/numpy Star ⭐ Numpy 儲存庫 --- 3.**Pandas** -------- 資料操作和分析的基石,提供直覺的資料結構和操作來操作數值表和時間序列。又一個Python不可或缺的函式庫,必知的函式庫。 https://github.com/pandas-dev/pandas Star ⭐ Pandas 儲存庫 --- **4.Matplotlib** ---------------- 一種多功能工具,用於建立各種靜態、最小和互動式視覺化。有很多參數可供使用,這個函式庫在繪製 ML 和 AI 圖表時非常有用。 https://github.com/matplotlib/matplotlib Star ⭐ Matplotlib 儲存庫 --- **5.SciPy** ----------- 專注於技術和科學計算,使用 Scipy,您可以進行最佳化、整合、插值等。 https://github.com/scipy/scipy Star ⭐ SciPy 儲存庫 --- **6.Scikit-learn** -------------- 機器學習的首選庫,提供廣泛的監督和無監督學習演算法。開始機器學習時您應該了解的唯一庫。 https://github.com/scikit-learn/scikit-learn Star ⭐ Scikit-learn 儲存庫 --- **7.TensorFlow** ---------------- 機器學習的綜合框架提供了各種工具、庫和社區資源。學習曲線可能有點陡峭,但在 Python 和 ML 領域中了解 TF 很重要。 https://github.com/tensorflow/tensorflow Star ⭐ TensorFlow 儲存庫 --- **8.PyTorch** ------------- 由於其靈活性,提供動態神經網路建立和操作,因此受到學術研究和生產的青睞。 https://github.com/pytorch/pytorch Star ⭐ PyTorch 儲存庫 --- 9.**Keras** -------- 用於建立和訓練深度學習模型的高級 API,旨在促進神經網路的建構和使用。 https://github.com/keras-team/keras Star ⭐ Keras 儲存庫 --- 10.**requests** --------- 簡化了發出 HTTP 請求的過程,使 Web 抓取和 API 消費更容易存取。 https://github.com/psf/requests Star ⭐ 請求儲存庫 --- 11.**Beautiful Soup** ----------- 一種網頁抓取工具,有助於從 HTML 和 XML 文件中提取資料。 https://github.com/waylan/beautifulsoup Star ⭐ Beautiful Soup 儲存庫 --- 12.**Flask** --------- 一個輕量級且可擴展的 Web 框架,非常適合建立中小型 Web 應用程式。 https://github.com/pallets/flask Star ⭐ Flask 儲存庫 --- 13.**Django** --------- 這個高級框架專為快速開發和簡潔、實用的設計而設計。 https://github.com/django/django Star ⭐ Django 儲存庫 --- 14.**Selenium** -------- 該程式庫使 Web 瀏覽器自動化,從而能夠模擬實際使用者操作以測試 Web 應用程式。 https://github.com/SeleniumHQ/selenium Star ⭐ Selenium 儲存庫 --- **15.Pygame** ------------- 提供用於編寫視訊遊戲的 Python 模組,包括圖形和聲音庫。 https://github.com/pygame/pygame Star ⭐ Pygame 儲存庫 --- 16.**Pillow** --------------- 擴充Python圖像庫功能,支援各種圖像檔案格式。 https://github.com/python-pillow/Pillow Star ⭐ Pillow 儲存庫 --- 17.SQL**Alchemy** ------------- 本函式庫提供了一整套透過 Python 處理資料庫的工具,提供了強大的 ORM 層和 SQL 表達式語言。 https://github.com/sqlalchemy/sqlalchemy Star ⭐ SQLAlchemy 儲存庫 --- **18.PySpark** -------------- 從名稱中可以看出,該程式庫將 Apache Spark 的強大功能引入了 Python,透過 Pythonic 方法促進大資料處理和分析。 https://github.com/apache/spark Star ⭐ PySpark 儲存庫 --- 19.**dash** --------- 允許直接在 Python 中建立分析 Web 應用程式,無需深入了解 Web 開發。 https://github.com/plotly/dash Star ⭐ dash 儲存庫 --- 20.**Plotly** --------- 專注於建立適合網路和行動應用程式的互動式且具有視覺吸引力的圖形和圖表。 https://github.com/plotly/plotly.py Star ⭐ Plotly 儲存庫 --- 21. **Nltk** ------------ 該庫使自然語言處理變得易於存取且易於使用。 https://github.com/nltk/nltk Star ⭐ Nltk 儲存庫 --- 22.**SpaCy** ---------- 提供工業級自然語言處理能力以及針對多種語言的預訓練模型。 https://github.com/explosion/spaCy Star ⭐ SpaCy 儲存庫 --- **23.Gensim** ------------- 專注於無監督主題建模和自然語言處理,您可以使用該程式庫來分析文件相似性。 https://github.com/RaRe-Technologies/gensim Star ⭐ Gensim 儲存庫 --- 24.Py**Test** ----------- 一個強大的框架,用於編寫小型到複雜的功能測試,增強測試的可讀性和可維護性。 https://github.com/pytest-dev/pytest Star ⭐ PyTest 儲存庫 --- 25.**單元測試** ----------- 用於建置和執行測試的內建框架,反映了其他語言中的 xUnit 架構。 Unitest 是內建的。 --- 26.**Fabric** --------- 簡化應用程式部署或系統管理任務的 SSH,自動執行遠端 shell 命令。 https://github.com/fabric/fabric Star ⭐ Fabric 儲存庫 --- 27.**Vizzu** -------- Vizzu 旨在動畫資料視覺化和講故事,是建立動態和互動式圖表的首選庫。 https://github.com/vizzuhq/vizzu-lib Star ⭐ Vizzu 儲存庫 --- 28.**Polars** --------- 針對效能和效率進行最佳化的 DataFrame 庫,能夠輕鬆處理大型資料集。 https://github.com/pola-rs/polars Star ⭐ Polars 儲存庫 --- 29.Docker **-Py** ----------------- 提供對 Docker Remote API 的 Pythonic 存取,從而實現 Docker 容器管理的自動化。 https://github.com/docker/docker-py Star ⭐ Docker-Py 儲存庫 --- **30.OpenCV** ------------- 電腦視覺和影像處理的主要內容,提供一整套演算法和工具。 https://github.com/opencv/opencv Star ⭐ OpenCV 儲存庫 --- 31.Scikit**-image** --------------- 它致力於影像處理,將 SciPy 和 NumPy 的功能擴展到視覺領域。 https://github.com/scikit-image/scikit-image Star ⭐ Scikit-image 儲存庫 --- 32.**SymPy** --------- 該庫專為符號計算而設計,提供從代數求解到微積分的各種功能。 https://github.com/sympy/sympy Star ⭐ SymPy 儲存庫 --- 33.**Virtualenv** ----------- 對於建立隔離的 Python 環境和乾淨地管理專案依賴至關重要。 https://github.com/pypa/virtualenv Star ⭐ Virtualenv 儲存庫 --- 34.**Click** --------- 簡化命令列介面的建立,促進可組合且易於擴展的程式碼。 https://github.com/pallets/click 為 Click 儲存庫加註星標 --- 35. **argparse** ---------------- 促進命令列參數的解析,這對於 CLI 應用程式開發至關重要。 Argparse 是內建的。 --- 36.**日誌記錄** ----------- 提供靈活的日誌記錄系統,從簡單的日誌記錄到複雜的每個模組配置。 日誌記錄是內建的。 --- **37.PyYAML** ------------- 處理 YAML 文件,支援 Python 物件與 YAML 之間的序列化和反序列化。 https://github.com/yaml/pyyaml Star ⭐ PyYAML 儲存庫 --- 38.xlrd **/xlwt** ----------------- 非常適合讀取和寫入 Excel 文件,彌補了 Python 和 Excel 文件之間的差距。 https://github.com/python-excel/xlrd Star ⭐ xlrd 儲存庫 https://github.com/python-excel/xlwt Star ⭐ xlwt 儲存庫 --- 39. **Pandas 分析** ----------------- 從 pandas 產生全面的配置文件報告 https://github.com/pandas-profiling/pandas-profiling Star ⭐ Pandas-Profiling 儲存庫 --- 40.**全面品質管理** ------------- Tqdm 是任何循環的包裝器,它將透過進度條追蹤進度。 https://github.com/tqdm/tqdm Star ⭐ TQDM 儲存庫 --- 41.**Faker** --------- 需要看起來真實的假資料嗎? Faker 為你提供支援。 https://github.com/joke2k/faker Star ⭐ Faker 儲存庫 --- 42.**Flake8** ---------- 一個必備的庫,可以使您的程式碼保持簡潔,並輕鬆實現樣式檢查。 https://github.com/PyCQA/flake8 Star ⭐ Flake8 儲存庫 --- 43.**Black** --------- 將您的程式碼格式提升到一個新的水平。 https://github.com/psf/black Star ⭐ Black 儲存庫 --- 44.**Mypy** ---------- 這就像有一位文法老師來教你的程式碼,但教你的類型。 https://github.com/python/mypy Star ⭐ Mypy 儲存庫 --- 45.**Pydantic** ----------- 驗證 Python 腳本所需的函式庫。 https://github.com/samuelcolvin/pydantic Star ⭐ Pydantic 儲存庫 --- **46.FastAPI** -------------- FastAPI 是一個用於建立 RESTful API 的 Web 框架。 https://github.com/tiangolo/fastapi Star ⭐ FastAPI 儲存庫 --- 47.**Catboost** ---------- 為您的機器學習模型提供處理分類資料的方法。 https://github.com/catboost/catboost Star ⭐ Catboost 儲存庫 --- 48.**Seaborn** ---------- 提高資料可視化水準。 https://github.com/mwaskom/seaborn Star ⭐ Seaborn 儲存庫 --- 49.**Turtle graphics ** --------- 透過酷炫的圖形和動畫將程式設計帶入生活。學習並開始使用 Python 的好方法。 Turtle graphics 是內建的。 --- 50.**Asciimatics** ------------ Asciimatics 是一個可讓您建立全螢幕文字 UI 的程式庫。 https://github.com/peterbrittain/asciimatics Star ⭐ Asciimatics 儲存庫 --- 原文出處:https://dev.to/taipy/python-libraries-you-need-to-know-in-2024-37ka

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

在本文中,我將概述各種[部落格平台及其程式碼注入功能](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

給女性工程師:10 項增強女性科技能力的線上資源

長話短說 ---- 隨著 3 月 8 日的臨近,我總是會在這個時候反思我作為科技女性的經歷。 我從事科技工作已有五年多了。 我一開始是資料科學家,現在我正在探索並喜歡[Taipy](https://github.com/Avaiga/taipy)的 DevRel 🥑 角色。 不用說,科技領域的發展充滿了起起落落,以及介於兩者之間的一切。 ![性別差異](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/upvr60s75yue60ebdbff.png) 國際婦女節不僅僅是日曆上的一個日期;它也是一個節日。今天對我們來說至關重要,我們要思考如何改善情況。這是慶祝、反思和改變的一天! --- 這一天讓我質疑自己的進化。 儘管我必須克服冒名頂替症候群並不斷尋找歸屬感,但科技的進步是有益且令人興奮的。 然而,這些挑戰塑造了今天的我,並磨練了我最大的優勢。 在我的發展過程中,我在各種社區和資源中發現了令人難以置信的支持系統,我很高興與您分享: --- 社群 -- ### PyLadies ![皮拉迪斯](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8uky29xpk6n2mfmge5l.png) 身為 Python 愛好者,Pyladies 在我心中佔有特殊的地位。我參加過他們在巴黎和倫敦舉行的聚會,他們也在全球 50 多個城市舉辦了活動。顧名思義,該小組是開源 Python 社群的活躍組成部分。一個了解 Python 最新趨勢的好地方,甚至可以透過他們的研討會提高您的編碼技能。 https://pyladies.com/ --- ### 機器學習和資料科學領域的女性 ![無線MLDS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aodbft8fk16d9mmcrj9p.png) 機器學習和資料科學領域的女性支持並促進有興趣在機器學習和資料科學領域取得進步的女性。 參加他們在法國和英國舉行的活動是了解最新趨勢並與其他資料科學愛好者交流的絕佳機會。 https://wimlds.org/ --- ### 科技女性 ![威特](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inilre39c2idekq9bh2m.png) 這個全球組織致力於幫助女性擁抱科技。他們擁有全球視野,致力於賦權和縮小性別差距。 https://women-in-tech.org/ --- ### 編碼的女性 ![WWC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z1av53wrxvufpn1vmttv.png) 旨在賦予女性技術職業權力。他們舉辦編碼活動和資源,專注於幫助女性走上職業道路。如果您覺得需要一些職業指導,請不要猶豫查看他們的網站。 https://womenwhocode.com/ --- 會議 -- ### 格蕾絲·霍珀慶祝活動 ![生長激素](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qltssqxjnvsc40v98el.png) 聚集科技領域女性的最重要活動。該活動以一位鼓舞人心的電腦科學家的名字命名,展示了女性在電腦領域的職業和研究。 https://ghc.anitab.org/ --- ### 女性科技節 ![盛宴](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cggr6cyfcd9mzng2tdsf.png) 這是一個充滿活力的活動,透過鼓舞人心的演講、社交和研討會展示科技領域的女性。 https://ghc.anitab.org/ --- ### 資料和人工智慧領域的女性+ ![華加](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4jf6of1jozopgqiqefa.png) 這是我去年最喜歡的會議之一。能量是 鼓舞人心-現代會議展示了代表性不足的性別在該領域的貢獻。 https://www.womenintechfestivalglobal.com/womenintechfestivalglobal2024/en/page/home --- podcast ------- ### 女性科技播客 ![特別是](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y6dl4vh8g6lalb3eo4if.png) 該播客將為您講述科技業女性的精彩故事,從她們的旅程到挑戰,以及一些精心建置的建議。 https://podcast.womenintechshow.com/ --- ### 科技女孩演員陣容 ![TC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ez1o0kwicb0urqt26ii.png) 另一個勵志播客,它將帶您了解女性的故事,並在日常生活中激勵您。 https://podcasts.apple.com/us/podcast/tech-girls-cast/id1557400427 --- ### She Talks Tech ![她說話](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bppajq0h7sgs4zp0zsl.png) 該播客與該行業的主要女性進行了強有力的對話。它們涵蓋了領導力和個人成長等主題。 https://wearetechwomen.com/she-talks-tech-podcast/ --- 子版塊 --- ### /girlsgonewired ![有線](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osdfsw36p2rm08vuuwj9.png) STEM 領域的女性和非二元性別社群。您可以分享您的故事並尋求建議。用作支援系統的 Reddit 子版塊。 https://www.reddit.com/r/girlsgonewired/ --- ### /女性科技 ![機智](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kyleoylj40cdebwb81ar.png) 這是一個很棒的 Reddit 子版塊,用於討論您在科技行業的經歷、挑戰和成就。在 Reddit 子版塊中閃耀並支持女性。 https://www.reddit.com/r/womenintech/ --- ### /科學女士們 ![女士們](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcej49410cof23q1zaes.png) 這是一個為科技業女性提供支援的 Reddit 子版塊。您會找到證詞、建議和展示。 https://www.reddit.com/r/LadiesofScience/ --- 最後的想法 ----- 我的科技之旅既有時感覺自己像個局外人,又學著擁抱自己的優勢和獨特性。這些社區在這一演變中發揮了作用。 讓我們繼續打破障礙,確保科技世界具有包容性和歡迎性。 不要猶豫,在評論中分享您最喜歡的資源! --- 原文出處:https://dev.to/marisogo/10-resources-to-empower-women-in-technology-2m6n

身為軟體工程師,提升我工作效率的工具

作為開發人員,**確定工具的優先順序至關重要,**因為它們對於實現您的目標至關重要。熟練是有價值的,但正確的工具可以增強這些技能,從而在您的領域中取得更大的成功。 在本文中,我將分享我每天使用的提高工作效率的工具。這些工具幫助我完成各種任務,包括文件、線框圖、開發、測試、除錯和研究。 我將這些工具分為三類:*任務管理*、*開發*和*文件*。 **讓我們事不宜遲地深入研究這些工具。** 任務管理工具:讓事情井井有條 -------------- 對於軟體開發人員來說,保持任務有序非常重要。無論您計劃的是本月、一周還是今天,都沒關係。寫下所有任務會很有幫助,這樣您就可以了解自己的表現。 我主要使用兩個工具: **Notion**和**Linear** 。 ### Notion 當您是軟體工程師時,您經常需要組織您的工作、快速做筆記,甚至即時編寫一些文件。概念對此非常有用。 Notion讓我可以輕鬆地整理我的想法、規劃我的內容並安排我的工作。日曆範本使用簡單,您可以根據自己的喜好進行更改,並且可以加入許多詳細資訊(例如標籤)來追蹤您的任務。 我使用 Notion 來規劃我的內容。 ![概念工作區](https://cdn.hashnode.com/res/hashnode/image/upload/v1709480859081/0841cd7a-18f5-4dd7-8ad4-48d5cec3001b.png) 概念也有利於團隊追蹤任務。但是,我要討論的下一個工具對於管理軟體開發任務來說更加簡單。 我真的不喜歡到處移動門票或花很多時間在門票報告上。這個工具讓這些任務變得更容易,我認識的許多開發人員都喜歡每天使用它。這就是為什麼我想介紹 Linear。 ### [Linear](https://linear.app/) 我之前嘗試過使用 Trello 或 Jira 等工具,但我不太喜歡它。 Trello 太簡單,功能不多,而 Jira 功能很多,但使用起來很複雜(甚至 GitLab 的 Issue board 似乎更好)。 然後我找到了 Linear,它是為像我們這樣的開發者設計的。 Linear 可以輕鬆實現工作流程自動化並整合其他工具,而不會使事情變得複雜。而且用起來真的很好。使用線性,您可以獲得: - 根據您的 PR/MR 變更或進度自動更新票證。 - 複製分支名稱的簡單方法。 - 不傷眼的深色模式。 - 鍵盤快捷鍵可讓您在應用程式中快速移動。 - 使用起來既快速又有趣。 使用 Linear,我不會花太多時間在任務管理上,因此我可以更專注於編碼。 現在我們已經討論瞭如何管理任務,接下來我將分享我用於文件編寫的工具。 文件工具:軟體工程的支柱 ------------ 文件是軟體工程的基石,以至於人們常說,最好的開發人員花在編寫文件的時間比編寫程式碼的時間還要多。它有多種用途,從規劃和假設建立到追蹤效能、教育用戶以及詳細說明功能或錯誤。 在這裡,我將分享我用於文件編寫的工具,從文字編寫到圖形建立。 ### [Obsidian](https://obsidian.md/) 雖然 Notion 是一個很棒的工具,許多人可能想知道為什麼它不是我的文件首選,但我發現它更傾向於記筆記。儘管 Notion 的小部件增強了其功能,但我最近需要一個簡單的離線工具來組織想法並有效地將它們連結起來。 Obsidian 在這一領域表現出色,在反向連結和創意組織方面超越了 Notion。 ![黑曜石接口](https://cdn.hashnode.com/res/hashnode/image/upload/v1709482594433/228c2d3a-14a8-4326-a76e-a6c793ec36f2.png) Obsidian 提供了一套廣泛的筆記記錄和知識管理功能。它的知識圖直觀地表示了筆記之間的聯繫,有助於深入挖掘資訊。完整的 Markdown 支援允許靈活且強大的格式設定。 該應用程式的離線功能確保其無需網路連接即可使用,拼字檢查、API 支援以及將筆記發佈為網站或使用統一筆記結構模板的能力等其他功能也很突出。 ![知識圖譜](https://cdn.hashnode.com/res/hashnode/image/upload/v1709482699496/09473cc6-4022-4407-82d4-d3ba31b56bad.png) 我主要使用 Obsidian 來整理我的筆記。組織好後,我將它們轉移到 Notion 進行共享,因為 Obsidian 缺乏共享和同步功能。儘管如此,與 Notion 相比,Obsidian 仍然是我首選的文件工具。 然而,Notion和Obsidian只能幫助你寫文件。那麼,一些視覺效果怎麼樣呢?我們來談談[Excalidraw](https://excalidraw.com/) 。 ### [Excalidraw](https://excalidraw.com/) 向遠距工作的過渡讓我懷念使用記號筆和白板進行腦力激盪的簡單性。當語言無法表達時,視覺效果可以彌補理解複雜想法的差距。 Excalidraw 以數位方式重新建立白板體驗,這對於補充文件的快速圖表或插圖具有無價的價值。 這是我建立的圖表範例,用於闡明 React 元件生命週期。 ![React 元件生命週期圖](https://cdn.hashnode.com/res/hashnode/image/upload/v1709483920056/7325dab0-f384-4f02-917d-28a56b869ceb.png) 這些工具構成了我作為軟體工程師的文件實踐的基礎。接下來,我們將探討提高編碼效率的開發工具。 開發工具 ---- 多年來,我的開發工具發生了很大變化,隨著最近人工智慧的引入和大量使用,我發現自己發現了更多可以幫助我作為軟體工程師提高工作效率的工具。但在討論人工智慧工具之前,我們先來談談程式設計和測試工具。 我是軟體工程師,使用 Django、Next.js,有時也使用 Golang 進行開發。我建立了為資料提供服務的 API 和/或使用這些資料的接口,因此編碼和測試我的工作非常重要。這就是 Jetbrains 發揮作用的地方。 ### 編碼工具 [Jetbrains](https://www.jetbrains.com/)提供了非常強大的 IDE,讓您的工作變得更輕鬆。我堅信他們為開發人員打造了最好的 IDE。我認為,無需配置即可立即開始編碼,這是一個很大的優勢。 這是我離開使用 VsCode 的原因之一(嗯,我仍然將它用於快速專案或不太複雜的專案),因為我每次都需要同步才能確保我可以開始工作。例如,當使用 Webstorm 啟動 Next.js 專案時,我只需選擇在每次儲存時執行 Eslint、prettier 的選項,還可以自動設定可以在寫入提交之前執行的 git 掛鉤。 [Webstorm](https://www.jetbrains.com/webstorm/) IDE 還有一個漂亮的 UI,用於在偵錯模式下執行專案,而且功能非常強大。我在 Pycharm 和 Goland 上也經歷過。這些 IDE 功能強大且使用起來非常簡單。 不要誤會我的意思,VsCode 已經很強大了,但是當涉及到執行基本任務時,沒有太多麻煩或配置,例如:搜尋、重構、Git 任務(獲取、拉取、推送、PR/MR 管理等)。 感謝 Jetbrains 團隊製作如此強大的工具。 ### 測試:Insomnia、Postman 起初,我使用[Postman](https://www.postman.com/)來測試API,因為它有很多功能。但我改用了[Insomnia](https://insomnia.rest/) ,因為它更容易使用並且讓一切井井有條。 Insomnia 的一個大問題是,當它讓我建立一個帳戶來繼續使用它時,它刪除了我所有保存的工作。 這讓我在一年後又回到了 Postman。 Postman 沒有太大變化,但它有一些我非常喜歡的新的、更好的功能。現在,我又回到了 Postman,因為它感覺像是我熟悉的東西,但也有新的東西。 Insomnia 使用起來很簡單,但每次打開筆記型電腦時都必須登錄,這很煩人。本來可以很棒,但這些問題讓我開始尋找別的東西。 現在,我正在考慮嘗試[Bruno](https://www.usebruno.com/) ,這是我聽說過的新工具。 Bruno 擁有您想要的所有功能,例如 Websocket 的支援。 Bruno 的優點是一次性支付僅需 19 美元,看起來很划算。我想看看它對我來說效果如何,是否像聽起來那麼好。我很高興嘗試一下,也許稍後再討論。 ### 人工智慧工具 我經常使用 ChatGPT 和[Phind.ai](https://www.phind.com/)來撰寫技術文章、除錯和集思廣益解決方案。雖然有些人可能對他們的回答持保留態度,但我發現它們是有用的指南,補充了我自己的研究。 [Phind.ai](https://www.phind.com/)對於提供進一步探索和加強研究過程的連結特別有價值。 其他 AI 工具(例如[Copilot](https://github.com/features/copilot)和[Codium.ai)](https://www.codium.ai/)與流行的編輯器和 IDE(例如 VSCode)很好地集成,從而提高您的技能和生產力。 在編碼中有效使用人工智慧需要清楚了解您的目標。在沒有紮實掌握底層技術和清晰的溝通技巧的情況下,不建議初學者開始使用人工智慧進行編碼。一旦您掌握了這些技能,人工智慧工具就可以極大地有益於您的工作。 結論 -- 這是我很久以來第一次寫一篇與程式設計無關的文章。由於生產力是軟體工程的一個重要方面,因此我希望撰寫更多有關幫助我成為更好的軟體工程師的工具或策略的文章。 如果您對本文有疑問或回饋,請在評論中分享。您的意見有助於使該資源更好地服務每個人。以下是我們在本文中所使用的資源。 目前為止就這樣了。快樂編碼!🚀 --- 原文出處:https://dev.to/koladev/tools-that-make-me-productive-as-a-software-engineer-2dge

30 個 JavaScript 奇妙小技巧

歡迎使用我們精選的 JavaScript 技巧集合,它將幫助您優化程式碼、使其更具可讀性並節省您的時間。 讓我們深入研究 JavaScript 的功能和超越傳統的技巧,並發現這種強大的程式語言的全部潛力。 ### 1. 使用!!轉換為布林值 使用雙重否定快速將任何值轉換為布林值。 ``` let truthyValue = !!1; // true let falsyValue = !!0; // false ``` ### 2. 預設功能參數 設定函數參數的預設值以避免未定義的錯誤。 ``` function greet(name = "Guest") { return `Hello, ${name}!`; } ``` ### 3. 短 if-else 的三元運算符 `if-else`語句的簡寫。 ``` let price = 100; let message = price > 50 ? "Expensive" : "Cheap"; ``` ### 4. 動態字串的範本文字 使用模板文字在字串中嵌入表達式。 ``` let item = "coffee"; let price = 15; console.log(`One ${item} costs $${price}.`); ``` ### 5. 解構賦值 輕鬆從物件或陣列中提取屬性。 ``` let [x, y] = [1, 2]; let {name, age} = {name: "Alice", age: 30}; ``` ### 6. 用於陣列和物件克隆的擴展運算符 克隆陣列或物件而不引用原始陣列或物件。 ``` let originalArray = [1, 2, 3]; let clonedArray = [...originalArray]; ``` ### 7. 短路評估 使用邏輯運算子進行條件執行。 ``` let isValid = true; isValid && console.log("Valid!"); ``` ### 8. 可選連結 (?.) 如果引用為`nullish`則可以安全地存取巢狀物件屬性,而不會出現錯誤。 ``` let user = {name: "John", address: {city: "New York"}}; console.log(user?.address?.city); // "New York" ``` ### 9. 空合併運算子 (??) 使用`??`為`null`或`undefined`提供預設值。 ``` let username = null; console.log(username ?? "Guest"); // "Guest" ``` [![monday.com 的互動式橫幅展示了在數位介面上組織工作流程任務的雙手,並帶有「告訴我如何做」號召性用語按鈕。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xd7qdmxdd726kskelydk.png)](https://try.monday.com/9xf9ftaa4i2f) ### 10. 使用`map` 、 `filter`和`reduce`進行陣列操作 無需傳統循環即可處理陣列的優雅方法。 ``` // Map let numbers = [1, 2, 3, 4]; let doubled = numbers.map(x => x * 2); // Filter const evens = numbers.filter(x => x % 2 === 0); // Reduce const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); ``` ### 11.標記模板文字 使用模板文字進行函數呼叫以進行自訂字串處理。 ``` function highlight(strings, ...values) { return strings.reduce((prev, current, i) => `${prev}${current}${values[i] || ''}`, ''); } let price = 10; console.log(highlight`The price is ${price} dollars.`); ``` ### 12.使用Object.entries()和Object.fromEntries() 將物件轉換為陣列並返回以方便操作。 ``` let person = {name: "Alice", age: 25}; let entries = Object.entries(person); let newPerson = Object.fromEntries(entries); ``` ### 13. 唯一元素的集合物件 使用 Set 儲存任何類型的唯一值。 ``` let numbers = [1, 1, 2, 3, 4, 4]; let uniqueNumbers = [...new Set(numbers)]; ``` ### 14. 物件中的動態屬性名稱 在物件文字表示法中使用方括號來建立動態屬性名稱。 ``` let dynamicKey = 'name'; let person = {[dynamicKey]: "Alice"}; ``` ### 15. 使用bind()進行函數柯里化 建立一個新函數,在呼叫時將其 this 關鍵字設定為提供的值。 ``` function multiply(a, b) { return a * b; } let double = multiply.bind(null, 2); console.log(double(5)); // 10 ``` ### 16. 使用 Array.from() 從類別陣列物件建立陣列 將類似陣列或可迭代的物件轉換為真正的陣列。 ``` let nodeList = document.querySelectorAll('div'); let nodeArray = Array.from(nodeList); ``` ### 17. 可迭代物件的 for...of 循環 直接迭代可迭代物件(包括陣列、映射、集合等)。 ``` for (let value of ['a', 'b', 'c']) { console.log(value); } ``` ### 18. 使用 Promise.all() 實作並發 Promise 同時執行多個 Promise 並等待所有的都解決。 ``` let promises = [fetch(url1), fetch(url2)]; Promise.all(promises) .then(responses => console.log('All done')); ``` ### 19. 函數參數的剩餘參數 將任意數量的參數捕獲到陣列中。 ``` function sum(...nums) { return nums.reduce((acc, current) => acc + current, 0); } ``` [![Coursera Plus 訂閱產品包括 AWS 基礎、Google IT 支援專業憑證和商業網路安全專業化。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ys5gk2tfzcn4iniuq48s.png)](https://imp.i384100.net/c/3922519/1320997/14726) ### 20. 用於性能優化的記憶 儲存函數結果以避免冗餘處理。 ``` const memoize = (fn) => { const cache = {}; return (...args) => { let n = args[0]; // assuming single argument for simplicity if (n in cache) { console.log('Fetching from cache'); return cache[n]; } else { console.log('Calculating result'); let result = fn(n); cache[n] = result; return result; } }; }; ``` ### 21. 使用 ^ 交換值 使用 XOR 以位元運算子交換兩個變數的值,無需使用臨時變數。 ``` let a = 1, b = 2; a ^= b; b ^= a; a ^= b; // a = 2, b = 1 ``` ### 22. 使用 flat() 展平陣列 使用`flat()`方法輕鬆展平巢狀陣列,並將展平深度作為可選參數。 ``` let nestedArray = [1, [2, [3, [4]]]]; let flatArray = nestedArray.flat(Infinity); ``` ### 23. 用一元加法轉換為數字 使用一元加運算子快速將字串或其他值轉換為數字。 ``` let str = "123"; let num = +str; // 123 as a number ``` ### 24. HTML 片段的模板字串 使用模板字串建立 HTML 片段,使動態 HTML 生成更加清晰。 ``` let items = ['apple', 'orange', 'banana']; let html = `<ul>${items.map(item => `<li>${item}</li>`).join('')}</ul>`; ``` ### 25. 使用 Object.assign() 合併物件 將多個來源物件合併到一個目標物件中,有效地組合它們的屬性。 ``` let obj1 = { a: 1 }, obj2 = { b: 2 }; let merged = Object.assign({}, obj1, obj2); ``` [![符合人體工學的垂直滑鼠旨在減輕手腕壓力](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56c0z4lzxtkthnnk8gsb.jpg)](https://amzn.to/3UXGIyx) 使用符合人體工學的滑鼠優化您的編程設置,專為舒適和長時間的編碼會話而定制。 ### 26. 預設值短路 處理潛在的未定義或空變數時,使用邏輯運算子指派預設值。 ``` let options = userOptions || defaultOptions; ``` ### 27. 使用括號表示法動態存取物件屬性 使用括號表示法動態存取物件的屬性,當屬性名稱儲存在變數中時非常有用。 ``` let property = "name"; let value = person[property]; // Equivalent to person.name ``` ### 28. 使用 Array.includes() 進行存在檢查 使用includes() 檢查陣列是否包含某個值,它是indexOf 的更清晰的替代方法。 ``` if (myArray.includes("value")) { // Do something } ``` ### 29. Function.prototype.bind() 的強大功能 將函數綁定到上下文(此值)並部分套用參數,以建立更多可重複使用和模組化的程式碼。 ``` const greet = function(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; }; const greetJohn = greet.bind({name: 'John'}, 'Hello'); console.log(greetJohn('!')); // "Hello, John!" ``` ### 30.防止物件修改 使用`Object.freeze()`防止物件進行修改,使其不可變。對於更深層的不變性,請考慮更徹底地強制不變性的函式庫。 ``` let obj = { name: "Immutable" }; Object.freeze(obj); obj.name = "Mutable"; // Fails silently in non-strict mode ``` 我希望這些 JavaScript 技巧為您提供如何進行[JavaScript 程式設計的](https://www.w3schools.com/js/)新視角。 從利用模板文字的簡潔功能到掌握`map` 、 `filter`和`reduce`的效率,這些 JavaScript 技巧將豐富您的開發工作流程並激發您的下一個專案。 讓這些 JavaScript 技巧不僅可以完善您目前的專案,還可以在您的[程式設計之旅](https://www.webdevstory.com/programming-roadmap/)中激發未來創新的靈感。 ***支持我們的技術見解*** [![請我喝杯咖啡](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/30-javascript-tricky-hacks-gfc

在 React 中編寫乾淨、可重複使用的元件(最佳實踐)

🔑 關鍵概念 ------ 什麼是可重複使用的 React 元件?您可以將它們視為建置塊。 它們是獨立的程式碼片段,可以在整個網站中重複使用,以節省您的時間和精力。 它們可以是從簡單按鈕到複雜表單的任何內容。 **為什麼要使用可重複使用的元件?** 隨著您的網站的發展,您可以透過組合現有元件輕鬆加入新功能。這使您的程式碼更具可擴展性和適應性。 您可以**在未來的專案中使用可重複使用的程式碼,**而無需從頭開始重新編寫。 --- 🧩 如何寫出乾淨、可重複使用的 React 元件 ------------------------ 在編寫乾淨的可重複使用 React 元件時,需要記住以下*兩個最重要的*事情: ![React 可重複使用元件的圖像](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yuprsf67nntkgexmo92w.jpg) **1. 🩼避免副作用。**不要將與外部資料互動的邏輯(例如進行 API 呼叫)直接放入可重複使用元件中。相反,將此邏輯作為`props`傳遞給元件。 例如,如果一個按鈕不僅僅是看起來漂亮,例如從網路獲取資料,**那麼它可能無法重複使用。** 在您掌握透過最佳實踐傳遞 props 的概念之前,我不會向您展示這方面的範例。繼續閱讀。 這是一個可重複使用的按鈕元件。但它缺乏最佳實踐。我將在範例部分向您展示原因。 ``` // This is a reusable button component (bad practice!) const Button = () => { return ( <button> Click Me </button> ); } ``` ‍ **2. 🗃️使用 props。** Props 是傳遞給元件以自訂其行為和外觀的參數。這允許您將相同的元件用於不同的目的。 ``` // This is a button component that can change its color const Button = ({ color }) => { return ( <button style={{ backgroundColor: color }}> Click Here </button> ); } ``` 這仍然是一個不好的做法,因為您有一個名為「點擊這裡」的固定標籤。如果您想更改按鈕上的文本,例如“註冊”,那麼您必須返回按鈕元件並進行更改。 這意味著每次我們想要使用不同的文字時,我們都必須返回並編輯程式碼。換句話說,它不再可重複使用。 **💪 挑戰:**那麼解決方案是什麼? 你已經有了答案。但如果您不這樣做,我將在範例部分向您展示。 **🌴提示:**考慮一下您可能希望如何在不同情況下使用該元件,並將其設計得靈活且適應性強。 🍃可重複使用 React 元件的範例 ------------------ 以下是可重複使用 React 元件的一些常見範例,以及一些幫助您入門的程式碼範例: **1. 按鈕:**具有不同樣式和功能的基本按鈕。 ``` // Button component import React from "react"; const Button = ({ color, label, onClick }) => { return ( <button className={`padding-2 shadow-none hover:shadow background-light-${color} hover:background-dark-${color}`} onClick={onClick} > {label} </button> ); }; export default Button; // Using the Button component <Button color="blue" label="Click Here" onClick={() => console.log("Button clicked!")} /> ``` 正如你所看到的,我沒有在按鈕元件中寫“Click Here”。我想讓我的按鈕可重複使用,因此它不知道有關自訂樣式或文字的任何資訊。 因此,我將它們作為 props(例如,顏色、標籤、onClick)傳遞,以便將來更改它們,而無需觸摸原始按鈕元件。希望這能說清楚。 > **🪜解決方案:**您需要將每個功能作為可重複使用元件中的`props`傳遞 - 就是這樣。 **2. 導覽列:**在整個網站上提供一致導覽的導覽列。 ``` // Navbar component import React from "react"; const Navbar = ({ isLoggedIn }) => { return ( <div className="navbar"> <div className="navbar-container"> <div className="navbar-logo"> <img src={logo} alt="logo" /> </div> <div className="navbar-links"> <a href="/">Home</a> <a href="/about">About</a> <a href="/contact">Contact</a> {isLoggedIn ? ( <a href="/profile">Profile</a> ) : ( <a href="/login">Login</a> )} </div> </div> </div> ); }; export default Navbar; // Using the Navbar component <Navbar isLoggedIn={true} /> ``` 如您所見,我通過了`<Navbar isLoggedIn={true} />` 此行利用`Navbar`元件並傳遞值為 true 的`isLoggedIn`屬性,表示使用者已登入。這將顯示「個人資料」連結並隱藏「登入」連結。 與按鈕元件類似,導覽列元件是可重複使用的,並接受 props 來自訂其行為。完美的! **3. 為什麼在按鈕元件中呼叫 API 是不好的做法** 現在,您已經了解了 React 中可重複使用元件的所有內容。 讓我們透過解決一個複雜的問題來更深入地研究。 考慮這樣的場景,您有一個執行 API 呼叫的按鈕。按鈕元件的程式碼可以如下: ``` import React from "react"; import doAPICall from "../api" const SaveButton = () => { return ( <button onClick={() => { doAPICall(); }} > Save </button> ); } export default SaveButton ``` 很明顯,您不能在多個地方重複使用上述按鈕,因為該按鈕元件內部包含副作用( `doAPICall()` )。 為了使該元件可重複使用,首先,您必須提取副作用並將其作為 prop 傳遞給按鈕元件,如下所示: ``` const App = () => { function doAPICall() { // Does an API call to save the current state of the app. } return ( <div> <SaveButton onClick={doAPICall}/> </div> ) } ``` 按鈕元件將如下所示: ``` const SaveButton = ({ onClick }) => { return ( <button onClick={onClick} > Save </button> ); } ``` 正如您所看到的,現在可以在您想要透過點擊按鈕儲存資料的所有位置重複使用上面的按鈕。該按鈕現在可以在多個地方像這樣使用: ``` const App = () => { function saveUser() { // Does an API call to save the user. } function saveProject() { // Does an API call to save the project. } return ( <div> <SaveButton onClick={saveUser}/> <SaveButton onClick={saveProject}/> </div> ) } ``` 您也可以透過使用 prop 來控制標籤,使按鈕元件更具可重複使用性,如下所示: ``` const App = () => { function saveUser() { // Does an API call to save the user. } function saveProject() { // Does an API call to save the project. } return ( <div> <SaveButton onClick={saveUser} label="Save user" /> <SaveButton onClick={saveProject} label="Save project" /> </div> ) } ``` 按鈕元件將如下所示: ``` const SaveButton = ({ onClick, label }) => { return ( <button onClick={onClick} > {label} </button> ); } ``` **🫧 推薦:🤖🦾 [Figma AI:](https://psxid.figma.com/8g4g75niz3sg-7v0ro9)** 如果您需要快速設計簡報或網站,Figma AI 可以提供現成的範本。並且它可以與[Figma](https://psxid.figma.com/pq1612tcamkx)順利配合。只要告訴它你想要什麼,畫一點,然後繁榮——你的想法就變成現實了! ![Figma AI 影像](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cj5cxhykwmfnmve77co.png) 您可以嘗試[Figma AI](https://psxid.figma.com/8g4g75niz3sg-7v0ro9) ,目前它對所有人免費。 --- 👏結論 --- 恭喜!您已經成功學習如何使用最佳實踐建立乾淨的可重複使用 React 元件。 請記住,**可重複使用元件是健壯的 React 開發的建置塊**。透過練習可重複使用元件,您可以建立更乾淨、更有效率、更易於維護的 React 應用程式。 您練習得越多,您就越能更好地辨識在專案中使用它們的機會! 如果您喜歡這篇文章,您可能也會喜歡我的[𝕏](https://twitter.com/shahancd)帳戶,以了解更多有關前端開發的課程。 **閱讀更多:**[前端開發的未來](https://dev.to/codewithshahan/the-future-of-frontend-development-1amd) --- 原文出處:https://dev.to/codewithshahan/writing-clean-reusable-components-in-react-best-practices-2gka

技能問題:如何以 10 倍的速度掌握任何事物,同時又不顯得愚蠢

是的,初學者的挫折感是一種常見的情緒,而且很自然。不要因為這種傾向而責怪,因為我們自己都可能經歷過這種情況。不要撒謊,您可能在某個領域中發現初學者很煩人,因為您擁有更高水平的專業知識。 如果你沒有,那麼,做你自己就很糟糕。 然而,必須認識到,應對初學者的挑戰不僅是學習旅程的一部分,也是個人成長的重要面向。在本文中,我們深入研究了初學者旅程中伴隨的各種挫折,並探討了為什麼承認和解決這些挫折對於任何努力提高技能和知識的人來說都是至關重要的。 因此,讓我們探索初學者階段的複雜性,並發現有價值的見解,以推動您在學習努力中前進: **1. 缺乏知識:** 作為初學者通常意味著缺乏基礎知識,讓我們面對現實吧,這對您和您想要向其學習的人來說可能會令人沮喪。當你剛開始時,你可能有遠大的抱負,但將它們分解為可操作的步驟感覺就像試圖蒙住眼睛解開魔術方塊。 初學者並沒有意識到自己的無能,這常常會導致他們尷尬地提出看似愚蠢的問題——我曾經經歷過,這樣做過,現在仍然偶爾這樣做。 *舉個例子*:幾年前,我竟然厚顏無恥地詢問谷歌員工是否可以給比爾蓋茲發一封電子郵件來解決我的 Windows 7 啟動金鑰問題。我感到很羞愧,但這也提醒我們,即使是職業選手也曾經是一無所知的新手。 你需要提出問題來獲取知識,即使它們可能很愚蠢。 感謝像 chatGPT 這樣的工具,與以前不同的是,你現在可以安全地提出愚蠢的問題,唯一會嘲笑你的人就是 openai 的工程師。 ***從人們那裡獲取知識而不顯得愚蠢*** 現在,有些東西你只能從人們那裡學習/獲得,並且你可能需要為了這類事情而接近他們(但僅限於這類事情)。當向他人尋求幫助時,一切都取決於給予和索取。人們更有可能幫助解決棘手的問題,從而帶來某種互惠互利。所以,在你向某人尋求建議之前,想想這對他們有什麼好處? 如果你的問題對他們沒有幫助或沒有興趣,你應該問chatGPT,因為chatGPT 沒有更好的事情要做。 ***害怕顯得愚蠢*** 這是完全正常的。但事情是這樣的——承認自己的無知是邁向智慧的第一步。畢竟,每個人都是從新手開始的,暴露我們專業知識的缺乏只是成為真正專家的旅程的一部分。 你是否因為擔心別人認為你很蠢而害怕與人接觸? Lmao,如果你回到根本上,你害怕看起來很愚蠢的原因只是因為你很愚蠢——就這個話題而言。 遲早,你在該領域的技能不足將會暴露出來。所以無論如何也無所謂,最好還是仔細揭開,快速獲得知識。 **2、不耐煩:** 正如他們所說,耐心是一種美德,但讓我們面對現實吧——這是一顆難以吞嚥的藥丸,尤其是當你深入學習新事物的時候。無論是掌握一項技能、達到財務里程碑,還是等待您最喜歡的系列的下一部,不耐煩常常會出現。 想想看:您是否願意等待我的下一篇文章(知道它將於下週一下午 3:00 準時發布),還是因為“下個月的某個時候”的模糊承諾而懸而未決?確切地。擁有明確的時間表可以讓你更容易保持耐心。 在追逐那個難以捉摸的夢想時,你能喚起聖人的耐心嗎?這是一個艱難的問題,尤其是當成功之路充滿不確定性時。你看,你無法計算出達到那個里程碑的確切時刻,因為你還沒有走過那條路。這是一個第二十二條軍規:你需要知識來實現目標,但在實現目標之前你不會真正掌握這些知識。令人困惑,對吧? 例如,你不知道你需要多長時間才能賺到第一個十億,因為你沒有所需的知識,而當你擁有所需的知識時,你應該已經賺到了十億,因為這是了解您是否已獲得有關如何賺取第一個十億美元的足夠知識的唯一方法。 如果你認為你知道如何賺到第一個十億,但你還沒有做到,那麼你告訴自己為什麼*你沒有*做到的任何故事,都代表著一個知識空白,你需要填補這個空白,以更接近所需的真正知識。賺十億。 你和你的目標之間的每一個障礙都是一個等待學習的教訓。這都是旅程的一部分,我的朋友。擁抱不確定性,享受挑戰,並記住-成功之路不是一條直線;而是一條直線。這是一條蜿蜒的道路,充滿了曲折、轉彎,偶爾會走彎路。 作為一個初學者,你會感到焦慮,因為你似乎無法填補你現在的位置和你想要的位置之間的差距;因為你沒有所需的知識。 你獲得的知識越多,前方的道路就會變得更清晰、更光明。越接近隧道的盡頭,路就越清晰,盡頭的光會照亮前路,雖然沒有確切的“隧道盡頭”,但學習是一生的旅程(至少,是這樣)本來就是這樣的)。 因此,與其專注於目的地,不如專注於旅程本身。旅程就是目的地。享受過程,享受挑戰,享受一路上的小勝利。畢竟,正是不確定性才讓這段旅程變得值得。如果一定能成功,那還有什麼樂趣呢? 正是不確定性使得耐心變得困難,如果在特定時間範圍內保證成功,許多懶惰的人就會開始投入工作。 當你開始看到曙光時,你就已經到了意識到自己無能的階段。 現在您可以清楚地看到自己的限制以及您想要獲得的技能的困難。這是覺醒的階段。 這是29%的人開始懈怠、70%的人放棄的階段。 但如果你已經讀到這裡,我就知道那不是你。 **3.害怕失敗:** 把事情做好是最好的學習方式,但一開始你就不可能不失敗就完成任何事。 *失敗是知道下一步該學什麼的唯一方法。* 很多人只是堅持低階學習;在大腦中記憶和儲存事實和數字而不付諸實踐,認為自己知道很多,這可能會導致鄧寧克魯格效應。 對於那些不知道的人來說,鄧寧-克魯格效應是一種認知偏差,它會導致人們高估自己的能力,從而導致錯誤的自信感。換句話說,他們認為自己知道一切,而實際上,他們只觸及了表面。 但事實是:真正學習的唯一方法就是捲起袖子、親力親為。知識只有付諸實踐才有價值。否則,這些只是無用的瑣事擾亂你的大腦。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhbdwzqqg19yiepp483l.jpeg) *你想要堆疊知識還是想要堆疊結果?* 在我的環境中流行一句話:「沒有知識就是浪費」。我不相信這是真的。如果你不使用知識,知識絕對會浪費。 這句話一定起源於人們過去所知道的一些隨機的事情拯救了他們時所獲得的多巴胺。 嗯,是的,隨機知識有時會有所幫助。但是,您是否願意繼續累積知識並希望它派上用場,並祈禱您所擁有的知識會以某種方式幸運地使您成功,或者您是否會採取積極的步驟來確保您掌控自己的成功。你會驚訝地發現有多少人仍然不選擇後者。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rczg3q28hj0ts7weo1lv.jpg) 人生不是一場學校考試,透過反省事實和數字就能為你贏得**A** ,如果你學到了一些你沒有應用的東西,你就是在浪費時間。人生是一場親力親為、充滿動感的冒險,成功只屬於那些願意捲起袖子、親力親為的人。 你需要做更多,而不是知道更多。 不要挑選一本新書,而是按照你在上一本中讀過的內容去做。閱讀本文將為您提供訊息,但只有將這些訊息付諸實踐才能帶來轉變。 如果你想以十倍的速度學習,並在幾個月內實現別人需要幾年才能實現的目標,*那麼你需要學習如何學習。* 不要毫無目的地學習,而是這樣做: - 了解基礎知識 - 概述一個專案來解決您遇到的問題,或其他人正在為初學者解決的基本問題 - 開始建立專案 - 一路學習 **4. 過於急切:** 這就像在你還沒學會爬行之前就試圖衝刺一樣。一頭扎進新事物可能會令人興奮,但不要超前。 當初學者太急於證明自己時,他們常常會為了急於引起轟動而忽略重要的說明並跳過關鍵的基礎知識。這就像在不先打地基的情況下試圖建造一座房子一樣——你注定會失敗。 問題的根源是什麼?***感性太多,理性思考不夠***。充滿熱情固然很好,但當你的情緒影響了你的判斷時,你就會走向災難。不要幻想所有的可能性,醒來並注意現實的冷酷事實。相信我,它並不那麼迷人,但它是完成真正工作的地方。 所以,事情是這樣的:努力保持謙虛和冷靜。把你的牌放在胸前-不要讓你的情緒出賣你。無論有人給你 1 美元還是 100 萬美元,都要保持撲克臉。表達自己的情緒可能有助於人際關係,但在生意上呢?沒那麼多。 當人們在人際關係中充分理解你時,溝通就會變得更容易。當人們在商業上更了解你時,操縱就會變得更容易 在商業中,情緒可以發揮複雜的作用。雖然理解和同理心在人際關係中很受重視,但在商業世界中,它們有時可能被視為可利用的漏洞。如果您顯得絕望,如果對方利用這種情況(也許是提高價格),請不要感到驚訝。在商業交易中保持專業和冷靜以避免被操縱至關重要,同時仍然認識到真正的聯繫和同理心的重要性,儘管是在不同的背景下。 控制住你的情緒,你就會非常有效地學習,而不會顯得愚蠢。 **5、不一致:** 大多數人都是懶惰的,只在自己想做的時候做自己想做的事。 > 我有這條法律;*如果你花在不做某件事上的時間比持續做某件事所花的時間多,那麼你就是在倒退並浪費你過去付出的努力。* 對我來說,速度是獲勝的最重要因素,你能以多快的速度完成這項高品質的工作?這才是重要的。 很多人說“聰明的工作勝過艱苦的工作”,我認為這並不重要,“快速的工作永遠會獲勝”(記住,我先說的) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/er4xf5iiz4zb6wcpoqbu.png) 聰明的工作可以打敗艱苦的工作的唯一原因是它更快。找到一種快速完成某件事的方法是明智之舉。速度更重要。當你不一致時,你就會失去動力。 如果某人持續努力,而你慢慢變得聰明,他/她會因為速度更快而獲得更多結果。 力求努力、聰明、快速地工作。 **6. 太害羞不敢成功:** 許多人喜歡用可笑的藉口說他們是一個內向的人,並且由於他們的“天性”而無法將自己放在那裡尋找機會。 那是廢話,社交是一種技能,而不是個性,你需要發展你的社交技能,讓你的創造力變得有價值。擁有創造性技能並不足以取得成功,您需要補充技能才能充分利用它;說服的藝術。 從技術上來說,你可能在某件事上擁有最好的技能,但如果人們不了解你,那就毫無用處。你擁有與你的臉一樣獨特的價值,如果你不能讓人們注意到它,你就無法將這種價值賦予任何人。 有時,您可能會看到那些技能不如您的人享受您沒有的機會,只是因為他們建立了人際網絡,而不總是因為這對他們來說更容易。 在過去,你要成功,顯然仍然需要人脈,但為了獲得這些人脈,你需要努力學習,進入最好的學校,結識最好的人,你需要錢去長途旅行才能見到最好的人。名人和成功人士可能會或可能不會聽你的,所以如果你真的想成功,你必須走出你的舒適區,發揮你的魅力,讓自己看起來很體面。如今,借助網路,您可以向任何名人發送私訊(有些人仍然不知道這的意義,不幸的是,這幾乎就像他們在睡覺一樣)。 ***利用網路。*** 從一開始就推銷自己,不要害怕表現得像個初學者,也不要害怕認為有些人可能會認為你很愚蠢或基礎。讓我給你看一些東西: 看看這個 Twitter/𝕏 帳號。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uklxf1g6cfznt1ph1vqu.jpg) 它實際上只是發布“這一年已完成 x%”,沒有其他內容,並且它有 130 萬粉絲。我目前在…稍後在 Twitter 上關注我🥺。 > 皇室教訓:我們就像網路上超過 5,300,500,000 人一樣,無論你是否基礎並不重要,仍然有人會喜歡你所做的事情,無論如何你都有義務接觸他們什麼。 大多數討厭你的基本內容的人可能會嫉妒他們有相同的想法或知識,但沒有足夠的勇氣將其表達出來。 不要等到成為大師才開始表現自己。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4asnioh5gafj8v0idvi.jpeg) *山頂孤獨,戰壕裡擁擠。* 人們更喜歡能夠產生共鳴,你的地位越低,越多的人能夠產生共鳴並欣賞你的成長。當你達到前 1% 時,就會變得更加孤獨,也更難找到可以交往的人。 許多人都在尋求他們能夠實現目標、能夠變得偉大的保證:「如果這個人能夠在面臨挑戰的情況下表現出這一點,我當然也可以!先讓我了解他們是如何做到的」。 所以本質上我的觀點是,大師非常有價值,但你可以輕鬆地向更接近你水平的人學習,也許領先 1-10 步,越接近越容易。 例如,我從伊隆馬斯克那裡學到了很多東西,但我從我網絡中目前的導師那裡學到了更多,因為他們是「平易近人的」。 此人可能是該領域的專家,但如果無法聯繫到他/她,他/她就無法為您提供針對您的具體情況的個人化建議。 您可能正在聽一些優秀的 YouTuber 的講話,這個人可能會給出準確的建議,但您的情況可能是這些事實的例外,可能存在一些細微差別,導致這些事情不起作用。 然後你可能最終會得出結論,這個人所說的是無稽之談(儘管我並不是說每個人都總是正確的)。 處於金字塔底部的你更容易接近。利用這一點來發揮你的優勢。 我認為名人面臨的問題之一是,他們通常會受到很多人試圖「接近」他們的轟炸,他們沒有足夠的時間或精力去照顧每個人。但你做到了,這是你擁有伊隆馬斯克所沒有的東西,利用它來攀登金字塔。 > 透過你的可及性為人們提供價值,從而成為難以接近的人;你所給予的價值將變得非常有價值,以至於初學者或金字塔底層的人很難獲得它。 您可以展示您的專業知識,並為近距離和個人聯繫收取額外費用。 我希望這對你有幫助,我很讚揚,我幫助人們從他們的創造力中賺錢,[訂閱我的時事通訊](https://tbk.beehiiv.com)(商業造王者)以獲得關於如何從你的創造力中賺錢印鈔系統的更多新鮮觀點。我為人們創造更多機會透過我的專案等賺取更多收入,我的時事通訊是與我聯繫並了解更多有關我的專案的好方法。 請務必將這篇文章轉發並分享給困擾您的煩人的初學者。和平✌️ --- 原文出處:https://dev.to/drpraze/skill-issue-how-to-master-anything-10x-faster-without-looking-stupid-2hj4

您需要了解的有關軟體要求的訊息

想像一下,您的客戶要求您建立一個 Web 應用程式,以滿足其客戶的所有需求。這就是你的客戶會告訴你的全部內容,你能建造它嗎?很明顯,您錯過了更多訊息,並且存在要求,但您知道什麼是要求嗎?如果我們沒有明確定義它們,會產生什麼後果?存在哪些類型的需求?本文將嘗試以簡單的方式回答其中一些問題。 概括 -- - [什麼是要求?](#what-is-a-requirement) - [為什麼它們很重要以及需求定義不明確的後果](#why-they-matters-and-consequence-of-poorly-defined-requirements) - [我們怎樣才能有一個好的需求](#how-we-can-have-a-good-requirement) - [要求等級](#levels-of-requirements) ``` - [User requirements](#user-requirements) ``` ``` - [System requirements](#system-requirements) ``` - [要求類型](#types-of-requirements) ``` - [Functional requirements](#functional-requirements) ``` ``` - [Non-functional requirements](#non-functional-requirements) ``` - [結論](#conclusion) 什麼是要求? ------ 首先,我們需要了解軟體開發環境中的需求是什麼。 Shari Pleeger 給我們下了一個很好的定義: > 系統特徵或系統為實現其目標而能夠完成的事情的描述 換句話說,要求是辨識產品或流程操作、功能或設計約束或特徵的聲明,該聲明是**明確的**、**可**測試的、**可測量**的,並且是接受產品或流程所必需的(由消費者或內部品質保證指南) 。 每個需求都是基於現實中存在的問題而建立的,而軟體只是解決它的一種方法。 為什麼它們很重要以及需求定義不明確的後果 -------------------- 現在我們知道什麼是要求,我們必須問: > “為什麼對於從事 IT 工作的人來說了解它如此重要?” 我會給你一些原因。根據要求,我們能夠: - 為專案規劃提供依據 - 對於研究變更請求至關重要 - 允許從開發的最早階段進行風險管理 - 它們是驗收測試的基礎 - 合約管理 ![圖片1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x13z95rmek1u9g1bhnu4.png) 如果我們的需求定義不明確會發生什麼事? 第一個也是合乎邏輯的後果是專案交付的延遲。當我們開始一個專案或衝刺時,沒有對要開發的內容有所有明確的要求,存在錯誤的可能性是巨大的!因此,開發人員和 QA 團隊需要承擔更多工作,從而降低了他們的生活品質。 如果出現在Production中,就會有許多不滿意的用戶,讓產品的信心降低,甚至導致系統報廢。另一個後果是維護系統的成本。 我們怎樣才能有一個好的需求 ------------- 需求的編寫方式因團隊而異,但它們都應該具有以下三個特徵: - 明確 - 可測試 - 可測量的 要求等級 ---- ### 用戶要求 使用者需求定義了我們的軟體需要做什麼。它可以透過使用者的需求、期望、限制和介面來描述使用者面臨的問題。 此類需求是為客戶編寫的,我們使用自然語言加圖表。我們可以使用待辦事項清單應用程式的使用者需求範例: > 使用者應該能夠在清單中插入新任務 ### 系統需求 系統需求是定義系統有效運作所需的硬體、軟體和網路元件的功能和約束的規格。 這些要求對於設計、實施和維護系統至關重要。他們負責描述必須做什麼,而不是如何做。 使用我們已經使用過的相同要求,我們可以詳細了解它: |用戶需求|系統需求 | | -------------------------------------------------- ------ | -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------------------------------ | |使用者應該能夠在清單中插入新任務 | - 使用者應該能夠點擊插入按鈕以新增任務 \- 當使用者點擊按鈕時,任務新增應出現在清單中 插入 \- 只有登入使用者才能存取任務清單| 要求類型 ---- ### 功能要求 功能需求描述了一個系統應該執行的操作。它們描述了系統應該提供什麼、系統應該如何對特定輸入做出反應以及系統在特定情況下應該如何表現。他們還可以描述系統不應該做什麼。 功能需求可以描述使用者或系統需求。 ### 非功能性需求 非功能性需求描述系統或環境應提供的服務或功能。他們負責定義系統的架構。我們對非功能性需求有不同的類別,它們是: 1. **表現:** ``` - _Response Time:_ Specifies the maximum acceptable time for the system to respond to user input. ``` ``` - _Throughput:_ Defines the number of transactions or operations the system can handle in a given time. ``` 2. **可用性:** ``` - _User Interface (UI) and User Experience (UX):_ Specifies criteria related to the design, ease of use, and overall user experience. ``` ``` - _Accessibility:_ Ensures that the system is usable by individuals with disabilities. ``` 3. **可靠性:** ``` - _Availability:_ Specifies the percentage of time the system should be operational. ``` ``` - _Fault Tolerance:_ Defines the system's ability to continue functioning in the presence of faults or errors. ``` 4. **安全:** ``` - _Authentication:_ Describes how users are identified and verified. ``` ``` - _Authorization:_ Specifies the level of access granted to different users or roles. ``` ``` - _Data Encryption:_ Requires the use of encryption to protect sensitive data. ``` 5. **可擴充性:** ``` - _Horizontal Scalability:_ Describes how well the system can handle an increase in load by adding more hardware. ``` ``` - _Vertical Scalability:_ Describes how well the system can handle an increase in load by increasing the capacity of existing hardware. ``` 6. **相容性:** ``` - _Hardware Compatibility:_ Ensures the system can run on specified hardware configurations. ``` ``` - _Software Compatibility:_ Ensures the system can work with specified software components. ``` 7. **可維護性:** ``` - _Modifiability:_ Describes the ease with which the system can be modified or updated. ``` ``` - _Testability:_ Specifies the ease with which the system can be tested to ensure its correctness. ``` 8. **可移植性:** ``` - _Platform Independence:_ Describes the ability of the system to run on different operating systems. ``` ``` - _Data Portability:_ Ensures that data can be easily transferred between different systems. ``` 9. **文件:** ``` - _User Documentation:_ Specifies the requirements for user manuals and guides. ``` ``` - _Technical Documentation:_ Describes the requirements for system architecture, API documentation, etc. ``` 10. **監理與合規:** ``` - _Legal Requirements:_ Ensures that the system complies with relevant laws and regulations. ``` ``` - _Industry Standards:_ Specifies adherence to industry-specific standards. ``` 結論 -- 正如我們所看到的,軟體需求對我們來說非常重要,並且有很多不同類型的需求,但最重要的是確保在開始開發之前,我們已經很好地定義了它們。 我希望這些內容對您有用。 如果您有任何疑問,請隨時與我聯繫! 親吻💅🏼 --- 原文出處:https://dev.to/m4rri4nne/what-you-need-to-know-about-software-requirements-2hc0

為什麼事件溯源是微服務通訊的反模式

--- 標題:為什麼事件溯源是一種微服務通訊反模式 發表:真實 描述:這篇文章描述了合理的事件溯源用例,並提供了微服務之間通訊的替代方案。 封面圖片:https://github.com/OLibutzki/blog/raw/master/water-4230596\_1920.jpg 標籤: 事件溯源, 領域驅動設計, 領域事件 --- 一般而言,事件驅動架構,特別是事件溯源,在過去幾年中獲得了廣泛關注。這種趨勢是由於我們努力建構具有彈性和可擴展性的模組化系統而引起的。*微服務*是在這種情況下經常使用的術語。在我看來,微服務只是實現有界脈絡的一種方法。模組化系統的核心是模組的邊界,而如何辨識這些邊界最有前途的想法是 Eric Evans 的領域驅動設計中引入的[戰略設計](https://en.wikipedia.org/wiki/Domain-driven_design#Strategic_domain-driven_design)。它可以幫助您辨識/發現模組及其邊界([有界上下文](https://martinfowler.com/bliki/BoundedContext.html)),並描述這些有界上下文相互關聯的方式(上下文映射)。 *注意:我會預設某些術語的一些預知,因為我不想第一千次解釋它們。我決定連結到[microservices.io](https://microservices.io/) 、[維基百科](https://en.wikipedia.org)或[Martin Fowler 的 Bliki](https://martinfowler.com/bliki/)的解釋,因此,如果您對自己的知識感到不舒服,則可以更深入地研究某個主題。* 領域事件作為通用語言的核心 ============= 儘管在 Eric 的書中沒有明確提及,但領域事件很好地促進了 DDD 概念的發展。像 Alberto Brandolini 的[事件風暴](https://en.wikipedia.org/wiki/Event_storming)這樣的技術將事件的焦點從技術層面轉移到組織/業務層面。我們不討論一些 UI 層事件,例如*ButtonClickedEvent* ,而是討論領域事件,它們是業務領域的一部分,並由業務專家說出和理解。這些領域事件是一流的概念,並提供了一種形成所有參與者(領域專家、開發人員…)都同意的[通用語言的](https://martinfowler.com/bliki/UbiquitousLanguage.html)好方法。 用於跨界通訊的領域事件 =========== 領域事件可用於促進有界上下文之間的溝通。假設我們有一家具有三個限界上下文的線上商店:訂單、交貨、發票。 訂單上下文的網域事件是*訂單已接受*。發票和交付上下文對此事件的發生感興趣,因為它會導致一些內部流程啟動。 脫鉤神話 ==== 使用領域事件可以幫助您開發解耦的模組。模組可能會暫時離線。領域事件不關心不可用的模組,它們描述過去發生的事情。這取決於其他模組處理事件的速度。您得到的是一個設計上具有彈性的系統。 除了時間解耦之外,領域事件還具有另一個優勢,至少乍看之下是這樣: Order 上下文不必知道 Invoice 和 Delivery 上下文會偵聽其事件。實際上它甚至不需要知道這些上下文的存在。 這很酷,但具有挑戰性的部分是事件負載。哪些資料放入事件中? 簡單的答案:事件溯源! =========== 事件很有用,所以為什麼不賦予它們盡可能多的權力(和責任)。這就是[事件溯源](https://microservices.io/patterns/data/event-sourcing.html)的基本思想。您不會透過更新資料 (CRUD) 而是透過套用事件流來儲存聚合的狀態。 除了您可以重播事件以重建應用程式狀態之外,事件來源的一個重要功能是您可以免費獲得完整且可靠的審核日誌。因此,當需要這樣的稽核日誌時,在評估持久性策略時一定要考慮事件溯源。 事件溯源只是一種持久性策略嗎? =============== 您可能想知道為什麼我從領域事件直接轉向持久化策略,因為這些概念顯然適用於不同的層/抽象層級。 ……這就是我的觀點:事件溯源是由單一限界上下文做出的本地決策!這些事件不該向外界曝光!其他限界上下文則不知道彼此的持久化策略,因此它們不知道也不關心另一個限界上下文是否使用事件溯源。 如果您在全球範圍內使用事件溯源,則會暴露您的持久層。 您的持久性將成為您的公共 API。每次限界上下文調整其持久性資料時,我們都必須處理公共 API 變更。 我很確定每個人都同意,由於開發和執行時耦合,不同的限界上下文[共享(關係)資料庫中的資料](https://microservices.io/patterns/data/shared-database.html)是一個壞主意。但差別在哪裡呢? 空無一人。我們是否共享事件或資料庫表並不重要。在這兩種情況下,我們都會分享持久性詳細資訊。 有出路 === 我仍然認為領域事件非常適合限界上下文之間的通信,但這些事件不應與用於事件溯源的事件相對應。 我提出的解決方案是合乎邏輯的結果:無論您使用 CRUD 還是事件溯源方法來實現持久性,您都可以將領域事件發佈到全域事件儲存。這些領域事件是有界上下文的公共 API。如果您喜歡在限界上下文中使用事件溯源,則可以將這些事件儲存在本機事件儲存中,該儲存只能從此限界上下文存取 選擇的自由 ===== 在公共 API 中擁有專用領域事件可讓您決定如何對這些事件建模。您不受事件來源事件預先定義的佈局的限制。 對於每次發生的“現實世界事件”,您有兩個選擇: 使用已發布語言的開放主機服務 -------------- 準確發布一個網域事件,其中包含其他限界上下文可能需要的所有資料。在 DDD 術語中,我們將其稱為具有已發布語言的開放主機服務。 ![使用已發布語言的開放主機服務](https://raw.githubusercontent.com/OLibutzki/blog/master/OpenHostService.png) 現實世界事件*Order Accepted*的發生會導致發布一個網域事件*OrderAccepted* 。此事件的有效負載包含 Order 期望其他限界上下文感興趣的所有資料...因此希望 Invoice 和 Delivery 上下文找到它們所需的所有資訊。 客戶/供應商 ------ 發布多個專用領域事件,每個事件使用者一個。您必須與另一方(消費者)討論每個特定的領域事件,而不必定義共享模型。 DDD 將這種關係稱為客戶/供應商。 ![客戶/供應商](https://github.com/OLibutzki/blog/raw/master/CustomerSupplier.png) 現實世界事件*Order Accepted*的發生會導致每個消費限界上下文發布一個網域事件: *InvoiceOrderAccepted*和*DeliveryOrderAccepted* 。每個領域事件都包含消費上下文所請求的資料。 我不想討論這兩種選擇的優缺點。我只是想強調一下,您可以自由選擇領域事件的數量及其有效負載。 這是您不應低估的巨大優勢,因為您可以決定如何發展有界上下文的 API,而不必致力於事件溯源所需的事件。 結論 == 向外界公開持久性細節是一種眾所周知的反模式。在談論持久性時,我們首先想到的是資料庫表,但我解釋了為什麼用於事件來源的事件只是持久性資料的另一種方式。因此,暴露這些事件也是一種反模式。 {% 推特 784691906005635072 %} 如果以適當的(本地)方式使用事件溯源,它會非常強大。乍一看,它似乎是事件驅動架構的靈丹妙藥,但如果您深入研究,您會意識到它可能會引導您進入緊密耦合(分散式)系統……當然,這不是您的目標。 參考 == 除了我的個人經驗之外,我還從不同的摘要和會議演講中獲得了許多靈感。我想重點介紹 Eberhard Wolff 的演講《*基於事件的架構以及使用 Kafka 和 Atom 的實現》* 。特別是[事件溯源](https://youtu.be/Ecg7lvvm8aU?t=1178)和[事件中有什麼?](https://youtu.be/Ecg7lvvm8aU?t=655)與本博文的上下文高度相關。我選擇的線上商店範例是受到這次演講的啟發。 如果您想獲取更多訊息,還可以查閱其他一些資源: - [領域事件與事件溯源,](https://www.innoq.com/en/blog/domain-events-versus-event-sourcing/)作者:Christian Stettler,部落格文章 - [關於事件溯源,他們沒有告訴您的事情](https://medium.com/@hugo.oliveira.rocha/what-they-dont-tell-you-about-event-sourcing-6afc23c69e9a)由 Hugo Rocha 撰寫,部落格文章 - [DDD、CQRS、事件溯源的十年(CQRS/ES 不是頂級架構),](https://youtu.be/LDW0QWie21s?t=1259)作者:Greg Young,會議演講 --- 原文出處:https://dev.to/olibutzki/why-event-sourcing-is-a-microservice-anti-pattern-3mcj

有興趣為開源做出貢獻嗎?這是您需要了解的

您是否想過為開源做出貢獻?如果你在這裡,你可能就這麼做了😄 對於初學者來說,這可能會讓人感到困惑,但我可以理解——我自己也經歷過這樣的情況。然而,您找到了繼續前進的意志力並了解了有關此過程的更多訊息,我希望本文將向您展示它並不像看起來那麼複雜。 大多數接受貢獻的儲存庫通常都有一個您應該留意的**CONTRIBUTING.MD**檔案。由於並非所有存儲庫都是相同的,因此該文件將告訴您有關為該特定存儲庫做出貢獻的過程的更多資訊。 然而,一些通用規則可以應用於大多數儲存庫,我們將在本文中討論它們。 ### 支持我們🙏🏻 ![GitHub 星星](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt38z2e3fdro7h4u82ig.gif) 在我們開始之前,如果您為我們的[開源儲存庫](https://github.com/cyclops-ui/cyclops)加註星標並幫助我們獲得我們的產品,我們將非常高興 在其他開發者面前的工具 ⭐ 在哪裡貢獻? ------ 我想到的第一個問題是:在哪裡做出貢獻? 好吧,您應該從**您已經使用的**專案開始。也許某些庫需要更新,或者某些工具有錯誤? 您可能想為您的**專業領域**內的某些專案或使用您熟悉的**技術堆疊的**專案做出貢獻。 這些都是偉大的競爭者,你應該研究一下他們。 如果您不了解任何專案但仍想做出貢獻,請瀏覽 GitHub 或造訪[Quine](https://quine.sh/contribute)等網站,其中許多開源儲存庫正在尋找貢獻者。 在本文中,我們將使用我們的開源儲存庫 - [**Cyclops**](https://github.com/cyclops-ui/cyclops) 。 如何知道哪些方面需要改進? ------------- 無論您是在尋找事情做,還是已經知道需要修復的錯誤,所有貢獻都從同一個地方開始 -**問題選項卡**。 ![問題](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fs9j1leiduys83giz360.png) 如果您是該專案的新手,您可以尋找大多數儲存庫都有的「 ***goodfirst issues」***標籤。顧名思義,它們是參與專案的一個很好的切入點。所有問題都應該有問題的描述。 如果您知道此處未列出的問題或錯誤,或者您希望看到新功能的引入,請開啟一個新問題!一旦您提出問題,維護人員將決定下一步要做什麼,您應該**在開始編碼之前等待他們的答案**。 **專業提示**:如果您要提出錯誤問題,請務必寫下如何重現該錯誤的步驟! 如何貢獻? ----- 好的,我們找到了一個儲存庫,這是我們想要解決的問題,與維護人員進行了交談,並獲得了解決該問題的綠燈。最後讓我們開始編碼吧! ### 1. 分叉倉庫 第一步是**分叉**儲存庫。這將**複製該專案**並將其新增至您的 GitHub 帳戶。 ![叉](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y2v05gd5fxwe25wmckzu.png) ### 2. 克隆倉庫 現在轉到您的儲存庫並找到分叉的儲存庫。點選**< > 程式碼**按鈕並選擇選項之一 (HTTPS / SSH / GitHub CLI)。 ![複製](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ddnmxdf9vz4kwoelcot.png) **複製框中的內容**。現在打開終端並將自己定位到要在本地儲存專案的位置。一旦您定位好自己,請在終端機中輸入以下命令: ``` git clone <paste the copied content> ``` 片刻之後,您的 PC 上就應該有該專案了! ### 3. 建立新分支 現在,轉到本機資料夾並建立新分支。請務必查看專案的**CONTRIBUTE.md** ,看看維護者是否希望您遵循一些**分支命名**規則! ### 4. 提交並推動您的更改 一旦準備好分支,您就可以開始更改程式碼庫。完成後,提交更改並將其推送到您的分叉存儲庫。如果儲存庫已就位,請務必遵循**提交訊息約定**(檢查**CONTRIBUTING.md** )。 ### 5. 發起拉取請求 現在您已經推送了更改並希望將它們合併到主存儲庫,是時候建立拉取請求了!再次,您應該檢查**CONTRIBUTING.md**規則,看看維護者是否希望您**在建立 PR 時遵循命名約定**以及他們希望在描述中看到什麼。 ![開設公關](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zou62itckl8p1fx1s80j.png) ❗請務必將`base repository`設定為您衍生的原始儲存庫❗ 我建立了一個 PR,現在怎麼辦? ---------------- 您對所做的更改感到滿意,並成功建立了對主儲存庫的拉取請求。現在怎麼辦?現在,你**等著吧**。 根據您的 PR 正在修復的問題的緊急程度以及維護人員的日程安排,您將不得不等待有人**審核**您的拉取請求。準備好解釋為什麼以及你做了什麼(如果你在 PR 描述中沒有做好),並在必要時**進行更改**。 不要以個人名義接受任何變更要求。你們所有人都是為了專案的進步而來到這裡,沒有人懷有惡意。**如果您不同意**審稿人的意見,**請告訴他們**!健康的討論從來都不是壞事。 為什麼分叉? 🍴 -------- 您可能想知道為什麼我們必須分叉存儲庫。為什麼不直接克隆原始版本並在單獨的分支上工作?基本上,刪除#1 步驟,其餘的不是一樣嗎? 好吧,您可以嘗試,但是一旦您推送更改,您就會發現您**沒有這樣做的授權**!透過分叉儲存庫,您將成為複製儲存庫的擁有者,並**有權更改程式碼庫**。這是一個簡潔的系統,可以確保只有原始維護者批准的變更才能通過。 去貢獻吧! ----- 現在您已經掌握了這些訊息,您就可以開始在開源世界中留下自己的印記了!去幫助您已經聽說過和沒有聽說過的無數專案,並加入這個不斷發展的社區。我們將非常感謝您的幫助,我確信這一點😉 您知道有哪些開源專案帶有很多「第一期好」的標籤嗎?請在評論中分享! --- 原文出處:https://dev.to/cyclops-ui/interested-in-contributing-to-open-source-heres-what-you-need-to-know-g1c

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

適用於 Windows 10 的最佳免費 terminal

如果您嘗試在 PC 上使用終端,我對您的痛苦表示歉意。以下是一些可以提高您的工作流程的最佳終端模擬器: ### [1.Cmder](https://cmder.net//) ![Cmder終端鏡像](https://thepracticaldev.s3.amazonaws.com/i/q9a46hoblnd4mq48hhz0.png) [Cmder](https://cmder.net//)是一款便攜式控制台模擬器,基於已經流行的[Conemu](https://conemu.github.io/)建置; Conemu 也值得一試,因為它可以作為 Cmder 的替代品。 Cmders網站對此解釋得非常完美: > 將 cmder 更多地視為一個軟體包,而不是一個單獨的應用程式。所有的魔力都是透過 ConEmu 發生的。透過[Clink](https://mridgers.github.io/clink/)的增強。 儘管 Cmder 有時會出現速度問題(儘管建議它是便攜式的),但它仍然是一個很好的基本控制台模擬器,可以用來實現您的目標。 ### [2.Hyper.is](https://hyper.is/) ![超級終端神奇寶貝主題](https://thepracticaldev.s3.amazonaws.com/i/ltxa2x4fakh4vtn3iqj3.PNG) [Hyper](https://hyper.is/)是一款時尚的終端,可在 PC 和 MAC 上使用,並且在主題、插件和 shell 方面是完全可自訂的。由於我在安裝[zsh shell](https://github.com/robbyrussell/oh-my-zsh)時遇到了一些錯誤,因此在開發方面似乎仍然有很多工作要做。不管怎樣,它都是一個值得研究的出色的可擴展終端。 (注意:如果您想選擇 Pokemon 主題,可以[在這裡獲取!](https://github.com/klaussinani/hyper-pokemon) ) ### [3. terminus](https://eugeny.github.io/terminus/) ![terminus](https://res.cloudinary.com/tangobango/image/upload/v1565020537/terminus_2.0_hnhplq.png) 「現代的航站」是總站的口號。該終端看起來類似於開發環境,它帶有分割窗格、完全可配置的快捷方式、選項卡以及對所有主要 shell 的支援。這個航站樓仍然是新的,接下來會發生什麼將會令人興奮。 ### [4. FluentTerminal](https://github.com/felixse/FluentTerminal) ![FluentTerminal](https://res.cloudinary.com/tangobango/image/upload/v1565492456/Dev.to%20Articles/Best%20Standalone%20Terminals%202019/fluent-terminal_qfyrna.jpg) 適用於PowerShell、CMD、WSL 或自訂shell 的終端,甚至可以從iTerm 獲取主題,並具有可編輯的鍵綁定和快速可調整的設置,[Fluent](https://github.com/felixse/FluentTerminal) 絕對值得一試。 ### [5. alacritty](https://github.com/jwilm/alacritty) ![alacritty 終端](https://res.cloudinary.com/tangobango/image/upload/v1565493826/Dev.to%20Articles/Best%20Standalone%20Terminals%202019/2ebd0288-d06c-11e6-95d3-4a2889dbbd6f_xvc26b.png) 這個有點爭議。 Alacritty 聲稱自己是「現有最快的終端模擬器」。雖然這[可能不完全正確,](https://news.ycombinator.com/item?id=16936181)但許多用戶報告使用 Alacritty 時速度很快,但使用速度很低。因此,儘管它可能不適合所有人,但它也許能夠解決您在終端上遇到的一些滯後/撕裂問題。 這篇文章是否有幫助或想要加入一些內容/提出問題?請隨意 [connect](https://dev.to/connect) 並[關注](https://dev.to/adnanmostafa)以了解更多資訊或在下面發表評論! --- 原文出處:https://dev.to/adnanmostafa/the-best-free-standalone-terminals-for-windows-2019-kmj

Rust 中的 Laravel?我這樣做是有原因的。

大家都怎麼啦! 過去幾個月我一直在研究 Rust,並且總是試圖接近 Laravel 環境中的實際堆疊。 對於我來說,作為一名 PHP 開發人員,跳入 Rust 確實是一項艱鉅的任務,因為我以前從未接觸過函數式編程,但是我找到了一種方法讓這變得「更容易」。 銹據我所知 ----- 整個生態系統由不同的套件組成,您應該根據需要加入它,這與 Laravel 不同,Laravel 圍繞框架有一個非常強大的環境。 所以我的第一印像是我必須學習如何使用基礎包: - 多滕維 - 時空 - 時間 - Uuid - 等等。 但是當涉及到 Web 開發(主要是 API)時,您必須從眾多現有框架中選擇一個。喜歡: - Actix(目前使用) - 阿克蘇姆(評估中) - Rocket(人們總是告訴我不要使用它,仍然不知道為什麼) 而且它們都沒有 Laravel 那種固執己見的“結構”,所以我決定建立我的。 Rust 中的 Laravel --------------- 我為什麼要談 Laravel?因為它為我們提供的“MVC”結構對於使用 Rust Web 的小型專案來說足夠優雅。 當我使用固定的 Laravel 框架編寫 Rust 程式碼時,事情開始變得有意義。這是我正在談論的內容的結構: ``` ./src ├── app.rs ├── config.rs ├── http │   ├── controllers │   │   ├── leaderboard_controller.rs │   │   ├── mod.rs │   │   └── submissions_controller.rs │   ├── mod.rs │   └── requests │   ├── leaderboard_request.rs │   ├── mod.rs │   └── submission_request.rs ├── main.rs ├── models │   ├── leaderboard.rs │   ├── mod.rs │   └── submission.rs └── repositories ├── leaderboard_repository.rs ├── mod.rs └── submission_repository.rs ``` 該專案目前正在使用中: - [阿克泰克斯](https://actix.rs/docs/) - [卡律布狄斯 ORM](https://github.com/nodecosmos/charybdis) 你可以在這個[Pull Request](https://github.com/DanielHe4rt/leaderboard-rust/pull/1)中檢查我的混亂情況 問題是:在使用 Rust 時遵循這個想法會是一件好事嗎?簡單性和良好的維護結構是我在專案中一直追求的目標。 另外,我正在考慮寫一些關於我的流程如何從 PHP 遷移到 Rust 的系列文章,您有興趣閱讀類似的內容嗎? 希望在這裡看到您的想法,謝謝! --- 原文出處:https://dev.to/danielhe4rt/laravel-inside-rust-i-have-a-reason-for-that-ke3

真正去打造一樣幫助他人前進的東西,就不會失去動力

看到站長翻譯的[Ruby on Rails 之父:我怎麼學會寫程式的?](https://codelove.tw/@howtomakeaturn/post/k312Ya)這篇文章,想發表一點想法。 ## 真正的學習 我不是本科出身的,很長一段時間我都是亂無章法的自學。報名了一堆線上課程,也請過家教,甚至一度想要參加訓練營。但我一直都知道,那不適合我。 從架設第一個網站開始,我就有很「明確的問題」需要解決,所以一路上我幾乎都是「做中學」,問題在於,無人可以「糾正」我的錯誤,而錯誤不糾正,就難以進步。 所以我意識到我需要的不是尋找更好的課程,而是一個教練,一個可以糾正我的錯誤並引導我的教練,而不是手把手的教員。 教練很重要,教練的品格更重要,我無法把一個人的專業和他的品格分開(我找過不良品)。有些事情不能妥協,所以我才找到阿川。 DHH寫的這篇文章很簡短,我相信有很多「前後文」沒有提到。每個人的人生都有前後文,我們來自不同背景,或正在經歷不同的處境,我們都不是從零開始。 我們都被那些「學習神話」給洗腦了,如果有一種方式可以學會任何東西,那我想就是你那顆向學的「心」,而不是那些看起來很完美的學習路徑。 ## 為了重新學習美語發音,我開發了一個小工具 我正在重新學習美語發音,為了幫助和我一樣正在學習的人更好的練習,我開發了一個輔助的小工具,並藉此機會學習 JavaScript 這個程式語言。 這個小工具是根據[蕭博士](https://www.facebook.com/wen.hsiao.100)研發設計的PA注音符號表所建立,附上這張表的連結:https://drive.google.com/file/d/1mbxaL4yTAcJvWREeN179DAeQMgC2yCuK/view?usp=sharing 小工具中的注音符號卡,就是根據這張表的子音與母音的搭配所產生,我只是打亂出現的順序,作為學習者練習的輔助工具。下面附上這個小程式的連結: https://practicetool.github.io/ 歡迎各位前輩或同儕抓蟲或給我建議,我們一起學習。 - - - 我相信把心放到你的程式裡,就會產生動力。

使用 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

如何寫偽程式碼

您曾經遇到過非常複雜的程式設計問題嗎?您可能可以寫出邏輯,但不太確定應該使用的語法?編寫偽程式碼是一個很好的起點。 這是什麼 ---- 偽程式碼是一種“語言”,您可以在其中編寫所有編碼邏輯,而無需編寫一行特定於語言的程式碼。在演算法研究中,尤其是機器學習演算法中,你經常會看到這種情況。這並不意味著您不能將其用於 Web 開發。 為什麼你會使用它 -------- 有些專案非常龐大,如果您不花時間編寫一些偽程式碼,您最終可能會迷失在大量已實現的程式碼中。當您編寫一些偽程式碼時,它讓您有機會真正思考潛在的問題。您可以查看純邏輯和程式流程,而不必擔心程式碼如何運作。 在開始輸入真實程式碼之前編寫偽程式碼也將幫助您更快地完成專案。將其視為藍圖。您知道一切需要去哪裡以及一切如何協同工作。因此,當您進入實際的建造階段時,您無需考慮太多,因為您已經考慮過需要做什麼。 最好的部分是偽程式碼不依賴任何程式語言。您剛剛寫出的邏輯可以被任何人採用並翻譯成他們選擇的語言。它使您可以自由地重複使用和改進您正在建立的應用程式的體系結構。 偽程式碼更微妙的用途之一是與其他人共享它。有時,您會有一個可以在多個專案中使用的特定邏輯,但它們都採用不同的程式語言。當您擁有可用的偽程式碼時,您可以將其提供給其他程式設計師,他們可以用他們需要的任何語言編寫該邏輯。 另一個很棒的功能是您可以以任何您喜歡的格式編寫偽程式碼。您可以使用學術格式。它的結構和細節令人難以置信,但往往涉及大量數學知識。或者,您可以寫出您希望程式碼執行的操作的簡單概述。 怎麼寫 --- 這是我在一篇學術論文中寫的一些偽程式碼的範例: ![偽程式碼-ex.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1556193559488/mwN-ITK1f.png) 我將是第一個承認這對 Web 開發來說可能有點大材小用的人。如果您發現自己使用 LaTex 編寫偽程式碼,則可能會使其變得比您需要的更複雜。很可能在 Word 甚至記事本中快速寫下一些內容就足夠了。 以下是我為我的一個 Web 開發專案編寫的一些簡單偽程式碼的範例: ``` IF userlogin = true API call to get user data Assign data to variables Re-route user to dashboard ELSEIF userlogin failed more than 3 times Don't allow more attempts Send user notification email Re-route user to home page ELSE Log bad login attempt Show error message Clear login form ``` 您不必對偽程式碼掌握太多技術,但通常您可以包含的細節越多,編寫實際程式碼就越容易。想像一下你正在為你的程式寫一個大綱。它讓您有機會真正思考您想要完成的任務,並且可以準確地看到所有程式碼是如何組合在一起的。 使用偽程式碼需要關注的主要內容是: - 程式的邏輯流程 - 程序中複雜部分的詳細訊息 - 一致的格式 除了一些硬核思考之外,編寫偽程式碼其實並不需要太多。當您編寫它時,您將開始看到可以加入更多細節的地方或可以刪除一些細節的地方。請記住,這主要是供您使用,因此請以對您有意義的方式編寫。 就我個人而言,我喜歡偽程式碼。當我開始輸入真正的程式碼時,它可以幫助我保持思路清晰。當您規劃好所有邏輯後,您就有更多時間來試驗效能和最佳化。最重要的是,當您深入程式碼編寫時,您不必費力思考(是的,有計劃的懶惰)。 你怎麼認為?您認為偽程式碼值得花時間還是您寧願直接跳入程式碼? --- 嘿!你應該在 Twitter 上關注我:https://twitter.com/FlippedCoding --- 原文出處:https://dev.to/flippedcoding/how-to-write-pseudo-code-2jfe

請問,exp這個不是可以隨便改的嗎?我以為是expire?

## 前情提要 是這樣的,最近研究逆向,其實研究很久了XD 常常看到網站上的mp4連結,會是一串位置,後面附上很多數值,類似token或加密的東西: 舉例 https://某網站net/exp=1708705706~acl=%2Fbdf53323-8c58-41e9-8d85-00c7b9174e08%2F%2A~hmac=bb644cf702c45f8e70a3a61c6e0c8c8b6353d6b4e2ecf0f5e395f89fc57660c3/bdf53323-8c58-41e9-8d85-00c7b9174e08/parcel/video/c089253b.mp4 有些數值是固定的我知道,但看起來exp似乎是expire,感覺是cookie的有效時間, 數字上看起來也像是timeStamp(時間戳) 理論上我修改一下應該不會有問題才對?結果無法,假設我更改成1708703980 就會變成是Access Denied 看來是說這網址的所有參數都是固定的囉? ## 深入研究 我嘗試關掉重開,第二次去看一下它發送的請求: https://某網站net/exp=1708707048~acl=%2Fbdf53323-8c58-41e9-8d85-00c7b9174e08%2F%2A~hmac=41582b98b6eaa767b327b060a358e63b082606777a83c5475ccdc4d49054151e/bdf53323-8c58-41e9-8d85-00c7b9174e08/parcel/video/27479ba9.mp4? 這就玄妙了,沒想到一切參數除了acl似乎沒有變化以外,居然連mp4前面的東西都變了 。 我這兩個是同一段影片才對,照理說應該要是固定的檔案名稱。 看來它應該是每次都有不同的加密去產生這樣的網址,後端再去作出驗證? 而exp可能也不是我單純想像的,而是也像是一組密碼?