🔍 搜尋結果:作業

🔍 搜尋結果:作業

🏆如何使用 Taipy 和 PySpark 掌握 📊 大資料管道 🐍

本文將透過一個簡單的範例來示範如何**將 PySpark 與 Taipy 整合**,以將您的 **大資料處理需求** 與 **智慧作業執行** 結合。 #### 讓我們開始吧! ![開始使用](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyd6pq09thphujynk66n.gif) <小時/> ### 將 PySpark 與 Taipy 結合使用 Taipy 是一個**強大的工作流程編排工具**,具有**易於使用的框架**,可輕鬆應用於您現有的資料應用程式。 Taipy 建立在堅實的概念基礎上: - **場景、任務和資料節點** - 這些概念非常強大,允許開發人員**輕鬆地對其管道進行建模**,即使在沒有明確支援的情況下使用第3 方包也是如此。 <小時/> ![QueenB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdhmkkqpyjxko242wa2v.gif) {% cta https://github.com/Avaiga/taipy %} Star ⭐ Taipy 儲存庫 {% endcta %} 我們感謝任何幫助我們發展社區的幫助🌱 <小時/> *如果您已經熟悉 PySpark 和 Taipy,則可以跳至「2. Taipy 設定 (*config.py*)」。 *該部分深入探討了為 Taipy 任務定義函數來執行 PySpark 應用程式的本質。否則,請繼續閱讀!* <小時/> ### 一個簡單的例子:*palmerpenguins* 我們以 [palmerpenguins](https://allisonhorst.github.io/palmerpenguins/) 資料集為例: ``` >>> penguin_df ┌───────┬─────────┬───────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┬──────┐ │ index │ species │ island │ bill_length_mm │ bill_depth_mm │ flipper_length_mm │ body_mass_g │ sex │ year │ ├───────┼─────────┼───────────┼────────────────┼───────────────┼───────────────────┼─────────────┼────────┼──────┤ │ 0 │ Adelie │ Torgersen │ 39.1 │ 18.7 │ 181.0 │ 3750.0 │ male │ 2007 │ │ 1 │ Adelie │ Torgersen │ 39.5 │ 17.4 │ 186.0 │ 3800.0 │ female │ 2007 │ │ 2 │ Adelie │ Torgersen │ 40.3 │ 18.0 │ 195.0 │ 3250.0 │ female │ 2007 │ │ 3 │ Adelie │ Torgersen │ NaN │ NaN │ NaN │ NaN │ NaN │ 2007 │ │ 4 │ Adelie │ Torgersen │ 36.7 │ 19.3 │ 193.0 │ 3450.0 │ female │ 2007 │ │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ └───────┴─────────┴───────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┴──────┘ ``` <小時/> 該資料集僅包含 344 筆記錄——幾乎不是一個需要 Spark 處理的資料集。 然而,該資料集是可存取的,且其大小與演示 Spark 與 Taipy 的整合無關。 如果必須使用更大的資料集進行測試,您可以根據需要多次複製資料。 ![DAG 應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exxtbt00ia5y6avzcy8z.png) *我們簡單的企鵝應用程式的 DAG* <小時/> 我們將設計一個執行**兩個主要任務**的工作流程: #### 1- Spark 任務(*spark_process*): - 載入資料; - 依「*物種*」、「*島嶼*」和「*性別*」將資料分組; - 求其他欄位的平均值(「*bill_length_mm*」、「*bill_depth_mm*」、「*flipper_length_mm*」、「*body_mass_g*」); - 儲存資料。 #### 2- Python 任務(*過濾器*): - 載入Spark任務之前儲存的輸出資料; - 給定“*物種*”、“*島嶼*”和“*性別*”,傳回聚合值。 我們的小專案將包含 4 個檔案: ``` app/ ├─ penguin_spark_app.py # the spark application ├─ config.py # the configuration for our taipy workflow ├─ main.py # the main script (including our application gui) ├─ penguins.csv # the data as downloaded from the palmerpenguins git repo ``` <小時/> 您可以找到每個檔案的內容(*penguins.csv* 除外,您可以從 [palmerpenguins 儲存庫](https://github.com/allisonhorst/palmerpenguins/blob/main/inst/extdata/penguins.csv 取得) )在本文的程式碼區塊中。 <小時/> ## 1. Spark 應用程式 (*penguin_spark_app.py*) 通常,我們使用 *spark-submit* 命令列實用程式來執行 PySpark 任務。 您可以在他們自己的文件中閱讀有關以這種方式提交Spark 作業的內容和原因的更多資訊[此處](https://spark.apache.org/docs/latest/submitting-applications.html) 。 當使用 Taipy 進行工作流程編排時,我們可以繼續做同樣的事情。 唯一的區別是,我們不是在命令列中執行命令,而是讓工作流程管道產生一個[子進程](https://docs.python.org/3/library/subprocess.html),它使用以下命令執行Spark 應用程式*火花提交*。 在開始討論之前,我們首先**看看我們的 Spark 應用程式**。 只需瀏覽一下程式碼,然後**繼續閱讀有關此腳本功能的簡短說明**: ``` ### app/penguin_spark_app.py import argparse import os import sys parser = argparse.ArgumentParser() parser.add_argument("--input-csv-path", required=True, help="Path to the input penguin CSV file.") parser.add_argument("--output-csv-path", required=True, help="Path to save the output CSV file.") args = parser.parse_args() import pyspark.pandas as ps from pyspark.sql import SparkSession def read_penguin_df(csv_path: str): penguin_df = ps.read_csv(csv_path) return penguin_df def clean(df: ps.DataFrame) -> ps.DataFrame: return df[df.sex.isin(["male", "female"])].dropna() def process(df: ps.DataFrame) -> ps.DataFrame: """The mean of measured penguin values, grouped by island and sex.""" mean_df = df.groupby(by=["species", "island", "sex"]).agg("mean").drop(columns="year").reset_index() return mean_df if __name__ == "__main__": spark = SparkSession.builder.appName("Mean Penguin").getOrCreate() penguin_df = read_penguin_df(args.input_csv_path) cleaned_penguin_df = clean(penguin_df) processed_penguin_df = process(cleaned_penguin_df) processed_penguin_df.to_pandas().to_csv(args.output_csv_path, index=False) sys.exit(os.EX_OK) ``` <小時/> 我們可以透過在終端機中輸入以下命令來提交此 Spark 應用程式以供執行: ``` spark-submit --master local[8] app/penguin_spark_app.py \ --input-csv-path app/penguins.csv \ --output-csv-path app/output.csv ``` <小時/> 它將執行以下操作: 1.提交*penguin_spark_app.py*應用程式在8個CPU核心上本地執行; 2. 從 *app/penguins.csv* CSV 檔案載入資料; 3. 依「*物種*」、「*島嶼*」和「*性別*」分組,然後按平均值聚合其餘欄位; 4. 將產生的 DataFrame 儲存到 *app/output.csv*。 此後,*app/output.csv* 的內容應如下所示: ![資料](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1bjjxzb6vzypq2cj3mzl.png) <小時/> 另請注意,我們已對 **Spark 應用程式進行了編碼以接收 2 個命令列參數**: 1.  - *input-csv-path* :輸入企鵝 CSV 檔案的路徑;和 2.  - *output-csv-path* :Spark 應用程式處理後儲存輸出 CSV 檔案的路徑。 <小時/> ## 2. Taipy 設定 (*config.py*) 此時,我們有了 *penguin_spark_app.py* PySpark 應用程式,並且需要建立一個 **Taipy 任務來執行此 PySpark 應用程式**。 再次快速瀏覽 *app/config.py* 腳本,然後繼續閱讀: ``` ### app/config.py import datetime as dt import os import subprocess import sys from pathlib import Path import pandas as pd import taipy as tp from taipy import Config SCRIPT_DIR = Path(__file__).parent SPARK_APP_PATH = SCRIPT_DIR / "penguin_spark_app.py" input_csv_path = str(SCRIPT_DIR / "penguins.csv") # -------------------- Data Nodes -------------------- input_csv_path_cfg = Config.configure_data_node(id="input_csv_path", default_data=input_csv_path) # Path to save the csv output of the spark app output_csv_path_cfg = Config.configure_data_node(id="output_csv_path") processed_penguin_df_cfg = Config.configure_parquet_data_node( id="processed_penguin_df", validity_period=dt.timedelta(days=1) ) species_cfg = Config.configure_data_node(id="species") # "Adelie", "Chinstrap", "Gentoo" island_cfg = Config.configure_data_node(id="island") # "Biscoe", "Dream", "Torgersen" sex_cfg = Config.configure_data_node(id="sex") # "male", "female" output_cfg = Config.configure_json_data_node( id="output", ) # -------------------- Tasks -------------------- def spark_process(input_csv_path: str, output_csv_path: str) -> pd.DataFrame: proc = subprocess.Popen( [ str(Path(sys.executable).with_name("spark-submit")), str(SPARK_APP_PATH), "--input-csv-path", input_csv_path, "--output-csv-path", output_csv_path, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) try: outs, errs = proc.communicate(timeout=15) except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() if proc.returncode != os.EX_OK: raise Exception("Spark training failed") df = pd.read_csv(output_csv_path) return df def filter(penguin_df: pd.DataFrame, species: str, island: str, sex: str) -> dict: df = penguin_df[(penguin_df.species == species) & (penguin_df.island == island) & (penguin_df.sex == sex)] output = df[["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]].to_dict(orient="records") return output[0] if output else dict() spark_process_task_cfg = Config.configure_task( id="spark_process", function=spark_process, skippable=True, input=[input_csv_path_cfg, output_csv_path_cfg], output=processed_penguin_df_cfg, ) filter_task_cfg = Config.configure_task( id="filter", function=filter, skippable=True, input=[processed_penguin_df_cfg, species_cfg, island_cfg, sex_cfg], output=output_cfg, ) scenario_cfg = Config.configure_scenario( id="scenario", task_configs=[spark_process_task_cfg, filter_task_cfg] ) ``` 您也可以**使用[Taipy Studio](https://docs.taipy.io/en/latest/manuals/studio/config/)** 建立Taipy 配置,這是一個Visual Studio Code 擴展,它提供了圖形編輯器建構 Taipy *.toml* 設定檔。 <小時/> ### Taipy 中的 PySpark 任務 我們對產生這部分 DAG 的程式碼部分特別感興趣: ![DAG](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19t1otocpcrsa5qtdt2n.png) <小時/> 讓我們提取並檢查 *config.py* 腳本的相關部分,該腳本在 Taipy 中建立「*spark_process*」Spark 任務(及其 3 個關聯的資料節點),如上圖所示: ``` ### Code snippet: Spark task in Taipy # -------------------- Data Nodes -------------------- input_csv_path_cfg = Config.configure_data_node(id="input_csv_path", default_data=input_csv_path) # Path to save the csv output of the spark app output_csv_path_cfg = Config.configure_data_node(id="output_csv_path") processed_penguin_df_cfg = Config.configure_parquet_data_node( id="processed_penguin_df", validity_period=dt.timedelta(days=1) ) # -------------------- Tasks -------------------- def spark_process(input_csv_path: str, output_csv_path: str) -> pd.DataFrame: proc = subprocess.Popen( [ str(Path(sys.executable).with_name("spark-submit")), str(SPARK_APP_PATH), "--input-csv-path", input_csv_path, "--output-csv-path", output_csv_path, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) try: outs, errs = proc.communicate(timeout=15) except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() if proc.returncode != os.EX_OK: raise Exception("Spark training failed") df = pd.read_csv(output_csv_path) return df spark_process_task_cfg = Config.configure_task( id="spark_process", function=spark_process, skippable=True, input=[input_csv_path_cfg, output_csv_path_cfg], output=processed_penguin_df_cfg, ) ``` <小時/> 由於我們設計 *penguin_spark_app.py* Spark 應用程式來接收 2 個參數(*input_csv_path* 和 *output_csv_path*),因此我們選擇將這 2 個參數表示為 Taipy 資料節點。 請注意,**您的用例可能有所不同,您可以(並且應該!)根據您的需求修改任務、函數和關聯的資料節點**。 例如,您可以: 1. 有一個 Spark 任務,執行一些例行 ETL 並且不回傳任何內容; 2. 偏好對輸入和輸出路徑進行硬編碼,而不是將它們持久化為資料節點;或者 3. 將其他應用程式參數儲存為資料節點並將其傳遞給 Spark 應用程式。 然後,我們將 *spark-submit* 作為 Python 子進程執行,如下所示: ``` subprocess.Popen( [ str(Path(sys.executable).with_name("spark-submit")), str(SPARK_APP_PATH), "--input-csv-path", input_csv_path, "--output-csv-path", output_csv_path, ], ) ``` <小時/> 回想一下,清單元素的順序應保留以下格式,就像它們在命令列上執行一樣: ``` $ spark-submit [spark-arguments] <pyspark-app-path> [application-arguments] ``` <小時/> 同樣,根據我們的用例,我們可以根據需要指定不同的 Spark-submit 腳本路徑、Spark 參數(我們在範例中未提供任何參數)或不同的應用程式參數。 <小時/> ### 讀取並回傳*output_csv_path* 請注意,*spark_process* 函數的結束如下: ``` def spark_process(input_csv_path: str, output_csv_path: str) -> pd.DataFrame: ... df = pd.read_csv(output_csv_path) return df ``` <小時/> 在我們的例子中,我們希望 Taipy 任務在 Spark -  處理資料後輸出資料,以便可以將其寫入 *processed_penguin_df_cfg* [Parquet 資料節點](https://docs.taipy.io/en/latest /手冊/核心/配置/資料節點配置/#parquet)。 我們可以做到這一點的一種方法是手動讀取輸出目標(在本例中為 *output_csv_path*),然後將其作為 Pandas DataFrame 傳回。 但是,如果您不需要 Spark 應用程式的返回資料,您可以簡單地讓 Taipy 任務(透過 *spark_process* 函數)返回 *None*。 <小時/> ### 快取 Spark 任務 由於我們將 *spark_process_task_cfg* 配置為 *True*,當重新執行該場景時,Taipy 將 **跳過 ** ***spark_process 的重新執行*** **任務** 並重複使用持久化任務輸出:* processed_penguin_df_cfg* Pandas DataFrame。 但是,我們也為 *processed_penguin_df_cfg* 資料節點定義了 1 天的 *validity_period*,因此如果 DataFrame 最後一次快取超過一天,Taipy 仍會重新執行任務。 <小時/> ## 3. 建構 GUI (*main.py*) 我們將透過**建立我們在本文開頭看到的 GUI** 來完成我們的應用程式: ![應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvfpy6aobtbzdhbf55sv.png) <小時/> 如果您不熟悉 Taipy 的 GUI 功能,可以在此處找到[快速入門](https://docs.taipy.io/en/latest/getting_started/getting-started-gui/)。 無論如何,您只需為 *app/main.py* 複製並貼上以下程式碼,因為它不是我們的重點: ``` ### app/main.py from pathlib import Path from typing import Optional import taipy as tp from config import scenario_cfg from taipy.gui import Gui, notify valid_features: dict[str, list[str]] = { "species": ["Adelie", "Chinstrap", "Gentoo"], "island": ["Torgersen", "Biscoe", "Dream"], "sex": ["Male", "Female"], } selected_species = valid_features["species"][0] selected_island = valid_features["island"][0] selected_sex = valid_features["sex"][0] selected_scenario: Optional[tp.Scenario] = None data_dir = Path(__file__).with_name("data") data_dir.mkdir(exist_ok=True) def scenario_on_creation(state, id, payload): _ = payload["config"] date = payload["date"] label = payload["label"] properties = payload["properties"] # Create scenario with selected configuration scenario = tp.create_scenario(scenario_cfg, creation_date=date, name=label) scenario.properties.update(properties) # Write the selected GUI values to the scenario scenario.species.write(state.selected_species) scenario.island.write(state.selected_island) scenario.sex.write(state.selected_sex.lower()) output_csv_file = data_dir / f"{scenario.id}.csv" scenario.output_csv_path.write(str(output_csv_file)) notify(state, "S", f"Created {scenario.id}") return scenario def scenario_on_submission_change(state, submittable, details): """When the selected_scenario's submission status changes, reassign selected_scenario to force a GUI refresh.""" state.selected_scenario = submittable selected_data_node = None main_md = """ <|layout|columns=1 4|gap=1.5rem| <lhs|part| # Spark with **Taipy**{: .color-primary} ## Scenario <|{selected_scenario}|scenario_selector|on_creation=scenario_on_creation|> ---------- ## Scenario info <|{selected_scenario}|scenario|on_submission_change=scenario_on_submission_change|> |lhs> <rhs|part|render={selected_scenario}| ## Selections <selections|layout|columns=1 1 1 2|gap=1.5rem| <|{selected_species}|selector|lov={valid_features["species"]}|dropdown|label=Species|> <|{selected_island}|selector|lov={valid_features["island"]}|dropdown|label=Island|> <|{selected_sex}|selector|lov={valid_features["sex"]}|dropdown|label=Sex|> |selections> ---------- ## Output **<|{str(selected_scenario.output.read()) if selected_scenario and selected_scenario.output.is_ready_for_reading else 'Submit the scenario using the left panel.'}|text|raw|class_name=color-primary|>** ## Data node inspector <|{selected_data_node}|data_node_selector|display_cycles=False|> **Data node value:** <|{str(selected_data_node.read()) if selected_data_node and selected_data_node.is_ready_for_reading else None}|> <br/> ---------- ## DAG <|Scenario DAG|expandable| <|{selected_scenario}|scenario_dag|> |> |rhs> |> """ def on_change(state, var_name: str, var_value): if var_name == "selected_species": state.selected_scenario.species.write(var_value) elif var_name == "selected_island": state.selected_scenario.island.write(var_value) elif var_name == "selected_sex": state.selected_scenario.sex.write(var_value.lower()) if __name__ == "__main__": tp.Core().run() gui = Gui(main_md) gui.run(title="Spark with Taipy") ``` <小時/> 然後,從專案資料夾中,您可以執行主腳本,如下所示: ``` $ taipy run app/main.py ``` <小時/> ## 結論 現在您已經看到如何將 PySpark 與 Taipy 結合使用的範例,請繼續嘗試使用這兩個工具來**增強您自己的資料應用程式**! 如果您一直在努力應對其他工作流程編排工具減慢您的工作並妨礙您的工作,請不要讓它阻止您嘗試 Taipy。 Taipy 易於使用,並且努力不限制自己可以使用的第 3 方軟體包 - **其強大而靈活的框架使其可以輕鬆適應任何資料應用程式**。 <小時/> ![GIF 結束](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/839kmsq22emwpkuerxys.gif) 希望您喜歡這篇文章! <小時/> 您可以在此[儲存庫](https://medium.com/r?url=https%3A%2F%2Fgithub.com%2FAvaiga%2Fdemo-pytorch-penguin-app)上找到所有程式碼和資料。 --- 原文出處:https://dev.to/taipy/how-to-master-big-data-pipelines-with-taipy-and-pyspark-14oe

我該如何教 Git

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

使用 Markdown 與 React 在 Github 文件中顯示淺色模式、深色模式

警告:所表達的觀點可能不適合所有受眾! 😂 ## 簡介 在本文結束時,您將了解並能夠根據使用者偏好 - **深色**或**淺色**模式展示您的 Markdown 影像。 1. 我將介紹如何在 GitHub README.md 中加入兩個圖像 - 根據所選的“主題”,您的圖像將正確回應。 2. 我將引導您在 Markdown 中合併影像的過程,並示範如何使用 React 使它們回應。 😎 ___ ## 你使用淺色還是深色? 我不了解你的情況,但無論平台如何,如果他們可以選擇在淺色和深色模式之間切換,那就沒有競爭了。 淺色主題正在切換為深色,事實上,當然在我寫這篇文章的時候! ![深色主題](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0yfjc2yv5pfyacu74go.png) 話雖如此,在軟體開發的快速發展中,創造無縫的使用者體驗至關重要。 這種體驗的一部分涉及適應使用者偏好,例如淺色和深色模式。 我還記得幾年前,Github 宣布了用戶可以切換到「深色模式」的選項,這是一件非常大的事情。 ![GitHub 深色主題](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qi10urco3o6fdojm6ipf.png) 【Github揭曉黑暗主題的重要時刻】(https://t.co/HEotvXVJ7R) 🤩 2020 年 12 月 8 日🎆 近年來,使用者介面中深色和淺色模式選項的出現已成為一種流行趨勢。 我絕對不是唯一一個喜歡使用深色主題選項的人,根據 Android 用戶的說法,[91.8% 的用戶更喜歡深色模式](https://www.androidauthority.com/dark-mode-poll-results-1090716/) 所以我們可以猜測這個數字在所有作業系統中都相當高。 這當然可能會引起激烈的爭論,所以我會盡力將自己的觀點降到最低。 ![輕模式迷因](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3yiepj8a46rwhu69fgw.png) ## 改善使用者體驗 主要目標是透過在應用程式中提供選項來改善用戶體驗。 有多種方法可以建立每個圖像的多個版本,在本教程中我們不會深入討論細節。 只要確保您的圖像在兩個主題中脫穎而出並具有透明背景,您就會獲得成功。 **_讓我們開始派對吧!_** ## GitHub 自述文件中的響應式圖像 您有一個專案並想讓您的 GitHub 專案 README.md 真正流行嗎? 無論使用者使用什麼淺色主題,我們都需要一種方法來指定圖像應在 Markdown 中顯示哪種主題(淺色或深色)。 當您想要根據使用者選擇的配色方案優化圖片的顯示時,這特別有用,並且它涉及將 **HTML `<picture>`** 元素與 `prefers-color-scheme` 媒體功能結合使用如下所示。 繼續將圖片檔案直接拖曳到 GitHub 中並放在“srcset=”後面。 ``` <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/boxyhq/.github/assets/66887028/df1c9904-df2f-4515-b403-58b14a0e9093"> <source media="(prefers-color-scheme: light)" srcset="https://github.com/boxyhq/.github/assets/66887028/e093a466-72ea-41c6-a292-4c39a150facd"> <img alt="BoxyHQ Banner" src="https://github.com/boxyhq/jackson/assets/66887028/b40520b7-dbce-400b-88d3-400d1c215ea1"> </picture> ``` 瞧! ![SAML Jackson 暗模式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q51g41fjfqnposn50una.png) ![SAML Jackson 燈光模式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0xzs88txjylnixilaqu.png) 太好了,你有 5 秒嗎? https://github.com/boxyhq/jackson --- ## 使用 React 在 Markdown 中回應影像 假設今天我將像平常一樣用 Markdown 編寫博客,並將其發佈到我的網站上。 我使用的圖像需要根據使用者偏好做出回應,但在 Markdown 中不可能偵聽本地儲存和設定狀態中的「主題」變更。 ![本機儲存](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vrjz4to8x17h63dtybxn.png) 值得慶幸的是,如果我們將 React 匯入到 Markdown 檔案中,但先建立一個元件,就有一種方法可以解決這個困境。 ## 反應文件 ``` src/components/LightDarkToggle.js import React, { useEffect, useState } from 'react'; function ToggleImages() { // Define a state variable to track the user's login status const [currentTheme, setcurrentTheme] = useState(localStorage.getItem('theme')); // Add an event listener for the 'storage' event inside a useEffect useEffect(() => { const handleStorageChange = (event) => { console.log('Storage event detected:', event); // Check the changed key and update the state accordingly console.log("event", event.key) if (event.key === 'theme') { setcurrentTheme(event.newValue); } }; window.addEventListener('storage', handleStorageChange); // Clean up the event listener when the component unmounts return () => { window.removeEventListener('storage', handleStorageChange); }; }, []); // The empty dependency array ensures that this effect runs once when the component mounts return ( <div className="image-container"> {currentTheme == 'light'? ( <img id="light-mode-image" src="/img/blog/boxyhq-banner-light-bg.png" alt="Light Mode Image" ></img> ):( <img id="dark-mode-image" src="/img/blog/boxyhq-banner-dark-bg.png" alt="Dark Mode Image" ></img> )} </div> ); } export default ToggleImages; ``` 我在程式碼中加入了註釋和一些控制台日誌,以幫助了解正在發生的事情,但讓我們快速分解它。 - React useState 鉤子管理 `currentTheme` 的狀態,它代表使用者選擇的儲存在本機儲存中的主題。 - useEffect 掛鉤用於為「儲存」事件新增事件偵聽器。當儲存事件發生時(表示本機儲存發生變化),元件會檢查變更的鍵是否為“theme”,並相應地更新“currentTheme”狀態。 - 此元件根據使用者選擇的主題呈現不同的影像,如果主題是“淺色”,則顯示淺色模式影像;如果主題是其他主題,則顯示深色模式影像。 酷,讓我們繼續吧! ## 降價文件 讓我們為新部落格建立一個 .md 檔案。 ``` --- slug: light-and-dark-mode-responsive-images title: 'Light and Dark Mode Responsive Images' tags_disabled: [ developer, react, javascript, open-source, ] image: /img/blog/light-dark.png author: Nathan Tarbert author_title: Community Engineer @BoxyHQ author_url: https://github.com/NathanTarbert author_image_url: https://boxyhq.com/img/team/nathan.jpg --- import ToggleImages from '../src/components/LightDarkToggle.js'; ## 🤩 Let's start this blog off with a bang! Our business logo is now responsive with each user's preference, whether it's **light** or **dark** mode! <div> <ToggleImages /> </div> More blog words... ``` 此時,我們只需匯入 React 元件並將其呈現在 Markdown 檔案中。 由於這是一個 Next.js 應用程式,讓我們啟動伺服器“npm run dev”並查看結果。 ![貓鼓滾](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lyjzjqgcwaubyj5ve1o3.gif) ![網站深色模式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qraltb34mrl9y8j9jppq.png) 並切換到淺色主題 ![網站燈光模式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u33jgzha5fbfy6tlb4hs.png) 讓我們打開控制台來查看我們的事件 ![console.log](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpxz5gbhwt308vatsnkp.png) 你有它! 這些是在 Markdown 中展示響應式映像的幾種方法,其中一個範例使用 React 來幫助我們在本地儲存中設定狀態。 我希望您喜歡這篇文章,如果您喜歡開發,請在 [X (Twitter)](https://twitter.com/nathan_tarbert) 上關注我,我們下次再見! --- 原文出處:https://dev.to/nathan_tarbert/the-zebras-guide-to-showcase-your-images-in-light-dark-17f5

