標籤: iphone維修

  • 程序員必備基礎:Git 命令全方位學習

    程序員必備基礎:Git 命令全方位學習

    前言

    掌握Git命令是每位程序員必備的基礎,之前一直是用smartGit工具,直到看到大佬們都是在用Git命令操作的,回想一下,發現有些Git命令我都忘記了,於是寫了這篇博文,複習一下~

    https://github.com/whx123/JavaHome

    公眾號:撿田螺的小男孩

    文章目錄

    • Git是什麼?
    • Git的相關理論基礎
    • 日常開發中,Git的基本常用命令
    • Git進階之分支處理
    • Git進階之處理衝突
    • Git進階之撤銷與回退
    • Git進階之標籤tag
    • Git其他一些經典命令

    Git是什麼

    在回憶Git是什麼的話,我們先來複習這幾個概念哈~

    什麼是版本控制?

    百度百科定義是醬紫的~

    版本控制是指對軟件開發過程中各種程序代碼、配置文件及說明文檔等文件變更的管理,是軟件配置管理的核心思想之一。

    那些年,我們的畢業論文,其實就是版本變更的真實寫照…腦洞一下,版本控制就是這些論文變更的管理~

    什麼是集中化的版本控制系統?

    那麼,集中化的版本控制系統又是什麼呢,說白了,就是有一個集中管理的中央服務器,保存着所有文件的修改歷史版本,而協同開發者通過客戶端連接到這台服務器,從服務器上同步更新或上傳自己的修改。

    什麼是分佈式版本控制系統?

    分佈式版本控制系統,就是遠程倉庫同步所有版本信息到本地的每個用戶。嘻嘻,這裏分三點闡述吧:

    • 用戶在本地就可以查看所有的歷史版本信息,但是偶爾要從遠程更新一下,因為可能別的用戶有文件修改提交到遠程哦。
    • 用戶即使離線也可以本地提交,push推送到遠程服務器才需要聯網。
    • 每個用戶都保存了歷史版本,所以只要有一個用戶設備沒問題,就可以恢複數據啦~

    什麼是Git?

    Git是免費、開源的分佈式版本控制系統,可以有效、高速地處理從很小到非常大的項目版本管理。

    Git的相關理論基礎

    • Git的四大工作區域
    • Git的工作流程
    • Git文件的四種狀態
    • 一張圖解釋Git的工作原理

    Git的四大工作區域

    先複習Git的幾個工作區域哈:

    • Workspace:你電腦本地看到的文件和目錄,在Git的版本控制下,構成了工作區。
    • Index/Stage:暫存區,一般存放在 .git目錄下,即.git/index,它又叫待提交更新區,用於臨時存放你未提交的改動。比如,你執行git add,這些改動就添加到這個區域啦。
    • Repository:本地倉庫,你執行git clone 地址,就是把遠程倉庫克隆到本地倉庫。它是一個存放在本地的版本庫,其中HEAD指向最新放入倉庫的版本。當你執行git commit,文件改動就到本地倉庫來了~
    • Remote:遠程倉庫,就是類似github,碼雲等網站所提供的倉庫,可以理解為遠程數據交換的倉庫~

    Git的工作流程

    上一小節介紹完Git的四大工作區域,這一小節呢,介紹Git的工作流程咯,把git的操作命令和幾個工作區域結合起來,個人覺得更容易理解一些吧,哈哈,看圖:

    git 的正向工作流程一般就這樣:

    • 從遠程倉庫拉取文件代碼回來;
    • 在工作目錄,增刪改查文件;
    • 把改動的文件放入暫存區;
    • 將暫存區的文件提交本地倉庫;
    • 將本地倉庫的文件推送到遠程倉庫;

    Git文件的四種狀態

    根據一個文件是否已加入版本控制,可以把文件狀態分為:Tracked(已跟蹤)和Untracked(未跟蹤),而tracked(已跟蹤)又包括三種工作狀態:Unmodified,Modified,Staged

    • Untracked: 文件還沒有加入到git庫,還沒參与版本控制,即未跟蹤狀態。這時候的文件,通過git add 狀態,可以變為Staged狀態
    • Unmodified:文件已經加入git庫, 但是呢,還沒修改, 就是說版本庫中的文件快照內容與文件夾中還完全一致。 Unmodified的文件如果被修改, 就會變為Modified. 如果使用git remove移出版本庫, 則成為Untracked文件。
    • Modified:文件被修改了,就進入modified狀態啦,文件這個狀態通過stage命令可以進入staged狀態
    • staged:暫存狀態. 執行git commit則將修改同步到庫中, 這時庫中的文件和本地文件又變為一致, 文件為Unmodified狀態.

    一張圖解釋Git的工作原理

    日常開發中,Git的基本常用命令

    • git clone
    • git checkout -b dev
    • git add
    • git commit
    • git log
    • git diff
    • git status
    • git pull/git fetch
    • git push

    這個圖只是模擬一下git基本命令使用的大概流程哈~

    git clone

    當我們要進行開發,第一步就是克隆遠程版本庫到本地呢

    git clone url  克隆遠程版本庫
    

    git checkout -b dev

    克隆完之後呢,開發新需求的話,我們需要新建一個開發分支,比如新建開發分支dev

    創建分支:

    git checkout -b dev   創建開發分支dev,並切換到該分支下
    

    git add

    git add的使用格式:

    git add .	添加當前目錄的所有文件到暫存區
    git add [dir]	添加指定目錄到暫存區,包括子目錄
    git add [file1]	添加指定文件到暫存區
    

    有了開發分支dev之後,我們就可以開始開發啦,假設我們開發完HelloWorld.java,可以把它加到暫存區,命令如下

    git add Hello.java  把HelloWorld.java文件添加到暫存區去
    

    git commit

    git commit的使用格式:

    git commit -m [message] 提交暫存區到倉庫區,message為說明信息
    git commit [file1] -m [message] 提交暫存區的指定文件到本地倉庫
    git commit --amend -m [message] 使用一次新的commit,替代上一次提交
    

    把HelloWorld.java文件加到暫存區后,我們接着可以提交到本地倉庫啦~

    git commit -m 'helloworld開發'
    

    git status

    git status,表示查看工作區狀態,使用命令格式:

    git status  查看當前工作區暫存區變動
    git status -s  查看當前工作區暫存區變動,概要信息
    git status  --show-stash 查詢工作區中是否有stash(暫存的文件)
    

    當你忘記是否已把代碼文件添加到暫存區或者是否提交到本地倉庫,都可以用git status看看哦~

    git log

    git log,這個命令用得應該比較多,表示查看提交歷史/提交日誌~

    git log  查看提交歷史
    git log --oneline 以精簡模式显示查看提交歷史
    git log -p <file> 查看指定文件的提交歷史
    git blame <file> 一列表方式查看指定文件的提交歷史
    

    嘻嘻,看看dev分支上的提交歷史吧要回滾代碼就經常用它喵喵提交歷史

    git diff

    git diff 显示暫存區和工作區的差異
    git diff filepath   filepath路徑文件中,工作區與暫存區的比較差異
    git diff HEAD filepath 工作區與HEAD ( 當前工作分支)的比較差異
    git diff branchName filepath 當前分支的文件與branchName分支的文件的比較差異
    git diff commitId filepath 與某一次提交的比較差異
    

    如果你想對比一下你改了哪些內容,可以用git diff對比一下文件修改差異哦

    git pull/git fetch

    git pull  拉取遠程倉庫所有分支更新併合併到本地分支。
    git pull origin master 將遠程master分支合併到當前本地master分支
    git pull origin master:master 將遠程master分支合併到當前本地master分支,冒號後面表示本地分支
    
    git fetch --all  拉取所有遠端的最新代碼
    git fetch origin master 拉取遠程最新master分支代碼
    

    我們一般都會用git pull拉取最新代碼看看的,解決一下衝突,再推送代碼到遠程倉庫的。

    有些夥伴可能對使用git pull還是git fetch有點疑惑,其實
    git pull = git fetch+ git merge。pull的話,拉取遠程分支並與本地分支合併,fetch只是拉遠程分支,怎麼合併,可以自己再做選擇。

    git push

    git push 可以推送本地分支、標籤到遠程倉庫,也可以刪除遠程分支哦。

    git push origin master 將本地分支的更新全部推送到遠程倉庫master分支。
    git push origin -d <branchname>   刪除遠程branchname分支
    git push --tags 推送所有標籤
    

    如果我們在dev開發完,或者就想把文件推送到遠程倉庫,給別的夥伴看看,就可以使用git push origin dev~

    Git進階之分支處理

    Git一般都是存在多個分支的,開發分支,回歸測試分支以及主幹分支等,所以Git分支處理的命令也需要很熟悉的呀~

    • git branch
    • git checkout
    • git merge

    git branch

    git branch用處多多呢,比如新建分支、查看分支、刪除分支等等

    新建分支:

    git checkout -b dev2  新建一個分支,並且切換到新的分支dev2
    git branch dev2 新建一個分支,但是仍停留在原來分支
    

    查看分支:

    git branch    查看本地所有的分支
    git branch -r  查看所有遠程的分支
    git branch -a  查看所有遠程分支和本地分支
    

    刪除分支:

    git branch -D <branchname>  刪除本地branchname分支
    

    git checkout

    切換分支:

    git checkout master 切換到master分支
    

    git merge

    我們在開發分支dev開發、測試完成在發布之前,我們一般需要把開發分支dev代碼合併到master,所以git merge也是程序員必備的一個命令。

    git merge master  在當前分支上合併master分支過來
    git merge --no-ff origin/dev  在當前分支上合併遠程分支dev
    git merge --abort 終止本次merge,並回到merge前的狀態
    

    比如,你開發完需求后,發版全需要把代碼合到主幹master分支,如下:

    Git進階之處理衝突

    Git版本控制,還是多個人一起搞的,多個分支並存的,這就難免會有衝突出現~

    Git合併分支,衝突出現

    同一個文件,在合併分支的時候,如果同一行被多個分支或者不同人都修改了,合併的時候就會出現衝突。

    舉個粟子吧,我們現在在dev分支,修改HelloWorld.java文件,假設修改了第三行,並且commit提交到本地倉庫,修改內容如下:

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello,撿田螺的小男孩!");
        }
    }
    

    我們切回到master分支,也修改HelloWorld.java同一位置內容,如下:

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello,jay!!");
        }
    }
    

    再然後呢,我們提交一下master分支的這個改動,並把dev分支合併過下,就出現衝突啦,如圖所示:

    Git解決衝突

    Git 解決衝突步驟如下:

    • 查看衝突文件內容
    • 確定衝突內容保留哪些部分,修改文件
    • 重新提交,done

    1.查看衝突文件內容

    git merge提示衝突后,我們切換到對應文件,看看衝突內容哈,,如下:

    2.確定衝突內容保留哪些部分,修改文件

    • Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,
    • <<<<<<<HEAD是指主分支修改的內容,>>>>>>> dev是指dev分支上修改的內容

    所以呢,我們確定到底保留哪個分支內容,還是兩個分支內容都保留呢,然後再去修改文件衝突內容~

    3.修改完衝突文件內容,我們重新提交,衝突done

    Git進階之撤銷與回退

    Git的撤銷與回退,在日常工作中使用的比較頻繁。比如我們想將某個修改后的文件撤銷到上一個版本,或者想撤銷某次多餘的提交,都要用到git的撤銷和回退操作。

    代碼在Git的每個工作區域都是用哪些命令撤銷或者回退的呢,如下圖所示:

    有關於Git的撤銷與回退,一般就以下幾個核心命令

    • git checkout
    • git reset
    • git revert

    git checkout

    如果文件還在工作區,還沒添加到暫存區,可以使用git checkout撤銷

    git checkout [file]  丟棄某個文件file
    git checkout .  丟棄所有文件
    

    以下demo,使用git checkout — test.txt 撤銷了暫存區test.txt的修改

    git reset

    git reset的理解

    git reset的作用是修改HEAD的位置,即將HEAD指向的位置改變為之前存在的某個版本.

    為了更好地理解git reset,我們來回顧一下,Git的版本管理及HEAD的理解

    Git的所有提交,會連成一條時間軸線,這就是分支。如果當前分支是master,HEAD指針一般指向當前分支,如下:

    假設執行git reset,回退到版本二之後,版本三不見了哦,如下:

    git reset的使用

    Git Reset的幾種使用模式

    git reset HEAD --file
    回退暫存區里的某個文件,回退到當前版本工作區狀態
    git reset –-soft 目標版本號 可以把版本庫上的提交回退到暫存區,修改記錄保留
    git reset –-mixed 目標版本號 可以把版本庫上的提交回退到工作區,修改記錄保留
    git reset –-hard  可以把版本庫上的提交徹底回退,修改的記錄全部revert。
    

    先看一個粟子demo吧,代碼git add到暫存區,並未commit提交,就以下醬紫回退,如下:

    git reset HEAD file 取消暫存
    git checkout file 撤銷修改
    

    再看另外一個粟子吧,代碼已經git commit了,但是還沒有push:

    git log  獲取到想要回退的commit_id
    git reset --hard commit_id  想回到過去,回到過去的commit_id
    

    如果代碼已經push到遠程倉庫了呢,也可以使用reset回滾哦(這裏大家可以自己操作實踐一下哦)~

    git log
    git reset --hard commit_id
    git push origin HEAD --force
    

    git revert

    與git reset不同的是,revert複製了那個想要回退到的歷史版本,將它加在當前分支的最前端。

    revert之前:

    revert 之後:

    當然,如果代碼已經推送到遠程的話,還可以考慮revert回滾呢

    git log  得到你需要回退一次提交的commit id
    git revert -n <commit_id>  撤銷指定的版本,撤銷也會作為一次提交進行保存
    

    Git進階之標籤tag

    打tag就是對發布的版本標註一個版本號,如果版本發布有問題,就把該版本拉取出來,修復bug,再合回去。

    git tag  列出所有tag
    git tag [tag] 新建一個tag在當前commit
    git tag [tag] [commit] 新建一個tag在指定commit
    git tag -d [tag] 刪除本地tag
    git push origin [tag] 推送tag到遠程
    git show [tag] 查看tag
    git checkout -b [branch] [tag] 新建一個分支,指向某個tag
    

    Git其他一些經典命令

    git rebase

    rebase又稱為衍合,是合併的另外一種選擇。

    假設有兩個分支master和test

          D---E test
          /
     A---B---C---F--- master
    

    執行 git merge test得到的結果

           D--------E
          /          \
     A---B---C---F----G---   test, master
    

    執行git rebase test,得到的結果

    A---B---D---E---C‘---F‘---   test, master
    

    rebase好處是: 獲得更優雅的提交樹,可以線性的看到每一次提交,並且沒有增加提交節點。所以很多時候,看到有些夥伴都是這個命令拉代碼:git pull –rebase

    git stash

    stash命令可用於臨時保存和恢復修改

    git stash  把當前的工作隱藏起來 等以後恢復現場後繼續工作
    git stash list 显示保存的工作進度列表
    git stash pop stash@{num} 恢復工作進度到工作區
    git stash show :显示做了哪些改動
    git stash drop stash@{num} :刪除一條保存的工作進度
    git stash clear 刪除所有緩存的stash。
    

    git reflog

    显示當前分支的最近幾次提交

    git blame filepath

    git blame 記錄了某個文件的更改歷史和更改人,可以查看背鍋人,哈哈

    git remote

    git remote   查看關聯的遠程倉庫的名稱
    git remote add url   添加一個遠程倉庫
    git remote show [remote] 显示某個遠程倉庫的信息
    

    參考與感謝

    感謝各位前輩的文章:

    • 一個小時學會Git
    • 【Git】(1)—工作區、暫存區、版本庫、遠程倉庫
    • Git Reset 三種模式
    • Git恢復之前版本的兩種方法reset、revert(圖文詳解)
    • Git撤銷&回滾操作(git reset 和 get revert)
    • 為什麼要使用git pull –rebase?

    公眾號

    • 歡迎關注我個人公眾號,交個朋友,一起學習哈~
    • 如果文章有錯誤,歡迎指出哈,感激不盡~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 【思考】URI和URL的區別?以及URL的結構

    【思考】URI和URL的區別?以及URL的結構

    URI = Universal Resource Identifier
    URL = Universal Resource Locator

    在學習中,我們難免會遇到 URI 和 URL,有時候都傻傻分不清,為啥這邊是 URI 那邊又是 URL,這兩者到底有什麼區別呢?

    我們從名字上看

    • 統一資源標識符(Uniform Resource Identifier, URI):是一個用於標識某一互聯網資源名稱的字符串。
    • 統一資源定位符(Uniform Resource Locator, URL):是一個用於標識和定位某一互聯網資源名稱的字符串。

    可能大家就比較困惑了,這倆好像是一樣的啊?那我們就類比一下我們現實生活中的情況:
    我們要找一個人——張三,我們可以通過他的唯一的標識來找,比如說身份證,那麼這個身份證就唯一的標識了一個人,這個身份證就是一個 URI
    而要找到張三,我們不一定要用身份證去找,我們還可以根據地址去找,如 在清華大學18號宿舍樓的404房間第一個床鋪的張三,我們也可以唯一確定一個張三,
    動物住址協議://地球/中國/北京市/清華大學/18號宿舍樓/404號寢/張三.人。而這個地址就是我們用於標識和定位的 URL
    我們從上面可以很明顯的看出,URI 通過任何方法標識一個人即可,而 URL 雖然也可以標識一個人,但是它主要是通過定位地址的方法標識一個人,所以 URL 其實是 URI 的一個子集,即 URL 是靠標識定位地址的一個 URI

    Url 的構成

    URL(Uniform Resource Locator,統一資源定位符),用於定位網絡上的資源,每一個信息資源都有統一的且在網上唯一的地址。

    Url一般有以下部分組成
    scheme://host:port/path?query#fragment

    Scheme: 通信協議,一般為http、https等;
    Host: 服務器的域名主機名或ip地址;
    Port: 端口號,此項為可選項,默認為80;
    Path: 目錄,由“/”隔開的字符串,表示的是主機上的目錄或文件地址;
    Query: 查詢,此項為可選項,可以給動態網頁傳遞參數,用“&”隔開,每個參數的名和值用“=”隔開;
    Fragment: 信息片段,字符串,用於指定網絡資源中的某片斷;

    其實,把 URL 說成是網址其實是很不嚴謹的說法,因為 URL 有很嚴格的結構,表示也很靈活、有彈性。
    在 RFC 3986: Uniform Resource Identifier (URI): Generic Syntax 的 Syntax Components 把 URL 描述為如下圖:

    如圖所示,把 URL 分成幾個部分,這樣便可以了解URL的構成。 在 URI scheme – Wikipedia 頁面中對 URL 的描述更為詳細,如下圖:

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • C++ Primer Plus(四)

    完整閱讀C++ Primer Plus 

      系統重新學習C++語言部分,記錄重要但易被忽略的,關鍵但易被遺忘的。

     

    友元、異常和其他

      1、拋出異常類時,雖然catch的是一個引用,但是也會產生一次拷貝,因為當拋出異常的函數在棧解退的過程中會會調用異常類的析構函數,異常類將不復存在。

      2、如果有一個異常類層次結構,應該這樣排列catch塊:將捕獲位於層次結構最下面的異常類的catch語句放在最前面,將捕獲基類異常的catch語句放在最後面。拋出異常的順序要與catch塊相反。

      3、在catch語句中使用基類對象時,將捕獲所有的派生類對象,但派生類特性將被剝去,因此將使用虛方法的基類版本。

      4、 將dynamic_cast用於引用時,由於沒有與空指針對應的引用值,因此無法使用特殊的引用值來表示失敗,當請求不正確時,將引發bad_cast的異常。

      5、reinterpret_cast運算符並不支持所有的類型轉換,例如,可以將指針類型轉換為足以存儲指針的整數,但不能將指針轉換為更小的整型或浮點型。另一個限制是,不能將函數指針和數據指針互相轉換。

     

    string類和標準模板庫

      6、使用new分配內存時,可以使用auto_ptrunique_ptrshared_ptr、但只有unique_ptr有使用new[]和delete[]的版本。

      7、在unique_ptr為右值時,可以將其賦值給shared_ptr,模板shared_ptr包含一個顯式構造函數,可以用於將右值unique_ptr轉換為shared_ptr。

      8、對於所有內置的算術運算符、關係運算符和邏輯運算符,STL都提供了等價的函數符(仿函數)。

      9、valarray模板類重載了許多運算符,可以直接參与大多數數值運算;slice類可用作數組索引,它接受三個值初始值:起始索引、索引數、跨距。

    1 valarry<double> arr(10);
    2 arr[slice(1,4,3)] = 10;

      slice(1,4,3)創建的對象表示選擇4個索引,這可以將arr的第1、4、7、10個元素都設置為10。

      10、迭代器類型

    Input iterator(輸入迭代器)  讀,不能寫;只支持自增運算
    Output iterator(輸出迭代器) 寫,不能讀;只支持自增運算
    Forward iterator(前向迭代器) 讀和寫;只支持自增運算
    Bidirectional iterator(雙向迭代器) 讀和寫;支持自增和自減運算
    Random access iterator(隨機訪問迭代器) 讀和寫;支持完整的迭代器算術運算

     

    輸入、輸出和文件

      11、對於標準錯誤輸出,是沒有緩衝區的。

      12、在使用cout時,可以使用成員函數width()設置下一次輸出時的字段寬度,默認右對齊並以空格填充空白字段,當字段寬度不足時,C++不對截斷輸出寬度;使用成員函數fill()用來填充空白字段;使用成員函數precision()來設置浮點數輸出精度;成員函數setf()與unsetf()提供了更豐富的輸出格式設置方法,但使用標準控制符將更加簡單。

      13、對於cin的get()方法和getline()方法來說,如果沒有讀取到任何字符(getline()將換行符視為一個字符),則設置failbit;如果讀取了最大數目的字符,但行中還有其他字符,getline()將設置failbit

      14、cin的peak()方法可以查看輸入流中的下一個字符,gcount()方法可以返回最後一個非格式化抽取方法讀取的字符數,putback()方法可以將字符插入到輸入字符串中。

      15、fstream類中的方法seekg()和seekp()分別將輸入指針和輸出指針移到指定的文件位置,事實上,由於fstream類使用緩衝區來存儲中間數據,因此指針指向的是緩衝區中的位置,而不是實際的文件。

      16、fstream類中的方法tellg()和tellp()方法分別返回輸入流、輸出流當前指針的位置,對於fstream對象,輸入輸出指針將一前一后地移動,因此它們的返回值相同。但對於使用istream對象來管理輸入流,而使用ostream對象來管理同一個文件的輸出流,則輸入指針和輸出指針將彼此獨立的移動。

      17、關於如何生成臨時文件,使用tmpnam()可以生產TMP_NAM個不同的文件名,其中每個文件名包含的字符不超過L_tmpnam個。

      18、C++庫還提供了sstream族(包含ostringstream類和istringstream類),它們使用相同的接口提供程序和string對象之間的IO。

     

    探討C++新標準

      19、新標準引入的移動語義,用來修飾六個特殊函數的default關鍵字,用來刪除任意成員函數的delete關鍵字,以及使用類似初始化列表的方式在一個構造函數中使用另一個構造函數(被稱為委託構造),以及使用using 類名::函數名,使基類所有的非特殊成員函數對派生類可以用(繼承構造函數),以及显示聲明重寫(覆蓋)某個虛函數的標識符override,以及禁止派生類覆蓋特定的虛函數標識符final

      20、C++11引入lambda表達的主要目的是能夠將類似於函數的表達式用作接受函數指針或函數符的函數的參數。

      21、C++提供了多個包裝器對象,用於給其他編程接口提供更一致或更合適的接口。C++11提供了包括模板bind(替代bind1st和bind2nd)men_fn(將成員函數作為常規函數傳遞)reference_wrapper(創建行為像引用但可被複制的對象)以及funtion(以統一的方式處理多種類似於函數的形式,使用模板時可減少可執行代碼的規模)

      22、正確使用遞歸實現可變參數模板

      23、C++11增加了對并行編程的支持,以及相當多的新增庫等。

     

    附錄

      24、C++允許定義指向類成員(包括數據和函數)的指針,這種語法需要使用到成員解除引用運算符(* 、->*)。

      25、C++11新增了alignof運算符,它接受一個類型作為參數,返回這個類型的對齊方式;noexcept關鍵字用於指出函數不會引發異常,它也可以用作運算符,判斷表達式是否可能引發異常,不引發返回true。

      26、STL提供了豐富的全局函數,包括查詢,排序,複製等一系列算法。

      

      2020年6月2日,星期二,凌晨2點01分,首次完整讀完這本書,共勉。

      學如逆水行舟,不進則退;心似平原放馬,易縱難收。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 澳洲野火燒破千人家園 雪梨收災難性警告

    摘錄自2019年11月11日ETtoday 綜合報導

    澳洲東部森林野火自8日起延燒多日,造成3人死亡、數十人受傷以及上千人失去家園,仍在燃燒的上百處火場燒掉了至少150棟的住宅。當局指出,恐怕連雪梨及周邊地區也會受影響,12日將迎接「災難性」野火威脅。

    外電報導,新南威爾斯州(New South Wales)及昆士蘭州(Queensland)等地接連傳出災情,當局派出將近1300名消防員也沒能撲滅燒了4天的大火,只能宣布災區進入「緊急狀態」,並警告人口最多的雪梨地區,高溫恐飆破攝氏37度,要面臨「災難性」(catasrophic)的野火威脅。

    這也是澳洲2009年推出新的「火情危險評級制度」(Fire Danger Rating,分為中等、高、非常高、嚴重、極高及災難性)後,雪梨第一次面臨「災難性」等級,預計12日雪梨會出現攝氏37度高溫、強風、乾燥等,不排除比澳洲東部當初被野火襲擊時更嚴重。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 增近四倍 2015年Q1中國純電動車銷量達1.54萬輛

    近日,中國汽車工業協會發佈最新統計資料顯示,今年第一季,中國汽車銷售達615.3萬輛,與去年同期相比增長3.9%,但相較去年同期9.2%的增幅,下滑明顯。此外新能源汽車產銷分別為2.73萬輛和2.66萬輛,與去年同期相比分別增長2.9倍和2.8倍。   其中,純電動車型銷售1.54萬輛,與去年同其相比增長3.7倍;插電式混合動力車型銷售1.12萬輛,與去年同期相比增長2.1倍。與新能源汽車相比,傳統車型銷量增幅卻明顯回落。根據資料,一季度乘用車共銷售530.51萬輛,與去年同其相比增長8.95%,增幅比去年同期回落1.14個百分點。在乘用車四類車型中,轎車銷量與去年同期相比下滑0.4%。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 推薦一種通過刷leetcode來增強技術功底的方法

    推薦一種通過刷leetcode來增強技術功底的方法

    背景

    如果前人認為這個一種學習提高或者檢驗能力的成功實踐。而自己目前又沒有更好的方法,那就不妨試一試。

    而不管作為面試官還是被面試者,編碼題最近越來越流行。而兩種角色都需要思考的問題是希望考察什麼能力,通過什麼題目,需要達到怎樣的程度可以說明面試者具有了這樣的能力。

    而要找到上面這些問題的答案,比較好的方式除了看一些理論性文章和接受培訓之外,自己動手刷一刷leetcode切身實踐一下不失為一個不錯的方式。而既然要花精力去做這件事情,那就需要解決一個問題:我從中可以獲得什麼提高。以下是個人的一些經驗和感受。

     

    收益

    對底層有了更深入的了解和思考

    leetcode一些常見題也是平時工作中常用的一些底層數據結構的實現方法。 

    先舉個大家使用比較多的算法:LRU(最近最少使用),在Java的實現中實現特別簡單。只是使用了LinkedHashMap這種數據結構。而如果看一些開源包里LRU的實現,發現也是這樣實現的。實際上動手實現一遍,LRU就再也不會忘了。

    再舉個數據結構的例子:字典樹又叫前綴樹。它是搜索和推薦的基礎。標準點的定義是:

    字典樹又稱單詞查找樹,Tire樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希樹高。

    因為之前做過搜索引擎,一直也對這塊很有興趣,所以對它底層知識的補充對個人而言,感覺深度有增加。

     

      

    養成評估時空開銷的習慣

    我刷leetcode必看官方解答里估算的時間和空間複雜度。這也是作為一個架構師的必備基本能力。

    數組、哈希這些因為數據的位置不需要進行查找,只需要算數計算就可以得到,所以它們的時間複雜度是O(1)。

    鏈表如果直接在頭部或者尾部插入,因為不需要查找,所以時間複雜度也是O(1),但是定位的話因為涉及查找,按遍歷查找來算是log(n)。所以對於jdk1.7之前,hashmap底層採用的是數組+鏈表的數據結構。所以如果不算哈希衝突和擴容的話,獲取和插入數據的時間複雜度是O(1);如果出現了哈希衝突,則插入因為是頭部插入,時間複雜度還是O(1);獲取時間複雜度因為涉及先查找,所以是O(n),這個n代表衝突的數量。

    對於在有序數據中進行查找,因為可採用二分查找等優化,時間複雜度可降到log(n).

    對於遞歸調用,如果遞歸方法內進行2次調用。對於層數n來說,時間複雜度是2的n次方。舉個例子就是一個數等於前面兩個數之和。當然,如果是前面3個數之和,不進行優化的情況下時間複雜度就是3的n次方。

    對於一個n*m的二維數組等需要進行嵌套循環遍歷的,時間複雜度是O(n*m),有個特殊情況是n*m,這時候時間複雜度是n的平方。

    對於全排列的情況,時間複雜度是O(n!)。

     

    代碼簡化的方法

     

    我習慣的一種學習方法是先做題,有了一定自己的總結和思考之後,再看書學習別人的總結思考方法。對於刷leetcode相關性高,也比較受認可的書是《Cracking the Coding interview(6th)》,中文版翻譯是《程序員面試金典》。這本書對於面試官和面試者來說讀了都會有一定的收穫。

     

    我讀了這本書,對我印象最深的是介紹了兩種代碼優化的方法:BUD和BCR。

     

    BUD 

    BUD是瓶頸、不必要工作、重複工作 三個詞組首字母的縮寫。

     

    作者提出拿到一道編程題,可先嘗試用暴力解法將題目寫出來,之後找到解法的性能瓶頸,針對瓶頸進行優化,之後在去掉不必要的工作,最後去掉重複的工作。

    這個經典的編程優化方法不只可應用於編程,還可應用於整個程序的優化,也是最常規的優化方法。

     

    BCR

    BCR是Best Conceivable Runtime的縮寫,意思是想知道自己可以優化到什麼程度,先估算可達到的最優情況。

    比如:在一個無序數組中,查找兩個兩個相同的數。直覺來說如果找到這兩個數,最起碼需要將每個數都遍歷一遍,所以可達到的最優情況是O(n),無論怎麼優化,都不可能比這個更好。所以這就是優化的上限。

    這本書里還介紹了其他的優化方法如:使用額外數據結構、通過構建測試用例、根據題目的限制和提示來尋找線索,大家看這本書的時候可以了解下。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 特斯拉將公布儲能電池計畫 F-貿聯可望受惠

    特斯拉傳將於 4 月 30 日公布儲能電池計畫,市場推測,該計畫將包括住宅與公共規模市場,業界傳出,本來就是特斯拉重要夥伴的 F-貿聯,有機會獲新電力線訂單。而今年在特斯拉需求持續強勁,與 Type C 等外接式擴充介面的需求爆發下,法人預期,該公司今年營運將逐季走高,全年營收看增 15%。   F-貿聯首季營收 19.04 億元,年增 10%,創下歷年同期新高。而特斯拉傳出將於下周公布最新的儲能電池計畫,據市場傳言表示,貿聯本來就是特斯拉在電動車電池模組用線與超級充電樁的線束的主力供應商,未來不排除大型公共規模用儲能電池,將與現有充電站結合。而貿聯有機會順勢切入取得新產品,並以公用市場應用的電力線為主,近日有望已小量出貨。   法人預估,F-貿聯今年除來自特斯拉的需求成長外,另在其他車用佈局方面,包括全地形車大客戶全車線束、擴大歐美客戶於倒車雷達等新產品的開發;另外針對美國官方對公營單位節能環保的需求,也配合客戶開發出得以改裝現有車為電動車的配備裝置,隨未來該商業模式成型,貢獻將逐步擴大。  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 最新的一波Vue實戰技巧,不用則已,一用驚人

    Vue中,不同的選項有不同的合併策略,比如 data,props,methods是同名屬性覆蓋合併,其他直接合併,而生命周期鈎子函數則是將同名的函數放到一個數組中,在調用的時候依次調用

    Vue中,提供了一個api, Vue.config.optionMergeStrategies,可以通過這個api去自定義選項的合併策略。

    在代碼中打印

    console.log(Vue.config.optionMergeStrategies)
    

      

     通過合併策略自定義生命周期函數

    背景

    最近客戶給領導反饋,我們的系統用一段時間,瀏覽器就變得有點卡,不知道為什麼。問題出來了,本來想甩鍋到後端,但是瀏覽器問題,沒法甩鍋啊,那就排查吧。

    後來發現頁面有許多定時器,ajax輪詢還有動畫,打開一個瀏覽器頁簽沒法問題,打開多了,瀏覽器就變得卡了,這時候我就想如果能在用戶切換頁簽時候將這些都停掉,不久解決了。百度裏面上下檢索,找到了一個事件visibilitychange,可以用來判斷瀏覽器頁簽是否显示。

    有方法了,就寫唄

    export default {
      created() {
        window.addEventListener('visibilitychange', this.$_hanldeVisiblityChange)
        // 此處用了hookEvent,可以參考小編前一篇文章
        this.$on('hook:beforeDestroy', () => {
          window.removeEventListener(
            'visibilitychange',
            this.$_hanldeVisiblityChange
          )
        })
      },
      methods: {
        $_hanldeVisiblityChange() {
          if (document.visibilityState === 'hidden') {
            // 停掉那一堆東西
          }
          if (document.visibilityState === 'visible') {
            // 開啟那一堆東西
          }
        }
      }
    }
    

    通過上面的代碼,可以看到在每一個需要監聽處理的文件都要寫一堆事件監聽,判斷頁面是否显示的代碼,一處兩處還可以,文件多了就頭疼了,這時候小編突發奇想,定義一個頁面显示隱藏的生命周期鈎子,把這些判斷都封裝起來

    自定義生命周期鈎子函數

    定義生命周期函數 pageHidden 與 pageVisible

    import Vue from 'vue'
    
    // 通知所有組件頁面狀態發生了變化
    const notifyVisibilityChange = (lifeCycleName, vm) => {
      // 生命周期函數會存在$options中,通過$options[lifeCycleName]獲取生命周期
      const lifeCycles = vm.$options[lifeCycleName]
      // 因為使用了created的合併策略,所以是一個數組
      if (lifeCycles && lifeCycles.length) {
        // 遍歷 lifeCycleName對應的生命周期函數列表,依次執行
        lifeCycles.forEach(lifecycle => {
          lifecycle.call(vm)
        })
      }
      // 遍歷所有的子組件,然後依次遞歸執行
      if (vm.$children && vm.$children.length) {
        vm.$children.forEach(child => {
          notifyVisibilityChange(lifeCycleName, child)
        })
      }
    }
    
    // 添加生命周期函數
    export function init() {
      const optionMergeStrategies = Vue.config.optionMergeStrategies
      // 定義了兩個生命周期函數 pageVisible, pageHidden
      // 為什麼要賦值為 optionMergeStrategies.created呢
      // 這個相當於指定 pageVisible, pageHidden 的合併策略與 created的相同(其他生命周期函數都一樣)
      optionMergeStrategies.pageVisible = optionMergeStrategies.beforeCreate
      optionMergeStrategies.pageHidden = optionMergeStrategies.created
    }
    
    
    // 將事件變化綁定到根節點上面
    // rootVm vue根節點實例
    export function bind(rootVm) {
      window.addEventListener('visibilitychange', () => {
        // 判斷調用哪個生命周期函數
        let lifeCycleName = undefined
        if (document.visibilityState === 'hidden') {
          lifeCycleName = 'pageHidden'
        } else if (document.visibilityState === 'visible') {
          lifeCycleName = 'pageVisible'
        }
        if (lifeCycleName) {
          // 通過所有組件生命周期發生變化了
          notifyVisibilityChange(lifeCycleName, rootVm)
        }
      })
    }
    

    應用

    1. main.js主入口文件引入
    import { init, bind } from './utils/custom-life-cycle'
    
    // 初始化生命周期函數, 必須在Vue實例化之前確定合併策略
    init()
    
    const vm = new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
    // 將rootVm 綁定到生命周期函數監聽裏面
    bind(vm)
    

      2. 在需要的地方監聽生命周期函數

    export default {
      pageVisible() {
        console.log('頁面显示出來了')
      },
      pageHidden() {
        console.log('頁面隱藏了')
      }
    }
    

      

    provideinject,不止父子傳值,祖宗傳值也可以

    Vue相關的面試經常會被面試官問道,Vue父子之間傳值的方式有哪些,通常我們會回答,props傳值,$emit事件傳值,vuex傳值,還有eventbus傳值等等,今天再加一種provideinject傳值,離offer又近了一步。(對了,下一節還有一種)

    使用過React的同學都知道,在React中有一個上下文Context,組件可以通過Context向任意後代傳值,而Vueprovideinject的作用於Context的作用基本一樣

    先舉一個例子

    使用過elemment-ui的同學一定對下面的代碼感到熟悉

    <template>
      <el-form :model="formData" size="small">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="formData.name" />
        </el-form-item>
        <el-form-item label="年齡" prop="age">
          <el-input-number v-model="formData.age" />
        </el-form-item>
        <el-button>提交</el-button>
      </el-form>
    </template>
    <script>
    export default {
      data() {
        return {
          formData: {
            name: '',
            age: 0
          }
        }
      }
    }
    </script>
    

      

    看了上面的代碼,貌似沒啥特殊的,天天寫啊。在el-form上面我們指定了一個屬性size="small",然後有沒有發現表單裏面的所有表單元素以及按鈕的 size都變成了small,這個是怎麼做到的?接下來我們自己手寫一個表單模擬一下

    自己手寫一個表單

    自定義表單custom-form.vue

    <template>
      <form class="custom-form">
        <slot></slot>
      </form>
    </template>
    <script>
    export default {
      props: {
        // 控製表單元素的大小
        size: {
          type: String,
          default: 'default',
          // size 只能是下面的四個值
          validator(value) {
            return ['default', 'large', 'small', 'mini'].includes(value)
          }
        },
        // 控製表單元素的禁用狀態
        disabled: {
          type: Boolean,
          default: false
        }
      },
      // 通過provide將當前表單實例傳遞到所有後代組件中
      provide() {
        return {
          customForm: this
        }
      }
    }
    </script>
    

      

    在上面代碼中,我們通過provide將當前組件的實例傳遞到後代組件中,provide是一個函數,函數返回的是一個對象

    自定義表單項custom-form-item.vue

    沒有什麼特殊的,只是加了一個label,element-ui更複雜一些

    <template>
      <div class="custom-form-item">
        <label class="custom-form-item__label">{{ label }}</label>
        <div class="custom-form-item__content">
          <slot></slot>
        </div>
      </div>
    </template>
    <script>
    export default {
      props: {
        label: {
          type: String,
          default: ''
        }
      }
    }
    </script>

    自定義輸入框 custom-input.vue

    <template>
      <div
        class="custom-input"
        :class="[
          `custom-input--${getSize}`,
          getDisabled && `custom-input--disabled`
        ]"
      >
        <input class="custom-input__input" :value="value" @input="$_handleChange" />
      </div>
    </template>
    <script>
    /* eslint-disable vue/require-default-prop */
    export default {
      props: {
        // 這裏用了自定義v-model
        value: {
          type: String,
          default: ''
        },
        size: {
          type: String
        },
        disabled: {
          type: Boolean
        }
      },
      // 通過inject 將form組件注入的實例添加進來
      inject: ['customForm'],
      computed: {
        // 通過計算組件獲取組件的size, 如果當前組件傳入,則使用當前組件的,否則是否form組件的
        getSize() {
          return this.size || this.customForm.size
        },
        // 組件是否禁用
        getDisabled() {
          const { disabled } = this
          if (disabled !== undefined) {
            return disabled
          }
          return this.customForm.disabled
        }
      },
      methods: {
        // 自定義v-model
        $_handleChange(e) {
          this.$emit('input', e.target.value)
        }
      }
    }
    </script>
    

      


    form中,我們通過
    provide返回了一個對象,在
    input中,我們可以通過
    inject獲取
    form中返回對象中的項,如上代碼
    inject:['customForm']所示,然後就可以在組件內通過
    this.customForm調用
    form實例上面的屬性與方法了

    在項目中使用

    <template>
      <custom-form size="small">
        <custom-form-item label="姓名">
          <custom-input v-model="formData.name" />
        </custom-form-item>
      </custom-form>
    </template>
    <script>
    import CustomForm from '../components/custom-form'
    import CustomFormItem from '../components/custom-form-item'
    import CustomInput from '../components/custom-input'
    export default {
      components: {
        CustomForm,
        CustomFormItem,
        CustomInput
      },
      data() {
        return {
          formData: {
            name: '',
            age: 0
          }
        }
      }
    }
    </script>
    

      執行上面代碼,運行結果為:

    <form class="custom-form">
      <div class="custom-form-item">
        <label class="custom-form-item__label">姓名</label>
        <div class="custom-form-item__content">
          <!--size=small已經添加到指定的位置了-->
          <div class="custom-input custom-input--small">
            <input class="custom-input__input">
          </div>
        </div>
      </div>
    </form>
    

      

    通過上面的代碼可以看到,input組件已經設置組件樣式為custom-input--small

    inject格式說明

    除了上面代碼中所使用的inject:['customForm']寫法之外,inject還可以是一個對象。且可以指定默認值

    修改上例,如果custom-input外部沒有custom-form,則不會注入customForm,此時為customForm指定默認值

    {
      inject: {
        customForm: {
          // 對於非原始值,和props一樣,需要提供一個工廠方法
          default: () => ({
            size: 'default'
          })
        }
      }
    }
    

      

    使用限制

    1.provideinject的綁定不是可響應式的。但是,如果你傳入的是一個可監聽的對象,如上面的customForm: this,那麼其對象的屬性還是可響應的。

    2.Vue官網建議provideinject 主要在開發高階插件/組件庫時使用。不推薦用於普通應用程序代碼中。因為provideinject在代碼中是不可追溯的(ctrl + f可以搜),建議可以使用Vuex代替。 但是,也不是說不能用,在局部功能有時候用了作用還是比較大的。

     

    插槽,我要鑽到你的懷裡

    插槽,相信每一位Vue都有使用過,但是如何更好的去理解插槽,如何去自定義插槽,今天小編為你帶來更形象的說明。

    默認插槽

    <template>
      <!--這是一個一居室-->
      <div class="one-bedroom">
        <!--添加一個默認插槽,用戶可以在外部隨意定義這個一居室的內容-->
        <slot></slot>
      </div>
    </template>
    

      

    <template>
      <!--這裏一居室-->
      <one-bedroom>
        <!--將傢具放到房間裏面,組件內部就是上面提供的默認插槽的空間-->
        <span>先放一個小床,反正沒有女朋友</span>
        <span>再放一個電腦桌,在家還要加班寫bug</span>
      </one-bedroom>
    </template>
    <script>
    import OneBedroom from '../components/one-bedroom'
    export default {
      components: {
        OneBedroom
      }
    }
    </script>

    具名插槽

    <template>
      <div class="two-bedroom">
        <!--這是主卧-->
        <div class="master-bedroom">
          <!---主卧使用默認插槽-->
          <slot></slot>
        </div>
        <!--這是次卧-->
        <div class="secondary-bedroom">
          <!--次卧使用具名插槽-->
          <slot name="secondard"></slot>
        </div>
      </div>
    </template>
    

      

    <template>
      <two-bedroom>
        <!--主卧使用默認插槽-->
        <div>
          <span>放一個大床,要結婚了,嘿嘿嘿</span>
          <span>放一個衣櫃,老婆的衣服太多了</span>
          <span>算了,還是放一個電腦桌吧,還要寫bug</span>
        </div>
        <!--次卧,通過v-slot:secondard 可以指定使用哪一個具名插槽, v-slot:secondard 也可以簡寫為 #secondard-->
        <template v-slot:secondard>
          <div>
            <span>父母要住,放一個硬一點的床,軟床對腰不好</span>
            <span>放一個衣櫃</span>
          </div>
        </template>
      </two-bedroom>
    </template>
    <script>
    import TwoBedroom from '../components/slot/two-bedroom'
    export default {
      components: {
        TwoBedroom
      }
    }
    </script>

    作用域插槽

    <template>
      <div class="two-bedroom">
        <!--其他內容省略-->
        <div class="toilet">
          <!--通過v-bind 可以向外傳遞參數, 告訴外面衛生間可以放洗衣機-->
          <slot name="toilet" v-bind="{ washer: true }"></slot>
        </div>
      </div>
    </template>
    

      

    <template>
      <two-bedroom>
        <!--其他省略-->
        <!--衛生間插槽,通過v-slot="scope"可以獲取組件內部通過v-bind傳的值-->
        <template v-slot:toilet="scope">
          <!--判斷是否可以放洗衣機-->
          <span v-if="scope.washer">這裏放洗衣機</span>
        </template>
      </two-bedroom>
    </template>  

    插槽默認值

    <template>
      <div class="second-hand-house">
        <div class="master-bedroom">
          <!--插槽可以指定默認值,如果外部調用組件時沒有修改插槽內容,則使用默認插槽-->
          <slot>
            <span>這裡有一張水床,玩的夠嗨</span>
            <span>還有一個衣櫃,有點舊了</span>
          </slot>
        </div>
        <!--這是次卧-->
        <div class="secondary-bedroom">
          <!--次卧使用具名插槽-->
          <slot name="secondard">
            <span>這裡有一張嬰兒床</span>
          </slot>
        </div>
      </div>
    </template>
    

      

    <second-hand-house>
        <!--主卧使用默認插槽,只裝修主卧-->
        <div>
          <span>放一個大床,要結婚了,嘿嘿嘿</span>
          <span>放一個衣櫃,老婆的衣服太多了</span>
          <span>算了,還是放一個電腦桌吧,還要寫bug</span>
        </div>
      </second-hand-house>
    

    dispatchbroadcast,這是一種有歷史的組件通信方式

    dispatch
    broadcast是一種有歷史的組件通信方式,為什麼是有歷史的,因為他們是
    Vue1.0提供的一種方式,在
    Vue2.0中廢棄了。但是廢棄了不代表我們不能自己手動實現,像許多UI庫內部都有實現。本文以
    element-ui實現為基礎進行介紹。同時看完本節,你會對組件的
    $parent,
    $children,
    $options有所了解。

    方法介紹

    $dispatch: $dispatch會向上觸發一個事件,同時傳遞要觸發的祖先組件的名稱與參數,當事件向上傳遞到對應的組件上時會觸發組件上的事件偵聽器,同時傳播會停止。

    $broadcast: $broadcast會向所有的後代組件傳播一個事件,同時傳遞要觸發的後代組件的名稱與參數,當事件傳遞到對應的後代組件時,會觸發組件上的事件偵聽器,同時傳播會停止(因為向下傳遞是樹形的,所以只會停止其中一個恭弘=叶 恭弘子分支的傳遞)。

    $dispatch實現與應用

    1. 代碼實現

     // 向上傳播事件
     // @param {*} eventName 事件名稱
     // @param {*} componentName 接收事件的組件名稱
     // @param {...any} params 傳遞的參數,可以有多個
     
    function dispatch(eventName, componentName, ...params) {
      // 如果沒有$parent, 則取$root
      let parent = this.$parent || this.$root
      while (parent) {
        // 組件的name存儲在組件的$options.componentName 上面
        const name = parent.$options.name
        // 如果接收事件的組件是當前組件
        if (name === componentName) {
          // 通過當前組件上面的$emit觸發事件,同事傳遞事件名稱與參數
          parent.$emit.apply(parent, [eventName, ...params])
          break
        } else {
          // 否則繼續向上判斷
          parent = parent.$parent
        }
      }
    }
    
    // 導出一個對象,然後在需要用到的地方通過混入添加
    export default {
      methods: {
        $dispatch: dispatch
      }
    }  

    2. 代碼應用

    • 在子組件中通過$dispatch向上觸發事件

      import emitter from '../mixins/emitter'
      export default {
        name: 'Chart',
        // 通過混入將$dispatch加入進來
        mixins: [emitter],
         mounted() {
           // 在組件渲染完之後,將組件通過$dispatch將自己註冊到Board組件上
          this.$dispatch('register', 'Board', this)
        }
      }
    • Board組件上通過$on監聽要註冊的事件

    $broadcast實現與應用

    1. 代碼實現

      //向下傳播事件
      // @param {*} eventName 事件名稱
      // @param {*} componentName 要觸發組件的名稱
      // @param  {...any} params 傳遞的參數
     
    function broadcast(eventName, componentName, ...params) {
      this.$children.forEach(child => {
        const name = child.$options.name
        if (name === componentName) {
          child.$emit.apply(child, [eventName, ...params])
        } else {
          broadcast.apply(child, [eventName, componentName, ...params])
        }
      })
    }
    
    // 導出一個對象,然後在需要用到的地方通過混入添加
    export default {
      methods: {
        $broadcast: broadcast
      }
    }  

    2. 代碼應用

    在父組件中通過$broadcast向下觸發事件

    import emitter from '../mixins/emitter'
    export default {
      name: 'Board',
      // 通過混入將$dispatch加入進來
      mixins: [emitter],
      methods:{
      	//在需要的時候,刷新組件
      	$_refreshChildren(params) {
      		this.$broadcast('refresh', 'Chart', params)
      	}
      }
    }
    

    在後代組件中通過$on監聽刷新事件

    export default {
      name: 'Chart',
      created() {
        this.$on('refresh',(params) => {
          // 刷新事件
        })
      }
    }
    

    總結

    通過上面的例子,同學們應該都能對$dispatch$broadcast有所了解,但是為什麼Vue2.0要放棄這兩個方法呢?官方給出的解釋是:”因為基於組件樹結構的事件流方式實在是讓人難以理解,並且在組件結構擴展的過程中會變得越來越脆弱。這種事件方式確實不太好,我們也不希望在以後讓開發者們太痛苦。並且 $dispatch$broadcast 也沒有解決兄弟組件間的通信問題。“

    確實如官網所說,這種事件流的方式確實不容易讓人理解,而且後期維護成本比較高。但是在小編看來,不管黑貓白貓,能抓老鼠的都是好貓,在許多特定的業務場景中,因為業務的複雜性,很有可能使用到這樣的通信方式。但是使用歸使用,但是不能濫用,小編一直就在項目中有使用。

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 印尼蘇門答臘象身首異處慘死 象牙遭盜採

    摘錄自2019年11月8日自由時報屏東報導

    印尼保育官員今天表示,1頭列為極危(critically endangered)物種的蘇門答臘象屍體被發現,牠的頭被砍下且象牙被拔走,顯然是一宗盜獵案件。

    蘇門答臘島廖內省(Riau)一名農園工人昨天發現這頭40歲公象腐爛的屍體。當地保育機關主管蘇哈尤諾(Suharyono)在聲明中說:「大象的頭被砍下,切斷的象鼻落在距離象身1公尺處。」當局正在追查犯案人士。

    森林濫伐造成蘇門答臘象的天然棲地縮減,導致牠們和人類的衝突加劇。另一方面,蘇門答臘象象牙在野生動物黑市交易中價值連城。

    去年在印尼亞齊省(Aceh)也發現一具顯然被毒死的蘇門答臘象屍體,當時牠的象牙也不見。印尼環境部估計,境內野生蘇門答臘象只剩不到2000頭。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 痞子衡嵌入式:利用i.MXRT1xxx系列ROM提供的FlexSPI driver API可輕鬆IAP

    痞子衡嵌入式:利用i.MXRT1xxx系列ROM提供的FlexSPI driver API可輕鬆IAP

      大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT系列ROM中的FlexSPI驅動API實現IAP

      痞子衡的技術交流群里經常有群友提問: i.MXRT中的FlexSPI驅動API到底怎麼用啊?這個問題已經出現過好幾次了,本來痞子衡不打算專門為這個寫文章的,因為這部分內容在芯片手冊System Boot章節里的最後一節ROM APIs里其實介紹得非常詳細了,但是既然還是有不少朋友在問這個,看起來手冊里的內容藏得有點深,這麼好的東西被埋沒太可惜了,那麼今天痞子衡就跟大家再認真聊一聊。

    一、ROM API簡介

    1.1、API產生背景

      i.MXRT系列都是Flashless(沒有內置NVM)的芯片,所以BootROM必不可少。BootROM是個很特殊的東西,本質上它是一個完整的C代碼寫成的系統級App,這個系統級App專門用於從外部存儲器中加載用戶級App執行。簡單地說,BootROM就是PC機里的BIOS。

      BootROM代碼是存放在專門的ROM區域的(前面講i.MXRT系列沒有內置NVM,其實不夠準確,其實是有內部ROM空間的,只不過這個ROM區域用戶無法下載程序使用,因此等效於沒有NVM),ROM顧名思義Readonly,所以BootROM代碼只能隨着芯片一起Tapeout,代碼無法更改(其實也有ROM patch機制,以後再介紹)。

      ROM空間其實挺大的,從64KB到512KB不等,因芯片啟動功能複雜程度而異。下圖是i.MXRT1050系列的BootROM所佔空間,ROM起始地址是0x200000(起始地址在i.MXRT上都一樣),ROM大小為96KB(這是標準啟動功能所要的代碼長度。在i.MXRT1010上是64KB – 精簡啟動功能,在i.MXRT1170上是256KB – 複雜啟動功能)。

      BootROM代碼其實並沒有佔滿全部ROM空間,總有些剩餘空間(因為工藝原因,ROM空間都是8/16KB倍數),這部分空間浪費了着實可惜。如果我們能把SDK里的一些常用模塊驅動(比如WDOG)順便放進去供用戶調用,既充分利用ROM空間,也為用戶節省Flash空間,豈不是一舉兩得。此外,BootROM功能代碼中也有一些現成模塊驅動(比如各種啟動設備存儲器驅動接口)可以一併導出,這便是API由來。

    1.2、API設計實現

      有了API想法,現在就是設計實現了。其實i.MXRT ROM API設計並不是重頭開始的,在這個MCU系列被主推之前,Kinetis系列也曾當紅過,Kinetis中也內置了ROM,並且提供了ROM API,痞子衡之前為此寫過一篇文章 《飛思卡爾Kinetis系列MCU啟動那些事(11)- KBOOT特性(ROM API)》。 i.MXRT ROM API設計思路完全復用了Kinetis ROM API的設計。

      API說到底就是一個個功能函數的結合,我們知道工程代碼都是由鏈接器自動分配的,因此每個函數實際鏈接地址是無法預期的(在鏈接文件里給每個函數分配固定地址鏈接這種方法不在考慮範疇,當函數數量眾多時,這種方法太麻煩),業界上一個比較通用的做法是定義成員是函數指針的結構體,i.MXRT ROM API就是採用的業界通用方式,下面bootloader_api_entry_t便是i.MXRT1060中API原型,g_bootloaderTree就是實例:

    typedef struct
    {
        const uint32_t version;
        const char *copyright;
        void (*runBootloader)(void *arg);
        const hab_rvt_t *habDriver;
    
        //!< FlexSPI NOR Flash API
        const flexspi_nor_driver_interface_t *flexSpiNorDriver;
    
        const nand_ecc_driver_interface_t *nandEccDriver;
        const clock_driver_interface_t *clockDriver;
        const rtwdog_driver_interface_t *rtwdogDriver;
        const wdog_driver_interface_t *wdogDriver;
        const stdlib_driver_interface_t *stdlibDriver;
    } bootloader_api_entry_t;
    
    // Bootloader API Tree
    const bootloader_api_entry_t g_bootloaderTree = {
        .copyright = "Copyright 2018 NXP",
        .version = MAKE_VERSION(1, 0, 0),
        .runBootloader = run_bootloader,
        .habDriver = &hab_rvt,
    
        .flexSpiNorDriver = &g_flexspiNorDriverInterface,
    
        .nandEccDriver = &g_nandEccDriverInterface,
        .clockDriver = &g_clockDriverInterface,
        .rtwdogDriver = &g_rtwdogDriverInterface,
        .wdogDriver = &g_wdogDriverInterface,
        .stdlibDriver = &g_stdlibDriverInterface,
    };
    

      從上面代碼我們可以看出,bootloader_api_entry_t成員好像並不是函數指針,是的,為了分組方便,bootloader_api_entry_t成員還是一個個結構體,它的這些結構體成員(比如flexspi_nor_driver_interface_t)才是真正包含一個個函數指針的結構體。API從功能來分一共提供了7類:HAB、FlexSPI NOR、NAND ECC、Clock、RT-WDOG、WDOG、stdlib。

      設計到這裏,我們通過g_bootloaderTree結構體常量就可以調用所有的API函數了,最後剩下的問題就是如何在ROM里找一個確定的地方保存隨機鏈接的g_bootloaderTree地址(只要4字節即可)。是的,還是Kinetis ROM API用的那個巧妙的方法,下面是BootROM工程的startup文件(Keil版),BootROM將g_bootloaderTree的地址放到了中斷向量表第8個向量的位置處(該向量為ARM Cortex-M未定義的系統向量),因此0x20001c處開始的4bytes便固定是g_bootloaderTree地址。

                    PRESERVE8
                    THUMB
    
    ; Vector Table Mapped to Address 0 at Reset
    
                    AREA    RESET, DATA, READONLY
                    EXPORT  __Vectors
                    EXPORT  __Vectors_End
                    EXPORT  __Vectors_Size
                    IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|
                    IMPORT  g_bootloaderTree
    
    __Vectors       DCD     |Image$$ARM_LIB_STACK$$ZI$$Limit|
                    DCD     Reset_Handler
                    DCD     DefaultISR
                    DCD     HardFault_Handler
                    DCD     DefaultISR
                    DCD     DefaultISR
                    DCD     DefaultISR
                    DCD     g_bootloaderTree
                    DCD     0
                    DCD     0
                    DCD     0
                    DCD     SVC_Handler
                    DCD     DefaultISR
                    DCD     0
                    DCD     DefaultISR
                    DCD     DefaultISR
    		        ;; ...
    

    1.3、API調用方法

      了解了前面介紹的ROM API產生背景與設計實現,它的調用方法就非常簡單了,以WDOG API調用為例,只需要如下簡單3句代碼:

    // 找到API根結構體
    #define g_bootloaderTree (*(bootloader_api_entry_t **)0x0020001c)
    // 定義WDOG模塊配置變量
    wdog_config_t config;
    // 調用API中WDOG_Init()
    g_bootloaderTree->wdogDriver->WDOG_Init(WDOG1, config);
    

    1.4、支持API的i.MXRT型號

      截止目前,i.MXRT1xxx系列一共出了7款型號,但並不是每個型號都開放了ROM API,最早誕生的三款型號(105x、1021、1015)就並沒有開放API(不是沒有API,而是沒有嚴格測試),其餘型號都支持API。

    RT芯片型號 是否支持ROM API
    i.MXRT117x 支持
    i.MXRT1064 支持
    i.MXRT106x 支持
    i.MXRT105x 未開放
    i.MXRT1021 未開放
    i.MXRT1015 未開放
    i.MXRT1011 支持

    二、API之FlexSPI驅動

      前面鋪墊了太多ROM API設計細節,到這裏才算進入正題,本文其實主要是要跟大家聊如何利用API里的FlexSPI NOR驅動實現IAP。痞子衡在前面鋪墊那麼多的原因其實主要是想告訴大家,API里的每個驅動都是經過完善測試的,尤其是這個FlexSPI NOR驅動,更是經過了千錘百鍊,無論是易用性、運行穩定性還是Flash型號的支持度上都是首屈一指的。

      對於JESD216標準下的串行SPI接口Flash驅動,大家知道更多的可能是RT-Thread技術總監朱天龍大神的開源 SFUD 項目,但痞子衡告訴你,i.MXRT ROM API里的這個串行Flash驅動也毫不遜色(持續維護與優化了近6年,歷經多款MCU的ROM,是真正的產品級),只是不如開源項目那麼知名,不過它的源代碼也是開源在SDK里的(\SDK\middleware\mcu-boot\src\drivers\flexspi_nor),BSD-3-Clause許可證。

    2.1 FlexSPI驅動原型

      flexspi_nor_driver_interface_t便是FlexSPI NOR驅動的原型,尋常的讀寫擦功能自然不在話下,除此以外,API裏面還有一個非常厲害的xfer()函數,這個函數可以用來實現其他定製化的Flash操作函數,有興趣的朋友可以進一步去研究。

    typedef struct
    {
        uint32_t version;
        status_t (*init)(uint32_t instance, flexspi_nor_config_t *config);
        status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t dst_addr, const uint32_t *src);
        status_t (*erase_all)(uint32_t instance, flexspi_nor_config_t *config);
        status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, uint32_t lengthInBytes);
        status_t (*read)(uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t addr, uint32_t lengthInBytes);
        void (*clear_cache)(uint32_t instance);
        status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer);
        status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase, uint32_t seqNumber);
        status_t (*get_config)(uint32_t instance, flexspi_nor_config_t *config, serial_nor_config_option_t *option);
    } flexspi_nor_driver_interface_t;
    

    2.2 FlexSPI驅動使用示例

      FlexSPI驅動使用基本三步走,先調用get_config()獲取完整FlexSPI模塊配置,然後調用init()函數去初始化FlexSPI以及訪問Flash獲取SFDP表信息,最後就是調用Flash操作函數(比如erase())。

    // 找到API根結構體
    #define g_bootloaderTree (*(bootloader_api_entry_t **)0x0020001c)
    
    // 定義FlexSPI, Flash配置變量
    flexspi_nor_config_t config;
    serial_nor_config_option_t option;
    option.option0.U = 0xC0000008; // QuadSPI NOR, Frequency: 133MHz
    uint32_t instance = 0;
    
    // 調用API中get_config()函數
    g_bootloaderTree->flexSpiNorDriver->get_config(instance, &config, &option);
    // 調用API中init()函數
    g_bootloaderTree->flexSpiNorDriver->init(instance, &config);
    // 調用API中erase()函數
    g_bootloaderTree->flexSpiNorDriver->erase(instance, &config, 0x40000, 0x1000);
    

    2.3 FlexSPI驅動特點

      因為FlexSPI NOR驅動API來自於BootROM,因此其在使用上有一些小小的限制,也算是其特點吧。FlexSPI驅動API里並沒有提供Flash連接的Pinmux配置,其Pinmux配置已經寫死在init()函數中,就是ROM支持啟動的FlexSPI PORTA上的那些pin(片選是SS0)。

      在上面的使用示例代碼中,你會看到option.option0.U = 0xC0000008代碼,這算是FlexSPI驅動最大的特點了,這是一個簡化的option配置word(其原型可在芯片手冊里找到),通過這個簡化的option,用戶可以輕鬆配置來訪問不同廠商的Flash,下面是常用的Flash模式配置值。

    • QuadSPI NOR - Quad SDR Read: option0 = 0xc0000008 (133MHz)
    • QuadSPI NOR - Quad DDR Read: option0 = 0xc0100003 (60MHz)
    • HyperFLASH 1V8: option0 = 0xc0233009 (166MHz)
    • HyperFLASH 3V0: option0 = 0xc0333006 (100MHz)
    • MXIC OPI DDR (OPI DDR enabled by default): option=0xc0433008(133MHz)
    • Micron Octal DDR: option0=0xc0600006 (100MHz)
    • Micron OPI DDR: option0=0xc0603008 (133MHz), SPI->OPI DDR
    • Micron OPI DDR (DDR read enabled by default): option0 = 0xc0633008 (133MHz)
    • Adesto OPI DDR: option0=0xc0803008(133MHz)
    

    2.4 FlexSPI驅動用作IAP

      IAP其實就是在App中實現Flash擦寫,單純從技術上來說並不是一個很難的東西。但i.MXRT上很多時候App代碼本身也在同一片Flash里執行(也叫XIP),而市面上很多Flash都是不支持RWW(Read-While-Write)的,這就導致一個問題,當你調用Flash操作函數去擦寫Flash時,CPU又需要繼續去Flash獲取指令,違反了RWW,因此你只能把Flash相關操作函數全部放在RAM中去執行(這涉及分散加載了,對於初級嵌入式用戶來說稍微有點難)。

      現在我們有了ROM API,FlexSPI驅動代碼體全部都在ROM空間里,並不佔用Flash空間,因此不存在RWW問題,真是天然為IAP而生,再也不用再管什麼分散加載這麼麻煩的事了。

    三、FlexSPI API業界應用

      最後再介紹一下i.MXRT FlexSPI API在業界的應用,這個API其實並不小眾,目前已被主流IDE和調試工具用作i.MXRT Flash下載算法。

    3.1 用於IAR下載算法

      如果你的IAR版本夠新,能夠支持i.MXRT1060等型號,隨便打開一個i.MXRT1060 SDK工程,在工程Option里找到Debugger,然後進入Flashloader配置,你會看到頁面里有Extra parameters一欄,在下面的解釋里有這個參數的示例,它就是前面2.3節里介紹的option0。有了這種方式設計的Flash下載算法,你再也不用手動更新下載算法文件去支持不同的Flash了,改參數就行了。

    3.2 用於J-Link下載算法

      目前最新的Jlink驅動里的下載算法也是基於ROM API的,痞子衡有一個開源項目,收集了i.MXRT所有型號的下載算法源代碼工程,其中jlink算法是最全的,其他IDE算法還在陸續完善中。

    https://github.com/JayHeng/imxrt-tool-flash-algo

      至此,i.MXRT系列ROM中的FlexSPI驅動API實現IAP痞子衡便介紹完畢了,掌聲在哪裡~~~

    歡迎訂閱

    文章會同時發布到我的 博客園主頁、CSDN主頁、微信公眾號 平台上。

    微信搜索”痞子衡嵌入式“或者掃描下面二維碼,就可以在手機上第一時間看了哦。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心