🔍 搜尋結果:pcl

🔍 搜尋結果:pcl

關於 GIT 你需要了解的一切

我相信您可以想像版本控製程式碼的重要性,以便我們可以**恢復變更**並**恢復遺失的資料以及其他可能性**。 我打賭你知道有人*(不是我呵呵)*通過使用越來越有創意的名稱建立文件副本來對其文件進行版本控制... ![GIF 模擬具有不同名稱的檔案的複製。範例:文章、文章最終版本等。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sffhjyl41pfnjklivnsv.gif) 在 1972 年之前,隨著**SCCS *(原始碼控制系統)***的發布,這是有史以來第一個**集中式**版本控制軟體之一,這可能是任何人對其程式碼進行版本控制的方式。 但我們在這裡不是在談論 SCCS,我們現在真正感興趣的是**GIT** ,這是一款**分散式**開源版本控制軟體,明年*(07/04/2005)*將慶祝其誕生 20 週年。 目錄 -- - [1.GIT是什麼?](#chapter-1) - [2.GIT如何運作?](#chapter-2) - [3.安裝GIT](#chapter-3) - [4.配置GIT](#chapter-4) - [5. 啟動本機儲存庫](#chapter-5) - [6. 使用 GIT](#chapter-6) - [7. 認識分支](#chapter-7) - [8. 與遠端倉庫同步](#chapter-8) - [9. 結論](#chapter-9) - [10. 參考文獻](#chapter-10) 1.GIT是什麼?<a name="chapter-1"></a> --------------------------------- GIT 是一個開源**分散式**版本控制系統,於 2005 年推出,由 Linus Torvald *(Linux 核心建立者)*開發。 使用GIT,我們可以在本地**控制專案的版本***(在工作資料夾中)*並將所有變更同步到遠端儲存庫*(例如在GitHub上)* 。 2.GIT如何運作?<a name="chapter-2"></a> ---------------------------------- 想像一個**實體**文件櫃,其中有一個包含所有專案文件的資料夾。每當有人需要操作文件時,他們都必須將其拾取,**將其從資料夾中刪除,並在完成後將其返回到資料夾中**。因此,兩個人**不可能**處理同一個文件,完全避免了任何可能的衝突。 **但這不是 Git 的工作原理!** *(感謝上帝)* 這就是**集中式**版本控制系統的工作方式,其中使用者需要**「簽出」**和**「簽入」**文件,即每當有人需要處理特定文件時,他們需要簽出該文件,**刪除從儲存庫中獲取**文件,然後在工作完成後簽入該文件,**並將其返回儲存庫**。 ![GIF 模擬集中式版本控制系統的操作。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26a9j0m4pcu5prb0kauo.gif) 在像**GIT**這樣的**分散式**系統中,多個人可以存取同一個遠端儲存庫中的檔案。每當有人需要操作文件時,他們只需將其**克隆***(或克隆整個存儲庫)*到本地計算機,然後將修改發送回遠端存儲庫。這使得**多人可以處理同一個專案**,甚至操作**相同的文件**。 ![GIF 模擬分散式版本控制系統的操作。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nb0x0u9kc6m7z2hz7zfj.gif) 這就是允許大型**開源**專案的分佈,來自世界不同地區的人們在同一個專案上工作,管理修改和可能的衝突*(是的,這裡可能會發生合併衝突)* 。 3.安裝GIT<a name="chapter-3"></a> ------------------------------- **GIT**適用於主要作業系統*(Windows、Linux、MacOs...),*安裝過程非常簡單,可以透過**命令列**或透過[git-scm.com](https://git-scm.com/)的**官方安裝程式**完成。 ### 3.1 在 Windows 上 要在 Windows 上安裝 GIT,只需存取官方網站並[下載安裝程式即可](https://git-scm.com/download/win)。然後只需**按照說明進行操作**,一切都會好起來,然後我們就可以在終端機中使用 GIT 命令了。 ### 3.2 在Linux上 對於Linux,我們可以使用以下**命令**安裝**GIT** : ``` sudo apt install git-all ``` 透過這樣做, **GIT**必須準備好在我們的終端中運作。 ### 3.3 在 MacOS 上 對於 Mac,安裝**GIT**最簡單的方法是安裝[Homebrew](https://brew.sh/) ,然後在終端機中執行以下**命令**: ``` brew install git ``` 然後**GIT**必須準備好在我們的終端中運作。 4.配置GIT<a name="chapter-4"></a> ------------------------------- 安裝後,使用以下**命令設定 GIT**很重要: ``` git config --global user.name "[username]" # e.g. John Doe ``` ``` git config --global user. email "[[email protected]]" # e.g. [email protected] ``` > 此外,也可以透過刪除`--global`標籤來為某些本機儲存庫配置特定使用者。 5. 啟動本機儲存庫<a name="chapter-5"></a> ---------------------------------- 配置**GIT**後,我們就可以**啟動本地儲存庫了**。為此,我們可以**從頭開始一個新的儲存庫**或**複製現有的遠端儲存庫**。 ### 5.1 從頭開始(git init) 要啟動新儲存庫,只需導航到所需的儲存庫**根資料夾**並執行以下命令: ``` git init ``` ![Linux 終端機執行「git init」命令並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw35xe9jwrwzo937to5b.png) 透過這樣做,將在專案資料夾內建立一個`.git`目錄,該目錄將**負責此本機儲存庫的工作資料夾中的版本控制**。 ### 5.2 克隆現有儲存庫(git clone) 克隆現有遠端儲存庫就像從頭開始建立新儲存庫一樣簡單。為此,只需使用`git clone`命令,將要複製**的儲存庫的 URL**作為參數傳遞到要複製儲存庫的資料夾中: 複製現有的遠端儲存庫就像從頭開始建立新的儲存庫一樣簡單。為此,只需使用`git clone`命令,將**遠端儲存庫 URL**克隆到我們要下載儲存庫的資料夾中: ``` git clone [repository-url] ``` ![Linux 終端機執行「git clone」命令並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlt8rtua4ri7kpnna5el.png) 然後整個存儲庫必須**克隆**到我們的本地機器並**自動連結到相關的遠端存儲庫**。 > 有了複製的儲存庫,我們以後就不再需要使用`git remote`指令了。 6. 使用 GIT<a name="chapter-6"></a> --------------------------------- 在我們的**本機儲存庫**中,我們可以建立專案所需的文件,但它們**不會由 GIT 自動同步**,當有任何變更需要版本控制時,我們需要報告它。 因此,我們可以根據需要**操作**文件,並**在完成所需的變更後**,**將更新的文件傳送到 GIT** 。 為此,重要的是要了解版本控制中有**3 個階段的無限流***(是的,無限)* : ``` MODIFY -> STAGE -> COMMIT ``` - **修改:**版本控制的第一階段,我們在這裡找到與上一個可用版本相比**已更改的檔案**。 - **STAGE:**版本控制的第二階段,這是我們放置**要新增到下次提交的修改檔案**的地方。 - **COMMIT:**版本控制的最後階段,當我們**確認變更**時,將階段中修改的檔案傳送到本機儲存庫。 提交修改後的文件後,我們在本地存儲庫中有一個可用的**新版本**,它可以再次接收更新,再次*“修改”* ,然後放入*“階段”* ,並再次*“提交”* ,確認較新的版本版本等等*(因此,「無限」哈哈)* 。 > 值得注意的是,提交不會覆蓋已修改文件的舊版本,而是包含新版本以及指向最後版本的指針,從而追蹤 GIT 追蹤的每個文件的版本。 ### 6.1 新增和提交(git add 和 git commit) 儘管聽起來很複雜,但執行版本控制流程**非常簡單**。由於所需的修改已完成,我們使用以下命令**新增要在舞台上提交的修改後的檔案**: ``` git add [filename] ``` > `git add -A` -> 將所有修改的檔案立即加入階段。 > > `git add *.[extensão-do-arquivo]` -> 將所有具有指定檔案副檔名的已修改檔案一次新增至暫存區(例如`git add *.html` ) 我們可以隨時使用`git status`指令檢查目前本機儲存庫**狀態**: ![Linux 終端機執行「git status」命令並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e28lg7wym8s0woytgzqz.png) 請注意,當我們**在建立新檔案後**在儲存庫中執行`git status`時,新檔案將顯示為*「Untracked」* 。這意味著該文件是**全新的**,仍然需要加入到任何提交中才能被**GIT追蹤**。 > 可以讓 GIT 忽略儲存庫中的特定檔案或資料夾。為此,我們只需將一個名為`.gitignore`的檔案新增到根資料夾中,並在其中寫入應忽略的檔案或資料夾的名稱。 > > 注意:被忽略的檔案和資料夾將不再出現在 GIT 軌道上,甚至不會顯示為「未追蹤」。要重置跟踪,只需從`.gitignore`文件中刪除所需的名稱即可。 要包含文件,我們可以使用要新增的文件的名稱來執行`git add`命令*(在本例中為「index.html」)* : ![Linux 終端機的螢幕截圖,執行命令“git add”,然後執行“git status”,顯示結果。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pz1hnk9gpnxtv2s8xort.png) 這樣,透過重新執行`git status`我們可以看到新檔案已新增至*「stage」* ,並**最終準備好在下一次提交中發送**,這可以使用以下命令完成: ``` git commit -m "[descriptive-message]" ``` > 提交具有唯一的 ID(雜湊碼)並且是**IMMUTABLE 的**,即一旦確認就無法修改。 > > `git commit -a` -> 執行直接提交,將所有修改的檔案新增至暫存區並提交它們。 **成功提交檔案後**,執行`git status`時,我們注意到**沒有更多修改的檔案需要上傳**,因為所有修改都已在上次**提交**時有效保存在我們的本機儲存庫中。 ![Linux 終端機的螢幕截圖,執行指令“git commit”,然後執行“git status”,顯示結果。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ryqqs4mi46hxtuex199i.png) 此外,還可以使用`git log`指令查看儲存庫的提交日誌來**驗證所做的更改**,該指令顯示所有提交的一些元資料,例如雜湊碼、分支、作者、日期等。 ![Linux 終端機執行命令「git log」並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljvj9se6srxslsu8vsj7.png) 可以重複整個過程來加入我們專案所需的新文件,修改它們並透過進行新的提交將它們發送到本地儲存庫。 ![GIF 模擬 GIT 分支中的多次提交。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uez3henix8kchperuw8.gif) > `git log -N` -> 顯示最近 N 次提交的日誌。 > > `git log [branch-A] [branch-B]` -> 顯示「branch-B」中但不在「branch-A」中的提交日誌。 > > `git log --follow [filename]` -> 顯示更改指定檔案的提交日誌,即使它已更改名稱。 > > `git diff` -> 列出與儲存庫中最新可用版本相比所做的變更。 > > `git diff [nome-do-arquivo]` -> 列出對指定檔案相對於儲存庫中最後一個可用版本所做的變更。 ### 6.2 撤銷提交前後的更改 **在提交之前**,對本地存儲庫所做的任何更改都可以撤消或更改,但**一旦提交,就無法更改**。這是因為提交是**不可變的物件**,這意味著不可能編輯或更改提交中的資料。 但是,**可以進行新的提交**來撤銷更改,或更正先前提交中的不正確資訊。無論哪種方式,我們都可以使用以下命令之一: ``` git checkout -- [filename] # Discards changes made to the local file before the commit (irreversible action) ``` ``` git reset --hard HEAD # Discards changes made to a file that is in stage (before the commit) ``` ``` git reset --hard HEAD~1 # Discards the last commit made in the local repository (only the last commit) ``` ``` git commit --amend # Creates a new commit, replacing the last commit made in the local repository ``` ``` git revert [commit-hash] # Creates a new commit, reverting the changes of the specified commit ``` 7. 認識分支<a name="chapter-7"></a> ------------------------------- **分支**只不過是儲存庫的一個**分支**,到目前為止,所有操作都已在分支`master/main'`上執行。 > 預設情況下,儲存庫中建立的第一個分支是`master/main` ,它是儲存庫的主分支。 ### 7.1 為什麼要使用分支? 乍看之下似乎沒什麼,但**分店卻為專案的發展賦予了巨大的力量**。 想像一下,我們正在開發一個 Web 平台,並且想要**測試一項新功能**,但我們的儲存庫已經**託管或與其他人共享**,任何有問題的更改都可能會給他們帶來糟糕的體驗。我們可以做什麼? 如果您一直在考慮**複製並貼上**專案資料夾,建立一個新的**「測試版本」** ,那麼您是對的!嗯,差不多… 透過 GIT,我們可以對分支做類似的事情。由於它們是**分支**,我們可以簡單地**建立一個名為「test」的新分支**,從而在完全**隔離的分支**中擁有我們專案的一個版本,準備好進行翻轉,**而不會危及主分支**。 ![GIF 模擬使用新提交建立新分支。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szc081nndoi18vklty5r.gif) ### 7.2 建立分支(git分支) 建立**分支**意味著建立可以獨立工作的儲存庫的並行副本,而不會影響`master/main`分支。為此,我們只需執行以下命令: ``` git branch [branch-name] ``` > 在沒有特定分支名稱的情況下執行`git branch`指令必須顯示儲存庫中可用分支的列表,並用「\*」標記目前正在使用的分支。 在執行`git branch test`指令之前, `git branch`指令只會傳回`master`分支。 ![Linux 終端機執行「gitbranch」命令、建立新分支並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqpdrtvhze9qhr31ewbh.png) 建立新分支後,我們可以執行以下命令在可用分支之間切換: ``` git checkout [branch-name] ``` 執行`git checkout test`指令後,我們可以看到**活動分支已切換**。從那一刻起,**所有提交的資訊都會傳送到儲存庫的`test`分支**,而不會影響分支`master/main` 。 ![Linux 終端機執行「git checkout」命令、切換活動分支並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/egau7y81leig8za36myi.png) > 可以根據需要建立任意數量的分支,並且我們可以使用以下命令與現有分支進行互動: > > `git checkout -b [branch-name]` -> 使用給定名稱建立一個新分支並直接切換到它。 > > `git branch -d [branch-name]` -> 刪除指定分支。 > > `git branch -m [new-name]` -> 將目前分支的名稱變更為給定名稱。 ### 7.3 合併分支(git merge) **當完成不同分支**上的工作,並且我們確定所做的更改不會在專案中引起任何問題時,我們可以將當前分支**合併**到`master/main`分支中,**應用當前分支中的所有更改分支到儲存庫的主分支**。 要**合併**分支,我們需要**切換到將接收更改的分支**並執行以下命令: ``` git merge [branch-name] # Merge the given branch into the active branch ``` 在這裡,由於我們位於分支`test`上,因此我們應該使用`git checkout`命令**切換到分支`master`** ,然後使用我們要合併的分支的名稱*(在本例中為“test”)*執行`git merge`命令。 ![Linux 終端機的螢幕截圖,執行命令“git checkout”,切換到 master 分支,然後“git merge”,顯示結果。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6tqap1uu5gjipkety6w.png) 透過這樣做,在分支`test`上完成的所有工作*(在本例中為`style.css`檔案的建立)*將合併到分支`master`中。 ![模擬兩個分支之間的合併過程的 GIF。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hs17m005ogm0qlbedjy.gif) ### 7.4 合併衝突 如果**在同一行上更改了一個或多個檔案**並且合併無法**自動**完成,則使用`git merge`合併不同分支可能會導致一些**衝突**。 ![執行「git merge」命令的 Linux 終端機的螢幕截圖,該命令傳回衝突警告。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mwedudsff1yf8kpzguc.png) 出現這種情況時,我們可以執行`git status`指令來檢查**哪些檔案**有衝突。 ![出現合併衝突警報後執行「git status」指令的 Linux 終端機的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcfpmta2yiy1xpyc729u.png) 在繼續合併之前,我們需要**解決衝突**,方法是定義應該進行哪些更改,或檢查更改以使它們相互相容。為此, **GIT 將在衝突文件中插入標記**以協助解決問題。 ![在文字編輯器中開啟的衝突檔案的螢幕截圖,顯示了 GIT 建立的用於幫助解決衝突的標記。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0e727le3ogmhtuq0g7w.png) 解決衝突後,我們只需要將修改過的檔案放回舞台,提交新的無衝突版本,然後再次執行`git merge`指令,這一定會成功合併,沒有任何問題。 8. 與遠端倉庫同步<a name="chapter-8"></a> ---------------------------------- 我們已經知道可以**將本地存儲庫連接到遠端存儲庫**並遠端同步我們的所有工作,使其**保持最新**。 為此,我們需要執行`git push`命令,該**命令將所有提交從本地存儲庫發送到遠端存儲庫**,但首先我們需要\*\*配置遠端存儲庫。 ### 8.1 配置遠端倉庫 啟動*遠端儲存庫*非常簡單。這裡我們將使用**GitHub**來完成它。 首先,我們需要在 GitHub 帳戶中**啟動一個新的空白儲存庫***(只需選擇一個名稱並點擊「建立儲存庫」)* : ![GitHub 上儲存庫建立頁面的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ug1aw5eq15du36vd1hri.png) 接下來,我們需要透過在本機儲存庫中執行以下命令**來配置遠端儲存庫和本機儲存庫之間的關係**: ``` git remote add origin [remote-repository-url] ``` ![Linux 終端機執行「git Remote」命令並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhrlfg0f0pgljl5b18vl.png) > `git remote -v` -> 顯示實際連接到本機儲存庫的遠端儲存庫的 URL。 正確**連接**遠端儲存庫後,我們需要使用指令`git branch -m main`**將本機分支`master/main`的名稱變更**為「main」 *(如果您的本機分支已稱為`main` ,請忽略此步驟)* : ![Linux 終端機執行「gitbranch」指令、將「master」分支重新命名為「main」並顯示結果的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f0al7joro01pch0ac124.png) > 保持本機儲存庫的主分支與我們要推送到的遠端儲存庫的主分支同名非常重要。 最後,完成上述步驟後,我們可以使用以下命令首次將本機儲存庫與遠端儲存庫**同步**: ``` git push -u origin main ``` ![執行「git push」命令的 Linux 終端機的螢幕截圖,該命令需要 GitHub 身份驗證。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fvkdyis8y989xron52nc.png) 當我們執行`git push -u origin main`指令時,我們可能需要輸入**GitHub 憑證***(使用者和存取權杖)* 。 > 如果您不知道**GitHub 存取令牌**是什麼,或者您沒有設定存取令牌, [請按一下此處](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic)。 > > 我們也可以透過**使用 GitHub CLI 設定身份驗證**來解決此問題。 [按此處](https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git)了解具體方法。 經過**身份驗證**後, `git push`應該會成功執行,將本地儲存庫中的所有提交與遠端儲存庫同步。 ![Linux 終端機的螢幕截圖,顯示 GitHub 驗證後繼續執行「git Push」命令。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vp7q4htszkzrfc6vepcl.png) ![收到帶有新檔案的「git Push」後 GitHub 上的遠端儲存庫的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rqfhi6s6jh369tseh5y.png) ### 8.2 第一次後的Git推送(git推送) 完成上述所有步驟後,可以單獨使用`git push`指令完成**新同步**,無需任何其他參數,如下所示。 ![Linux 終端機執行「git status」、「git commit」和「git Push」命令、執行新提交並將更新推送到遠端儲存庫的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aoatcspiwosywih07nsm.png) ![收到新更新後 GitHub 上的遠端儲存庫的螢幕截圖。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jskxpghboi62mqw2qcb2.png) > 在這種情況下,使用**GitHub CLI**繞過了執行命令`git push`所需的身份驗證。您可以[點擊此處](https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git)了解具體方法。 ### 8.3 更新本地倉庫(git pull) 使用**分散式**遠端儲存庫,可以**遠端**進行更改*(直接在遠端儲存庫中)* ,從而導致我們的本機儲存庫**變得過時**。 考慮到這一點,**更新本機儲存庫**並同步我們在遠端儲存庫中獲得的任何變更非常重要,**以確保本機專案始終具有遠端儲存庫中可用的最新版本**。為此,我們可以執行以下命令: ``` git pull ``` 想像一下,一個**新檔案**`README.md`已**直接在我們的遠端儲存庫中**建立,因此我們的本機儲存庫現已過時。 ![遠端儲存庫的螢幕截圖,其中遠端新增了新的 README.md 檔案。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hccw00vyr7j6lcfm02d2.png) 在本機儲存庫中,我們可以使用上面提到的`git pull`**同步**遠端儲存庫中的變更。 ![Linux 終端機執行「git pull」命令的螢幕截圖,使用遠端儲存庫中的新變更更新本機儲存庫。](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/it9ylx1imd94yey5d0wd.png) > 當我們執行`git pull`指令時回傳的前 7 行是`git fetch`指令的回傳。換句話說,如果我們執行`git pull`命令而不先執行`git fetch`命令,GIT 將同時執行這兩個命令以從遠端存儲庫檢索更新並將其同步到本地存儲庫。 > > `git fetch` -> 從遠端儲存庫取得更新,但不同步本機儲存庫(需要`git pull` )。 9. 結論<a name="chapter-9"></a> ----------------------------- 這一切讓我們確信GIT是程式設計師日常生活中必備的版本控制系統,了解它的主要指令和用途可以成為我們技術資歷的轉捩點。最後,隨著本地和遠端儲存庫的同步和更新,以及我們迄今為止所學到的一切,我們已經準備好繼續推進這個令人敬畏的版本控制系統的實用性。 10. 參考文獻<a name="chapter-10"></a> --------------------------------- - [GIT 的官方文件。](https://git-scm.com/docs/git#_git_commands) - [\[PT-BR\] GIT:入門迷你課程(45 分鐘內學會) - Codigo Fonte TV](https://youtu.be/ts-H3W1uLMM?si=-hKGkUmwgT2lZJwy) --- 原文出處:https://dev.to/reenatoteixeira/everything-that-you-need-to-know-about-git-2440

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

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