增強您的 Windows 開發能力:WSL 終極指南🚀📟

## 你好!我是[鮑里斯](https://www.martinovic.dev/)! 我是一名軟體工程師,專門從事保險工作,教授其他開發人員,並在會議上發言。多年來,我使用了相當多的不同開發環境和作業系統,除了 .Net 開發之外,我個人從來不喜歡在 Windows 中進行開發。這是為什麼?讓我們更深入地研究一下。 好吧,我的大部分問題都可以歸結為一個詞:**麻煩**。無論是在日常使用中處理Windows,您都會經常遇到作業系統本身的不同方式帶給您的困擾。這樣的例子很多,無論是登錄問題、套件管理、切換節點版本或 Windows 更新,這些問題本身就可以讓人們放棄作業系統。 所以你可以明白為什麼我開始與下圖的烏鴉產生連結。 ![](https://res.cloudinary.com/practicaldev/image/fetch/s--KiM-kkXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3gpwe8ax86eeh6ccpgsi.png) 我並沒有放棄尋找可行的解決方案。而且,我(有點)找到了它。 ## 什麼是 WSL?我為什麼要對它感興趣? Windows Subsystem for Linux(或 WSL)讓開發人員可以直接在 Windows 上執行功能齊全的本機 GNU/Linux 環境。換句話說,我們可以直接執行Linux,而無需使用虛擬機器或雙重開機系統。 **第一個很酷的事情是 WSL 允許您永遠不用切換作業系統,但仍然可以在作業系統中擁有兩全其美的優點。** 這對我們普通用戶意味著什麼?當您查看WSL 在實踐中的工作方式時,它可以被視為一項Windows 功能,直接在Windows 10 或11 內執行Linux 作業系統,具有功能齊全的Linux 檔案系統、Linux 命令列工具、*** *** 和****** Linux GUI 應用程式(*真的很酷,順便說一句*)。除此之外,與虛擬機器相比,它使用的運作資源要少得多,並且不需要單獨的工具來建立和管理這些虛擬機器。 WSL 主要針對開發人員,因此本文將重點放在開發人員的使用以及如何使用 VS Code 設定完全工作的開發環境。在本文中,我們將介紹一些很酷的功能以及如何在實踐中使用它們。另外,理解新事物的最好方法就是實際開始使用它們。 ### 覺得這篇文章有用嗎? 我們正在 [Wasp](https://wasp-lang.dev/) 努力建立這樣的內容,更不用說建立一個現代的開源 React/NodeJS 框架了。 表達您支援的最簡單方法就是為 Wasp 儲存庫加註星標! 🐝 但如果您可以查看[存儲庫](https://github.com/wasp-lang/wasp)(用於貢獻,或只是測試產品),我們將不勝感激。點擊下面的按鈕給黃蜂星一顆星並表示您的支持! ![wasp_arnie_handshake](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axqiv01tl1pha9ougp21.gif) https://github.com/wasp-lang/wasp ## 在 Windows 作業系統上安裝 WSL 為了在 Windows 上安裝 WSL,請先啟用 [Hyper-V](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v )架構是微軟的硬體虛擬化解決方案。要安裝它,請右鍵單擊 Windows 終端機/Powershell 並以管理員模式開啟它。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6wm5xniz2nehrccczeh6.png) 然後,執行以下命令: ``` Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All ``` 這將確保您具備安裝的所有先決條件。然後,在管理員模式下開啟 Powershell(最好在 Windows 終端機中完成)。然後,執行 ``` wsl —install ``` 有大量的 Linux 發行版需要安裝,但 Ubuntu 是預設安裝的。本指南將介紹許多控制台命令,但其中大多數將是複製貼上過程。 如果您之前安裝過 Docker,那麼您的系統上很可能已經安裝了 WSL 2。在這種情況下,您將收到安裝所選發行版的提示。由於本教程將使用 Ubuntu,因此我建議執行。 ``` wsl --install -d Ubuntu ``` 安裝 Ubuntu(或您選擇的其他發行版)後,您將進入 Linux 作業系統並出現歡迎畫面提示。在那裡,您將輸入一些基本資訊。首先,您將輸入您的用戶名,然後輸入密碼。這兩個都是 Linux 特定的,因此您不必重複您的 Windows 憑證。完成此操作後,安裝部分就結束了!您已經在 Windows 電腦上成功安裝了 Ubuntu!說起來還是感覺很奇怪吧? ### 等一下! 但在我們開始討論開發環境設定之前,我想向您展示一些很酷的技巧,這些技巧將使您的生活更輕鬆,並幫助您了解為什麼 WSL 實際上是 Windows 用戶的遊戲規則改變者。 WSL 的第一個很酷的事情是您不必放棄目前透過 Windows 資源管理器管理檔案的方式。在 Windows 資源管理器的側邊欄中,您現在可以在網路標籤下找到 Linux 選項。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/647jdnzilrucsijtye3v.png) 從那裡,您可以直接從 Windows 資源管理器存取和管理 Linux 作業系統的檔案系統。這個功能真正酷的是,你基本上可以在不同的作業系統之間複製、貼上和移動文件,沒有任何問題,這開啟了一個充滿可能性的世界。實際上,您不必對文件工作流程進行太多更改,並且可以輕鬆地將許多專案和文件從一個作業系統移動到另一個作業系統。如果您在 Windows 瀏覽器上下載 Web 應用程式的映像,只需將其複製並貼上到您的 Linux 作業系統中即可。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqjsd1oz5a4alu6q08re.png) 我們將在範例中使用的另一個非常重要的事情是 WSL2 虛擬路由。由於您的作業系統中現在有作業系統,因此它們有一種通訊方式。當您想要存取 Linux 作業系統的網路時(例如,當您想要存取在 Linux 中本機執行的 Web 應用程式時),您可以使用 *${PC-name}.local*。對我來說,由於我的電腦名稱是 Boris-PC,所以我的網路位址是 boris-pc.local。這樣你就不必記住不同的 IP 位址,這真的很酷。如果您出於某種原因需要您的位址,您可以前往 Linux 發行版的終端,然後輸入 ipconfig。然後,您可以看到您的 Windows IP 和 Linux 的 IP 位址。這樣,您就可以毫無摩擦地與兩個作業系統進行通訊。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkhcfiybnobuoziitwtm.png) 我想強調的最後一件很酷的事情是 Linux GUI 應用程式。這是一項非常酷的功能,有助於使 WSL 對普通用戶更具吸引力。您可以使用流行的套件管理器(例如 apt(Ubuntu 上的預設值)或 flatpak)在 Linux 系統上安裝任何您想要的應用程式。然後,您也可以從命令列啟動它們,應用程式將啟動並在 Windows 作業系統中可見。但這可能會引起一些摩擦並且不方便用戶使用。此功能真正具有突破性的部分是,您可以直接從 Windows 作業系統啟動它們,甚至無需親自啟動 WSL。因此,您可以建立捷徑並將它們固定到「開始」功能表或任務欄,沒有任何摩擦,並且實際上不需要考慮您的應用程式來自哪裡。為了演示,我安裝了 Dolphin 檔案管理器並透過 Windows 作業系統執行它。您可以在下面看到它與 Windows 資源管理器並排的操作。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq1nxj244jd1fci13oay.png) ## WSL 開發入門 在了解了 WSL 的所有酷炫功能後,讓我們慢慢回到教學的正軌。接下來是設定我們的開發環境並啟動我們的第一個應用程式。我將設定一個 Web 開發環境,我們將使用 [Wasp](https://wasp-lang.dev/) 作為範例。 如果你不熟悉的話,Wasp 是一個類似 Rails 的 React、Node.js 和 Prisma 框架。這是開發和部署全端 Web 應用程式的快速、簡單的方法。對於我們的教程,Wasp 是一個完美的候選者,因為它本身不支援 Windows 開發,而只能透過 WSL 來支持,因為它需要 Unix 環境。 讓我們先開始安裝 Node.js。目前,Wasp 要求使用者使用 Node v18(版本要求很快就會放寬),因此我們希望從 Node.js 和 NVM 的安裝開始。 但首先,讓我們先從 Node.js 開始。在 WSL 中,執行: ``` sudo apt install nodejs ``` 為了在您的 Linux 環境中安裝 Node。接下來是 NVM。我建議存取 https://github.com/nvm-sh/nvm 並從那裡獲取最新的安裝腳本。目前下載的是: ``` curl -o- [https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh](https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh) | bash ``` 之後,我們在系統中設定了 Node.js 和 NVM。 接下來是在我們的 Linux 環境中安裝 Wasp。 Wasp 安裝也非常簡單。因此,只需複製並貼上此命令: ``` curl -sSL [https://get.wasp-lang.dev/installer.sh](https://get.wasp-lang.dev/installer.sh) | sh ``` 並等待安裝程序完成它的事情。偉大的!但是,如果您從 0 開始進行 WSL 設置,您會注意到下面有以下警告:看起來“/home/boris/.local/bin”不在您的 PATH 上!您將無法透過終端名稱呼叫 wasp。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/em932e89tlzajv4rm6up.png) 讓我們快速解決這個問題。為了做到這一點,讓我們執行 ``` code ~/.profile ``` 如果我們還沒有 VS Code,它會自動設定所需的一切並啟動,以便您可以將命令新增至檔案末端。每個人的系統名稱都會有所不同。例如我的是: ``` export PATH=$PATH:/home/boris/.local/bin ``` 偉大的!現在我們只需要將節點版本切換到 v18.14.2 即可確保與 Wasp 完全相容。我們將一次性安裝並切換到 Node 18!為此,只需執行: ``` nvm install v18.14.2 && nvm use v18.14.2 ``` 設定 Wasp 後,我們希望了解如何執行應用程式並從 VS Code 存取它。在幕後,您仍將使用 WSL 進行開發,但我們將能夠使用主機作業系統 (Windows) 中的 VS Code 來完成大多數事情。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/orifa202sph4swgbir2d.png) 首先,將 [WSL 擴充功能](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) 下載到 Windows 中的 VS Code。然後,讓我們啟動一個新的 Wasp 專案來看看它是如何運作的。開啟 VS Code 命令面板(ctrl + shift + P)並選擇「在 WSL 中開啟資料夾」選項。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1le8xvk6a8a8teog8eo.png) 我打開的資料夾是 ``` \\wsl.localhost\Ubuntu\home\boris\Projects ``` 這是我在 WSL 中的主資料夾中的「Projects」資料夾。我們可以透過兩種方式知道我們處於 WSL 中:頂部欄和 VS Code 的左下角。在這兩個地方,我們都編寫了 WSL: Ubuntu,如螢幕截圖所示。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mzhu765415sravn3vypu.png) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpy4kggtsobod1vk1dqn.png) 進入該資料夾後,我將打開一個終端。它還將已經連接到 WSL 中的正確資料夾,因此我們可以開始工作了!讓我們執行 ``` wasp new ``` 命令建立一個新的 Wasp 應用程式。我選擇了基本模板,但您可以自由建立您選擇的專案,例如[SaaS 入門](https://github.com/wasp-lang/SaaS-Template-GPT) 具有 GPT、Stripe 等預先配置。如螢幕截圖所示,我們應該將專案的當前目錄變更為正確的目錄,然後用它來執行我們的專案。 ``` wasp start ``` ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l453mcae56kfa3yrm7j4.png) 就像這樣,我的 Windows 電腦上將打開一個新螢幕,顯示我的 Wasp 應用程式已開啟。涼爽的!我的位址仍然是預設的 localhost:3000,但它是從 WSL 執行的。恭喜,您已透過 WSL 成功啟動了您的第一個 Wasp 應用程式。這並不難,不是嗎? ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfyfok2eg0xjhqcqhgoe.png) 對於我們的最後一個主題,我想重點介紹使用 WSL 的 Git 工作流程,因為它的設定相對輕鬆。您始終可以手動進行 git config 設置,但我為您提供了一些更酷的東西:在 Windows 和 WSL 之間共享憑證。要設定共享 Git 憑證,我們必須執行以下操作。在 Powershell(在 Windows 上)中,設定 Windows 上的憑證管理員。 ``` git config --global credential.helper wincred ``` 讓我們在 WSL 中做同樣的事情。 ``` git config --global credential.helper "/mnt/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe" ``` 這使我們能夠共享 Git 使用者名稱和密碼。 Windows 中設定的任何內容都可以在 WSL 中運作(反之亦然),我們可以根據需要在 WSL 中使用 Git(透過 VS Code GUI 或透過 shell)。 ## 結論 透過我們在這裡的旅程,我們了解了 WSL 是什麼、它如何有助於增強 Windows PC 的工作流程,以及如何在其上設定初始開發環境。 Microsoft 在這個工具方面做得非常出色,並且確實使 Windows 作業系統成為所有開發人員更容易使用和可行的選擇。我們了解如何安裝啟動開發所需的開發工具以及如何掌握基本的開發工作流程。如果您想深入了解該主題,這裡有一些重要的連結: - [https://wasp-lang.dev/](https://wasp-lang.dev/) - [https://github.com/microsoft/WSL](https://github.com/microsoft/WSL) - [https://learn.microsoft.com/en-us/windows/wsl/install](https://learn.microsoft.com/en-us/windows/wsl/install) - [https://code.visualstudio.com/docs/remote/wsl](https://code.visualstudio.com/docs/remote/wsl) --- 原文出處:https://dev.to/wasp/supercharge-your-windows-development-the-ultimate-guide-to-wsl-195m

🚀 發送 Github 星星監測通知的 4 種方式 ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️

# 簡介 在上一篇文章中,我討論了建立一個[GitHub stars 監視器](https://dev.to/triggerdotdev/take-nextjs-to-the-next-level-create-a-github-stars-monitor-130a)。 在這篇文章中,我想向您展示如何每天了解新星的資訊。 我們將學習: - 如何建立通用系統來建立和使用提供者。 - 如何使用提供者發送通知。 - 使用不同提供者的不同用例。 ![通知](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5uwpjomw3pbrpq885q8z.gif) --- ## 你的後台工作平台🔌 [Trigger.dev](https://trigger.dev/) 是一個開源程式庫,可讓您使用 NextJS、Remix、Astro 等為您的應用程式建立和監控長時間執行的作業!   [![GiveUsStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bm9mrmovmn26izyik95z.gif)](https://github.com/triggerdotdev/trigger.dev) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 https://github.com/triggerdotdev/trigger.dev --- ## 讓我們來設定一下 🔥 我們將建立不同的提供者來通知我們何時有新的明星。我們將設定「電子郵件」、「簡訊」、「Slack」和「Discord」通知。 我們的目標是讓每個貢獻者都足夠簡單,以便在未來貢獻更多的提供者。 每個提供者都會有一組不同的參數,有些只有“API 金鑰”,有些則有電話號碼,具體取決於提供者。 為了驗證這些金鑰,讓我們安裝“zod”;它是一個很棒的庫,可以定義模式並根據模式檢查資料。 您可以透過執行以下命令開始: ``` npm install zod --save ``` 完成後,建立一個名為「providers」的新資料夾,然後在其中建立一個名為「register.provider.ts」的新檔案。 這是文件的程式碼: ``` import {Schema} from "zod"; export function registerProvider<T>( name: string, options: {active: boolean}, validation: Schema<T>, run: (libName: string, stars: number, values: T) => Promise<void> ) { // if not active, we can just pass an empty function, nothing will run if (!options.active) { return () => {}; } // will validate and remove unnecessary values (Security wise) const env = validation.parse(process.env); // return the function we will run at the end of the job return async (libName: string, stars: number) => { console.log(`Running provider ${name}`); await run(libName, stars, env as T); console.log(`Finished running provider ${name}`); } } ``` 程式碼不多,但可能有點複雜。 我們首先建立一個名為「registerProvider」的新函數。該函數獲得一個通用類型“T”,基本上是我們所需的環境變數。 然後我們還有 4 個參數: - 名稱 - 可以是「Twilio」、「Discord」、「Slack」或「Resend」中的任何一個。 - 選項 - 目前,一個參數是提供者是否處於活動狀態? - 驗證 - 在這裡,我們在 .env 檔案中傳遞所需參數的「zod」模式。 - run - 實際上用於發送通知。請注意,傳入其中的參數是庫名稱、星星數量以及我們在「validation」中指定的環境變數 **然後我們就有了實際的功能:** 首先,我們檢查提供者是否處於活動狀態。如果沒有,我們發送一個空函數。 然後,我們驗證並提取我們在模式中指定的變數。如果變數缺少 `zod` 將發送錯誤並且不會讓應用程式執行。 最後,我們傳回一個函數,該函數會取得庫名稱和星星數量並觸發通知。 在我們的「providers」資料夾中,建立一個名為「providers.ts」的新文件,並在其中新增以下程式碼: ``` export const Providers = []; ``` 稍後,我們將在那裡加入所有提供者。 --- ## 修改 TriggerDev 作業 本文是上一篇關於建立 [GitHub stars 監視器](https://dev.to/triggerdotdev/take-nextjs-to-the-next-level-create-a-github-stars-monitor-130a)。 編輯檔案 `jobs/sync.stars.ts` 並將以下程式碼加入檔案底部: ``` const triggerNotification = client.defineJob({ id: "trigger-notification", name: "Trigger Notification", version: "0.0.1", trigger: invokeTrigger({ schema: z.object({ stars: z.number(), library: z.string(), providerNumber: z.number(), }) }), run: async (payload, io, ctx) => { await io.runTask("trigger-notification", async () => { return Providers[payload.providerNumber](payload.library, payload.stars); }); } }); ``` 此作業取得星星數量、圖書館名稱和提供者編號,並從先前定義的提供者觸發特定提供者的通知。 現在,我們繼續修改“getStars”,在函數末尾加入以下程式碼: ``` for (let i = 0; i < Providers.length; i++) { await triggerNotification.invoke(payload.name + '-' + i, { library: payload.name, stars: stargazers_count - payload.previousStarCount, providerNumber: i, }); } ``` 這將觸發每個圖書館的通知。 完整頁面程式碼: ``` import { cronTrigger, invokeTrigger } from "@trigger.dev/sdk"; import { client } from "@/trigger"; import { prisma } from "../../helper/prisma"; import axios from "axios"; import { z } from "zod"; import {Providers} from "@/providers/providers"; // Your first job // This Job will be triggered by an event, log a joke to the console, and then wait 5 seconds before logging the punchline. client.defineJob({ id: "sync-stars", name: "Sync Stars Daily", version: "0.0.1", // Run a cron every day at 23:00 AM trigger: cronTrigger({ cron: "0 23 * * *", }), run: async (payload, io, ctx) => { const repos = await io.runTask("get-stars", async () => { // get all libraries and current amount of stars return await prisma.repository.groupBy({ by: ["name"], _sum: { stars: true, }, }); }); //loop through all repos and invoke the Job that gets the latest stars for (const repo of repos) { await getStars.invoke(repo.name, { name: repo.name, previousStarCount: repo?._sum?.stars || 0, }); } }, }); const getStars = client.defineJob({ id: "get-latest-stars", name: "Get latest stars", version: "0.0.1", // Run a cron every day at 23:00 AM trigger: invokeTrigger({ schema: z.object({ name: z.string(), previousStarCount: z.number(), }), }), run: async (payload, io, ctx) => { const stargazers_count = await io.runTask("get-stars", async () => { const {data} = await axios.get(`https://api.github.com/repos/${payload.name}`, { headers: { authorization: `token ${process.env.TOKEN}`, }, }); return data.stargazers_count as number; }); await io.runTask("upsert-stars", async () => { await prisma.repository.upsert({ where: { name_day_month_year: { name: payload.name, month: new Date().getMonth() + 1, year: new Date().getFullYear(), day: new Date().getDate(), }, }, update: { stars: stargazers_count - payload.previousStarCount, }, create: { name: payload.name, stars: stargazers_count - payload.previousStarCount, month: new Date().getMonth() + 1, year: new Date().getFullYear(), day: new Date().getDate(), }, }); }); for (let i = 0; i < Providers.length; i++) { await triggerNotification.invoke(payload.name + '-' + i, { library: payload.name, stars: stargazers_count - payload.previousStarCount, providerNumber: i, }); } }, }); const triggerNotification = client.defineJob({ id: "trigger-notification", name: "Trigger Notification", version: "0.0.1", trigger: invokeTrigger({ schema: z.object({ stars: z.number(), library: z.string(), providerNumber: z.number(), }) }), run: async (payload, io, ctx) => { await io.runTask("trigger-notification", async () => { return Providers[payload.providerNumber](payload.library, payload.stars); }); } }); ``` 現在,有趣的部分🎉 讓我們繼續建立我們的提供者! 首先建立一個名為「providers/lists」的新資料夾 --- ## 1. Discord ![Discord](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sqw7u3s19vtffxc197up.png) 建立一個名為「discord.provider.ts」的新檔案並新增以下程式碼: ``` import {object, string} from "zod"; import {registerProvider} from "@/providers/register.provider"; import axios from "axios"; export const DiscordProvider = registerProvider( "discord", {active: true}, object({ DISCORD_WEBHOOK_URL: string(), }), async (libName, stars, values) => { await axios.post(values.DISCORD_WEBHOOK_URL, {content: `The library ${libName} has ${stars} new stars!`}); } ); ``` 如您所見,我們正在使用 `registerProvider` 建立一個名為 DiscordProvider 的新提供程序 - 我們將名稱設定為“discord” - 我們將其設定為活動狀態 - 我們指定需要一個名為「DISCORD_WEBHOOK_URL」的環境變數。 - 我們使用 Axios 的簡單 post 指令將資訊加入支票中。 若要取得“DISCORD_WEBHOOK_URL”: 1. 前往您的 Discord 伺服器 2. 點選其中一個頻道的“編輯” 3. 轉到“整合” 4. 點選“建立 Webhook” 5. 點選建立的 webhook,然後點選“複製 webhook URL” 在根專案上編輯“.env”檔案並加入 ``` SLACK_WEBHOOK_URL=<your copied url> ``` ![Spidy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oyxvihf75afjubopy6dp.png) --- ## 2. Slack ![Slack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9t9ep538nt39j0xylcqp.png) 建立一個名為「slack.provider.ts」的新檔案並新增以下程式碼: ``` import {object, string} from "zod"; import {registerProvider} from "@/providers/register.provider"; import axios from "axios"; export const SlackProvider = registerProvider( "slack", {active: true}, object({ SLACK_WEBHOOK_URL: string(), }), async (libName, stars, values) => { await axios.post(values.SLACK_WEBHOOK_URL, {text: `The library ${libName} has ${stars} new stars!`}); } ); ``` 如您所見,我們正在使用 `registerProvider` 建立一個名為 SlackProvider 的新提供者 - 我們將名稱設定為“slack” - 我們將其設定為活動狀態 - 我們指定需要一個名為「SLACK_WEBHOOK_URL」的環境變數。 - 我們使用 Axios 的簡單 post 指令將資訊加入支票中。 要取得“SLACK_WEBHOOK_URL”: 1. 使用下列 URL 建立新的 Slack 應用程式:https://api.slack.com/apps?new_app=1 2. 選擇第一個選項:“從頭開始” 3. 指定應用程式名稱(任意)以及您想要新增通知的 Slack 工作區。點擊“建立應用程式”。 4. 在“新增特性和功能”中,按一下“傳入掛鉤” 5. 在啟動傳入 Webhooks 中,將其變更為「開啟」。 6. 按一下「將新 Webhook 新增至工作區」。 7. 選擇您想要的頻道並點選「允許」。 8. 複製 Webhook URL。 在根專案上編輯“.env”檔案並加入 ``` SLACK_WEBHOOK_URL=<your copied url> ``` ![SlackBot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stlaf1xmprg629tjz7wv.png) --- ## 3. 電子郵件 ![電子郵件](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq6t424munx90pdtzp7c.png) 您可以使用不同類型的電子郵件提供者。例如,我們將使用**Resend**來傳送電子郵件。 為此,讓我們在我們的專案上安裝重新發送: ``` npm install resend --save ``` 建立一個名為「resend.provider.ts」的新檔案並新增以下程式碼: ``` import {object, string} from "zod"; import {registerProvider} from "@/providers/register.provider"; import axios from "axios"; import { Resend } from 'resend'; export const ResendProvider = registerProvider( "resend", {active: true}, object({ RESEND_API_KEY: string(), }), async (libName, stars, values) => { const resend = new Resend(values.RESEND_API_KEY); await resend.emails.send({ from: "Eric Allam <[email protected]>", to: ['[email protected]'], subject: 'New GitHub stars', html: `The library ${libName} has ${stars} new stars!`, }); } ); ``` 如您所見,我們正在使用 `registerProvider` 建立一個名為 ResendProvider 的新提供程序 - 我們將名稱設定為“重新發送” - 我們將其設定為活動狀態 - 我們指定需要一個名為「RESEND_API_KEY」的環境變數。 - 我們使用重新發送庫向自己發送一封包含新星數的電子郵件。 若要取得“RESEND_API_KEY”: 1. 建立一個新帳戶:https://resend.com 2. 前往「API 金鑰」或使用此 URL https://resend.com/api-keys 3. 按一下“+ 建立 API 金鑰”,新增金鑰名稱,選擇“傳送存取”並使用預設的“所有網域”。單擊新增。 4. 複製 API 金鑰。 在根專案上編輯“.env”檔案並加入 ``` RESEND_API_KEY=<your API key> ``` ![埃里克·阿拉姆](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhk2hd2f53yfojn96yf3.png) --- ## 4.簡訊 ![Twilio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/036fdgpt0mp5h7wrisrn.png) SMS 有點複雜,因為它們需要多個變數。 為此,我們在專案中安裝 Twilio: ``` npm install twilio --save ``` 建立一個名為「twilio.provider.ts」的新檔案並新增以下程式碼: ``` import {object, string} from "zod"; import {registerProvider} from "@/providers/register.provider"; import axios from "axios"; import client from 'twilio'; export const TwilioProvider = registerProvider( "twilio", {active: true}, object({ TWILIO_SID: string(), TWILIO_AUTH_TOKEN: string(), TWILIO_FROM_NUMBER: string(), TWILIO_TO_NUMBER: string(), }), async (libName, stars, values) => { const twilio = client(values.TWILIO_SID, values.TWILIO_AUTH_TOKEN); await twilio.messages.create({ body: `The library ${libName} has ${stars} new stars!`, from: values.TWILIO_FROM_NUMBER, to: values.TWILIO_TO_NUMBER, }); } ); ``` 如您所見,我們正在使用 `registerProvider` 建立一個名為 TwilioProvider 的新提供者 - 我們將名稱設定為“twilio” - 我們將其設定為活動狀態 - 我們指定需要環境變數:`TWILIO_SID`、`TWILIO_AUTH_TOKEN`、`TWILIO_FROM_NUMBER` 和 `TWILIO_TO_NUMBER` - 我們使用 Twilio「建立」功能發送簡訊。 取得“TWILIO_SID”、“TWILIO_AUTH_TOKEN”、“TWILIO_FROM_NUMBER”和“TWILIO_TO_NUMBER” 1. 在 https://twilio.com 建立一個新帳戶 2. 標記您要使用它來發送簡訊。 3. 點選“取得電話號碼” 4. 複製“帳戶 SID”、“身份驗證令牌”和“我的 Twilio 電話號碼” 在根專案上編輯“.env”檔案並加入 ``` TWILIO_SID=<your SID key> TWILIO_AUTH_TOKEN=<your AUTH TOKEN key> TWILIO_FROM_NUMBER=<your FROM number> TWILIO_TO_NUMBER=<your TO number> ``` ![TwilioSMS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/474q2p4ejvji18xuo9om.png) --- ## 建立新的提供者 正如您所看到的,現在建立提供者非常容易。 您也可以使用開源社群來建立新的提供程序,因為他們只需要在「providers/list」目錄中建立一個新檔案。 最後要做的事情是編輯“providers.ts”檔案並加入所有提供程序。 ``` import {DiscordProvider} from "@/providers/list/discord.provider"; import {ResendProvider} from "@/providers/list/resend.provider"; import {SlackProvider} from "@/providers/list/slack.provider"; import {TwilioProvider} from "@/providers/list/twilio.provider"; export const Providers = [ DiscordProvider, ResendProvider, SlackProvider, TwilioProvider, ]; ``` 請隨意建立更多推播通知、網路推播通知、應用程式內通知等提供者。 你就完成了🥳 --- ## 讓我們聯絡吧! 🔌 作為開源開發者,我們邀請您加入我們的[社群](https://discord.gg/nkqV9xBYWy),以做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: [https://github.com/triggerdotdev/blog/tree/main/stars-monitor-notifications](https://github.com/triggerdotdev/blog/tree/main/stars-monitor-notifications) 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/top-4-ways-to-send-notifications-about-new-stars-1cgb

作為開發者賺取額外現金的 50 種方法💰

目前大環境不好,但作為開發人員,我們擁有一套獨特的技能,如果您知道在哪裡尋找,這些技能的需求量很大! 這篇文章簡要概述了 50 個作為開發人員可以用來賺取額外收入的副業 --- ### 1. 銷售注意力 基於參與度的收入是指您將根據使用者在您的網站、個人資料或消費您的內容上花費的時間來獲得收入分成。它通常很小,至少對於較小的網站或創作者來說是這樣,但隨著時間的推移,它會增加,任何人都可以啟用它 - 所以你不會有任何損失。 - [Brave](https://creators.brave.com/) - 為使用 Brave 瀏覽器造訪您的網站、個人資料或查看您的內容的使用者付費。資金以 [BAT](https://basicattentiontoken.org/) 形式存入您的 Uphold 帳戶,然後可以以美元、英鎊或歐元形式提取至您的銀行帳戶 - [Flattr](https://flattr.com/) - 付費使用 Flattr 的用戶將其資金分配給用戶存取過其內容的創作者 > 幾年前,我親自報名了 Brave Rewards。在驗證了我的網域和個人資料的所有權後,我每月一直賺幾英鎊 - 到目前為止大約 200 英鎊以上(儘管我是 Firefox 用戶!)。雖然不多,但只需付出很少的努力,就值得了。 有關其工作原理的更多訊息,請查看 [webmonetization.org](https://webmonetization.org/) 規範,該規範利用了 [付款指針](https://paymentpointers.org/)通過[ILP](https://interledger.org/) 透過使用簡單的`<link rel="monetization" href="your-pointer-here" />` 標籤來串流來自支援WM 的訪客的收入。 --- ### 2. API 即服務 RapidAPI 等平台可讓您從 API 中[賺取被動收入](https://rapidapi.com/guides/earn-a-passive-venue-by-monetizing-apis-as-a-developer)。 建置並部署簡單的 API 後,您可以將其匯入 RapidAPI Hub,選擇使用和定價計劃,然後點擊發布。您的 API 可大可小,如您所願。 如果您正在為一個簡單的第一個專案尋找靈感,請考慮將開放資料集轉換為 API。對於初學者,RapidAPI 有一個關於如何入門的[影片系列](https://rapidapi.com/courses/build-and-sell-your-own-api)。其他想法可能包括將現有套件包裝為 API、向其他服務(如 OpenAI)加入功能或建置執行一些簡單計算的端點。 --- ### 3. 發放賞金 這些是開源專案的熱門功能請求。用戶可以在承諾一定金額的情況下提供“賞金”,然後將其支付給第一個完成並合併該功能的開發人員。 - [BOSS.dev](https://www.boss.dev/) - 完成功能請求和錯誤修復,獎金從 30 美元到 1000 美元不等。 --- ### 4. 贊助商 如果您在 GitHub 或其他平台上有業務,那麼啟用贊助是一種為您的工作帶來收入的有益方式。 不要忘記啟用贊助商按鈕。這適用於各種平台以及 GitHub 贊助商 - [GitHub Sponsors](https://github.com/sponsors) - 對於開發人員(無論規模大小)來說都是一個不錯的選擇。如果支持者已經在 GitHub 上,則零費用且進入門檻低 - [Patreon](https://www.patreon.com/) - 允許向您的支持者提供福利和獨家內容。如果您在 GitHub 以外的其他平台上有業務,這是一個不錯的選擇 - [LibrePay](https://liberapay.com/) - 針對那些建立開源內容的人 - [Open Collective](https://opencollective.com/) - 如果您正在為特定專案籌集資金,並使用收益來支持該專案(而不是個人),那麼這是一個不錯的選擇 - [Steday](https://steadyhq.com/en) - [TideLift](https://tidelift.com) - 更針對那些開發企業級開源專案的人,潛在收入更大,但僅限於最大的專案 - [LFX](https://lfx.linuxfoundation.org/) - 由 Linux 基金會提供 > 贊助(特別是GitHub 贊助商)是我個人最喜歡的方法之一,因為付費是可選的,所以你不會阻止那些無力承擔費用的人存取,而且那些支持你的人已經知道他們會預先得到什麼,所以您永遠不會讓客戶失望。 --- ### 5. 小費 您可能遇到過這樣的情況,您發現某個部落格文章、SO 答案、GitHub 儲存庫或論壇回應非常有幫助,以至於您希望可以為作者買一杯啤酒來表示感謝。 支援這些小額一次性付款的平台可以免費註冊,並且在您的個人資料中或在部落格文章末尾加入「提示」按鈕不會有任何損失。 - [Ko-fi](https://ko-fi.com/) - [請我喝杯咖啡](https://www.buymeacoffee.com/) - [Tipeee](https://en.tipeee.com/) - [PayPal Me](https://www.paypal.com/paypalme/) 提示:不要乞求。建立一些有用的內容,然後將提示連結放在底部。 --- ### 6. 企業贊助 許多具有一定下載量/經常性用戶的開源專案將開始被希望贊助創作者作品的公司接洽,以換取他們的公司徽標+連結包含在自述文件頂部附近。與個人贊助不同,這些贊助通常起價為 100-500 美元/月,專案使用量越大,贊助金額就越多。 --- ### 7. 黑客松 編碼競賽一直在遠端進行。這些通常由公司贊助,並向獲獎者支付現金獎勵。 - [程式碼之夏](https://summerofcode.withgoogle.com/) - 由 Google 執行,您將收到[貢獻者津貼](https://developers.google.com/open-source/gsoc/help/student-stipends) 成功接受後,金額從750 美元到6000 美元不等,金額取決於您所在的國家/地區和專案規模 - [CodeHeat](https://codeheat.org/) - 由 FOSS Asia 運營,每兩週 100 新元,外加較小的獎品 - [HackerEarth](https://www.hackerearth.com/challenges/hackathon/) - [Hackathon.com](https://www.hackathon.com/online) - [Devfolio 黑客松](https://devfolio.co/hackathons/upcoming) > 當我還是學生時,我[曾經參加過很多](https://alicia.omg.lol/hackathons) 黑客馬拉松(大部分是面對面的),並且經常能夠通過參加各種活動來資助我的暑假活動!這也是認識新朋友、學習新事物的好方法,而且非常有趣! --- ### 8. 依賴套件的贊助 如果您有一個軟體包(例如 NPM 模組),那麼在您的設定檔中啟用贊助將允許您的程式碼的使用者在財務上做出貢獻。 - NPM 資金 - 您可能熟悉執行“npm 基金”,並查看您正在使用的正在尋求資金的軟體包清單。新增了 [npm 基金](https://docs.npmjs.com/cli/v6/commands/npm-fund),以便更輕鬆地向專案所依賴的依賴項的維護者捐款。如果您維護 NPM 包,只需在 package.json 中包含「funding」字段,用戶將能夠更輕鬆地支援您。 - [StackAid](https://www.stackaid.us/) - 只需安裝 StackAid GitHub 應用程式並連結您的 Stripe 帳戶,直接或間接使用您專案的支持者捐贈的部分資金將分配給您每個月 - GitHub Sponsors - GitHub Sponsors 再次出現,因為它[讓用戶提供他們最常用的依賴項](https://github.com/sponsors/explore) - 儘管這是一個手動過程,而不是自動的。 --- ### 9. 回報問題 如果您注重安全性,或喜歡在應用程式中尋找錯誤和漏洞,那麼這款就適合您。最受歡迎的平台是[HackerOne](https://hackerone.com/opportunities/all/search?ordering=Highest+bounties),每個負責任地披露的錯誤都可以在其中賺取20 到200,000 美元的收入。 許多其他網站也直接提供負責任的揭露政策,他們會獎勵您的工作。如果您對此感興趣,我在以下位置保留了 1000 多個賞金計劃的清單:[https://bug-bounties.as93.net](https://bug-bounties.as93.net) > 我個人透過這種方法取得了很大的成功,而且也很有趣 - 所以我強烈推薦它! 其他值得查看的平台包括: - [HackerOne](https://www.hackerone.com/) - 排名第一的平台,最多的賞金以及良好的保護和支付率 - [Immunefi](https://immunefi.com/) - 專門針對 Web3 - [BugCrowd](https://www.bugcrowd.com/bug-bounty-list/) - [Intigriti](https://www.intigriti.com/) - [issuehunt](https://issuehunt.io/) --- ### 10.開放核心模型 這是您的大部分程式碼都是開源的,但某些擴充功能或附加元件(特別是針對企業客戶的擴充或附加元件)被授權為專有的。 因此,開發者可以在其他開源專案中自由使用該軟體。然而,公司必須為使用企業特定的模組或整合付費。 請記住,這通常說起來容易做起來難。您需要能夠分離專有功能,而大公司通常會採取一切措施(包括違反許可限制)來避免付費。 --- ### 11. 付費升級套件 這些服務可以輕鬆為常見註冊管理機構提供高級/付費方案。例如,如果您希望分發 NPM 模組的高級版本,或對特定軟體包功能收費,這可能是個不錯的選擇。 - [PrivJS](https://www.privjs.com/) - 分發 Node 套件的進階版本 - [CodeShip](https://codecodeship.com/) - 私人註冊中心,用戶需要付費才能使用你的包 --- ### 12.贊助支持 在開源專案中加入專業支援計劃選項使客戶能夠支付一次性或持續的幫助和支援費用,以啟動和執行。 這可以透過您自己的系統啟用,也可以使用現有的贊助平台(例如Patreon 和GitHub Sponsors),或使用專門的服務(例如[Otechie](https://otechie.com/))來啟用,該服務加入了付費功能+ 支援通過嵌入的聊天對話框。 [Calendly](https://calendly.com/) 等工具可以讓客戶將時間放入日曆中,或者對於較大的專案,投資專用的客戶支援平台,例如[HelpScout](https://www.helpscout.com/) 可能會讓這件事變得更容易。 --- ### 13. 寫文件 - [撰寫文件](https://www.writethedocs.org/) 是所有文件的首選位置。 - [文件季節](https://developers.google.com/season-of-docs) - 在 Google 的支持下,每年都有技術作家為開源專案做出貢獻。參與專案將獲得 5,000 至 15,000 美元的贈款,然後通常透過 Open Collective 分發給貢獻者。 - 如果你環顧四周,你會發現還有很多產品正在尋找技術作家。 Julia 列出了一份[好名單](https://dev.to/juliafmorgado/get-paid-to-write-technical-articles-16cl),列出了願意付費讓你撰寫技術內容的公司 - 版權也屬於這一類。 [scripted](https://www.scripted.com/) 等服務可讓您透過校對或編輯其他文字內容來賺錢。 即使只是記錄您自己的和其他開發人員的儲存庫也是一個不錯的起點。 如果專案被記錄下來,它的價值就會大幅增加。如果沒有文件,潛在使用者、客戶或開發人員將不知道它的用途、如何使用它、如何在其基礎上建立或如何做出貢獻。 > 我可能是唯一的一個,但我個人喜歡寫文件。 [我的所有專案](https://github.com/Lissy93?tab=repositories) 包括完整的使用、開發和貢獻文件。這促進了它們的成功和採用。我覺得如果你不花一點時間向人們展示如何使用它,那麼花幾個小時建立一些很棒的東西是沒有意義的。 --- ### 14. 廣告 在你跳過這一點之前——我也討厭廣告。它們很煩人,並且經常涉及某種形式的跟踪,從而損害用戶的隱私。但是,對於開源專案,還有一些其他選項沒有這些缺點。 - [Ethical Ads](https://www.ethicalads.io/) - [Carbon Ads](https://www.carbonads.net/open-source) 如果您正在維護獲得穩定流量的 GitHub 儲存庫、網站、部落格或服務,那麼這是一個不錯的選擇。通常每月至少需要約 10,000 個用戶,但如果您每月獲得 50,000 以上的用戶,您將獲得更好的回報。 --- ### 15. 出售你的程式碼 > 我個人不同意這種方法,只是因為出售的許多程式碼都是開源軟體的糟糕的重新設計版本,並且並不總是給予原始作者適當的榮譽。也就是說,一些開發商確實設法讓它發揮作用,建造簡單的專案然後將其出售。 - [IndieMaker](https://indiemaker.co/) - 出售您的整個專案 - [PieceX](https://www.piecex.com/) - 出售現成的原始碼 - [Codester](https://www.codester.com/info/seller) - 針對 PHP 和 Wordpress --- ### 16.銷售內容 當您查看開發人員的副業時,這是一個常見的建議。但有充分的理由 - 如果您能夠建立高品質的內容,您可以賺到很多錢。特別是如果您對新興領域有深入的了解。 銷售內容的熱門網站包括: - [GumRoad](https://gumroad.com/) - 程式碼、課程、貼文、藝術、設計、媒體(10% 費用) - [AppSumo](https://sell.appsumo.com/) - 程式碼、應用程式、擴充功能、課程、範本等 --- ### 17.寫作 這是一套獨特的技能。要么您非常擅長編寫引人入勝的內容,要么您對特定的熱門領域有深入的了解。否則,如果您對此感興趣,請考慮電子書出版,如果您的書不成功,也不會造成任何損失。 - [LeanPub](https://leanpub.com/) - 一個自助出版技術/開發電子書和課程的平台,具有豐厚的收入模式(您可以保留 70%) - [Amazon KDP](https://kdp.amazon.com/en_US/) - 發佈至 Amazon Kindle,並立即向全球數百萬用戶提供(亞馬遜將收取至少 30% 的佣金,可能會更多)小出版商) - [SmashWords](https://www.smashwords.com/) 和 [Draft2Digital](https://draft2digital.com/sw/) - 分發給全球其他電子書賣家,這是一種簡單的開始出版。他們收取的佣金比亞馬遜少,但比 LeanPub 多。 --- ### 18.補助金 補助金和企業贊助涉及多個領域,包括開源、創新、DeFi、人工智慧等。它們通常是為了幫助您在從事特定工作時支付短期生活費用。 - [GitHub Sponsors](https://github.com/sponsors) - 為個人和組織提供經濟支援開源開發者的平台。金額依贊助情況而有所不同。 - [Google Summer of Code (GSoC)](https://summerofcode.withgoogle.com/) - 學生開發者為開源專案做出貢獻的全球計劃,津貼通常為 1500 美元到 3300 美元不等。 - [Mozilla 開源支援 (MOSS)](https://www.mozilla.org/en-US/moss/) - 為開源軟體開發提供資助,特別是與 Mozilla 使命相符的專案。 - [Linux 基金會資助](https://www.linuxfoundation.org/) - 為從事 Linux 基金會專案的開發人員提供各種資助和獎學金。 - [NumFOCUS 小額發展補助金](https://numfocus.org/programs/small-development-grants) - 支援資料科學和科學計算的小型專案。資助金額各不相同(所有申請人均分配 285,000 美元)。 - [Apache 軟體基金會贊助](https://www.apache.org/foundation/sponsorship.html) - 對 Apache 軟體專案的財務支持,重點關注 Apache 軟體生態系統。 - [Outreachy](https://www.outreachy.org/) - 為技術領域代表性不足的群體提供為期三個月的實習機會,津貼通常約為 5,500 美元。 - [奈特基金會](https://knightfoundation.org/grants/) - 為促進優質新聞業的技術專案提供資助。根據專案範圍的不同,贈款金額差異很大。 - [原型基金](https://prototypefund.de/) - 在六個月內提供高達 47,500 歐元的開源原型支持,重點支持德國的軟體開發人員。 - [斯隆基金會](https://sloan.org/programs/digital-technology) - 為開放科學社群計畫提供資助,特別是那些增強研究中的開源軟體的計畫。 - [Chan Zuckerberg Initiative 開源軟體專案](https://chanzuckerberg.com/rfa/) - 專注於支援對生物醫學研究至關重要的開源軟體。資助金額各不相同。 - [Raspberry Pi 基金會](https://www.raspberrypi.org/grants/) - 為涉及 Raspberry Pi 和計算教育的教育計畫提供補助。 - [GitCoin](https://gitcoin.co/) - 一個為開源專案提供資金的眾籌平台,特別是在以太坊和 Web3 領域。資金根據社區支持而有所不同。 - [NLnet 基金會](https://nlnet.nl/foundation/) - 支援網路科技與網路研究計畫。補助金額各不相同。 - [開放技術基金](https://www.opentech.fund/) - 支持開發促進人權和開放社會的開放技術的專案。資金各不相同。 --- ### 19. 舉辦活動 活動空間是一個利潤豐厚的行業,尤其是如果您能夠舉辦一場精彩的活動並為自己贏得大型贊助商的話。雖然不適合所有人,但舉辦活動可以帶來以下 10 個潛在收入來源: - **門票銷售**:透過收取入場費來產生收入。使用 [Eventbrite](https://www.eventbrite.co.uk/)、[Meetup](https://meetup.com/) 或 [Ticketmaster](https://ticketmaster.com) 等平台取得門票管理。 - **贊助**:確保科技公司的財務捐助,以換取活動中的促銷機會。 - **研討會和培訓課程**:提供特定技術或程式語言的專業實務學習經驗,收取額外費用。 - **虛擬活動**:使用[Zoom](https://zoom.us/)、[WebEx](https://www.webex.com/) 或 [Hopin](https://hopin.com/)。 - **黑客馬拉松**:舉辦收取報名費的程式設計競賽,或尋找贊助商來支付費用並提供獎金。 - **社交活動**:針對技術專業人士的社交活動收費,可能會吸引招聘公司的贊助。 - **演講活動**:利用您在特定技術領域的專業知識,組織並負責演講活動或小組討論。 - **企業培訓及靜修**:為企業內部培訓或團隊建立活動提供活動組織服務。 - **聯盟行銷**:在活動期間利用科技產品或服務的聯盟行銷來獲取額外收入。 - **產品發布**:與科技公司合作舉辦產品發布活動,為您提供收費的組織服務。 --- ### 20.研究 您的意見很有價值,尤其是作為開發人員。有些研究人員會付錢給你參加他們的研究、調查或智庫。通常,好的研究機會很少而且相距甚遠,或者報酬相當低。 這類工作的熱門平台包括:[Testable Minds](https://minds.testable.org/)、[Respondent](https://app.respondent.io/signup) --- ### 21. 建立課程 - [Skillshare](https://www.skillshare.com/teach) - 根據課程觀看分鐘數提供付款以及推薦獎金。 - [Coursera](https://www.coursera.org/for-universities) - 與機構合作提供課程;付款通常基於收入分享協議。 - [LinkedIn Learning](https://www.linkedin.com/learning/instructors) - 講師可以為專業人士建立課程;薪酬詳細資訊由 LinkedIn 安排。 - [Thinkific](https://www.thinkific.com/) - 提供建立、行銷和銷售線上課程的工具,具有各種定價計劃,包括免費選項。 - [Kajabi](https://kajabi.com/) - 線上課程、行銷、支付和網站建立的一體化平台。 - [Podia](https://www.podia.com/) - 提供一個用於舉辦課程、網路研討會和數位下載的平台,並直接向觀眾銷售。 - [Pluralsight](https://www.pluralsight.com/teach) - 專注於科技與創意課程;根據課程的受歡迎程度向教師支付版稅。 - [MasterClass](https://www.masterclass.com/teach) - 高品質、名人主導的課程;講師通常是各自領域的知名專家或名人。 - [uTeach](https://ueach.io/) - [NewLine](https://www.newline.co/) --- ### 22.時事通訊 隨著流行的社群媒體管道變得更加集中和受控,電子郵件通訊和基於訂閱的 RSS 來源正在慢慢捲土重來。 這種模式的工作方式要么是提供對技術主題或新聞的有價值的見解,並建立一個龐大的(因此有價值的)訂閱者基礎,要么是向少數用戶收取更新費用。 提供此功能的流行平台包括: - [子堆疊](https://substack.com) - [ButtonDown](https://buttondown.email/) - [ConvertKit](https://convertkit.com/) - [穩定](https://steadyhq.com) - [幽靈](https://ghost.org/) --- ### 23. 僅限會員的網站 - [MemberSpace](https://www.memberspace.com/) - 讓您能夠為網站的某些部分付費,僅供會員使用 - [Patreon](https://www.patreon.com/) - 因設定具有獨家內容和福利的會員等級而廣受歡迎。 - [Substack](https://substack.com/) - 新聞通訊的理想選擇;提供付費訂閱獨家內容的能力。 - [Ghost](https://ghost.org/) - 內建會員和訂閱功能的專業發布平台。 - [Podia](https://www.podia.com/) - 允許銷售會員資格、線上課程和數位下載。 - WordPress 與 [MemberPress 外掛程式](https://memberpress.com/) - 供 WP 使用者建立會員網站的外掛程式。 - [Wild Apricot](https://www.wildapricot.com/) - 與您的網站整合的會員管理軟體。 - [Kajabi](https://kajabi.com/) - 提供用於建立線上課程、會員網站等的工具,重點是行銷。 - [Mighty Networks](https://www.mightynetworks.com/) - 建立一個包含會員資格、訂閱和課程的社群。 --- ### 24. VIP 貼文 還有許多公司會為您在其平台上分享的優質貼文付費。這既可以提高您的知名度(幫助您擴大人脈並獲得未來的工作),也可以帶來一些短期收入。 如果您正在努力獲得這些計劃的錄取,請先編寫自己的帖子並將其發佈到流行的基於開發的社交網絡(例如 DEV.to!)。這將增強您的寫作技巧,並幫助您向潛在公司展示您的知識。 例如,以下網站將為高品質的訪客貼文付費: - [Linode](https://www.linode.com/lp/write-for-linode/) - [日誌火箭](https://blog.logrocket.com/become-a-logrocket-guest-author/) - [Smashing 雜誌](https://www.smashingmagazine.com/contact/?Becoming%20an%20Author/Reviewer%20(自動回覆)) - [Auth0](https://auth0.com/apollo-program) - [CSS 技巧](https://css-tricks.com/guest-writing-for-css-tricks/) - [DelftStack](https://www.delftstack.com/write-for-us/) - [DigitalOcean](https://www.digitalocean.com/community/pages/write-for-digitalocean) - [Infatica](https://infatica.io/contribute/) - [蜜罐](https://blog.honeypot.io/write-for-honeypot/) - [進階編碼](https://premiumcoding.com/write-for-us-premiumcoding/) - [反思](https://reflectoring.io/contribute/become-an-author/) - [Strapi](https://strapi.io/write-for-the-community) - [Android 權威](https://www.authoritymedia.com/jobs) - [SitePoint](https://www.sitepoint.com/write-for-us/) - [TutorialsPoint](https://www.tutorialspoint.com/about/tutorials_writing.htm) - [真正的Python](https://realpython.com/jobs/tutorial-writer/) - [Dart Creations](https://www.dart-creations.com/about-us/write-for-us.html) Dmytro Spilka 編制了一份包含 300 多個[接受訪客貼文的網站](https://solvid.co.uk/180-websites-that-accept-guest-posts/) 的清單。另一個很棒的清單[由 Julia 在 Dev.to 上整理](https://dev.to/juliafmorgado/get-paid-to-write-technical-articles-16cl)。 --- ### 25. 諮詢 您可能沒有意識到,您從日常工作中累積的技能和經驗對許多公司來說可能非常有價值。尤其是尚無法聘請全職專家的新創公司和小型企業。對能夠提供最新趨勢、工具和最佳實踐見解的專業人士的需求非常高。 尖端: - 以適當的速度開始的最佳方式是透過網路和口碑。但如果做不到這一點,總有自由工作網站可以幫助您累積經驗。 - 記錄你所獲得的經驗,或在你工作的過程中建立一個投資組合,因為這將幫助你在未來獲得更好的工作。 - 在開始任何專案之前,請先明確您的空閒時間、條款、日薪和工作範圍。 - 切勿拒絕潛在的聯絡人。您會驚訝地發現,即使多年後,誰可能會重新與您聯繫並尋求諮詢支援。 --- ### 26. 指導 無論您的級別如何,您作為開發人員的經驗都可以真正幫助經驗不足的其他人。指導是一種非常有益的方式,可以幫助他人,同時也能帶來一些額外的收入。 - [MentorCruise](https://mentorcruise.com/) - 主要是長期的,按月付費,非常適合建立專業關係(每個學員每月賺取 50-500 美元) - [CodeMentor](https://www.codementor.io/) - 更適合短期,按小時收費,非常適合解決特定問題(每小時賺取 60-300 美元) --- ### 27.輔導 隨著 CompSci 現在成為國家課程的一部分(至少在英國和大部分歐洲),大量學生(11 歲至 18 歲以上)正在尋找導師來幫助他們獲得編碼技能並準備考試。收入範圍為每小時 15 美元到 150 美元以上,具體取決於級別、經驗和背景。 - [Super Prof](https://www.superprof.co.uk/) - 列出您的全球服務(30-300 美元/小時) - [The Profs](https://www.theprofs.co.uk/become-a-private-tutor/) - 經過驗證的導師(收入未知) - [我的導師](https://www.mytutor.co.uk/) - 僅限英國,(22-55 英鎊/小時) - [Tutor.com](https://www.tutor.com/) - 美國高中學費($75-$100/小時) --- ### 28.社群媒體 市場存在巨大空白,等待主流社群媒體平台上真正優秀的、注重發展的影響者來填補。 許多社群媒體平台允許您透過內容貨幣化,您通常會按觀看次數付費,金額根據內容類別、地區和聲譽而有所不同。但請注意,您通常必須擁有一定數量的追蹤者才有資格,而且您還將受到「演算法」的支配。 - YouTube - 每年至少需要 1,000 訂閱者 + 4,000 小時觀看時間 - X - 需要 Twitter Blue 訂閱,無最低追蹤人數 - TikTok - 需要至少 10k 追蹤者 + 100k 瀏覽量/月 - Instagram - 需要至少 10k 追蹤者 - Snap - 1,000 名追蹤者,1,000 次瀏覽/月,10 多個每月貼文 - Facebook - 10k 追蹤者或 600k 影片觀看分鐘 - Twitch - 350 位每月付費訂閱者 --- ### 29.品牌優惠 繼上面的社群媒體部分之後,一旦您成功突破了數百名訂閱者,您可能還可以開始考慮品牌交易,這有助於帶來額外收入。同樣,這些需要您的受眾達到一定程度的參與度,您可能還需要同意提供贊助的公司的條款。 --- ### 30.串流媒體 開發串流是一個快速成長的利基市場,不要指望立即[加入排行榜](https://twitch.pages.dev/),但它可能是一個很好的起點,特別是如果您已經有串流媒體經驗(例如影片遊戲)。 Nick Taylor 寫了一篇關於 [開發串流媒體入門的精彩文章](https://dev.to/nickytonline/getting-started-with-streaming-on-twitch-4im7)。 --- ### 31.SaaS 如果您能夠做到這一點,那麼它就是開源專案的最佳收入模式之一。您的程式碼仍然是 100% 免費和開源的,用戶仍然可以免費下載和自行託管它,但您還提供付費/託管計劃,您可以在其中託管應用程式並負責小型伺服器的所有伺服器管理經常性費用。 此模型符合開源精神,同時也使您的應用程式可供更廣泛的用戶使用。 [Stripe](https://stripe.com/docs/payments) 等服務讓您的應用程式接受付款和新增訂閱功能變得非常簡單。 --- ### 32.微型 SaaS 如果從頭開始建立一個生產就緒的應用程式聽起來像是一項艱鉅的任務(因為它確實如此!),那麼另一種方法就是 Micro-SaaS 應用程式。這些是較小的應用程式,它們執行一項非常具體的任務,例如: - 自動執行重複和/或乏味的任務。 - 執行目前手動計算的計算。 - 連接不同的系統。 - 取代 Excel 電子表格解決方法。 - 填補宿主生態系中缺失功能的空白 - 加強報告 --- ### 33. 寫外掛 與 SaaS 應用程式不同,一旦建置並發布了擴展,通常不需要太多的持續管理。您也可能會發現,如果您的專案為已經完善的網站加入功能,那麼您的專案會更容易快速獲得關注。 儘管網路擴展似乎是一個過時的或完全飽和的市場,但仍然有很多可以做的事情,而且這些對於新開發人員來說都是很棒的專案。 以下是一些可以幫助您入門的想法: - [WA Web Plus](https://chrome.google.com/webstore/detail/wa-web-plus-by-elbruz-tec/ekcgkejcjdcmonfpmnljobemcbpnkamh) 已下載 200 萬次(22k 評級),收費 12 美元/每個用戶的月。為什麼不為 Telegram、Threema、Wire、Messenger 等建立類似的東西呢? - Runkeeper擁有4500萬用戶,但UI在資料顯示方式方面有所欠缺。為什麼不建立一個擴充功能來加入更好的報告、過濾以及與相關外部資料的組合? (與 [Elevate for Strava](https://chrome.google.com/webstore/detail/elevate-for-strava/dhiaggccakkgdfcadnklkbljcgicpckn) 類似,但適用於 RunKeeper) - 選擇一個提供基本服務但 UI 過於不切實際的網站(也許是 Microsoft Azure?),然後建立一個擴充功能以簡化導覽、顯示關鍵指標或提供不那麼難看的使用者體驗 - 使用人工智慧增強任何現有網站。這比聽起來容易得多,您的擴充功能可以利用 OpenAI 的 API 等服務來總結網頁,或重新措辭選定的內容(用於複製/貼上到作業中!?) - 如果您知道某個網站的使用者數量很高,但 UI 很糟糕,那麼一個簡單的擴充想法可以是應用 CSS 覆蓋來重新設計它的樣式。例如亞馬遜、雅虎、Instagram 都是高流量網站,設計改善空間巨大(深色模式?!) - 即使是簡單的獨立擴展應用程式也可能具有很大的潛力。就像番茄計時器、貨幣轉換器、IP 位址小部件或只是一個網路應用程式快捷方式。 --- ### 34. 發布應用程式 建立簡單的應用程式或遊戲,並將其在平台應用程式商店上提供,使您能夠透過簡單的盈利模型來瞄準數百萬客戶。所有主流應用程式商店 - Google Play、Apple App Store、Windows Store、Steam 等都提供對付費應用程式、進階功能和應用程式內購買的支援。 請記住,在發布第一個應用程式之前通常需要支付安裝費,應用程式商店也會從您的收入中抽取一部分,並且小型創作者獲得單次或雙次下載的情況並不罕見。人物。 --- ### 35. 為小型企業開發網站 許多小型企業都專注於自己的業務,沒有時間或專業知識建立自己的網站。作為開發人員,這是我們能夠很快完成的事情,如果您也託管他們的網站,您將能夠收取定期付款。 一旦您開始進行網頁設計和開發,並為一些客戶提供服務,您就會發現透過口碑和展示您的作品集來找到未來的工作要容易得多。 為了在這方面取得成功,您可能還需要設計、溝通和銷售方面的技能。 --- ### 36. 兜售網域 隨著新 TLD 的湧入,域名經銷商市場正在迎來第二波受歡迎。域名翻轉涉及註冊未來可能有價值的域名,然後將其轉售給想要將該名稱用於企業或專案的買家。 雖然這可能有利可圖,但它確實涉及高風險,並且需要對市場有充分的了解。 尖端: - 研究簡短或令人難忘的域名,或者可能具有較高關鍵字潛力的域名(您可以使用諸如使用 Google 關鍵字規劃師等工具來幫助進行這項研究) - 停放您目前未使用的域名,以便您同時獲得一些廣告收入 - 查看最近過期的域名,特別是那些正在使用的域名,因為這些域名可能會收到流量 - 接收流量的網域更有價值。因此,請考慮在您持有網域時為其建立網站、應用程式或登入頁面 --- ### 37. 使用者測試 開發應用程式的公司通常需要獲得用戶的回饋。這就是用戶測試服務的用武之地。您花 10-30 分鐘嘗試給定的網站或應用程式,然後提供反饋或填寫調查,並獲得報酬! 儘管並非特定於開發人員,但憑藉您的技術背景,您會發現自己具有獨特的優勢,可以快速完成這些工作並提供良好的反饋,從而使您比普通用戶更快地賺錢。您還將獲得有關用戶測試流程如何運作的寶貴見解,這可能對您在自己的應用程式上進行委託測試時有用。 - [嘗試我的 UI](https://www.trymyui.com/) - 每個網站或應用程式測試平均費用為 10 美元 - [Userlytics](https://www.userlytics.com/user-experience-research/paid-ux-testing/) - 根據測試的複雜程度和長度,賺取 5 至 50 美元之間 - [使用者測試](https://www.usertesting.com/get-paid-to-test) - 透過 PayPal 付款,在測試會話期間需要螢幕共用和/或網路攝影機存取。每次測試賺取約 10 美元,較長時間或現場會議的某些測試最高可支付 50 美元 - [TestingTime](https://www.testingtime.com/en/become-a-paid-testuser/) - 面對面或視訊通話測試的選項。不太定期,但測試時間更長。當您考慮到會話之間的延遲時,報酬比其他選擇更低 - [IntelliZoom](https://www.intellizoom.com/) - 每 10 分鐘學習可賺取 2 至 10 美元。透過 PayPal 付款,延遲 3-5 天 --- ### 38.微任務 與開發人員的具體關係不大,但如果您來自技術背景,您可能會發現這些工作比那些沒有開發技能的工作更有利可圖。 - [Amazon Mechanical Turk](https://www.mturk.com/) - 外包虛擬微任務的眾包市場 - [Sequence Works](https://sequence.work/contributors/) - 影像標註、資料標記與分類 - [App Jobber](https://en.appjobber.com/) - 市場調查,去商店拍攝特定植入式廣告的照片 - [GigWalk](https://www.gigwalk.com/gigwalkers/) - 應用程式為基礎的行動微任務 - 請造訪 [GigWorker.com](https://gigworker.com/) 以了解更多微任務和零工工作 --- ### 39. 調查 儘管對具有某些技能(如軟體工程)的參與者的需求較高,但調查的報酬往往很低,因此可以賺更多一點。即便如此,除非您有大量時間,或使用比美元弱得多的貨幣,否則這可能不是一個好的選擇。 這些通常涉及測試新產品或服務,並提供回饋 - 或回答問題以協助市場研究活動。 有很多不同的基於調查的公司,所以我不會全部連結到它們。但 [Swagbucks](https://www.swagbucks.com/)、[20Cogs](https://20cogs.co.uk/)、[TestingTime](https://www.testingtime.com/en/become-a-paid-testuser) 是一些著名的。 --- ### 40.去中心化節點 這可能不適合所有人,因為收益通常以加密貨幣形式支付,而加密貨幣的波動性非常大。但是,您可以自願為許多 Web3 專案執行節點(通常在 Rasperry Pi、雲端伺服器或備用筆記型電腦上),這將為您支付正常執行時間、頻寬、磁碟空間、運算、IP/代理或其他一些費用。計算資源。 作為開發人員,管理基礎設施是我們所擅長的,因此,如果您有任何閒置資源,您也許可以將它們投入使用,並在睡覺時賺取一些額外的現金。 - [Storj](https://www.storj.io/node):執行Storj節點,用於去中心化雲端運算 - [Network3](https://network3.io/):用於訓練和驗證模型的 AIoT 第 2 層 - [Flux](https://runonflux.io/):去中心化基礎設施 - [Mysterium](https://mystnodes.com/): P2P VPN 節點 - [Koii](https://www.koii.network/node): 分散式雲 - [Helium](https://www.helium.com/mine):提供遠端物聯網設備無線連接 - [Filecoin](https://filecoin.io/):它是一個去中心化儲存網絡,將雲端儲存轉變為演算法市場。用戶可以出租閒置的儲存空間並賺取 Filecoin 代幣。 - [Sia Network](https://sia.tech/host):這是一個由區塊鏈技術所保障的去中心化儲存平台。 Sia 透過去中心化網路儲存和加密您的檔案。您可以透過出租未使用的硬碟空間來賺取 Siacoins。 - [Crust Network](https://wiki.crust.network/docs/en/nodeOverview):與 Filecoin、Sia 類似,Crust 支援 IPFS 等多種儲存層協議,並為應用層提供儲存介面。 - [Arweave](https://www.arweave.org/):一個基於區塊鏈的平台,以永久和去中心化的方式提供資料儲存。透過託管資料,用戶可以獲得 Arweave 代幣獎勵。 - [BitTorrent](https://docs.btfs.io/v2.0/docs/install-run-btfs20-node):該平台標記了世界上最大的文件共享協議,使用戶能夠通過在網路上。 - [HOLO](https://holo.host/):Holochain 應用程式 (hApps) 的點對點託管平台。在電腦上託管 hApp 的用戶將獲得 HOT 代幣獎勵。 --- ### 41.其他 Web3 方法 加密產業還有許多其他賺取被動收入的方式,從PoS 質押、持有生息數位資產、借貸、流動性挖礦、雲端挖礦、賺取股息的代幣、流動性挖礦、交易、本地/ PoW 思維、NFT 等等。很少。 我不會在這裡連結到任何具體細節,因為這是一個風險非常高的行業,因此您自己進行研究很重要。但作為技術專家,我們能夠理解任何給定協議或 Web3 資產背後的基本概念,並確定其可行性。 我的建議是閱讀白皮書,如果你不能立即理解它,那就遠離它!這是狂野的西部,所以除非一個專案的基本面是堅實的,否則你應該做好失去投資的任何資金的準備。 --- ### 42. 聯盟行銷 聯盟行銷對於那些剛開始的人來說是眾所周知的無利可圖,但我將其包含在此處是因為作為開發人員,有一定的空間來自動化許多過程。此外,您行銷的服務越細分,支付的佣金通常就越高。因此,如果您融入了技術社區,您可能處於銷售小批量高回報服務的有利位置。 同樣,如果您已經有了追蹤者(社交、部落格、YouTube 頻道...),那麼聯盟行銷可能更有意義,因為如果您獲得了大量點擊。 值得注意的是,您可能不應該在未透露它是附屬連結的情況下共享附屬連結。並儘量避免宣傳您自己沒有使用過或不會推薦給朋友的產品。 > 作為範例,[此處](https://notes.aliciasykes.com/p/3Ia4JzPw43) 是我使用過且擁有附屬帳戶的一些服務。我從未從其中任何一個身上賺過任何有意義的錢。 --- ### 43.經銷商 這涉及建立一個應用程式來包裝現有服務,同時加入 USP - 技術、客戶支援、UI 或其他功能。如果您有行銷或銷售背景,這可能適合您。如果您想加入功能或使流程自動化,那麼將需要大量的前期工作,但您將能夠更好地獲得收入。 您可以在大多數主要行業中找到提供經銷商計劃的服務提供者。 一些例子包括: - [Supermetrics](https://supermetrics.com/):行銷報告、分析、資料整合、20% 經常性佣金。 - [Keap](https://keap.com/):CRM、銷售與行銷自動化、20-30% 經常性佣金。 - [Klaviyo](https://www.klaviyo.com/):電子郵件與簡訊行銷,5–15% 一次性付款,10–20% 收入分成。 - [Drift](https://www.drift.com/):即時聊天軟體,20%收益分成。 - [ActiveCampaign](https://www.activecampaign.com/):電子郵件行銷、CRM、20–30% 佣金或折扣模式。 - [HubSpot](https://www.hubspot.com/):CRM、入站行銷、銷售、20% 營收分成。 - [Gorgias](https://www.gorgias.com/):電子商務幫助台,20% 收入分成。 - [Shopify](https://www.shopify.com/):電子商務平台,佣金20%,Shopify Plus 10%。 - [LiveChat](https://partners.livechat.com/):客戶服務平台,即時聊天,委託20%。 - [GetResponse](https://www.getresponse.com/):電子郵件行銷、線上活動管理、子帳號 35% 折扣、35% 經常性佣金。 --- ### 44. 人體實驗 這與技術根本無關。但作為程式設計師,我們通常可以在任何地方工作 - 那麼為什麼不在有報酬的地方編寫程式碼呢? 通常,您的收入在 2,000 美元到 10,000 美元之間,具體取決於試用期、持續時間、是否為住宅和具體情況。 像[流感營](https://flucamp.com/) 這樣的地方將支付您 4,000 英鎊,讓您在舒適的酒店式套房中入住兩週,同時他們會測試新的治療方法。那些患有氣喘等特定疾病的人可能可以透過參加更專業的試驗來賺取更多收入 --- ### 45.自由職業 自由職業可能會根據您的技能、經驗和您所在的領域而有所不同。對於新自由工作者來說,某些領域(例如網頁開發)的費率往往非常低,但您擁有的經驗和客戶滿意度越高,您就越能夠充電。 開發人員零工工作的三個主要平台是: - [Fiverr](https://www.Fiverr.com/):Fiverr 以其多元化的市場而聞名,非常適合剛開始從事自由職業的開發人員 - [Upwork](https://www.upwork.com/work):Upwork 迎合了廣泛的專業人士,但它對經驗豐富的開發人員特別有利。它提供了長期合約和高薪工作的潛力。該平台適合喜歡從事更實質專案的人。 - [People per Hour](https://www.peopleperhour.com/):這個平台對歐洲市場的開發者有好處。它強調當地的商業聯繫,並在短期和長期專案之間提供良好的平衡。 --- ### 46. 說話 面對面的和移除的開發者聚會和活動在全球各地不斷發生。這些活動需要演講者,許多人願意付費以獲得良好的演講。支付的金額根據規模、觀眾、主題、演講者(你!)和其他因素而有很大差異。通常,您必須先自願在當地的小型技術聚會上發表演講,然後逐步提高。 --- ### 47. 遠端技術支持 這不是最迷人的角色,但較小的公司通常無法聘請全職的專門技術支援人員,因此您可以找到很多兼職工作。如果您擁有雲端經驗或認證,這些的薪資等級會大幅提高。只需查看任何求職板(例如 [WeWorkRemotley](https://weworkremotely.com/)),您就會看到大量職位。 請注意,您通常需要在特定時間內有空,並期望您可以在給定的時間內回覆。在申請之前,請確保這是您可以解決的問題。 --- ### 49. 投資 是的,這不是副業——但聽我說完… 如果您每年的收入為 6 萬美元,生活成本為 4 萬美元,那麼 5 年後您可能會有 10 萬美元的儲蓄。如果您將其投資於年平均回報率為10 - 15% 的標準普爾500 指數- 您每月可能會從您的投資中獲得超過1,000 美元的額外收入,並且您的投資能力越強,收入就會不斷增加儲蓄(當然,投資可以減少也可以增加)。這已經比這裡列出的許多副業更好的回報了! --- ### 50. 就業 我們不要忘記,儘管目前情況看起來很艱難,但身為開發人員,即使只有一兩年的經驗,我們也處於非常幸運的地位,與平均收入者相比,我們的薪水很高。 如果你的工作不適合你——換公司通常是提高薪資的可靠方法,如果你不喜歡目前的工作,這可能是值得考慮的事情。 也許經歷了這一切之後,你所追求的不是副業,而是更好的「主業」? --- ## 真實的說話 儘管您可能會在 IndieHackers 和 Instagram 上看到一些內容,但副業並不是全部。這通常需要大量的工作,但回報卻非常有限。因此,在在這裡或其他地方進行任何事情之前,請退後一步,思考「我為什麼要這樣做?」。如果您這樣做是為了累積經驗、學習新技能並享受樂趣——那就太好了。如果你這樣做是為了快速致富 - 你可能會非常失望。 還有一點要注意的是,儘管看起來不公平,但與那些剛起步的人相比,那些已經擁有強大追隨者或幾個成功的開源專案的人將處於更好的位置來利用機會。 因此,從短期來看,作為一名開發人員,您的時間可能會更好地花在提升自己身上。如果您不確定從哪裡開始,這裡有 5 個關鍵提示: - **網絡** - 建立你的網絡,參加聚會、黑客馬拉松和開發活動,加入社區,結交朋友 - **開源** - 將您的工作放在那裡,公開學習,建立您感興趣的迷你專案,並且不要害怕失敗 - **經驗** - 獲得實務經驗,申請實習機會,作為自由開發人員提供服務 - **基礎知識** - 確保您對電腦科學基礎知識有深入的了解,其餘的就會容易得多 - **玩得開心!** - 你自然會在你真正熱愛的領域做得更好。如果你不喜歡你正在做的事情,請退一步,考慮不同的方法是否更適合你 --- ## 免責聲明 - 以上列表僅供您參考。 - 我沒有親自測試過這裡列出的所有服務。 - 如果您有 - 我很想聽聽您的回饋。同樣,如果有任何需要加入或刪除的內容,請在下面告訴我。 - 並非所有服務都在所有國家/地區提供(此清單主要針對英國/歐洲和美國🇬🇧🇪🇺🇺🇸) - 有些平台會抽取您的收入。這通常是一個很小的數額,但重要的是你要考慮到 - 如果您已經擁有大量追隨者或流行的開源專案,賺錢通常會容易得多 - 有些方法涉及風險。儘管我已盡力強調這一點,但請記住,您的投資可能會下降而不是上升 - 您的結果可能會有所不同 - 沒有保證 --- 原文出處:https://dev.to/lissy93/50-ways-to-bring-in-extra-cash-as-a-developer-19b6

分享從十開始的轉職之路

哈囉大家好,我加入這平台有九個多月,後來神隱一段時間,趁現在的我有空閒時間,趕緊與大家分享我的轉職之路。 神隱的這段時間想當然是成功轉職,跑去上班,才有辦法與大家分享我的轉職心得XDD。開始上班後,因為有太多東西都是第一次接觸到,忙得不可開交,所以拖到現在才上來分享,抱歉><" ! 那麼就進入正文吧! 我大學是有接觸過程式的經驗的,正確來說,應該是這科系本來就會接觸到( C、C++、Java、Python等),但因為學校的特殊性,也有碰到硬體,當時我是比較傾向於硬體,所以程式沒甚麼太專研,成績有過就好,後來畢業後也是從事硬體職位,程式幾乎只記得超基礎的東西。後來的職涯目標轉變,所以重新開始寫程式,跟別人不太一樣的地方大概就是有一點程式的基礎,但我也沒接觸過網頁,所以是從大概距離起跑點十的位置前進吧 ? 哈哈哈 ~ 確定目標後就果斷離職,離職後開始全力進修學網頁(3月開始),也設定了進度: 三月 ~ 六月 : 學會HTML、 CSS、JS最基本語法。 六月 : 開始準備履歷作品、學 Vue 及投履歷 七月 : 邊面試、邊修履歷及作品 八月 : 開始上班。 一開始在找資源時,因緣際會下,發現到這平台,就跟著站長的那些課程進度走。我就真的只有上站長這裡的課程,完全沒去其他地方,例如udemy、Hahow 好學校等。沒錯......因為我想省錢,當時是想說先上看看,真的不行再去買那些課程,結果就一路到現在都還沒買其他課程......。 跟了這平台的課程後,也認識到了一些學習前端的免費資源,像是W3Schools等,所以雖然是跟著進度上,但我是搭配著上。甚麼意思呢 ? 舉例來說,在學HTML時,站長在文內可能只說學第幾個(A)跟第幾個(B)即可完成這作業,但在W3Schools,A跟B之間可能還有其他的部分,所以我是照W3Schools裡的HTML的順序學下來,學到課程內要學的A跟B,就先去完成作業,再接下去學。因為也有提供中文的W3Schools,所以也是這樣喔 ! 寫過的就當作複習,加深印象,打好基礎~ 而在自學中,最難的事之一就是自律這件事,所以我每天都一定強迫自己最晚十點到圖書館,午休一個小時,因為有找個兼職,要維持生活開銷,所以到六點就會離開圖書館。除了休息那一個小時外,我都是在學習,沒有中途玩手機還是做其他事喔 ! (有時覺得用眼過度,抬頭看窗外,那不到三分鐘的事不算~),一周有六天都是如此,禮拜天就會給自己放鬆一下,只到下午兩、三點。 一直這狀態直到六月,開始準備作品,當時也是爬文、看別人都是怎麼準備、展示自己的作品的,就發現大多的人都會做個人網頁。而要怎麼做個人網頁呢?就繼續爬爬爬...,就認識到GitHub有提供GitHub Pages,能把自己的作品上傳到這後,別人就可以看到你的作品。真的是非常方便,而且又是免費的阿 ! 剛好課程也有教到Git指令,所以就架了一個自己的履歷網頁~ 準備得差不多後,就照著站長建議的,把工作經驗一年以下、無經驗的、可以應屆畢業生(雖然我不是),全都投了一遍,只留自己夢想公司,這樣就能先累積面試經驗,並依面試得到的反應去修改自己的作品,再最後投夢想公司。能投得都投後,就開始照課程學框架Vue,若有做出作品就隨時更新到我的個人履歷網頁上。 面試時,大多都是看履歷問問題,有些有筆試,但都是像網路上大家說的那樣,前端必學觀念,沒遇過上機coding的。有些有手寫code,通常只有1-2題,印象中,有個題目是要你把字串反過來,怎麼寫 ? 只要把網路上大家說的必考題目弄熟,基本沒有太大問題。 而個人面試時有遇到的幾個問題,第一個是作品有點陽春Q,本身也沒有UI/UX的經驗等,所以是這職位轉過來的在這點上就會很加分呢 ! 我只能靠時間一點點的累積、培養了QQ。再來就是很常被問自學的困難點,又加上我只在這平台學習(雖然他們不知道),但我認為最重要的還是有沒有決心想學前端,如果有就一定會去找資源,遇到問題,也不會那麼容易就挫折、放棄,畢竟已經下定決心自學的人都早就帶有這覺悟了吧 ? 就像在學Vue時,可能在這課程學到的不完善,但我認為這裏只是給一個學習的方向,那麼就可以朝這方向去找更多資訊,也能知道哪邊需要在著重釐清概念,就可以去看這部分的影片! 之後,運氣非常好的收到幾個offer,並在八月如預期開始上班,真是太開心了( 灑花~ 最後就說一些建議,雖然還是菜鳥,但應該可以多少能給一些方向 ? 這些建議是,若我今天還沒開始上班,我還會做甚麼事,大家就當作參考吧~ 假如我能帶著上班後遇到問題的記憶回到還沒就職時,我會: 1. 把JS基礎打好,多找題目做! 2. (假如學的框架是Vue),慢慢開始從API換成CLI,並多做作品,熟悉框架物件等等之間的傳遞。 3. 認識更多套件,並學會使用,可以的話套看看在框架上,並把每個套件組合成一個流暢性的作品。 4. Code Review : 會打程式固然重要,但專案的架構、流程設計的好,真是輕鬆一半,也會更清楚程式間是如何傳遞的! 5. 課程的每個作業能把它做到最好就做到最好~ 就先大概這樣吧,真的是有太多東西等著我去學,希望未來的我不要菜味這麼重,也能為大家提出更實質性的建議或幫助,不管是自學的還是轉職的,大家一起加油吧 ! ( 我可能又會神隱一段時間,但有時間,一定會繼續完成站長的課程 & 與大家分享工作心得的 !

Javascript Proxy Magic:我如何建立一個零依賴的 2kB 狀態管理器(以及它如何為我提供了兩個不同的工作機會)

狀態管理器到底是什麼?狀態管理器是一個智慧模組,能夠保留(應用程式或 Web 應用程式的)會話資料並對資料的變更做出反應。 您是網頁開發人員嗎?使用過 Redux、Mobx 或 Zustand 等函式庫嗎?恭喜!您已經使用了狀態管理器。 我記得我第一天嘗試為 React 設定(舊的)Redux。只要想到所有不必要的複雜性——調度程序、減速器、中間件,我就會患上創傷後壓力症候群(PTSD)!我只是想聲明一些變數,_請讓它停止_。 ![](https://media.tenor.com/Fj8YV_9ut8UAAAC/makeitstop-i-just-want-it-to-stop.gif) 這是一個過度設計、臃腫的庫,每個人都在使用!由於某種瘋狂的、未知的原因,它成為了當時的行業標準。 ###一些背景故事 2021 年的一個晚上,當我無法入睡時,我漫無目的地打開 GitHub,注意到我以前的大學課程老師(我在 GH 上關注過他)為他現在的學生上傳了一份作業。該作業要求學生使用公共 Pokemon API 建立一個 Pokedex 網站。目標是用 Javascript 實現它(沒有框架或函式庫,因為他目前的學生是 Web 開發初學者,仍在學習 Javascript 和開發的基礎知識)。 作為一個笑話,主要是因為我睡不著,我開始在我的神奇寶貝網站上工作。最終,我能夠建立一些可行的東西,而無需使用任何外部庫。 ### 但一路走來,我很掙扎...... 你看,我已經習慣了擁有一個狀態管理器,以至於在不使用外部框架或庫的情況下建置一個簡單的兩頁應用程式的要求讓我開始思考 - _為什麼狀態管理器必須如此復雜?這只是變數和事件._ 長話短說,我發現自己在凌晨 2 點組裝了一個超級簡單的狀態管理器模組,只是為了管理我的 Pokemon Web 應用程式的狀態。我將我的網站部署到了 GitHub 頁面,然後就忘記了這一切。 幾個月過去了,但出於某種原因,我時不時地思考我的狀態管理解決方案...你看,它有其他庫沒有的東西 - _它太簡單了。_ _“嘿!”我心想,「我應該將它重寫為 NPM 套件」。_ 當天晚上,我就這麼做了——我把它寫成了一個獨立的 NPM 包。最後,它的重量為 2kB(相比之下 Redux 的 150kB),具有零依賴性,並且使用起來非常簡單,您只需 3 行程式碼即可完成設定。 ### 我稱之為 VSSM 代表**_非常小的狀態管理器_**。 您可以在[GitHub](https://github.com/lnahrf/Vssm)上查看原始程式碼。另外,請查看使用 React 和 VSSM 建立的[文件網站](https://lnahrf.github.io/Vssm-docs/)。 第二天,我發布了我的 NPM 包,然後又忘記了這件事。 同年晚些時候,我面試了兩家不同公司的全端開發人員職位。我在第一家公司的面試中取得了優異的成績,這是一家非常成熟的科技公司。作為面試過程的一部分,他們要求我告訴他們我是否在空閒時間編碼,或者是否有我貢獻過的任何開源專案等等。 當時我做的唯一很酷的事情就是 VSSM,所以我告訴了他們。他們對我自己建立一個「Redux 替代方案」的想法印象深刻。 另一方面,我在第二家公司的面試中慘敗。我的大腦一片空白,我很緊張,無法回答簡單的問題,例如 > “React 會在狀態變更時重新渲染整個應用程式,還是在使用 Redux 時僅更新受影響的元件及其子元件?” “每次狀態更新時,它都會重新渲染整個應用程式”,我說。 ![](https://media.tenor.com/ZFc20z8DItkAAAAd/facepalm-really.gif) 我很緊張,哈哈,顯然我知道正確的答案是「它只渲染註冊的元件以及可能受影響的子元件」。 直到今天我也不明白為什麼二號公司決定給我第二次機會。他們邀請我再次接受採訪(是的!)。 在我的第二次面試中,他們要求我告訴他們我是否在空閒時間編碼、開源貢獻,你知道該怎麼做。當我告訴面試官我的小副專案時,他看起來很高興,似乎他喜歡我只是因為我從頭開始編寫了一個狀態管理器。 我想情況確實如此,因為我第二次面試也失敗了(在程式設計挑戰期間耗盡了時間),但仍然得到了一份工作機會。 1 號公司打算向我發送報價,但我已經與 2 號公司簽署了報價。 我的底線是——我建立 VSSM 幫助我獲得了這兩個機會。 ![](https://media.tenor.com/BuoCYXAkk0AAAAAC/big-lebowski.gif) ### 我是怎麼做到的? 您是否知道 Javascript 內建了監視變數變更所需的所有功能? 它被稱為代理(它很神奇)。 Javascript 代理程式是程式碼和變數分配之間的附加邏輯層。 如果您要將物件包裝在代理程式中,您可以決定在每次更新時將其值記錄到控制台,除了為該物件指派新值之外,無需執行任何操作。 ``` const target = { v: "hello" } const proxyTarget = new Proxy(target, { set: (target, property, value) => { console.log(`${property} is now ${value}`); target[property] = value; return target[property]; } }); proxyTarget.v = "world!" // v is now world! ``` VSSM 是基於代理建置,它在變數賦值和其餘程式碼之間建立了一個層。使用代理,您可以設定 setter、getter,並在操作或請求目標值時實現任何類型的邏輯。 VSSM 不僅僅是一個代理,它是各種智慧代理,它們知道分配給變數的值是它的新值還是回調方法。 例如,使用 VSSM,您只需幾行程式碼即可設定狀態、監聽變更並發出事件。 ``` import { createVSSM, createState } from 'vssm'; import { getVSSM } from 'vssm'; // Create the initial state createVSSM({ user: createState('user', { address: '' }) }); // Get the user proxy reference const { user } = getVSSM(); // Listen to events on user.address user.address = () => { console.log(`Address updated! the new address is ${user.address}`); }; // Emit the mutation event user.address = 'P.Sherman 42 Wallaby Way, Sydney' ``` 正如您所看到的,我確保我的狀態管理器盡可能簡單。我的目標是擺脫僅僅為了分配一些變數而陷入減速器、中間件和極其複雜的配置的困境。 現在,一切都透過分配變數來進行!想要設定監聽器嗎?將回調函數指派給變數。想要編輯值並發出事件嗎?只需指派一個新值即可。 直到今天我仍然不明白為什麼流行的狀態管理器必須如此複雜,也許我永遠不會。 我鼓勵您繼續閱讀 [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) 上有關 Javascript 代理的所有內容。 ### 這一切的結論是什麼? 我認為,對自己所做的事情充滿熱情是關鍵。 我建立 VSSM 只是為了突破自己的極限並發布合理的 NPM 包。它成功地給面試官和同事留下了深刻的印象,並讓我從那時起就進入了不同的職位。 沒有人會使用 VSSM,它不會流行。當我將其發佈到 NPM 時,我就意識到了這一事實。但我仍然選擇盡我所能,因為我熱衷於做一些我認為比行業標準更好的事情。我知道我可以做出一些必須更好的東西,即使這意味著它對我更好。 儘管 VSSM 已經死在 NPM 墓地裡,但它給我帶來了很多價值,並且因為這篇文章而繼續這樣做。 獲得開發工作的最佳方法是建立令人驚嘆的東西,即使您認為這一切以前都已經完成了 - 建置得更好。即使您認為沒有人會使用它,那又有什麼意義呢? - 現在建置,價值稍後顯現。 不要低估你的能力,如果你認為自己有不足,請知道你會進步。走出去,建構能夠帶來價值的專案,一次一小步。 祝您工程之旅順利。 --- 原文出處:https://dev.to/lnahrf/javascript-proxy-magic-how-i-built-a-2kb-state-manager-with-zero-dependencies-and-how-it-got-me-two-different-job-offers-2539

用 React 和 Node.js 建立 GPT Web 應用程式產生器 - 在 4 個月內,從點子到 25,000 個應用程式

我們正在開發 [Wasp](https://github.com/wasp-lang/wasp) - 一個基於 React、Node.js 和 Prisma 建置的全端 Web 框架。自從 GPT 出現以來,我們想知道是否可以使用它來更快地建立 Web 應用程式。這讓我們想到了 [MAGE - 一個由 GPT 驅動的 Web 應用程式產生器](https://usemage.ai/),它可以根據簡短的描述建立完整的堆疊程式碼庫。 我們已經寫過[MAGE 可以(和不能)做什麼](https://dev.to/wasp/gpt-web-app-generator-let-ai-create-a-full-stack-react-nodejs-codebase-based-on-your-description-2g39)和[它在幕後的工作原理](https://wasp-lang.dev/blog/2023/07/17/how-we-built-gpt-web-app-generator)。這是關於它的起源和採用的故事 - 為什麼我們決定建立它,開發人員如何發現它,以及他們實際上用它做什麼。 ## 為什麼要建構另一個 AI 編碼代理? 我們很晚才進入整個 GPT 編碼代理遊戲。在我們開始考慮建立自己的工具之前,Smol AI、GPT Engineer 和 MetaGPT 等工具就已經受到了廣泛的關注,我們對此也很清楚。 ![編碼代理景觀](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zw6vyjt79bxrsyvdhl78.png) 那為什麼還要麻煩呢?事實是,這些代理程式都不是專門為建立 Web 應用程式而設計的,而這正是我們看到機會的地方。 儘管使用 GPT 代理進行編碼可以讓人感覺超級強大,但它們通常會很慢並且沒有抓住要點,需要多次迭代,最終使該過程相當麻煩且昂貴。 有了這些經驗,我們想知道,*「如果我們為特定堆疊中的 Web 應用程式製作一個高度專業化的編碼代理,而不做其他事情會怎麼樣?它能變得更容易、更快、更便宜嗎?」* 儘管我們對此很感興趣,但作為一個兼顧多個優先事項的小團隊,我們仍然相當懷疑,幾乎放棄了整個專案。事實證明,它的效果比我們預期的還要好。 ## 第 1 步:建造它🛠️ ![運作方式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkqk1i0p311u9zd9ouk6.png) 在決定 MAGE(*Magic App Generator*)的 v0 版本時,我們考慮了多種選擇。第一個也是最直接的一個是將其與 Wasp CLI 集成,因為我們已經擁有了圍繞它的所有工具。然後,開發人員將執行“wasp new myProject -ai”,而不是執行“wasp new myProject -ai”,CLI 會要求他們提供應用程式描述和其他所需的輸入,然後在終端中完成所有工作。 另一方面,我們知道在開始描述您的應用程式之前下載並安裝 Wasp 是一個很大的要求。最重要的是,CLI 中的使用者介面功能非常有限。這就是我們選擇網路介面的原因 - 零摩擦開始,我們可以讓應用程式建立流程變得非常簡單且美觀。 花了幾週的時間來建構它,最終的結果是一個[用Wasp 製作的完全開源的Web 應用程式](https://github.com/wasp-lang/wasp/tree/wasp-ai/wasp-ai)可以在 https://usemage.ai/ 上免費使用,或在本地託管以獲得更多控制和靈活性(例如,使用您自己的 OpenAI API 金鑰)。 ### 超級具體(僅限網頁應用程式)得到了回報! ![法師計畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqkrqj8b3we67ufxl8gm.png) 如上所述,我們的主要賭注是建立一個專門的編碼代理來建立全端 Web 應用程式,而不是其他任何東西,這得到了回報。它使我們能夠預先為代理提供大量上下文和結構,並引入專門的啟發式方法來修復錯誤。此外,由於 Wasp 的高級抽象(例如身份驗證、專案結構、查詢和作業系統等),我們顯著減少了錯誤的表面積。 另一個結果是執行時間顯著減少,甚至更重要的是成本減少。典型的MAGE 建立的Web 應用程式成本在0.10 至0.20 美元之間,而一般編碼代理[同一應用程式的花費可能高達10 美元](https://wasp-lang.dev/blog/2023/08/01/smol-ai-vs-wasp-ai#thoughts--further-considerations)(所有價格均在 OpenAI 23 年 11 月 7 日定價更新公告之前)。 您可以閱讀有關Wasp 內部工作原理的更多資訊[此處](https://wasp-lang.dev/blog/2023/07/17/how-we-built-gpt-web-app-generator),以及它的比較其他編碼代理[此處](https://wasp-lang.dev/blog/2023/07/17/how-we-built-gpt-web-app-generator)。 ## 第 2 步:啟動它🚀 ![圖表](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/icff70qgd5ozu23ghgw7.png) 開發產品是一回事,但傳播產品並讓人們使用它則完全是另一回事。在我們建立了 MAGE 並在團隊內對其進行了測試之後,問題是下一步該做什麼?我們如何真正聯繫開發人員並開始接收回饋? ### 社區啟動 - 生命的證明 ![waspularity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwcmspqyer7hxjysjyjo.png) 由於當時我們已經擁有一個[擁有大約 1,000 名成員的 Wasp 社區](https://discord.gg/rzdnErX),因此我們[發布了 MAGE 作為我們發布週 #3 的一部分](https://wasp-lang.dev/blog/2023/06/22/wasp-launch-week-third#gpt-web-app-generator--friday-waspularity---tutorial-o-thon)。這是一個很好的測試平台,我們可以看到第一個應用程式正在建置。儘管如此,更多的開發人員本可以從更簡單的方式來啟動他們的 React 和 Node.js 專案中受益,但他們卻無法找到 MAGE。 ### 啟動 Product Hunt — 首次「真正」使用 ![mage-ph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3z5dkjuxn8502699s5a.png) 這就是為什麼我們決定在內部社群啟動後將 MAGE 放在 Product Hunt 上。儘管Product Hunt 不是一個特定於開發工具的平台,並且吸引了來自不同背景的人群,但仍然有很多開發人員在使用它,而且我們[之前有很好的經驗](https://www.producthunt.com/products/wasp-lang-alpha/launches) 與它。 Product Hunt 對於[獲得Wasp 的第一批用戶並在GitHub 上獲得1,000 顆星](https://wasp-lang.dev/blog/2022/09/29/journey-to-1000-gh-stars) 至關重要,因此我們想再試一次。 有些人在發布準備工作上投入了大量精力,需要提前幾個月才能準備好,但我們沒有那個時間,只是想盡快完成。我們要求我們的朋友和社區查看 [MAGE on Product Hunt](https://www.producthunt.com/products/wasp-lang-alpha#gpt-webapp-generator-for-react-node-js) 並提供支持我們在發布當天就進入了當天的前十名產品,後來又躋身本週排名第二的開發者工具! 這就是我們的目標,因為排名前十的產品最終會出現在第二天的電子報中,有超過 100 萬訂閱者閱讀。很快,我們看到建立的應用程式數量顯著增加,新的人開始加入我們的 Discord 社群! ### 有機成長(又稱「無所事事」)階段 在 Product Hunt 推出後,我們放鬆了行銷工作,因為其他與 Wasp 相關的任務趕上了團隊。我們必須為即將到來的[發布週#4](https://wasp-lang.dev/blog/2023/10/13/wasp-launch-week-four)做準備,所以 MAGE 最終被擱置了。在我們決定投入更多資源之前,我們也想看看社區如何接受它。 我們發布了更多後續文章,「[它是如何在後台工作的](https://wasp-lang.dev/blog/2023/07/17/how-we-built-gpt-web-app-generator)”和“[MAGE vs. 一般編碼代理](https://wasp-lang.dev/blog/2023/08/01/smol-ai-vs-wasp-ai)”,獲得平均數量牽引力,但沒有爆炸。我們在 Reddit 和 Hackernews 上也沒有什麼成功,感覺社群已經厭倦了所有的人工智慧內容。 儘管如此,使用 MAGE 建立的應用程式數量持續增長(約 200 個應用程式/天),偶爾會出現小規模爆炸。我們無法真正追蹤用戶來自哪裡 - MAGE 似乎是透過封閉社區和時事通訊中的口碑傳播的。 ### YouTube 和 TikTok 影片 - 病毒式傳播(每天 1,300 個應用程式!) 在我們的第 4 週發布週結束後,我們意識到,在近 2 個月的時間裡,在我們付出最小努力的情況下,MAGE 一直在不斷成長。這向我們表明它具有一定的實際價值,因此我們決定對其進行更多投資。 ![matt_yt_video](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sztjfowul34w6uqwzb56.png) 我們決定與該領域的影響者碰碰運氣。我們不想簡單地支付廣告費用,而是希望與真正對我們正在建立的產品感興趣並且想要客觀地審查 MAGE 的人合作。我們發現 [Matthew Berman](https://www.youtube.com/watch?v=KQrGu8cnwvA&t=2s&ab_channel=MatthewBerman) 涵蓋了 LLM 領域從最新模型到工具的所有內容,他將 MAGE 視為非常適合他的觀眾。 該影片在幾週內就準備好了,當它發佈時,觀看次數很快就達到了約 25,000 次。許多觀眾對透過 Web 介面從單一提示中獲取全端程式碼庫的可能性感到興奮,我們看到使用率和開發人員再次嘗試。 ![tiktok_video](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnfmgt026dvf5yn0a9sm.png) 大約一周後,我們看到建立的應用程式數量再次大幅增加,但無法弄清楚它來自哪裡。我們做了一些搜尋,在TikTok 上找到了一位開發者@techfren,他製作了[一個關於它的短影片](https://www.tiktok.com/@techfren/video/7288306291733269778)(MAGE 甚至最終無法在就是那個!),一天之內瀏覽量猛增至 20 萬次,並迅速走紅。如今,它的瀏覽量已接近 100 萬。 ## 現實 - 所有這些應用程式會發生什麼? 儘管 25,000 個建立的應用程式聽起來令人印象深刻,但退後一步並進一步細分是很好的。 與大多數人工智慧驅動的編碼工具一樣,想要建立自己產品的開發人員和非開發人員都對該領域抱持極大的興趣和好奇心。許多人甚至沒有想要建立的具體專案,但他們熱衷於嘗試一下,看看它是如何運作的。此外,由於法學碩士不是確定性的,因此還沒有任何工具能夠完美執行,並且經常會出現需要手動幹預和編碼知識的小錯誤。 雖然我們對此非常明確(https://wasp-lang.dev/blog/2023/07/10/gpt-web-app-generator#what-kind-of-apps-can-i-build-with-it )和其他[挑戰](https://wasp-lang.dev/blog/2023/07/10/gpt-web-app-generator#challenges)GPT驅動的工具面臨,MAGE仍然吸引了一部分的用戶對建置自己的產品感到興奮,但不精通編碼,需要幫助下載、執行和修復應用程式。另一方面,它是一種非常友好且易於參與 Web 開發和編碼的方式。我們不會阻止非編碼人員嘗試,而是盡可能透明地管理期望。 因此,大約 40% 的所有建立的應用程式都會下載並在本地執行。 ## 結論 事實證明,我們對 MAGE 的實驗非常成功,甚至超越了我們最初的預期。除了許多現有的通用編碼代理之外,高度專業化和結構化的方法可以以低廉的價格產生更好、更一致的結果。 此外,開發人員對啟動全端應用程式的簡單方法(其中包含最佳實踐)感到興奮,並且正在積極尋找這樣的解決方案並在彼此之間共享。 我預計人工智慧輔助的 SaaS 新創公司將成為 Web 開發的未來。如果有人可以使用已經為其應用程式定制的資料模型和 CRUD 邏輯來建立他們的應用程式,那麼為什麼有人會使用通用樣板啟動器呢?另一個問題是誰以及如何具體實現這一點,但我預計未來每個主流框架都會有一個。 ## 祝你好運! 我希望這篇概述對您有所幫助,並讓您了解建立和行銷新的(人工智慧驅動的)開發工具時幕後的情況。請記住,這是我們獨特的經歷,每個故事都是不同的,因此對一切都持保留態度,只選擇對您和您的產品有意義的內容。 我們祝您好運,如果您有任何疑問或想了解 [MAGE](https://usemage.ai/) 和 [Wasp](https://github.com/wasp-lang/wasp)! --- 原文出處:https://dev.to/wasp/how-we-built-a-gpt-web-app-generator-for-react-nodejs-from-idea-to-25000-apps-in-4-months-1aol

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

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

使用 Python 和 Pytest 自動化 API 測試

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

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

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

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

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

作為開發人員創建內容如何改變了我的生活

原文出處:https://dev.to/chaoocharles/how-creating-content-as-a-developer-changed-my-life-270e 大家好,我想鼓勵一些想要開始編碼職業但發現很難找到第一份工作或實習的人。還有那些因為開始懷疑自己的能力而難以開始/完成專案的人(冒名頂替症候群)。我將透過告訴你我自己的旅程以及我一路上學到的東西來做到這一點,希望你能有動力繼續你的旅程。 我叫查爾斯,來自非洲肯亞。我早在 2016 年就開始了我的編碼之旅,當時我進入大學,在 BTECH I.T 尋求職業生涯。在此之前,我對電腦了解不多,我只知道它們很酷,我想研究它們。在高中時,我是一名表現最好的學生,我的英語老師(也是副手,哈哈)認為我想做一些像計算機這樣簡單的事情而不是像工程、醫學、飛行員等更專業、更有前途的職業,這是愚蠢的,你說出它們的名字。好消息是我沒有聽,而是去做了我想要的事情,而且我不會為此感到後悔。事實是,IT 領域的任何職業都是當今世界上最好的職業之一,世界頂級公司之所以能處於領先地位,是因為程式碼。看看 Netflix、亞馬遜、微軟、Facebook、Airbnb 或 Uber。所有這些公司都透過程式碼賺了數十億美元,因此不要讓任何人欺騙您,讓您認為您走在錯誤的道路上。我來這裡是為了告訴你,你正走在最好的道路上。 回想2016年剛入職第一年的時候,由於出身卑微,擁有一台筆記型電腦對我來說是非常困難的,即使是一台簡單的筆記型電腦,甚至是二手的筆記型電腦。如果我要做 I.T,那麼擁有一個對我來說也是強制性的。擁有一部像樣的智慧型手機也是一個問題,但幸運的是,我有一個三星口袋(那些小三星手機,如果你還記得的話),並在說服朋友用它與我交換一些錢和一部按鍵手機後。這款手機在這個故事中很重要,因為它是我用來開始學習程式設計的手機。在學校裡,我們被教導如何編碼,是的,但這還不夠,它主要是理論。關於編碼的事情是你必須_練習_、_練習_、再_練習_。於是,我從同學那裡了解到了一個名為sololearn的應用程式。我在那裡開始學習 Web 開發,包括 html、css、javascript、php、sql,我想還有一點 jquery。這個應用程式教會了我有關這些主題的所有基礎知識,並且在完成每節課後它都有有趣的挑戰和徽章。唯一的問題是我不會透過手機進行完整的項目編碼。但重點是,當您等待購買筆記型電腦時,您絕對可以開始透過智慧型手機學習程式設計。因此,不要因為沒有筆記型電腦而高枕無憂。你越早意識到沒有人來拯救你越好。 我繼續從我的三星口袋裡學習了一個月,後來在內羅畢街頭(有很多扒手)它被神秘地偷走了。學校的朋友送了我一部HTC手機,螢幕碎了,有些地方根本碰不著,我得旋轉螢幕多次才能碰到地方😂,不過乞丐不挑食,我就繼續用了以便在本學期剩下的時間裡學習更多有關編碼的知識。 第二學期,現在是2017年,我收到了學生貸款。我的一些朋友/同學用他們的貸款去聚會、喝酒以及在俱樂部與女孩們玩耍。嗯,就我而言,我知道自己從哪裡來,也知道自己要去哪裡。於是,我拿了一些錢,立刻買了一台筆記型電腦。剩下的錢用來付學費和一點生活費。這對我的案例來說是一個巨大的進步,因為我現在可以了解更多資訊並開始從筆記型電腦上處理專案。因此,如果您有錢,請停止考慮參加聚會和購買昂貴的東西,而是考慮如何用這筆錢讓您的生活變得更好。 2017年至2019年期間,沒有太大變化。我只是在學習和做學校作業等。我想我還用 HTML 和 CSS 製作了兩個網站,我為這些工作獲得了一些報酬。我探索了更多關於編碼的知識,包括學習Java 中的OOP(物件導向程式設計),也用Java 進行了一些Android 應用程式開發,我的筆記型電腦無法處理android studio 😂,所以我又回到了Web 開發。我探索了 WordPress 以及如何用它製作博客等,並建立了一個 WordPress 博客,並以幾美分的價格出售。 2019年底,我正在讀三年級第三學期(這在我們大學被稱為內部工業實習),大約在這個時候,我被敲響了警鐘。我意識到我一直在學習編碼,但除了簡單的 html、css 和 WordPress 網站之外,我仍然無法建立一個完整的專案。我對任何一種程式語言都沒有足夠的信心。另外,畢業的要求是在第四年完成一個編碼專案。我也開始對未知產生恐懼,例如放學後要做的事情,因為只剩下一年了。我開始做很多研究如何創建一個完整的網絡應用程序,因為我已經了解了 javascript 的基礎知識,並且出現了一件我不知道的事情,即 javascript 框架,目前最流行的是 Angular、Vue並做出反應。當時我就知道我必須學習這些框架之一,而且很難決定選擇哪一個,但我最終選擇了 React,因為它是所有框架中最受歡迎、最有前途的工作,而且我仍然使用 React 進行編碼這點。 我嘗試從sololearn學習React,但進展並不順利。我嘗試了 youtube 並發現了 @thenetninja 頻道:https://www.youtube.com/@NetNinja,它非常有益健康,這就是我對 React 的很好的介紹。後來我從 Udemy 學習了兩門完整的 React 課程,一門由 _Stephen Grider_ 教授,另一門由 _Maximilian Schwarzmüller_ 教授。順便問一下,我沒有完成它們,誰完成了 udemy 課程? 😂 但這兩門課程教會了我更多關於 React 的高級知識。 在學習 React 的過程中,我也很好奇如何在放學後或還在學校的時候透過程式碼賺錢,我發現了幾個選擇,找工作、自由工作、創建內容(這可以是寫部落格或 YouTube 頻道) )、開始播客、寫書、創建像udemy 這樣的課程等等。由於我還在上學,我知道找工作很難處理,所以我決定嘗試自由工作和內容創作。還是在 2019 年,我開設了 YouTube 頻道:https://www.youtube.com/c/chaoocharles 來教授編碼,也開設了一個 upwork 帳號來從事自由專案的編碼。 我知道我不是一個好的作家,正如你從這篇文章中絕對可以看出的那樣,所以我嘗試了視頻而不是博客。這是另一個挑戰,因為我必須學習如何製作影片、學習錄製和編輯軟體等等。但我還是堅持了下來,並從同學那裡得到了我的前 100 個訂閱。我的影片一開始就很糟糕,而且我做了很多工作,甚至沒有得到一分錢。但從正面的角度來看,製作影片讓我更理解編碼概念。就像,對我來說,要解釋我必須先理解的東西。我主要用 html、css 和 React 創建了很多視頻,做得越多,我對創建視頻和編碼就越有信心。 2020 年,我有很多時間來做這一切,因為我們因新冠疫情關閉了學校近一年,經過一年的努力,我終於獲得了 1000 名替補,這對我來說是一個巨大的勝利。 2020 年中期,我取得了兩場重大勝利,在 YouTube 上達到了 1k,並且在 Upwork 上找到了我的第一個客戶。我需要 1000 訂閱者和 4000 小時的觀看時間,YouTube 才會開始向我付費。我距離 4k 觀看時間還很遠,但至少我已經達到了其中一項要求。 Upwork 也很難找到第一個客戶,我申請工作卻無濟於事,但這第一個客戶改變了遊戲規則。我讓他相信我知道如何編碼,並用我的 YouTube 教程證明了這一點。你看,在製作 YouTube 教學的同時,我也在為自己建立一個作品集,我也在我的 github 上發布了多個專案。這麼說吧,我的投資組合目前看起來非常好,這位客戶毫不猶豫地給了我一份合約。如果你碰巧教了一些東西,人們就會開始將你視為專家(即使你正在努力教那件事😂)這可以通過視頻或博客,我認為你應該嘗試一下。我在 upwork 專案上做得很好,這個客戶在那一年和接下來的一年裡繼續給我更多的專案。我做了他的大約 8 個項目,在 upwork 上獲得了上升人才徽章,後來又獲得了頂級徽章,這讓我贏得了更多客戶。 ![Upwork](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jut1tydubsj4mwlmnt3h.png) 是的,我知道我現在的工作成功分數很差,但你明白了,哈哈😂 在製作YouTube 影片和Upwork 專案時,我也在做我的最後一年項目,因為我現在已經是第四年了,但這些是我自從2020 年因新冠疫情在家以來所做的唯一事情。雖然在2020 年底,我們回去上學期,做了考試並展示了專案。 快轉到 2021 年,我現在正在做附件。這是我畢業的必要條件。我透過在 Facebook 和網路上尋找肯亞的網路開發公司,輕鬆獲得了這一點。評論他們的帖子並解釋我的 IT 背景。我從某家新創公司的 CEO 那裡得到了 DM,並且以前端開發人員的身份加入了那裡。我試圖就報酬進行談判,但最好的結果只是維持交通和午餐,每週去辦公室三天,並承諾在實習後獲得一份長期工作。作為一名學生,這已經足夠好了,所以我就這麼做了。 在這家公司,我的 CSS 和 React 技能給他們留下了深刻的印象。我改造了他們公司的網站,並在我在那裡的5個月期間又做了2個網站。學校對我進行了評估,後來公司給了我工作機會。我覺得每個月的收入對我的技能來說不夠好,因為我可以在一兩週內輕鬆地做同樣的自由職業,而且我在那裡的時候我的 YouTube 也得到了貨幣化。我只是拒絕了這個提議,並決定專注於我的自由職業和內容創作之旅。如果他們允許我在處理其他事情的同時遠端處理他們的項目,也許我會接受這個提議,但這不在他們的公司政策中。我不想滿足於更少。你看,過去幾年我所做的一切都給了我選擇,也讓我不再急於找到工作,並擁有競爭優勢。 2021 年年中,我以二等高年級畢業,我保證如果不是我用 YouTube 和 upwork 分散自己的注意力,我會獲得一等,第四年我表現最差。但我後悔嗎?不。問題是,我從來沒有用過那個學位,它仍然鎖在家裡的某個地方,沾滿了灰塵。好處是,技能和經驗是這個編碼和程式設計領域最重要的。只有少數公司可能會要求學位,但大多數公司不會。他們會詢問您過去的經驗、您從事過的項目,並希望您通過程式設計面試。因此,如果你正在尋找一份程式設計工作,並因為沒有獲得學位而指責自己沒有學位,那麼你應該停下來。我們大多數擁有學位的人甚至沒有使用它們。也許我們唯一的優勢是我們在學校建立的聯繫或從那裡獲得的技能。但說實話,我所知道的大部分內容都是我自學的,我相信每個程式設計師都是自學的程式設計師,無論他們是否上過學。你必須親自動手。光靠論文並沒有太大幫助。 畢業後,我開始從 YouTube 獲得專案邀請,以及報酬豐厚的專案。我也開始利用 YouTube 和 GitHub 來在 Upwork 上獲得更好的付費項目,透過分享我的個人資料連結來告訴客戶我所取得的成就。所以,所有這些加起來就很不錯了。現在,我僅透過內容創作來支付所有帳單,並透過在工作和外部工作項目上工作來獲得更多收入。我的時間也很靈活,在家工作,這很棒。 2022 年我只做了一份全職工作。雖然位置偏遠,但完全值得。 我的觀點是,如果你正在努力尋找一份工作或一個項目,你可以透過為自己建立一些東西來改變一切。建立部落格、建立播客、創建頻道、創建公司、創建課程、寫書、公開構建(啟動一個大型專案並在此處和 Twitter 上分享您的進度),只需在這裡展示您的技能即可您能做什麼,遲早你會開始從事高薪專案。停止追逐工作,而只是吸引他們。 正如你從我的旅程中可以看出的,這不是一天的成就,直到一年多我才得到一分錢的內容創作報酬,直到一年多我才得到一個客戶的工作。我不是一天就能學會程式設計的,我是從一部手機開始的,後來又是用學校貸款買的一台低階筆記型電腦(我甚至還沒付)。所有這些成功的人士和公司都是從某個地方開始的,您今天就可以開始改變您的生活。開始親自動手,兩三年後你甚至不會相信自己來自哪裡。 這是我的故事,我希望你學到了一兩件事✌️ 訂閱我的 YouTube 頻道:https://www.youtube.com/c/chaoocharles 在 Twitter 上關注我:https://twitter.com/ChaooCharles

2023 年開發者的 24 個開源專案🔥👍

原文出處:https://dev.to/madza/24-open-source-projects-for-developers-in-2023-391l --- 標題:2023 年開發人員的 24 個開源專案 🔥👍 發表:真實 描述: 標籤: 開源、 github、 程式設計、 Web 開發 封面圖:https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74998ffdt3doqvxtxumz.png canonical_url:https://madza.hashnode.dev/24-open-source-projects-for-developers-in-2023 --- 開源專案是創新、協作和創造力的遊樂場。它是來自世界各地的開發人員聚集在一起分享他們的想法、技能和熱情的中心。 在本文中,我精心挑選了 24 個涵蓋廣泛興趣和技術的開源專案。 從尖端的人工智慧框架到漂亮的生產力工具以及介於兩者之間的一切,每個開發人員都能找到適合自己的東西。 我提供了直接連結、描述和視覺效果,以便您可以立即獲得每個工具的初步印象。 --- ## 1\. [集算器SPL](https://github.com/SPLWare/esProc)(贊助) 集算器SPL是一種基於腳本的資料操作語言,與SQL資料庫集成,支援進階分析和高效能並行處理。 它適合處理大型資料集,與各種工具集成,提供資料視覺化,並跨多個平台工作。一些主要功能包括: **💪 強大的資料處理能力:** 集算器SPL是一種腳本語言,具有豐富的函數庫和強大的語法。 **✨ 預存程序等效項:** 它允許透過 JDBC 介面執行 SPL 腳本。 **📈 多功能視覺化:** 它提供了成熟的報告工具,具有廣泛的視覺化配置,用於建立各種類型的報告。 **⚡ 自動化工作流程:** 它支援軟體工作流程的自動化,包括用於程式碼建置、測試和部署的 CI/CD 流程。 **🔥 相比SQL更具彈性:** 與SQL語法不同,集算器SPL允許將資料處理程式碼寫在多條語句中。 ![esProc_SPL](https://cdn.hashnode.com/res/hashnode/image/upload/v1679824673641/82f843e0-72a1-44a4-bd99-68616f322534.pw?m=1600,4cro format =網頁) ⭐ 支援他們的 GitHub 倉庫:[https://github.com/SPLWare/esProc](https://github.com/SPLWare/esProc) ## 2\. [跳房子](https://github.com/hoppscotch/hoppscotch) 一種多功能開源 API 開發和測試工具,提供使用者友善的介面,用於發出 HTTP 請求來測試 API 並與 API 互動。 它簡化了製作和發送請求的過程,使其成為使用 API 的開發人員和測試人員的必備工具。 ![Hoppscotch](https://github.com/hoppscotch/hoppscotch/raw/main/packages/hoppscotch-common/public/images/banner-dark.png) ## 3\. [Supabase](https://github.com/supabase/supabase) Firebase 的開源替代方案,為開發人員提供了一組用於建立可擴展的即時應用程式的工具。 它提供了強大的後端即服務 (BaaS) 平台,具有身份驗證、資料庫管理和即時功能等功能,使其成為創建現代 Web 和行動應用程式的強大選擇。 ![Supabase](https://supabase.com/_next/image?url=%2Fimages%2Fproduct%2Fstorage%2Fheader--dark.png&w=1920&q=75) ## 4\. [超級代幣](https://github.com/supertokens/supertokens-core) 一種開源身份驗證解決方案,提供強大的安全功能和輕鬆集成,以增強 Web 和行動應用程式中的使用者身份驗證和授權。 它為開發人員提供了一個全面的工具包,用於保護用戶資料並確保無縫登入體驗。 ![Supertokens](https://supertokens.com/docs/static/assets/arch.png) ## 5\. [Git](https://github.com/git/git) Git 版本控制系統的官方開源程式庫,最初由 Linus Torvalds 建立。 Git 廣泛用於追蹤原始程式碼的更改,並透過提供強大的分支和合併功能來實現協作軟體開發。 ![Git](https://www.lumis.com.br/data/files/FC/F4/E3/0A/098EA7108FA5E7A7C808A8A8/Gitflow__-_blog___interna.png) ## 6\. [VS 代碼](https://github.com/microsoft/vscode) 由 Microsoft 開發的免費開源程式碼編輯器。 它提供了高度可自訂且高效的程式設計環境,具有 IntelliSense、調試支援和龐大的擴充庫等功能,可增強您的開發工作流程。 ![VS代碼](https://user-images.githubusercontent.com/35271042/118224532-3842c400-b438-11eb-923d-a5f66fa6785a.png) ## 7\. [OhMyZsh](https://github.com/ohmyzsh/ohmyzsh) 一個流行且高度可自訂的框架,用於在類 Unix 作業系統中管理 Zsh 配置。 它簡化了 shell 自訂,提供了大量插件和主題來增強您的命令列體驗。 ![OhMyZsh](https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png) ## 8\. [包子](https://github.com/oven-sh/bun) 一個開源 JavaScript 工具包,旨在簡化和優化為 Web 應用程式捆綁 JavaScript 程式碼的過程。 它提供了一種現代且快速的方法來建立捆綁包,從而增強了使用 JavaScript 專案時的效能和開發人員體驗。 ![Bun](https://cdn.hashnode.com/res/hashnode/image/upload/v1696318057709/5a1125cf-eb78-4e9d-9632-faebd228abe5.png) ## 9\. [SWR](https://github.com/vercel/swr) SWR(Stale-While-Revalidate)是一個用於在 React 應用程式中取得資料的 JavaScript 函式庫。 它可以在客戶端和伺服器之間實現高效、自動的資料同步,提供無縫的即時更新,同時確保資料保持新鮮和最新。 ![SWR](https://cdn.hashnode.com/res/hashnode/image/upload/v1696318453842/d9ab3384-becc-4040-93f7-8a9e064100b1.png) ## 10\. [Prisma](https://github.com/prisma/prisma) 用於現代應用程式開發的開源資料庫工具包,透過強大的查詢產生器和類型安全的 ORM(物件關聯映射)層簡化資料庫存取和操作。 它允許開發人員使用聲明性和直觀的方法管理資料庫並與之交互,從而使資料庫操作在各種資料庫系統中無縫且安全。 ![Prisma](https://i.imgur.com/O1lwo0v.png) ## 11\. [ElasticSearch](https://github.com/elastic/elasticsearch) 由 Elastic 開發的強大且可擴展的開源搜尋和分析引擎。 它旨在幫助用戶快速有效地搜尋、分析和視覺化大量數據,使其成為從全文搜尋引擎到日誌分析等應用程式的熱門選擇。 ![ElasticSearch](https://cdn.hashnode.com/res/hashnode/image/upload/v1696315923559/58c2db03-9a6c-4b98-9b48-a91025c507a2.png) ## 12\. [哈蘇拉](https://github.com/hasura/graphql-engine) 一款功能強大的開源工具,可簡化應用程式的 GraphQL API 開發。 借助 Hasura,您可以輕鬆建立、管理和保護 GraphQL API,從而更輕鬆地與資料來源互動並建立現代的資料驅動應用程式。 ![Hasura](https://assets.website-files.com/63e3d6905bacd6855fa38c1c/63e3d6905bacd64f08a38f95_Hasura.jpg) ## 13\. [BioDrop](https://github.com/EddieHubCommunity/BioDrop) 透過單一連結與您的受眾建立聯繫。在一處展示您創建的內容和項目。 讓人們更容易找到、關注和訂閱。 ![BioDrop](https://user-images.githubusercontent.com/624760/230707268-1f8f1487-6524-4c89-aae2-ab45f0e17f39.png) ## 14\. [Powertoys](https://github.com/microsoft/PowerToys) 適用於 Windows 的開源實用程序,可提高工作效率和自訂功能。 它提供了一系列方便的工具和實用程序,包括快速啟動器、文件預覽和視窗管理等功能,旨在簡化您的 Windows 體驗。 ![Powertoys](https://cdn.hashnode.com/res/hashnode/image/upload/v1696280333258/279d3728-4731-46eb-9836-c8300d3a9f75.png) ## 15\. [Strapi](https://github.com/strapi/strapi) 開源無頭內容管理系統 (CMS),使開發人員能夠快速建立強大且可自訂的 API。 它使團隊能夠輕鬆創建和管理內容豐富的網站和應用程序,為各種專案提供靈活性和可擴展性。 ![Strapi](https://cdn.hashnode.com/res/hashnode/image/upload/v1696316645227/6122feae-4b38-4c00-a8a1-30da5346568c.png) ## 16\. [看似合理](https://github.com/plausible/analytics) 一種開源網路分析工具,旨在為網站所有者提供對其網站效能的簡單且注重隱私的見解。 它提供用戶友好、輕量級的跟踪,且不會損害訪問者的隱私,使其成為那些重視數據分析而無需侵入性跟踪方法的人的理想選擇。 ![看似](https://cdn.hashnode.com/res/hashnode/image/upload/v1696280734881/0cc0aa58-46e1-49ac-a920-65f7eaad6e33.png) ## 17\. [Astro](https://github.com/withastro/astro) 現代靜態網站產生器,透過僅傳送頁面所需的 JavaScript 來提供閃電般的效能,從而實現近乎即時的載入時間。 它將傳統伺服器渲染框架的靈活性與靜態網站產生器的速度相結合,使其成為建立高效動態網站的絕佳選擇。 ![Astro](https://deegloo.com/wp-content/uploads/2022/11/blogblog-cover-1024x614.png) ## 18\. [混音](https://github.com/remix-run/remix) 用於建立現代 JavaScript 應用程式的 Web 框架,注重速度和開發人員體驗。 它使開發人員能夠透過無縫組合伺服器渲染和客戶端渲染的內容來創建高效能的 Web 應用程式。 ![混音](https://cdn.shopify.com/s/files/1/0779/4361/files/RemixRun_bcc7b8fd-ca3a-4385-b279-91a0606706e7.jpg?v=1666895610) ## 19\. [張量流](https://github.com/tensorflow/tensorflow) 由Google開發的開源機器學習框架。 它為建立和部署機器學習模型提供了靈活且全面的生態系統,使其成為人工智慧領域研究人員和開發人員的熱門選擇。 ![Tensorflow](https://m-alcu.github.io/assets/tensorflow-playground.png) ## 20\. [顫動](https://github.com/flutter/flutter) 由 Google 創建的開源 UI 軟體開發工具包,以其從單一程式碼庫建立適用於行動、Web 和桌面的本機編譯應用程式的能力而聞名。 它使開發人員能夠使用單一程式語言 Dart 跨多個平台創建美觀、快速且高度可自訂的使用者介面。 ![Flutter](https://cdn.hashnode.com/res/hashnode/image/upload/v1696281232879/35493958-0397-40c4-9c30-ca0faead9f39.png) ## 21\. [Kubernetes](https://github.com/kubernetes/kubernetes) 一個開源容器編排平台,可自動執行容器化應用程式的部署、擴充和管理。 它為編排容器提供了強大而靈活的基礎架構,使在雲端原生環境中大規模管理複雜的分散式系統變得更加容易。 ![Kubernetes](https://d33wubrfki0l68.cloud.net/2475489eaf20163ec0f54ddc1d92aa8d4c87c96b/e7c81/imaimages/docofs/components-d4c87c96b/e7c81/images/docofs/components-vv-uberknetes.svvv) ## 22\. [Docker](https://www.docker.com/community/open-source/) 一個開源工具,可簡化多容器 Docker 應用程式的管理。 它允許開發人員使用簡單的 YAML 檔案定義和運行多容器應用程序,從而更輕鬆地編排和部署複雜的服務。 ![Docker](https://cdn.hashnode.com/res/hashnode/image/upload/v1696316908120/7e01fe2b-a438-4882-8cd6-863b7f5effb0.png) ## 23\. [鉻](https://github.com/chromium/chromium) Google 的一個開源瀏覽器項目,旨在為所有使用者建立更安全、更快、更穩定的網路體驗方式。 它是開發人員在網路瀏覽技術領域做出貢獻和創新的平台。 ![Chromium](https://cdn.hashnode.com/res/hashnode/image/upload/v1696319343433/61d13e7f-512b-40b7-a127-b127a944cf9d.png) ## 24\. [Linux 核心](https://github.com/torvalds/linux) 由 Linus Torvalds 和全球貢獻者社群開發的開源、類別 Unix 作業系統核心。 它作為各種基於 Linux 的作業系統的核心組件,提供硬體互動和系統管理的基本功能。 ![Linux 核心](https://upload.wikimedia.org/wikipedia/commons/2/2e/Linux_Mint_21_%22Vanessa%22_%28Cinnamon%29.png) --- 寫作一直是我的熱情,幫助和激勵人們讓我感到很高興。如果您有任何疑問,請隨時與我們聯繫! 透過[Twitter](https://twitter.com/madzadev)、[LinkedIn](https://www.linkedin.com/in/madzadev/) 和[GitHub](https://github.com) 與我聯繫/madzadev)! 請訪問我的[部落格](https://madza.dev/blog)以獲取更多此類文章。

9 個開源 Repo,讓您的 SaaS 變得更好用、更賺錢

原文出處:https://dev.to/nathan_tarbert/9-open-source-repos-that-will-make-your-saas-gold-54h7 從頭開始建立軟體即服務 (SaaS) 可能是一項耗時的工作。 不用擔心,有預先配置的 SaaS 樣板可供使用,包括我將很快介紹的樣板,它可以為您提供所需的必要加速和節省時間的提升。 [![SAML Jackson 告訴我更多](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3nvwn4he63t4h3vfbaz2.gif)](https://github.com/boxyhq/jackson) ## 什麼是 SaaS? 我很高興您提出這個問題,簡而言之,軟體即服務 (SaaS) 是一種基於雲端的軟體模型,可透過瀏覽器向使用者交付應用程式。 軟體和基礎設施由 SaaS 供應商管理,用戶可以按需存取服務,通常採用訂閱或按使用付費定價模式 所有業務需求都可能非常不同,並且不同組織的精選功能清單也可能有所不同。 您的 SaaS 應用程式可以整合開源軟體以增強其功能並為您的用戶提供卓越的價值。 讓我們看看如何利用有價值的開源軟體來改變遊戲規則。 當您建立 SaaS 應用程式時,它為您的新興業務提供更通用、更強大的解決方案。 ___ ##。 [BoxyHQ 的 SaaS 入門套件](https://dub.sh/saas-starter-kit)這是您的 SaaS👇 ![BoxyHQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhvluqkvf9q8paeaogw7.gif) 使用 Next.js SaaS 樣板啟動您的企業應用程式開發。 - 透過利用開箱即用的預先建構樣板功能來促進開發。 - 顯著減少建立您自己的 SaaS 的時間,並專注於建立您的核心應用程式功能。 - 非常適合新創公司以及那些希望透過強大的開箱即用安全性來增強現有應用程式的公司。 https://dub.sh/saas-starter-kit 請加註星標 ⭐ BoxyHQ ___ ## 1. [Cerbos](https://github.com/cerbos/cerbos) ![Cerbos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6fner5o7ms26kyatqgrb.gif) Cerbos 是一個開源、可擴展的授權層,可簡化跨多個應用程式和服務的使用者角色和權限的實施和管理。 - 在 SaaS 應用程式中自訂存取控制,可讓您定義細粒度的權限以滿足使用者的獨特要求。 - 防止對敏感資料進行未經授權的存取,並確保資料免受未經授權的使用者的侵害。 - 增強的使用者體驗,使用戶能夠精確控制他們在應用程式中的存取權限。 https://github.com/cerbos/cerbos 請加註星標 ⭐ Cerbos --- ## 2. [SuperTokens](https://github.com/supertokens/supertokens-core) ![SuperTokens](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kli4jmrlj8mlk212gr7o.gif) SuperTokens 是一種開源身份驗證和授權解決方案,旨在為 Web 和行動應用程式提供安全登入和可擴展的存取管理。 - 支援多種身份驗證方法,包括會話管理和 JWT,確保使用者身份驗證無縫且安全。 - 防止常見的安全陷阱,例如會話劫持和資料洩露,增強使用者對應用程式的信任。 - 透過簡化使用者身分驗證流程,改善使用者體驗,確保使用者可以輕鬆、放心地存取應用程式。 https://github.com/supertokens/supertokens-core 請加註星標 ⭐ SuperTokens --- ## 3. [Retraced](https://dub.sh/retraced) ![回溯](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ee21hrk9ockrsyub52zj.gif) Retraced 是一項開源審核日誌服務,用於記錄整個組織的軟體系統內的活動。 - 提供對應用程式內所有活動的詳細跟踪,使您能夠監控誰存取了您的應用程式以及他們執行了哪些操作。 - 及早發現可疑或未經授權的行為,從而能夠快速回應潛在的安全事件。 - 透過提供使用者操作和系統事件的稽核追蹤來維持透明度和問責制。 https://dub.sh/retraced 請加註星標 ⭐ Retraced --- ## 4. [unleash](https://github.com/Unleash/unleash) ![unleash](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5d4z5n8jpl2elyi4gam.gif) unleash 可以輕鬆測試程式碼如何與實際生產資料配合使用,而不必擔心意外破壞使用者體驗。 - 控制功能推出,確保逐步引入更新和新功能,降低中斷和問題的風險。 - 在出現不可預見的問題或錯誤時快速緩解問題,使您能夠快速關閉有問題的功能,而無需重新部署完整的應用程式。 - 最大限度地減少中斷和問題的風險,確保更流暢的使用者體驗。 https://github.com/Unleash/unleash 請加註星標 ⭐ unleash --- ## 5. [ockam](https://github.com/build-trust/ockam) ![ockam](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sce1elhd9d4kd45xnpxg.gif) 促進應用層動態資料的安全資料真實性、完整性和機密性。 - 強大的安全框架,讓開發人員可以建立端到端的加密通訊通道,確保資料的真實性、完整性和機密性。 - 支援多種協議,允許安全通道跨越各種網路拓撲和傳輸協議。 - 提供身分建立、金鑰管理和憑證管理工具,這對於分散式系統中的安全通訊至關重要。 https://github.com/build-trust/ockam 請加註星 ⭐ Ockam --- ## 6. [Hasura](https://github.com/hasura/graphql-engine) ![Hasura](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsgpwztw5ljfrwrnrqxo.gif) Hasura 是一個開源引擎,它透過 Postgres 提供即時、即時的 GraphQL API,並具有資料庫事件的 Webhook 觸發器和業務邏輯的遠端模式。 - 簡化從資料庫取得資料的流程,同時保持強大的安全性,減少開發時間和潛在的安全風險。 - 透過權限和基於角色的存取控制(RBAC)對資料存取進行細微控制,確保只有授權使用者才能存取特定資料。 - 透過保護敏感資訊免遭未經授權的存取來增強資料安全性。 https://github.com/hasura/graphql-engine 請star ⭐ Hasura --- ## 7. [Meltano](https://github.com/meltano/meltano) ![Meltano](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayfw3uwoe0ewe28jeosh.gif) 聲明式、以程式碼優先的資料整合引擎,為開發人員提供跨各種來源和目的地移動、轉換和探索資料的工具。 旨在幫助解鎖 API 和資料庫,並促進資料和機器學習驅動的產品創意的建立。 - 支援 600 多個資料來源和目標,提供多功能整合解決方案。 - 用於管理資料管道的聲明式程式碼優先方法,這使其成為處理大規模資料的強大工具。 - 允許開發人員建立自訂連接器並整合現有資料工具,提供高水準的自訂和靈活性。 https://github.com/meltano/meltano 請star ⭐ Meltano --- ## 8. [Odigos](https://github.com/keyval-dev/odigos) ![Odigos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jcshttj6qtpnh3bd4bwl.gif) Odigos 是一個用於應用程式監控和可觀察性的開源專案,使用戶能夠主動檢測和解決安全問題。 **應用程式開發人員** - 透過利用 OpenTelemetry 和 eBPF 的強大功能來自動檢測應用程式,更加專注於編寫程式碼。利用一流的可觀測性資料為下一次生產事故做好準備。 **Odigos** - 根據應用程式的流量自動部署和擴展收集器。無需浪費時間部署和配置收集器。 https://github.com/keyval-dev/odigos 請加註星 ⭐ Odigos --- ## 9. [Trigger.dev](https://github.com/triggerdotdev/trigger.dev) ![Trigger.dev](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/km9n1wrzebvh722du70n.gif) Trigger.dev 是一個平台、SDK 和 API,用於在程式碼庫中建置和執行作業,由各種來源觸發,但無需擔心管理任何複雜的編排基礎架構。它可以從任何節點使用。 - 管理逾時時間短的無伺服器平台上長時間執行的作業。 - 提供使用者一個 SDK,用於在程式碼庫中建立作業,由事件、排程事件和 Webhooks 等各種來源觸發。 - 與 Slack、OpenAI、GitHub 等流行服務的開箱即用集成,大大簡化了與第三方服務互動的過程。 https://github.com/triggerdotdev/trigger.dev 請加註星標 ⭐ Trigger.dev ___ ## 🔥 [趨勢清單](https://github.com/github-20k/trending-list) ![趨勢清單](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tltxx778m87nzkaq1uj0.gif) 當您的開源專案成為 GitHub 上的即時熱門時,您將透過電子郵件收到通知。 https://github.com/github-20k/trending-list 請加註星標 ⭐ 趨勢清單 ___ 🤩 合併開源專案可以為您的 SaaS 應用程式帶來全面勝利。 感謝您查看這九個強大的開源專案,您應該考慮將它們整合到您的 SaaS 中以增強其功能,從而將您的應用程式變成黃金。🥇 **支援開源軟體的最佳方式之一就是加一顆星** 🌟

經營個人頻道,改變了我的軟體工程師生涯

大家好,我想鼓勵一些想要開始當工程師,但很難找到第一份工作的人。還有那些因為懷疑自己的能力而難以開始/完成專案的人(冒牌者症候群)。我將告訴你我自己的旅程,以及我一路上學到的東西來,希望你能有動力繼續你的旅程。 原文出處:https://dev.to/chaoocharles/how-creating-content-as-a-developer-changed-my-life-270e 我叫查爾斯,來自非洲肯亞。我早在 2016 年就開始了我的編碼之旅,當時我進入大學,在 BTECH I.T 尋求職業生涯。在此之前,我對電腦了解不多,我只知道它們很酷,我想研究它們。在高中時,我是一名表現最好的學生,我的英語老師(也是副手,哈哈)認為我想做一些像計算機這樣簡單的事情而不是像工程、醫學、飛行員等更專業、更有前途的職業,這是愚蠢的,你說出它們的名字。好訊息是我沒有聽,而是去做了我想要的事情,而且我不會為此感到後悔。事實是,IT 領域的任何職業都是當今世界上最好的職業之一,世界頂級公司之所以能處於領先地位,是因為程式碼。看看 Netflix、亞馬遜、微軟、Facebook、Airbnb 或 Uber。所有這些公司都透過程式碼賺了數十億美元,因此不要讓任何人欺騙您,讓您認為您走在錯誤的道路上。我來這裡是為了告訴你,你正走在最好的道路上。 回想2016年剛入職第一年的時候,由於出身卑微,擁有一台筆記型電腦對我來說是非常困難的,即使是一台簡單的筆記型電腦,甚至是二手的筆記型電腦。如果我要做 I.T,那麼擁有一個對我來說也是強制性的。擁有一部像樣的智慧型手機也是一個問題,但幸運的是,我有一個三星口袋(那些小三星手機,如果你還記得的話),並在說服朋友用它與我交換一些錢和一部按鍵手機後。這款手機在這個故事中很重要,因為它是我用來開始學習程式設計的手機。在學校裡,我們被教導如何編碼,是的,但這還不夠,它主要是理論。關於編碼的事情是你必須_練習_、_練習_、再_練習_。於是,我從同學那裡了解到了一個名為sololearn的應用程式。我在那裡開始學習 Web 開發,包括 html、css、javascript、php、sql,我想還有一點 jquery。這個應用程式教會了我有關這些主題的所有基礎知識,並且在完成每節課後它都有有趣的挑戰和徽章。唯一的問題是我不會透過手機進行完整的專案編碼。但重點是,當您等待購買筆記型電腦時,您絕對可以開始透過智慧型手機學習程式設計。因此,不要因為沒有筆記型電腦而高枕無憂。你越早意識到沒有人來拯救你越好。 我繼續從我的三星口袋裡學習了一個月,後來在內羅畢街頭(有很多扒手)它被神秘地偷走了。學校的朋友送了我一部HTC手機,螢幕碎了,有些地方根本碰不著,我得旋轉螢幕多次才能碰到地方😂,不過乞丐不挑食,我就繼續用了以便在本學期剩下的時間裡學習更多有關編碼的知識。 第二學期,現在是2017年,我收到了學生貸款。我的一些朋友/同學用他們的貸款去聚會、喝酒以及在俱樂部與女孩們玩耍。嗯,就我而言,我知道自己從哪裡來,也知道自己要去哪裡。於是,我拿了一些錢,立刻買了一台筆記型電腦。剩下的錢用來付學費和一點生活費。這對我的案例來說是一個巨大的進步,因為我現在可以了解更多資訊並開始從筆記型電腦上處理專案。因此,如果您有錢,請停止考慮參加聚會和購買昂貴的東西,而是考慮如何用這筆錢讓您的生活變得更好。 2017年至2019年期間,沒有太大變化。我只是在學習和做學校作業等。我想我還用 HTML 和 CSS 製作了兩個網站,我為這些工作獲得了一些報酬。我探索了更多關於編碼的知識,包括學習Java 中的OOP(物件導向程式設計),也用Java 進行了一些Android 應用程式開發,我的筆記型電腦無法處理android studio 😂,所以我又回到了Web 開發。我探索了 WordPress 以及如何用它製作博客等,並建立了一個 WordPress 博客,並以幾美分的價格出售。 2019年底,我正在讀三年級第三學期(這在我們大學被稱為內部工業實習),大約在這個時候,我被敲響了警鐘。我意識到我一直在學習編碼,但除了簡單的 html、css 和 WordPress 網站之外,我仍然無法建立一個完整的專案。我對任何一種程式語言都沒有足夠的信心。另外,畢業的要求是在第四年完成一個編碼專案。我也開始對未知產生恐懼,例如放學後要做的事情,因為只剩下一年了。我開始做很多研究如何建立一個完整的網絡應用程式,因為我已經了解了 javascript 的基礎知識,並且出現了一件我不知道的事情,即 javascript 框架,目前最流行的是 Angular、Vue並做出反應。當時我就知道我必須學習這些框架之一,而且很難決定選擇哪一個,但我最終選擇了 React,因為它是所有框架中最受歡迎、最有前途的工作,而且我仍然使用 React 進行編碼這點。 我嘗試從sololearn學習React,但進展並不順利。我嘗試了 youtube 並發現了 @thenetninja 頻道:https://www.youtube.com/@NetNinja 它非常有益健康,這就是我對 React 的很好的介紹。後來我從 Udemy 學習了兩門完整的 React 課程,一門由 _Stephen Grider_ 教授,另一門由 _Maximilian Schwarzmüller_ 教授。順便問一下,我沒有完成它們,誰完成了 udemy 課程? 😂 但這兩門課程教會了我更多關於 React 的高級知識。 在學習 React 的過程中,我也很好奇如何在放學後或還在學校的時候透過程式碼賺錢,我發現了幾個選擇,找工作、自由工作、建立內容(這可以是寫部落格或 YouTube 頻道) )、開始播客、寫書、建立像udemy 這樣的課程等等。由於我還在上學,我知道找工作很難處理,所以我決定嘗試自由工作和內容創作。還是在 2019 年,我開設了 YouTube 頻道:https://www.youtube.com/c/chaoocharles 來教授編碼,也開設了一個 upwork 帳號來從事自由專案的編碼。 我知道我不是一個好的作家,正如你從這篇文章中絕對可以看出的那樣,所以我嘗試了影片而不是博客。這是另一個挑戰,因為我必須學習如何製作影片、學習錄製和編輯軟體等等。但我還是堅持了下來,並從同學那裡得到了我的前 100 個訂閱。我的影片一開始就很糟糕,而且我做了很多工作,甚至沒有得到一分錢。但從正面的角度來看,製作影片讓我更理解編碼概念。就像,對我來說,要解釋我必須先理解的東西。我主要用 html、css 和 React 建立了很多影片,做得越多,我對建立影片和編碼就越有信心。 2020 年,我有很多時間來做這一切,因為我們因新冠疫情關閉了學校近一年,經過一年的努力,我終於獲得了 1000 名替補,這對我來說是一個巨大的勝利。 2020 年中期,我取得了兩場重大勝利,在 YouTube 上達到了 1k,並且在 Upwork 上找到了我的第一個客戶。我需要 1000 訂閱者和 4000 小時的觀看時間,YouTube 才會開始向我付費。我距離 4k 觀看時間還很遠,但至少我已經達到了其中一項要求。 Upwork 也很難找到第一個客戶,我申請工作卻無濟於事,但這第一個客戶改變了遊戲規則。我讓他相信我知道如何編碼,並用我的 YouTube 教程證明了這一點。你看,在製作 YouTube 教學的同時,我也在為自己建立一個作品集,我也在我的 github 上發布了多個專案。這麼說吧,我的投資組合目前看起來非常好,這位客戶毫不猶豫地給了我一份合約。如果你碰巧教了一些東西,人們就會開始將你視為專家(即使你正在努力教那件事😂)這可以通過影片或博客,我認為你應該嘗試一下。我在 upwork 專案上做得很好,這個客戶在那一年和接下來的一年裡繼續給我更多的專案。我做了他的大約 8 個專案,在 upwork 上獲得了上升人才徽章,後來又獲得了頂級徽章,這讓我贏得了更多客戶。 ![Upwork](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jut1tydubsj4mwlmnt3h.png) 是的,我知道我現在的工作成功分數很差,但你明白了,哈哈😂 在製作YouTube 影片和Upwork 專案時,我也在做我的最後一年專案,因為我現在已經是第四年了,但這些是我自從2020 年因新冠疫情在家以來所做的唯一事情。雖然在2020 年底,我們回去上學期,做了考試並展示了專案。 快轉到 2021 年,我現在正在做附件。這是我畢業的必要條件。我透過在 Facebook 和網路上尋找肯亞的網路開發公司,輕鬆獲得了這一點。評論他們的帖子並解釋我的 IT 背景。我從某家新創公司的 CEO 那裡得到了 DM,並且以前端開發人員的身份加入了那裡。我試圖就報酬進行談判,但最好的結果只是維持交通和午餐,每週去辦公室三天,並承諾在實習後獲得一份長期工作。作為一名學生,這已經足夠好了,所以我就這麼做了。 在這家公司,我的 CSS 和 React 技能給他們留下了深刻的印象。我改造了他們公司的網站,並在我在那裡的5個月期間又做了2個網站。學校對我進行了評估,後來公司給了我工作機會。我覺得每個月的收入對我的技能來說不夠好,因為我可以在一兩週內輕鬆地做同樣的自由職業,而且我在那裡的時候我的 YouTube 也得到了貨幣化。我只是拒絕了這個提議,並決定專注於我的自由職業和內容創作之旅。如果他們允許我在處理其他事情的同時遠端處理他們的專案,也許我會接受這個提議,但這不在他們的公司政策中。我不想滿足於更少。你看,過去幾年我所做的一切都給了我選擇,也讓我不再急於找到工作,並擁有競爭優勢。 2021 年年中,我以二等高年級畢業,我保證如果不是我用 YouTube 和 upwork 分散自己的注意力,我會獲得一等,第四年我表現最差。但我後悔嗎?不。問題是,我從來沒有用過那個學位,它仍然鎖在家裡的某個地方,沾滿了灰塵。好處是,技能和經驗是這個編碼和程式設計領域最重要的。只有少數公司可能會要求學位,但大多數公司不會。他們會詢問您過去的經驗、您從事過的專案,並希望您通過程式設計面試。因此,如果你正在尋找一份程式設計工作,並因為沒有獲得學位而指責自己沒有學位,那麼你應該停下來。我們大多數擁有學位的人甚至沒有使用它們。也許我們唯一的優勢是我們在學校建立的聯繫或從那裡獲得的技能。但說實話,我所知道的大部分內容都是我自學的,我相信每個程式設計師都是自學的程式設計師,無論他們是否上過學。你必須親自動手。光靠論文並沒有太大幫助。 畢業後,我開始從 YouTube 獲得專案邀請,以及報酬豐厚的專案。我也開始利用 YouTube 和 GitHub 來在 Upwork 上獲得更好的付費專案,透過分享我的個人資料連結來告訴客戶我所取得的成就。所以,所有這些加起來就很不錯了。現在,我僅透過內容創作來支付所有帳單,並透過在工作和外部工作專案上工作來獲得更多收入。我的時間也很靈活,在家工作,這很棒。 2022 年我只做了一份全職工作。雖然位置偏遠,但完全值得。 我的觀點是,如果你正在努力尋找一份工作或一個專案,你可以透過為自己建立一些東西來改變一切。建立部落格、建立播客、建立頻道、建立公司、建立課程、寫書、公開建置(啟動一個大型專案並在此處和 Twitter 上分享您的進度),只需在這裡展示您的技能即可您能做什麼,遲早你會開始從事高薪專案。停止追逐工作,而只是吸引他們。 正如你從我的旅程中可以看出的,這不是一天的成就,直到一年多我才得到一分錢的內容創作報酬,直到一年多我才得到一個客戶的工作。我不是一天就能學會程式設計的,我是從一部手機開始的,後來又是用學校貸款買的一台低階筆記型電腦(我甚至還沒付)。所有這些成功的人士和公司都是從某個地方開始的,您今天就可以開始改變您的生活。開始親自動手,兩三年後你甚至不會相信自己來自哪裡。 這是我的故事,我希望你學到了一兩件事✌️

24 款值得在 2023 年認識一下的 open source 專案

開源專案是創新、協作和創造力的遊樂場。它是來自世界各地的開發人員聚集在一起分享他們的想法、技能和熱情的中心。 在本文中,我精心挑選了 24 個涵蓋廣泛興趣和技術的開源專案。 從尖端的人工智慧框架到漂亮的生產力工具以及介於兩者之間的一切,每個開發人員都能找到適合自己的東西。 我提供了直接連結、描述和視覺效果,以便您可以立即獲得每個工具的初步印象。 原文出處:https://dev.to/madza/24-open-source-projects-for-developers-in-2023-391l --- ## 1\. [ esProc SPL](https://github.com/SPLWare/esProc)(贊助) 集算器SPL是一種基於腳本的資料操作語言,與SQL資料庫集成,支援進階分析和高效能並行處理。 它適合處理大型資料集,與各種工具集成,提供資料視覺化,並跨多個平台工作。一些主要功能包括: **💪 強大的資料處理能力:** 集算器SPL是一種腳本語言,具有豐富的函數庫和強大的語法。 **✨ 預存程序等效項:** 它允許透過 JDBC 介面執行 SPL 腳本。 **📈 多功能視覺化:** 它提供了成熟的報告工具,具有廣泛的視覺化配置,用於建立各種類型的報告。 **⚡ 自動化工作流程:** 它支援軟體工作流程的自動化,包括用於程式碼建置、測試和部署的 CI/CD 流程。 **🔥 相比SQL更具彈性:** 與SQL語法不同,集算器SPL允許將資料處理程式碼寫在多條語句中。 ![esProc_SPL](https://res.cloudinary.com/practicaldev/image/fetch/s--x_jHJEX4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1679824673641/82f843e0-72a1-44a4-bd99-68616f322534.png%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp) ⭐ 支援他們的 GitHub 倉庫:[https://github.com/SPLWare/esProc](https://github.com/SPLWare/esProc) ## 2\. [Hoppscotch](https://github.com/hoppscotch/hoppscotch) 一種多功能開源 API 開發和測試工具,提供使用者友善的介面,用於發出 HTTP 請求來測試 API 並與 API 互動。 它簡化了製作和發送請求的過程,使其成為使用 API 的開發人員和測試人員的必備工具。 ![Hoppscotch](https://github.com/hoppscotch/hoppscotch/raw/main/packages/hoppscotch-common/public/images/banner-dark.png) ## 3\. [Supabase](https://github.com/supabase/supabase) Firebase 的開源替代方案,為開發人員提供了一組用於建立可擴展的即時應用程式的工具。 它提供了強大的後端即服務 (BaaS) 平台,具有身份驗證、資料庫管理和即時功能等功能,使其成為建立現代 Web 和行動應用程式的強大選擇。 ![Supabase](https://supabase.com/_next/image?url=%2Fimages%2Fproduct%2Fstorage%2Fheader--dark.png&w=1920&q=75) ## 4\. [Supertokens](https://github.com/supertokens/supertokens-core) 一種開源身份驗證解決方案,提供強大的安全功能和輕鬆集成,以增強 Web 和行動應用程式中的使用者身份驗證和授權。 它為開發人員提供了一個全面的工具包,用於保護用戶資料並確保無縫登入體驗。 ![Supertokens](https://supertokens.com/docs/static/assets/arch.png) ## 5\. [Git](https://github.com/git/git) Git 版本控制系統的官方開源程式庫,最初由 Linus Torvalds 建立。 Git 廣泛用於追蹤原始程式碼的更改,並透過提供強大的分支和合併功能來實現協作軟體開發。 ## 6\. [VS Code](https://github.com/microsoft/vscode) 由 Microsoft 開發的免費開源程式碼編輯器。 它提供了高度可自訂且高效的程式設計環境,具有 IntelliSense、除錯支援和龐大的擴充庫等功能,可增強您的開發工作流程。 ![VS程式碼](https://user-images.githubusercontent.com/35271042/118224532-3842c400-b438-11eb-923d-a5f66fa6785a.png) ## 7\. [OhMyZsh](https://github.com/ohmyzsh/ohmyzsh) 一個流行且高度可自訂的框架,用於在類 Unix 作業系統中管理 Zsh 配置。 它簡化了 shell 自訂,提供了大量插件和主題來增強您的命令列體驗。 ![OhMyZsh](https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png) ## 8\. [Bun](https://github.com/oven-sh/bun) 一個開源 JavaScript 工具包,旨在簡化和優化為 Web 應用程式捆綁 JavaScript 程式碼的過程。 它提供了一種現代且快速的方法來建立捆綁包,從而增強了使用 JavaScript 專案時的效能和開發人員體驗。 ![Bun](https://cdn.hashnode.com/res/hashnode/image/upload/v1696318057709/5a1125cf-eb78-4e9d-9632-faebd228abe5.png) ## 9\. [SWR](https://github.com/vercel/swr) SWR(Stale-While-Revalidate)是一個用於在 React 應用程式中取得資料的 JavaScript 函式庫。 它可以在客戶端和伺服器之間實現高效、自動的資料同步,提供無縫的即時更新,同時確保資料保持新鮮和最新。 ![SWR](https://cdn.hashnode.com/res/hashnode/image/upload/v1696318453842/d9ab3384-becc-4040-93f7-8a9e064100b1.png) ## 10\. [Prisma](https://github.com/prisma/prisma) 用於現代應用程式開發的開源資料庫工具包,透過強大的查詢產生器和類型安全的 ORM(物件關聯映射)層簡化資料庫存取和操作。 它允許開發人員使用聲明性和直觀的方法管理資料庫並與之交互,從而使資料庫操作在各種資料庫系統中無縫且安全。 ![Prisma](https://i.imgur.com/O1lwo0v.png) ## 11\. [ElasticSearch](https://github.com/elastic/elasticsearch) 由 Elastic 開發的強大且可擴展的開源搜尋和分析引擎。 它旨在幫助用戶快速有效地搜尋、分析和視覺化大量資料,使其成為從全文搜尋引擎到日誌分析等應用程式的熱門選擇。 ![ElasticSearch](https://cdn.hashnode.com/res/hashnode/image/upload/v1696315923559/58c2db03-9a6c-4b98-9b48-a91025c507a2.png) ## 12\. [Hasura](https://github.com/hasura/graphql-engine) 一款功能強大的開源工具,可簡化應用程式的 GraphQL API 開發。 借助 Hasura,您可以輕鬆建立、管理和保護 GraphQL API,從而更輕鬆地與資料來源互動並建立現代的資料驅動應用程式。 ![Hasura](https://assets.website-files.com/63e3d6905bacd6855fa38c1c/63e3d6905bacd64f08a38f95_Hasura.jpg) ## 13\. [BioDrop](https://github.com/EddieHubCommunity/BioDrop) 透過單一連結與您的受眾建立聯繫。在一處展示您建立的內容和專案。 讓人們更容易找到、關注和訂閱。 ![BioDrop](https://user-images.githubusercontent.com/624760/230707268-1f8f1487-6524-4c89-aae2-ab45f0e17f39.png) ## 14\. [Powertoys](https://github.com/microsoft/PowerToys) 適用於 Windows 的開源實用程序,可提高工作效率和自訂功能。 它提供了一系列方便的工具和實用程序,包括快速啟動器、文件預覽和視窗管理等功能,旨在簡化您的 Windows 體驗。 ![Powertoys](https://cdn.hashnode.com/res/hashnode/image/upload/v1696280333258/279d3728-4731-46eb-9836-c8300d3a9f75.png) ## 15\. [Strapi](https://github.com/strapi/strapi) 開源無頭內容管理系統 (CMS),使開發人員能夠快速建立強大且可自訂的 API。 它使團隊能夠輕鬆建立和管理內容豐富的網站和應用程式,為各種專案提供靈活性和可擴展性。 ![Strapi](https://cdn.hashnode.com/res/hashnode/image/upload/v1696316645227/6122feae-4b38-4c00-a8a1-30da5346568c.png) ## 16\. [Plausible](https://github.com/plausible/analytics) 一種開源網路分析工具,旨在為網站所有者提供對其網站效能的簡單且注重隱私的見解。 它提供用戶友好、輕量級的跟踪,且不會損害存取者的隱私,使其成為那些重視資料分析而無需侵入性跟踪方法的人的理想選擇。 ![看似](https://cdn.hashnode.com/res/hashnode/image/upload/v1696280734881/0cc0aa58-46e1-49ac-a920-65f7eaad6e33.png) ## 17\. [Astro](https://github.com/withastro/astro) 現代靜態網站產生器,透過僅傳送頁面所需的 JavaScript 來提供閃電般的效能,從而實現近乎即時的載入時間。 它將傳統伺服器渲染框架的靈活性與靜態網站產生器的速度相結合,使其成為建立高效動態網站的絕佳選擇。 ![Astro](https://deegloo.com/wp-content/uploads/2022/11/blogblog-cover-1024x614.png) ## 18\. [Remix](https://github.com/remix-run/remix) 用於建立現代 JavaScript 應用程式的 Web 框架,注重速度和開發人員體驗。 它使開發人員能夠透過無縫組合伺服器渲染和客戶端渲染的內容來建立高效能的 Web 應用程式。 ![混音](https://cdn.shopify.com/s/files/1/0779/4361/files/RemixRun_bcc7b8fd-ca3a-4385-b279-91a0606706e7.jpg?v=1666895610) ## 19\. [Tensorflow](https://github.com/tensorflow/tensorflow) 由Google開發的開源機器學習框架。 它為建立和部署機器學習模型提供了靈活且全面的生態系統,使其成為人工智慧領域研究人員和開發人員的熱門選擇。 ![Tensorflow](https://m-alcu.github.io/assets/tensorflow-playground.png) ## 20\. [Flutter](https://github.com/flutter/flutter) 由 Google 建立的開源 UI 軟體開發工具包,以其從單一程式碼庫建立適用於行動、Web 和桌面的本機編譯應用程式的能力而聞名。 它使開發人員能夠使用單一程式語言 Dart 跨多個平台建立美觀、快速且高度可自訂的使用者介面。 ![Flutter](https://cdn.hashnode.com/res/hashnode/image/upload/v1696281232879/35493958-0397-40c4-9c30-ca0faead9f39.png) ## 21\. [Kubernetes](https://github.com/kubernetes/kubernetes) 一個開源容器編排平台,可自動執行容器化應用程式的部署、擴充和管理。 它為編排容器提供了強大而靈活的基礎架構,使在雲端原生環境中大規模管理複雜的分散式系統變得更加容易。 ## 22\. [Docker](https://www.docker.com/community/open-source/) 一個開源工具,可簡化多容器 Docker 應用程式的管理。 它允許開發人員使用簡單的 YAML 檔案定義和執行多容器應用程式,從而更輕鬆地編排和部署複雜的服務。 ![Docker](https://cdn.hashnode.com/res/hashnode/image/upload/v1696316908120/7e01fe2b-a438-4882-8cd6-863b7f5effb0.png) ## 23\. [Chromium](https://github.com/chromium/chromium) Google 的一個開源瀏覽器專案,旨在為所有使用者建立更安全、更快、更穩定的網路體驗方式。 它是開發人員在網路瀏覽技術領域做出貢獻和創新的平台。 ![Chromium](https://cdn.hashnode.com/res/hashnode/image/upload/v1696319343433/61d13e7f-512b-40b7-a127-b127a944cf9d.png) ## 24\. [Linux 核心](https://github.com/torvalds/linux) 由 Linus Torvalds 和全球貢獻者社群開發的開源、類別 Unix 作業系統核心。 它作為各種基於 Linux 的作業系統的核心元件,提供硬體互動和系統管理的基本功能。 ![Linux 核心](https://upload.wikimedia.org/wikipedia/commons/2/2e/Linux_Mint_21_%22Vanessa%22_%28Cinnamon%29.png) --- 以上,簡單分享!

不是共產黨,但是審查低質量雜訊很重要!自己寫一個JS腳本過濾!

本文轉自:https://ithelp.ithome.com.tw/articles/10338948 ## 前情提要 資訊大爆炸。 有時候我們瀏覽技術文章,不一定真的是想學深奧的高級技術。 然而劣質低端的文章充斥著,則會降低我們學習的效率、甚至變成噪音與雜訊,干擾我們的思緒。 因此針對某些洗文或是質量很低的作者,我們必須列為思想犯, 否則會降低閱讀質量,平白浪費自己的時間、平台的版面、上網的電力、看到垃圾資訊的副作用等等.... ## 構思來源 如果有個思想審查警衛可以:**去除那些垃圾低端,稱不上技術文章的雜訊。** 以確保未來瀏覽文章的時候,不會再被洗文打擾, 也可以針對不喜歡的主題去封鎖,讓時間與精神更能專注於自己想要學的資訊。 阻止一些垃圾就是喜歡把自己尚未整理的白痴內容一直丟上來, 什麼都還不懂,把技術文章當成個人日記簿,寫一堆自我囈語、無病呻吟, 每天大量狂發文章,昭告天下以為這就是努力,欺騙自己也浪費別人的人生。 ## 「思想審查警衛」出動! ## 功能 1. 把頭像屏蔽 2. 加上思想通緝犯、紅字與刪除線 3. 新增封殺按鈕 4. 版面通知封殺名單與文章數 5. 透過ajax確認某id的最新ID ## 效果截圖 ![](https://i.imgur.com/kgUYJbl.png) 此截圖僅是腳本示範,跟其使用者無關, 本人沒有任何覺得此使用者發的文章是差勁的意味,我認為非常上進、值得學習。 ![](https://i.imgur.com/G27Ea2F.png) 此圖也只是隨機挑user使用,純粹作為範例用途,不代表我個人意見。 ## 腳本下載 https://greasyfork.org/zh-TW/scripts/477283-%E6%80%9D%E6%83%B3%E7%8A%AF%E5%B0%81%E6%AE%BA ## 原理說明 鄭重聲明,這個示範真的毫無任何私人意味,此腳本也只是針對不同的主題去隱藏, 例如我想學python就不想看到JS的文章,因此使用此工具幫忙隱藏罷了。 取名做思想警察、比喻成清除垃圾等都只是文學上的趣味。 請勿當真Ꮚ・ꈊ・Ꮚ 這次的技術可說比較難,認真難很多!但是趣味性以及功能性是無比的超越! 可以說幾乎是我寫系列文以來,最頂、最派的一篇! 不過很多觀念已經出現過,在【前端小試身手】系列裡面,每次的腳本都是主打實用, 因此cookie或localstorage這種技術當然都會運用到。 ## JS原始碼 ``` // ==UserScript== // @license MIT // @name 👮思想犯封殺 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 把廢文製造機轟出去 // @author You // @match https://ithelp.ithome.com.tw/articles* // @match https://ithelp.ithome.com.tw/users/* // @icon https://www.google.com/s2/favicons?sz=64&domain=ithome.com.tw // @grant none // @run-at document-end // ==/UserScript== let URL = window.location.href; let ArticleSite = "https://ithelp.ithome.com.tw/articles?tab=tech" let UserSite = "https://ithelp.ithome.com.tw/users/" // 判斷URL的開頭部分 if (URL.startsWith(ArticleSite)) { //文章頁執行清理垃圾程序 CleanGarbage(); } else if (URL.startsWith(UserSite)) { UserCheck(); } else { console.log("這邊不執行腳本"); } // 餅乾儲存的機制函數------------------------------------------------- function setListInCookie(list) { document.cookie = 'myList=' + JSON.stringify(list) + '; expires=Wed, 31 Dec 2099 23:59:59 GMT;'; } // 從 Cookie 中獲取 list function getListFromCookie() { var cookieValue = document.cookie.replace(/(?:(?:^|.*;\s*)myList\s*\=\s*([^;]*).*$)|^.*$/, "$1"); return JSON.parse(cookieValue) || []; } function CleanGarbage(){ // 從本地存儲中獲取數據 var myListData = localStorage.getItem("myList"); // 解析數據到變量 list var list = myListData ? JSON.parse(myListData) : []; //list = getListFromCookie()||[]; // 儲存要刪除的字符串名單 // 找到所有CLASS是"qa-list__info-link"的<a>元素 var linkElements = document.querySelectorAll('.qa-list__info-link'); var removedCount = 0; // 初始化已清除的垃圾數量 for (var j = 0; j < list.length; j++) { // 遍歷這些<a>元素,確保文本內容包含"伍貳捌",然後刪除其父元素 for (var i = 0; i < linkElements.length; i++) { if (linkElements[i].textContent.includes(list[j])) { // 開始向上查找父元素 var parentElement = linkElements[i].parentElement; while (parentElement) { // 如果找到具有"classname"為"qa-list"的<div>元素,則刪除它 if (parentElement.classList.contains('qa-list')) { parentElement.remove(); removedCount++; // 增加清除的數量 console.warn('抓到"'+list[j]+'"這位思想犯'); break; // 找到並刪除後,結束循環 } parentElement = parentElement.parentElement; } } } if (removedCount>0){ // 顯示已清除的垃圾數量 console.log('已清除他的 ' + removedCount + ' 篇垃圾');} removedCount=0; } } //------------------------------------------------------- // 為了防止五百八改名,我們針對他的ID去ajax得到他最新的名稱 function FindBitch() { // 使用 Fetch API 獲取指定 URL 的內容 return fetch("https://ithelp.ithome.com.tw/users/20163468") .then(response => response.text()) .then(data => { // 創建一個臨時 div 元素以容納頁面內容 var tempDiv = document.createElement("div"); tempDiv.innerHTML = data; // 查找 class 為 "profile-header__name" 的元素 var profileNameElement = tempDiv.querySelector(".profile-header__name"); if (profileNameElement) { // 刪除元素內的所有 <span> 元素 var spanElements = profileNameElement.querySelectorAll("span"); spanElements.forEach(function(span) { span.remove(); }); // 讀取元素的文本內容,去掉前導和尾隨空格 var text = profileNameElement.textContent.trim(); // 返回處理後的文本內容 return text; } else { return "未找到元素"; } }) .catch(error => { console.error("發生錯誤: " + error); return "發生錯誤"; }); } //------------------------------------------------------- function UserCheck(){ //轉換資料從餅乾到localstorage var currentCookieValue = getCookie("myList"); // 2. 存儲數據到本地存儲 if (currentCookieValue) { var list = JSON.parse(currentCookieValue); // 存儲到本地存儲 localStorage.setItem("myList", JSON.stringify(list)); }else{ FindBitch() .then(text => { let FirstKill = [text]; setListInCookie(FirstKill); }) .catch(error => { console.error("找不到五百八:", error); }); } // 刪除不需要的ID document.querySelector('.profile-header__account').remove(); //封殺按鈕------------------------------------------------- // 找到具有class為"profile-header__right"的元素 var profileRightElement = document.querySelector('.profile-header__right'); var pullRightElement = profileRightElement.querySelector('.pull-right'); // 創建一個新按鈕元素 var BlockBtn = document.createElement('button'); BlockBtn.textContent = '封殺'; // 添加樣式和類名到按鈕 BlockBtn.style.marginTop = '10px'; BlockBtn.style.width = '100%'; BlockBtn.className = 'btn btn-trace trace_btn_border BlockBtn'; // 將按鈕元素添加到"pull-right"元素內部 pullRightElement.appendChild(BlockBtn); // 通緝犯名單的cookie------------------------------------------------ // 從 Cookie 中加載 list(例如,頁面加載時) list = getListFromCookie()||[]; let UserBlock = document.querySelector('.profile-header__name'); let text = UserBlock.textContent.trim(); // 如果使用者已經在封殺名單內的判斷,已存在或不存在 if (list.includes(text)) { BlockStart(BlockBtn); } else { // 針對封殺按鈕進行監聽事件 BlockBtn.addEventListener('click', function() { list.push(text); setListInCookie(list); //先加入到名單內,然後再執行封殺事件 BlockStart(); // 輸出到控制台 console.log('黑名單新增:' + text); //本地儲存機制----------------------------- let currentCookieValue = getCookie("myList"); let list2 = JSON.parse(currentCookieValue); // 存儲到本地存儲 localStorage.setItem("myList", JSON.stringify(list2)); }); } } // ------------------------------------------------ //封殺事件的函數 function BlockStart(){ let BlockBtn = document.querySelector('.BlockBtn'); BlockBtn.textContent = '已封殺'; BlockBtn.disabled = true; BadText(); BadImg(); } //封殺事件函數裡面的細項函數 function BadText(){ // 標記這傢夥是垃圾------------------------------------------------- let UserBlock = document.querySelector('.profile-header__name'); let newHeading = document.createElement('h1'); newHeading.textContent = '思想通緝犯'; // 把思想通緝犯這幾個大字加上去 UserBlock.parentElement.insertBefore(newHeading, UserBlock); UserBlock.style.textDecoration = "line-through"; UserBlock.style.color = "red"; } function BadImg(){ //圖片進行網點作業XD------------------------------------------------- var originalImage = document.querySelector('.profile-header__avatar'); // 創建一個包含交叉紅線的覆蓋層 <div> 元素 var overlayDiv = document.createElement('div'); overlayDiv.style.position = 'absolute'; overlayDiv.style.width = '150px'; overlayDiv.style.height = '150px'; overlayDiv.style.background = 'linear-gradient(45deg, black 50%, transparent 50%), linear-gradient(-45deg, black 50%, transparent 50%)'; overlayDiv.style.backgroundSize = '5px 5px, 5px 5px'; overlayDiv.style.backgroundPosition = '0 0, 0 2px'; // 將覆蓋層疊加到圖片上 originalImage.parentNode.appendChild(overlayDiv); // 設置覆蓋層的位置,以與原始圖像對齊 overlayDiv.style.top = originalImage.offsetTop + 'px'; overlayDiv.style.left = originalImage.offsetLeft + 'px'; // 設置覆蓋層的z-index,以確保它在圖片上方 overlayDiv.style.zIndex = '2'; } ``` ## 觀念筆記 這個腳本開發足足花了我一整個晚上,將近八小時之久。 有趣的是,其中為了進行測試才選某些user當作隱藏對象,否則腳本執行上會出錯。 細心的人若觀察原始碼,也會發現裡面有個firstKill, 那是必須要的段落,先設置好cookie的首要內容物,才有辦法繼續操作下去( ิ◕㉨◕ ิ) 也就是初始化的概念XD 另一個有趣的點是,為了防止使用者改名導致腳本出錯,我甚至不惜再寫一段ajax, 去更新一下ID,這樣不管人家怎麼改,都逃不了, 要改成「別抓我」也沒用,這個腳本都還是可以run。 ## 心得後記 我只能說這篇是自從「備份IT幫發文、一眼全覽」最強的JS教學文章! 超派,真的不騙( メ∀・) 有些人喜歡看前端小試身手,有些人喜歡前端動手玩創意; 其實這兩個系列的本質都是JS的教學,是可以互相連接的,但也有很多不同的重心。 這個系列就是以腳本為主,重點在於創意與發想,打到使用者痛點。 【前端動手玩創意】則是以建構網站為起點, 任何元素與概念都會變成網頁上的一部分,算是比較基礎工的建立。 如果對JS的強大感興趣,那麼可以把這兩個系列交互看,反覆的閱讀、實際動手操作, 這樣一來的學習非常踏實,甚至比YT學習都來的高效率、實際。 尤其此系列都是原創發想的腳本,當然超強! 喜歡記得關注,未來還有更多超酷的前端內容可以玩,下課⧸⎩⎠⎞͏(・∀・)⎛͏⎝⎭⧹