大尺寸+高配置 眾泰T800 13.98萬起售

4英寸中控屏第一時間抓住你的眼球,全車的功能基本都集成在裏面,旋鈕式換擋,全液晶儀錶,大面積軟性材料的包裹,檔次感還是營造的不錯,雖然說中控UI界面布局和反應速度還有提升的空間,但結合其價格來看,也能讓你產生“要啥自行車”的感慨了。

眾泰T800在5月8日晚正式上市,新車共推出五款車型,售價13.98-18.58萬元。T800作為眾泰首款七座中型SUV,其配置同尺寸都達到了品牌的新高度。動力總成為2.0T+6DCT,從某種角度上T800可視作T700的加長版本。

眾泰T800在外觀設計上可以說擁有相當高的原創度,前臉進氣格柵的面積非常大,採用星空點陣的設計元素,下部格柵採用了中國風的回紋設計,凸顯了整個正臉的設計感。頭燈部分採用了全LED光源,轉向燈則單獨設計在下部,同樣使用了LED,只有最下面的霧燈為鹵素光源,配置上十分厚道。

T800的整車尺寸為4910x1933x1735mm,軸距2850mm,在目前自主中型七座SUV中算比較大的,側門上用裝飾模擬了出風口的造型,並且還點綴有眾泰漢語拼音首字母“Z”字形的標誌,同時不忘加入一個裝飾燈,這樣的小心思在其它品牌上不多見。還了解到,T800的頂配車型搭載了電吸門,在這個級別中是獨一份的那個。

進入車內,10.4英寸中控屏第一時間抓住你的眼球,全車的功能基本都集成在裏面,旋鈕式換擋,全液晶儀錶,大面積軟性材料的包裹,檔次感還是營造的不錯,雖然說中控UI界面布局和反應速度還有提升的空間,但結合其價格來看,也能讓你產生“要啥自行車”的感慨了。

談到眾泰,怎能不提配置,T800的配置水平用“極限”形容毫不過分,全景天窗,360°影像這些只是基本操作,什麼電動腳踏,電吸門,氛圍燈,車聯網,前窗加熱······配置上堆的滿滿噹噹,重要的是,除了入門車型以外,很多配置都實現了全系覆蓋,可以說,T800隻要不買最低配,都可以達到越級體驗。

T800的2.0T動力,參數上達到了170kW/350N·m,在自主眾多中型七座中屬於動力靠前的那個,匹配六速雙離合變速箱,或許表現上沒有眾多愛信6AT那麼出色,但想想車內那麼豐富的配置,也能理解。前麥弗遜+后多連桿懸架是這個級別的標配,沒有四驅車型有些遺憾。但考慮其“城市SUV”的身份,也不會是什麼大問題。

對於T800,我們先拋開眾泰眾多的“歷史遺留問題”去看待它。設計有原創,配置高上天,價格有驚喜——這三點已經是很大的亮點。自主品牌中型七座SUV已經很多,但T800還是能找到獨特的競爭力。能不能賣爆款不好說,但還是能吸引部分消費者的眼球,至少我們能看到的,T800已經是眾泰拿出最大的誠意去做的產品。品牌和質量都是需要長時間積累的事情,眾泰想要像其他自主品牌一樣深入民心,還是有很長的路要走。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

7.99萬起,今天上市的大空間6座車真是家用好選擇!

尾部在擾流板、貫穿尾燈的鍍鉻裝飾、黑色塑料擋板的組合下,形成了一種層次感。敦厚的車尾部,更是讓它充滿安全感。仿木材料的中控台,還有一個大尺寸豎直中控屏幕,以及是手感極佳的平底式運動方向盤,它在簡約風格中加入一點運動的元素。

“一輛車究竟是多少座位最好?”,除了自己,還有配偶,加上父母以及自己的孩子,或許還是雙胞胎,這樣看來起碼是6座車型,另外一般而言緊湊型轎車後排乘坐3成年人是比較難受的事情。

正是為了滿足用戶這種需求,比亞迪宋MAX前期推出了7座車型,如今更是推出了6座車型,官方指導價為:7.99-12.99萬。那麼它表現如何呢?

6座,就是它最大的不同

6座版本的比亞迪宋max依然有着4680*1810*1680mm的車身尺寸,以及是能夠帶來充足空間表現的2785mm軸距。並且在座椅配置上一樣保留了7座車型的優點,諸如第三排寬大保護作用大的頭枕、座椅採用打孔真皮材質。

它採用了2+2+2的6座布局,獨立第二排座椅可以帶來比7座車型更舒適的體驗,加上獨立的可調節座椅扶手,座椅包裹性更佳。中部通道寬度為190mm,乘客進出第三排更為優雅。

在實際的乘坐它表現讓人滿意,前排以及第二排座椅包裹性極佳,並且頂配車型上前排座椅還具備主副駕座椅通風加熱、第二排預警限力安全帶等配置。唯獨是第三排由於車身結構問題坐墊與地板距離較近,腿部支撐較弱,中長途旅行中更適合乘坐小孩子或者是身材嬌小的成年人。

設計以及配置,和7座車型無異

6座版本的宋MAX在設計上和7座車型保持了一致,犀利大燈帶有的鍍鉻裝飾條以及LED日間行車燈和大尺寸進氣格柵橫向裝飾條連成一體,拉寬了視覺寬度。這個“大嘴”配上這兇狠犀利的大燈,就形成了它’Dragon Face”設計的基本元素。

4680mm車身長度的它在逐漸下滑的懸浮式車頂襯托下,顯得車身相當修長,低矮的腰線則降低了它的視覺重心,讓它整體有着一種動感、衝勁。

尾部在擾流板、貫穿尾燈的鍍鉻裝飾、黑色塑料擋板的組合下,形成了一種層次感。敦厚的車尾部,更是讓它充滿安全感。

仿木材料的中控台,還有一個大尺寸豎直中控屏幕,以及是手感極佳的平底式運動方向盤,它在簡約風格中加入一點運動的元素。

生產實力強大、自產自銷的比亞迪能夠有效控製成本,所以它在配置配備上相當慷慨,標配了ESp車身穩定系統、17英寸鋁合金輪轂、自動空調、遠程啟動等等。又或者是一些“跨級般”的配置,諸如選配的LED自動大燈。

操控,或許可以說沉穩自信?

比亞迪宋MAX全系車型採用前麥弗遜后扭力梁懸架,處於這個級別的主流水平。動力方面搭載了最大輸出功率154馬力的1.5T直噴發動機,匹配的是6MT或者6速雙離合變速箱。

在日常使用中它整體表現出同價位中較高的水準,應付城市使用這套渦輪增壓+雙離合的動力總成顯得游刃有餘,能夠有效地控制轉速在1500rpm之中,需要提速的時候154馬力的底蘊讓超車動作比較從容,而且它1750rpm就開始進入240牛米的最大輸出扭矩區間,相比競爭對手們最大扭矩輸出更早。只是低速在1、2擋切換時會有絕大多數雙離合變速箱的小不足——輕微頓挫感。

據比亞迪官方稱,比亞迪宋max有着前奔馳底盤調校工程師漢斯柯克參与調校,讓它更注重舒適性,所以宋max駕駛起來有着厚重的行駛質感。能夠過濾掉路面各種細碎振動的同時,還有讓人安心的高速穩定性。受制於全系搭配的液壓轉向助力,宋max轉向略有虛位,並不可以說是精準,幸好較強的懸架支撐帶來了較小的車身傾側,扳回了一城。

總的來說,比亞迪宋max相比同價位對手有着不少的優勢,諸如更佳的底盤表現、更豐富的配置以及更出色的外觀,這恰恰迎合如今消費者開始接受這些“體面”MpV的用戶趨勢。6座車型,則是滿足了消費者不同的乘坐需求,多一種選擇未嘗不是一種好事。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

6秒破百很厲害?國內10來萬買SUV更應該看這點!

值得說明的是,國內媒體測試的多為旱地剎車成績,而在售車型大多採用四季胎,在暴雨天氣下剎車距離或會明顯變長,所以各位在暴雨天氣下最好加長與前車距離。我們可以看到市面上多款SUV車型的100-0km/h剎停成績差異頗大,這也是因為大部分用戶對於剎車方面的關注度不高,導致廠家有出現“偷懶”情況。

大家有沒有發現汽車廠家往往會把宣傳重點放在動力性能和發動機技術上,但是對於剎車性能往往置於末位。而只要您多加留意,就會和朗編一樣發現各個級別中不少熱點車型都以動力性能作為賣點!

連柯迪亞克、途觀L和冠道這類型大尺寸的SUV車型都在極力宣傳動力性能,但這明顯是為迎合國內消費者喜好而做出的選擇,畢竟用戶大多認為動力表現能直接決定行駛的順暢感,或者說是快感!但是這份快感的權重能比得上行車安全性嗎?

再者,我國的高速公路最高限速為120km/h,過多的發動機功率是否必要還有待商榷。

而在我們日常行車過程中,能看見最多的車禍是啥?可能許多人會和朗編一樣會想到追尾事故,四車連環追尾、六車連環追尾等事故經常霸佔着新聞的首頁版面,而且在雨霧天氣,追尾事故發生的幾率也將大幅度上升。

而據之前相關的統計數據显示,汽車追尾在整個道路交通事故中約佔70%以上,造成追尾事故自然有一部分是駕駛員意識、駕駛技術的原因,同時也有一部分是駕駛員意識到位了但車輛的剎車性能不到位而造成的。

現在銷售火爆的15萬級別合資/自主SUV車型它們是否注重剎車性能?

值得說明的是,國內媒體測試的多為旱地剎車成績,而在售車型大多採用四季胎,在暴雨天氣下剎車距離或會明顯變長,所以各位在暴雨天氣下最好加長與前車距離!

我們可以看到市面上多款SUV車型的100-0km/h剎停成績差異頗大,這也是因為大部分用戶對於剎車方面的關注度不高,導致廠家有出現“偷懶”情況。

其實能影響剎車性能的元素有很多,除了輪胎規格、輪胎抓地力以外,還有剎車片和剎車盤的性能,剎車片和剎車盤的材料優劣,摩擦係數的大小都會直接影響到制動力的大小!

而且值得注意的還有剎車分泵的活塞數量,一般家用車型每個分泵只有一個活塞,其性能自然沒有性能車身上的多活塞設計那般剎車力度大而且均勻。

由此可見想要提升剎車性能其實是相當複雜而困難的,比較有效的方法是系統而專業地改裝剎車系統,這裏面包含了輪胎、剎車盤、剎車片、剎車分泵、剎車油等多個配件的升級。

另外一個方法就是廠家在研發車輛時強化車輛剎車系統的調校,優化配件選材以提升原廠車的剎車表現,當然了這要投入更多成本,但是長遠來看,廠家若以優異的剎車性能來作為賣點招攬顧客的話,或會效果斐然!

最後提點題外話,那就是歐洲人為什麼會這麼喜歡高爾夫GTI這一類小鋼炮車型?很重要的就是歐洲的某些高速公路限速更高,在高速區域剎車性能的重要性就完全體現出來了,對於安全性也有更明顯的影響!而咱們國內的汽車消費市場也應該對剎車性能有更高要求,從而倒逼廠家重視提升剎車性能!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

【溫故知新】 編程原則和方法論

寫了這麼多年代碼,依舊做不好一個項目

做好一個項目是人力、產品、業務、技術、運營的結合,可能還疊加一點時機的因素,就我們碼農而言,工作就是搬磚,實現產品, 給業務提供支撐。
“給祖傳代碼加 BUG 修 BUG”,“拿起鍵盤一把梭”這些戲謔程序員的話,聽多了真的會讓程序員麻木,彷彿大家都是這麼乾的。
從業多年,堆過 shi 山,接手過祖傳代碼, 已經不能沉下氣去查看、調試 shi 山代碼, 說實話,很累。
本人一直推崇寫流暢、自然、可自解釋的代碼,讓優雅成為一種習慣, 給自己留個念想、給後人留個好評。

溫故而知新,聊一聊現代編程幾大常見的編程原則

普世原則
KISS (Keep It Simple Stupid) 保持系統結構簡單可信賴
YAGNI (you aren’t gonna need it) 當前確實需要,再去做
Do The Simplest Things That Could Possibly Work 思考最簡單可行的辦法
Separation of Concerns 關注點分離
Keep Things DRY 保持代碼結構清爽 Don’t repeat yourself
Code For The Maintainer 站在維護者角度寫代碼
Avoid Premature Optimization 避免提前優化
Boy-Scout Rule 清掃戰場:清理口水話註釋、無效代碼
模塊(類)間
Minimise Coupling 低耦合
Law of Demeter Don’t talk to strangers,對象方法只接觸該接觸的對象、字段、入參
Composition Over Inheritance 組合而不是繼承
Orthogonality 正相關,概念上不相關的事物不應在系統中強行相關
Robustness Principle 代碼健壯性
Inversion of Control 控制反轉
模塊(類)
Maximise Cohesion 高內聚
Likov Substitution Principle 里斯替代原則:將程序中對象替換到子類型實例,不會報錯。
Open/Closed Principle 設計的實體對擴展開放,對修改關閉
Single Responsiblity Principle 單一責任原則
Hide Implementation Details 隱藏實施細節
Curly’s Law 柯里定律:為確定目標編寫特定代碼
Encapsulate What Changes 封裝變化
Interface Segregation Principle 接口隔離原則
Command Query Separation 命令查詢分離

KISS
大多數系統保持簡單,會運行的很好。

  • 更少的代碼消耗更好的時間,產生更少的 bug,並且容易修改
  • 複雜業務都是由簡單代碼堆砌而成
  • 完美並不是“沒有什麼東西可以再加”,而是“沒有什麼東西可以被去掉”

YAGNI
YAGNI 代表“you aren’t gonna need it.”,不要自以為是的提前實現某些邊角,直到真正需要的時候,再來做。

  • 提前做明天才需要做的工作,意味着當前迭代中需要花費更多精力
  • 導致代碼膨脹,軟件變得臃腫且複雜

Separation of Concerns
關注點分離是一種將計算機程序分為不同部分的設計原則,這樣每個部分都可以解決一個單獨的關注點。例如應用程序的業務邏輯是一個問題,而用戶界面是另外一個問題,更改用戶界面不應要求更改業務邏輯,反之亦然。

  • 簡化應用程序的開發和維護
  • 如果關注點分離得很好,則各個部分可以重複使用,也可以獨立開發和更新。

Interface Segregation Principle
接口隔離,將胖接口修改為多個小接口,調用接口的代碼應該比實現接口的代碼更依賴於接口。
why:
如果一個類實現了胖接口的所有方法(部分方法在某次調用時並不需要),那麼在該次調用時我們就會發現此時出現了(部分並不需要的方法),而並沒有機制告訴我們我們現在不應該使用這部分方法。
how: 避免胖接口,類永遠不必實現違反單一職責原則的接口。可以根據實際多職責劃分為多接口,類實現多接口后, 在調用時以特定接口指代對象,這樣這個對象只能體現特定接口的方法,以此體現接口隔離。

   public interface IA
    {
        void getA();
    }

    interface IB
    {
        void getB();
    }

    public class Test : IA, IB
    {
        public string Field { get; set; }
        public void getA()
        {
            throw new NotImplementedException();
        }

        public void getB()
        {
            throw new NotImplementedException();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            IA a = new Test();
            a.getA();       //  在這個調用處只能看到接口IA的方法, 接口隔離
        }
    }

Command Query Separation
命令查詢分離: 操作方法就只寫操作邏輯,查詢方法就只寫查詢邏輯,並以明顯的方法名區分自己的動作。
有了這個原則,程序員可以更加自信地進行編碼:由於查詢方法不會改變狀態,因此可以在任何地方以任何順序使用,使用操作方法時,也心中有數。

End

懂得這麼多道理,卻依舊過不好這一生。前人總結的編程原則和方法論需要在實踐中感悟,束之高閣,則始終不能體會編程的魅力和快感

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

LeetCode 77,組合挑戰,你能想出不用遞歸的解法嗎?

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是LeetCode第46篇文章,我們一起來LeetCode中的77題,Combinations(組合)。

這個題目可以說是很精闢了,僅僅用一個單詞的標題就說清楚了大半題意了。這題官方難度是Medium,它在LeetCode當中評價很高,1364人點贊,只有66個反對。通過率53.6%。

題意

題目的題意很簡單,給定兩個整數n和k。n表示從1到n的n個自然數,要求隨機從這n個數中抽取k個的所有組合

樣例

Input: n = 4, k = 2
Output:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

全排列的問題我們已經很熟悉了,那麼獲取組合的問題怎麼做呢?

遞歸

這是一個全組合問題,實際上我們之前做過全排列問題。我們來分析一下排列和組合的區別,可能很多人知道這兩者的區別,但是對於區別本身的理解和認識不是非常深刻。

排列和組合有一個巨大的區別在於,排列會考慮物體擺放的順序。也就是說同樣的元素構成,只要這些元素一些交換順序,那麼就會被視為是不同的排列。然而對於組合來說,是不會考慮物體的擺放順序的。只要是這些元素構成,無論它們怎麼調換擺放順序,都是同一種組合。

我們獲取全排列的時候用的是回溯法,我們當然也可以用回溯法來獲取組合。但問題是,我們怎麼保證獲取到的組合都是元素的組成不同,而不是元素之間的順序不同呢?

為了保證這一點,需要用到一個慣用的小套路,就是通過下標遞增來控制拿取元素的順序。如果我們限定了拿取元素的下標是遞增的,那麼就可以保證每一次拿取到的組合都是獨一無二的。所以我們就把這一點加在回溯法上即可,只要理解了,並不難實現。

在代碼的實現當中,我們用上了閉包,省略了幾個參數的傳遞,整體上來說編碼的難度降低了一些。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def dfs(start, cur):
            # 如果當前已經拿到了K個數的組合,直接加入答案
            # 注意要做深拷貝,否則在之後的回溯過程當中變動也會影響結果
            if len(cur) == k:
                ret.append(cur[:])
                return
            
            # 從start+1的位置開始遍歷
            for i in range(start+1, n):
                cur.append(i+1)
                dfs(i, cur)
                # 回溯
                cur.pop()
                
        ret = []
        dfs(-1, [])
        return ret

迭代

這題並不是只有一種做法,我們也可以不用遞歸實現算法。不用遞歸意味着沒有系統幫助我們建棧存儲中間信息了,需要我們自己把迭代過程當中所有變量的關係整理清楚。

我們假設n=8,k=3,那麼在所有合法的組合當中,最小的組合一定是[1,2,3],最大的組合一定是[6,7,8]。如果我們保證組合當中的元素是有序排列的,那麼組合之間的大小關係也是可以確定的。進而我們可以思考設計一種方案,使得我們可以從最小的組合[1,2,3]一直迭代到[6,7,8],並且我們還要保證在迭代的過程當中,組合當中元素的順序不會被打亂。

我們可以想象成這n個數在一根“直尺”上排成了一行,我們有k個滑動框在上面移動。這k個滑動框取值的結果就是n個元素中選取k個的組合,並且由於滑動框之間是不能交錯的,所以保證了這k個值是有序的。我們要做的就是設計一種移動滑動框的算法,使得能夠找到所有的組合情況。

我們可以想象一下,一開始的時候滑動框都聚集在最左邊,我們要移動只能移動最右側的滑動框。我們把滑動框從k移動到了k+1,那麼這個時候它的右側有k-1個滑動框,一共有k個位置。

那麼這個問題其實轉化成了k個元素當中取k-1個組合的子問題。我們把1-k的這個部分看成是新的“直尺”,我們要在其中移動k-1個滑動框獲取所有的組合。首先,我們需要把這k-1個滑動框全部移動到左側,然後再移動其中最右側的滑動框。然後循環往複,直到所有的滑動框都往右移動了一格為止,這其實是一個遞歸的過程。

我們不去深究這個遞歸的整個過程,我們只需要理解清楚其中的幾個關鍵點就可以了。首先,對於每一次遞歸來說,我們只會移動這個遞歸範圍內最右側的滑動框,其次我們清楚每一次遞歸過程中的起始狀態。開始狀態就是所有的滑動框全部集中在“直尺”的最左側,結束狀態就是全部集中在最右側。

我們把上面的邏輯整理一下,假設我們經過一系列操作之後,m個滑動框全部移動到了長度為n的直尺的最右側。這就相當於的組合都已經獲取完了。如果n+1的位置還有滑動框,並且它的右側還可以移動,那麼我們需要將它往右移動一個,到n+2的位置。這個時候剩下的局面就是,為了獲取這些組合,我們需要把這m個滑動框全部再移動到直尺的最左側,重新開始移動。

我們在實現的時候當然沒有滑動框,我們可以用一個數組記錄滑動框當中的元素。

我先用遞歸寫一下這段邏輯:

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def comb(window, m, ret):
            ret.append(window[:-1])

            # 如果第m位的滑動框不超過直尺的範圍並且m右側的滑動框
            while window[m] < min(n - k + m + 1, window[m+1] - 1):
                # 向右滑動一位
                window[m] += 1
                # 如果m左側還有滑動框,遞歸
                if m > 0:
                    # 把左側的滑動框全部移動到最左側
                    window[:m] = range(1, m+1)
                    comb(window, m-1, ret)
                else:
                    # 否則記錄答案
                    ret.append(window[:-1])

                
        ret = []
        window = list(range(1, k+1))
        # 額外多放一個滑動框作為標兵
        window.append(n+1)
        comb(window, k-1, ret)
        return ret

這種解法的速度比上面正規遞歸的速度快了許多,因為我們遞歸的過程當中做了諸多限制,剪掉了很多無關的情況,相當於做了極致的剪枝。

最關鍵的是上面的這段邏輯我們是可以用循環實現的,所以我們可以用循環來將遞歸的邏輯展開,就得到了下面這段代碼。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        # 構造滑動框
        window = list(range(1, k + 1)) + [n + 1]
        
        ret, j = [], 0

        while j < k:
            # 添加答案
            ret.append(window[:k])

            j = 0
            # 從最左側的滑動框開始判斷
            # 如果滑動框與它右側滑動框挨着,那麼就將它移動到最左側
            # 因為它右側的滑動框一定會向右移動
            while j < k and window[j + 1] == window[j] + 1:
                window[j] = j + 1
                j += 1
            # 連續挨着最右側的滑動框向右移動一格
            window[j] += 1
            
        return ret

這段代碼雖然非常精鍊,但是很難理解,尤其是你沒能理解上面遞歸實現的話,會更難理解。所以我建議,先把遞歸實現的滑動框的方法理解了,再來理解不含遞歸的這段,會容易一些。

總結

我們通過回溯法求解組合的方法應該是最簡單也是最基礎的,難度也不大。相比之下後面一種方法則要困難許多,我們直接去啃,往往不得要領。既會疑惑為什麼這樣可以保證能獲得所有的組合,又會不明白其中具體的實現邏輯。所以如果想要弄明白第二種方法,一定要從滑動框這個模型出發

從代碼實現的角度來說,滑動框方法的遞歸解法比非遞歸的解法還要困難。因為遞歸條件以及邏輯都比較複雜,還涉及到存儲答案的問題。但是從理解上來說,遞歸的解法更加容易理解一些,非遞歸的算法往往會疑惑於j這個指針的取值。所以如果想要理解算法的話,可以從遞歸的代碼入手,想要實現代碼的話,可以從非遞歸的方法入手。

這道題目非常有意思,值得大家細細思考。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

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

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

MongoDB 邏輯備份工具mongodump

mongodump是官方提供的一個對數據庫進行邏輯導出的備份工具,導出文件為BSON二進制格式,無法使用文本編輯工具直接查看。mongodump可以導出mongod或者mongos實例的數據,從集群模式來看,可以備份單實例、副本集、分片集集群。

mongodump作為MongoDB官方工具集中的一部分,從版本4.4開始,文檔說明統一到工具分類中:database-tools。本文是基於MongoDB 4.2 社區版本,具體環境如下:

OS:CentOS Linux release 7.6.1810 (Core)

DB version:v4.2.2

因為安裝的二進製版本,所以mongodump可執行文件通過解壓壓縮包就可以得到。

主要選項

mongodump支持的選項不算太多,可以通過--help選項獲得:

mongodump --help
Usage:
  mongodump <options>

Export the content of a running server into .bson files.

選項分為幾個大類:

  • general options:通用選項
  • connection options:連接選項
  • ssl options:安全連接選項
  • authentication options:驗證選項
  • kerberos options:基於kerboeros驗證選項
  • namespace options:命名空間選項
  • uri options:mongodb uri連接串選項
  • query options:查詢選項
  • output options:輸出選項
  • verbosity options:显示選項

general options(通用選項)

--help      # 打印工具使用方式,選項說明。
--version   # 打印工具版本並退出。

connection options(連接選項)

-h, --host=<hostname>   # 指定連接的實例主機名或者IP地址。                                 
    --port=<port>       # 指定連接的實例端口號。                                 

連接選項也可以分為三種實例模式:單實例、副本集、分片集。

  • Standalone(單實例)

只指定選項--host

mongodump --host="192.168.196.128:27017"

同時指定選項--host--port

mongodump --host="192.168.196.128" --port=27017
  • Replica Set(副本集)

指定選項--host

mongodump --host="dbabdSet/192.168.196.128:27017,192.168.196.128:27018,192.168.196.128:27019"

默認情況下,mongodump讀取Primary節點的數據,如果想讀取Secondary節點的數據,則需要使用選項readPreference

mongodump --host="dbabdSet/192.168.196.128:27017,192.168.196.128:27018,192.168.196.128:27019" --readPreference=secondary
  • Sharded Cluster(分片集群)

Replica Set(副本集)相同的指定方式。

注意:

–host選項默認值為:localhost:27017

–port選項默認值為:27017

執行副本集的導出備份時通常單獨連接某個Secondary節點進行導出備份:

mongodump --host="192.168.196.128:27018"

ssl options(安全連接選項)

--ssl                                                
--sslCAFile=<filename>                               
--sslPEMKeyFile=<filename>                            
--sslPEMKeyPassword=<password>                        
--sslCRLFile=<filename>                               
--sslAllowInvalidCertificates          
--sslAllowInvalidHostnames
--sslFIPSMode

關於ssl安全加密協議連接,本文不過多的贅述,詳情可以參考官方文檔:https://docs.mongodb.com/manual/reference/program/mongodump/index.html#cmdoption-mongodump-ssl

authentication options(驗證選項)

主要用於驗證連接實例的用戶的合法性。

-u, --username=<username>                   # 指定連接用戶
-p, --password=<password>                   # 指定連接用戶密碼
--authenticationDatabase=<database-name>    # 指定連接用戶驗證數據庫
--authenticationMechanism=<mechanism>       # 指定連接驗證機制

注意:

如果需要在導出時显示指示輸入密碼,而不是直接寫在選項中,則在指定--username選項的同時,不指定--password或者為--password選項指定一個空值,如:–password “”

如果沒有驗證數據庫,則mongodump假設指定導出的數據庫中包含用戶的驗證信息,如果沒有驗證數據庫並且也沒有指定導出數據庫,則mongodump假設admin數據庫包含用戶的驗證信息。

kerberos options(kerboeros驗證選項)

該選項主要是基於kerberos驗證機制的連接驗證選項,kerberos只有MongoDB企業版才支持,本文基於社區版本,有關kerberos可以參考官方文檔:https://docs.mongodb.com/manual/core/kerberos/

namespace options(命名空間選項)

主要是指定需要邏輯備份的數據庫和集合。

-d, --db=<database-name>             # 指定數據庫
-c, --collection=<collection-name>   # 指定集合                     

注意:

如果沒有指定導出數據庫,則mongodump導出實例中所有的數據庫,對於集合選項做相同的處理。

uri options(uri連接串選項)

從版本3.4.6開始新增加一種連接MongoDB實例的字符串格式,即URI。

--uri=mongodb-uri  # 指定uri連接字符串
  • Standalone(單實例)
--uri="mongodb://192.168.196.128:27017"
# 開始訪問控制驗證
--uri="mongodb://dbabd:admin@192.168.196.128:27017/?authSource=admin"
  • Replica Set(副本集)
--uri="mongodb://192.168.196.128:27017,192.168.196.128:27018,192.168.196.128:27019/?replicaSet=dbabdSet"
# 開始訪問控制驗證
--uri="mongodb://dbabd:admin@192.168.196.128:27017,192.168.196.128:27018,192.168.196.128:27019/?authSource=admin&replicaSet=dbabdSet"
  • Sharded Cluster(分片集群)

Replica Set(副本集)相同的指定方式。

當指定--uri連接串選項時,會與之前有關連接的選項互斥,這些選項包括:

  • –host
  • –port
  • –db
  • –username
  • –password(如果–uri連接串有指定連接密碼的話)
  • –authenticationDatabase
  • –authenticationMechanism

因為–uri連接串中已經包含了以上選項所涉及的部分。

query options(查詢選項)

指定mongodump導出的條件,可以參考db.collections.find()方法的查詢表達式。

-q, --query=<json>       # 指定符合JSON v2拓展格式的查詢語句 如:'{"x":{"$gt":1}}'
    --queryFile=<json>   # 指定包含JSON v2拓民格式查詢語句的文件
--readPreference=<string>|<json>  # 指定優先讀取順序   如:'nearest' 或 '{mode: "nearest", tagSets: [{a: "b"}],maxStalenessSeconds: 123}')
--forceTableScan         # 指定導出時遍歷集合使用的索引

注意:

如果指定--query選項,則必須同時指定--collection選項。

mongodump對於副本集默認優先讀取primary節點,如果需要從secondary,則指定選項–readPreference=secondary

mongodump導出時遍歷集合時默認使用索引_id,如果要使用其他索引,則指定選項--forceTableScan,該選項沒辦法確保mongodump導出基於某個時間點的快照,如果需要創建某一時間點的快照,則使用選項--oplog,該選項不能與選項--query一起使用。

output options(輸出選項)

指定mongodump導出時保存BSON文件的目錄,默認情況下,導出文件保存在執行mongodump當下目錄中的dump目錄里。

-o, --out=<directory-path>  # 指定導出文件保存目錄
--gzip                      # 使用gzip對導出文件進行壓縮
--oplog                     # 指定保存mongodump導出期間的oplog日誌,文件名為oplog.bson
--archive=<file-path>       # 指定導出文件合併歸檔的目的地
--dumpDbUsersAndRoles       # 指定導出數據庫的用戶和角色定義
--excludeCollection=<collection-name>  # 指定導出時排除某個集合,如有多個,需要指定多次
--excludeCollectionsWithPrefix=<collection-prefix>  # 指定導出時排除多個相同命名前綴的集合
-j, --numParallelCollections=  # 指定導出時可以并行的集合數,默認值為4
--viewsAsCollections # 指定導出時將只讀視圖當成集合保存成BSON文件

注意:

當指定選項--oplog時,mongodump在導出過程中同時會保存這一時間點產生的oplog,並保存為oplog.bson文件,使導出的備份是實例基於某個時間點的快照,如果使用mongorestore還原進行oplog回放時,需要指定選項--oplogReplay。如果沒有指定選項--oplog,則無法保證當前的導出在這一時刻的一致性,在導出過程中有對數據庫進行作何的更新操作都會影響導出的文件變化。

如果mongodump指定選項--oplog導出時客戶端執行以下命令會導致導出失敗:

  • renameCollection
  • db.collection.renameCollection()
  • db.collection.aggregate()並且執行操作符$out

當mongodump連接mongos實例進行整個分片集群的導出備份時,指定選項--oplog是沒有生效的,需要對各個分片節點集群單獨導出並指定--oplog

選項--oplog只有在副本集所有成員都開啟oplog功能才會生效。

選項--oplog並不會民出保存oplog的集合。

當mongodump指定選項--oplog導出時,必須包括導出實例所有的數據庫和集合,即不能指定選項--db只導出某個庫或選項--collection只導出某個集合。

verbosity options(显示選項)

指定導出時log輸出的显示的詳細級別。

-v, --verbose=<level>  # 指定日誌輸出詳細級別,如:-vvvvv 或 指定數值
--quiet                # 指定不輸出作何日誌信息

使用示例

備份導出數據庫

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd -o /data/mongodump/

備份導出數據庫某個集合

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd -
c orders -o /data/mongodump/

備份導出排除某個集合

# 如果有多個排除集合,則多次指定選項--excludeCollection
mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd -
-excludeCollection=orders -o /data/mongodump/

備份導出文件歸檔到文件

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd --archive=/data/mongodump/dbabd.archive

備份導出文件進行壓縮

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd -
-gzip -o /data/mongodump/

備份導出文件進行壓縮並歸檔

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd --gzip --archive=/data/mongodump/dbabd.archive

指定條件的備份導出

mongodump -h"192.168.196.128:27017" -uroot --authenticationDatabase admin -d dbabd -c orders -q='{"price":{"$in":[25,50]}}' -o /data/mongodump/

注意

mongodump不會導出有關於local數據庫的內容;

mongodump不會導出文檔的索引,而只會導出文檔的數據,在導出文件還原之後需要重建索引;

對於開啟了訪問控制機制的實例,mongodump要執行導出操作的話必須對每個需要導出的數據庫具有find權限,內建角色backup提供了備份所有數據庫的權限,所以可以對用戶直接授予backup角色。

總結

對於MongoDB實例的邏輯備份工具,mongodump是個不二選擇,官方出品,安全穩定性有保障;

mongodump較適合數據量較少的備份,相對於數據量較大的情況,備份效率不是太高;

mongodump導出時比較消耗服務器性能,而且不支持同時導出多個庫,對於導出庫數據的最終一致性選項--oplog也只能是全庫導出才支持。

參考

https://docs.mongodb.com/database-tools/mongodump

https://docs.mongodb.com/manual/reference/program/mongodump/index.html#cmdoption-mongodump-ssl2

https://docs.mongodb.com/manual/core/kerberos/

〖本人水平有限,文中如有錯誤還請留言批評指正!〗

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

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

求求你,別問了,Java字符串是不可變的

最近,又有好幾個小夥伴問我這個問題:“二哥,為什麼 Java 的 String 要設計成不可變的啊?”說實話,這也是一道非常經典的面試題,面試官超喜歡問。我之前寫過這方面的文章,現在讀起來似乎不太滿意,所以我決定再啰嗦最後一次,交出一份更滿意的答卷,讓小夥伴們在面試官面前更從容一些,更有底氣一些。

關於不可變對象,還有這樣一個小故事。Java 之父詹姆斯高司令曾在一次採訪中被問及這樣一個問題:“高司令,應該什麼時候使用不可變對象啊?”你猜高司令怎麼回答?

如有可能,我願意任何時候都使用不可變對象。

這就是高司令的答案,那有的小夥伴可能不服,老人家會說中文,你瞎扯吧你。也對哈,那就上英文唄:

I would use an immutable whenever I can.

這下徹底被打服了吧?老人家還說,不可變有着非常強大的功能,比如說,緩存、安全性、高性能等等。

01、什麼是不可變對象

不可變對象在創建后,它的內部狀態會保持不變,這就意味着,一旦我們將一個對象分配給一個變量,就無法再通過任何方式更改對象的狀態了。

關於不可變對象的更多信息,可以查看我之前寫的另外一篇文章——這次要說不明白immutable類,我就怎麼地,看完啥都明白了。你看,寫系列文章的好處就是這樣,不需要重複造輪子,用到的時候直接搬出來套上就行了。

02、為什麼 String 是不可變的

重點來了啊,為什麼 String 是不可變的?原因可以從四個方面說起,緩存、安全性、同步和高性能。

1)字符串常量池

字符串恐怕是 Java 中最常用的數據形式了,如果字符串非要謙虛地說自己是老二,就沒有人敢說自己是老大。

因此,把字符串緩存起來,並且重複使用它們會節省大量堆空間(堆內存用來存儲 Java 中的對象,無論是成員變量、局部變量,還是類變量,它們指向的對象都存儲在堆內存中),因為不同的字符串變量引用的是字符串常量池中的同一個對象。這也正是字符串常量池存在的目的。

字符串常量池是 Java 虛擬機用來存儲字符串的一個特殊的區域,由於字符串是不可變的,因此 Java 虛擬機可以在字符串常量池中只為同一個字符串存儲一個字符串副本來節省空間。

字符串常量池的主要使用方法有兩種:

  • 直接使用雙引號聲明出來的字符串對象會直接存儲在常量池中。
  • 否則,可以使用 String 類提供的 intern() 方法強制將當前字符串放入常量池中——常量池中查詢不到當前字符串。

來看下面這段代碼:

String s1 = "沉默王二";
String s2 = "沉默王二";

System.out.println(s1 == s2); // true

由於字符串常量池的存在,所以兩個不同的變量都指向了池中同一個字符串對象,從而節省了稀缺的內存資源。如果是通過 new 關鍵字創建的對象,則需要新的堆空間。

放心,關於字符串常量池,後面有時間的話,我再單獨寫一篇文章詳細地說一說。

2)安全性

字符串在 Java 應用程序中的使用範圍非常廣,幾乎無處不在,比如說存儲用戶名、密碼、數據庫連接地址等等這些非常敏感的信息,因此,必須要保證 String 類的絕對安全性。

來考慮一下下面這段代碼:

void criticalMethod(String userName) {
    // 檢查用戶名是否合法
    if (!isAlphaNumeric(userName)) {
        throw new SecurityException(); 
    }

    // 初始化數據庫連接
    initializeDatabase();

    // 準備修改用戶狀態
    connection.executeUpdate("UPDATE members SET status = 'active' " +
      " WHERE username = '" + userName + "'");
}

通常情況下,用戶名由客戶端傳遞到服務器端,服務器端接收后要先對用戶名進行檢查,再進行其他操作,因為客戶端傳遞過來的信息不一定值得信任。

如果字符串是可變的,那麼我們在執行 executeUpdate 更新數據庫的時候,就有點不放心,因為即便是安全性檢查通過了,字符串仍然有可能被修改。

在調用 isAlphaNumeric() 方法進行安全性檢查期間,userName 的值仍然有可能被 criticalMethod() 方法的調用者進行篡改,就容易造成 SQL 注入。

但如果字符串是不可變的,這方面的擔憂就不存在了。因為在執行更新之前,字符串的值是確定的,就是我們檢查安全性之後的值。

3)線程安全

由於字符串是不可變的,因此可以在多線程之間共享,如果一個線程把字符串的值修改為另外一個,那麼就會在字符串常量池中創建另外一個字符串,原有的字符串仍然會保持不變。

不過,很遺憾,我還不知道怎麼從代碼層面上去證明這一點,只能純理論 yy 一下。小夥伴誰有辦法的,教教我,在線等的那種。

4)哈希碼

字符串廣泛應用於 HashMap、HashTable、HashSet 等需要哈希碼作為鍵的數據結構中,在對這些哈希表進行操作的時候,需要頻繁調用 hashCode() 方法來獲取鍵的哈希碼。

public V put(K key, V value) {
    return putVal(hash(key), key, value, falsetrue);
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

由於字符串是不可變性,這就保證了鍵值的哈希值不會發生改變,因此在第一次調用 String 類的 hashCode() 方法時,就對哈希值進行了緩存,此後,就一直返回相同的值。

/** Cache the hash code for the string */
private int hash; // Default to 0

public int hashCode() {
    int h = hash;
    if (h == 0 && !hashIsZero) {
        h = isLatin1() ? StringLatin1.hashCode(value)
                : StringUTF16.hashCode(value);
        if (h == 0) {
            hashIsZero = true;
        } else {
            hash = h;
        }
    }
    return h;
}

由於哈希值被緩存了,這在另外一種層面上提高了哈希表的訪問性能,因為哈希值不用重新計算了。

假如字符串是可變的,那就意味着哈希碼會有多個,在通過鍵獲取值的時候,就不一定能夠獲取到對的值了。

你看,字符串常量池的存在,哈希碼的存在,在很大程度上提高了程序的性能。

03、總結

好了,我親愛的小夥伴們,以上就是本文的全部內容了。我相信你一定對字符串的不可變性有了充足的了解,由於字符串是不可變的,因此我們可以將它看作是一個特殊的基本數據類型,哪怕是在多線程的環境下,也不用擔心它的值是否會發生改變。

如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。

本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。

我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻

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

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Jmeter基礎004—-增加參數化

一、參數化概述

1、參數化概念

      參數化就是動態的獲取並設置數據,當執行批量操作時,如批量插入或批量刪除,之前每執行完一次就需要修改一次,效率太低,參數化可以代替人工獲取並設置數據,安全且高效!

2、Jmeter參數化組件

  • CSV Data Set Config—-CSV數據設置組件
  • 用戶參數
  • 用戶定義的變量
  • 函數

二、參數化實現之CSV Data Set Config

1、概述

     CSV Data Set Config—-CSV數據設置組件,是參數化的實現組件之一,通過這個組件可以動態獲取並設置數據,實現批量操作,如:批量添加操作(執行一次,將多條數據插入到數據庫)。

2、實例1:參數化登錄賬號

      我們錄製的腳本,內容都是固定的,比如手機號、驗證碼都是我們再錄製過程中輸入的,如果我們希望模擬不同用戶登陸,那麼我們並不需要錄製很多個腳本,而只要將腳本中的用戶名、密碼變成變量,而線程執行時,不同線程取得不同的變量值即可。
     下面我們就舉例說明如何參數化登陸賬號。

(1)測試登錄接口:如下圖,新建登陸的HTTP請求並運行,確保登錄接口運行正常。

 

(2)創建一個文本文檔,標準的CSV格式文件,如下圖包括3條數據,每一行數據對應一條登錄信息,不同字段之間使用英文逗號分隔。

(3)線程組右鍵—添加—配置元件—CSV Data Set Config—-CSV,創建一個CSV組件,並聲明數據源、編碼集以及解析格式,如下圖所示:

(4)設置線程組的線程數為3(因為文件中有三條登錄數據),並修改HTTP請求中的參數值,調用CSV數據文件設置中定義的變量,調用格式${變量名},如下圖所示:

 

 (5)運行測試計劃,查看結果樹的運行結果,如下圖所示:

 

 

 3、實例2:批量添加

 實現思想:

 

 

 實現步驟:

(1)創建CSV 數據文件設置,如下圖所示:

 (2)創建HTTP請求,並在請求中調用CSV中定義的變量

 (3)編輯文本文檔,存儲要添加的三條數據

 (4)設置線程組循環次數為3,並運行測試計劃,查看察看結果樹显示。

 

三、參數化之用戶參數

1、用戶參數與CSV參數化的區別

    用戶參數和CSV都是將數據設置進第三方,然後循環讀取數據,區別在於:CSV是將數據設置進外部的文本文檔,而用戶參數是將數據設置進Jmeter內置組件。

2、實現流程  

(1)搭建框架:創建測試計劃、線程組、HTTP請求(請求的JSON數據先不設置)。注意:執行次數是3次(不是設置循環次數,而是設置線程數)

(2)創建Jmeter內置組件存儲要插入的數據:測試計劃右鍵—-添加—-前置處理器—-用戶參數,在用戶參數組件界面添加4個變量、3個用戶,如下圖所示:

 注意:因為此處添加的是用戶,每個用戶對應一個線程 ,添加幾個用戶就應該設置幾個線程,所以這裏設置的是線程組而不是循環次數。

 

(3)將用戶參數組件中的變量名稱設置進HTTP請求的Json數據格式中,調用格式:${變量名},如下圖所示:

 (4)運行測試計劃,查看察看結果樹。

 

四、參數化之用戶定義的變量

1、需求

     當系統執行增刪改查操作時,資源路徑不一定相同,但存在部分相同,如:都是/api/departments/開頭,為了提高編寫路徑的效率,可以將公共路徑定義成變量,然後再在路徑中使用${變量名}調用變量。注:一般定義、存儲全局使用的變量。

2、實現過程

(1)將公共的路徑數據提取出來使用一個組件存儲,如:/api/departments/。測試計劃右鍵—-添加—-配置元件—-用戶定義的變量,創建用戶定義的變量組件,添加自定義變量,如下圖所示:

(2)分別創建HTTP請求,在路徑中公共部分調用定義的路徑變量,非公共部分路徑與原來一致,如下圖所示:

 (3)運行測試計劃,查看結果樹。

 

五、參數化之函數

1、需求

     函數是程序中最基本的封裝單元,封裝了一些常用的功能,比如計數器。在實際應用中當我們需要循環10次查詢信息時,結果數的請求名稱都是一樣的,我們可以使用計數函數添加標號以示區分。

2、實現流程

(1)打開Jmeter內置的函數組件,一共有三種方式:

  •  選項+函數助手對話框
  •  ctrl+shift+F1
  • 工具欄倒數第二個圖標

(2)選擇要使用的函數,給函數傳參,並用Jmeter生成調用格式,如下圖:

 

注:__counter函數的參數:true,每一個用戶單獨一個計數器;false,所有用戶共用一個計數器.

(3)在需要調用函數的位置使用Jmeter生成的調用格式:${_函數名(參數)}

 

 

 (4)運行測試計劃,查看結果樹,如下圖所示:

六、總結

1、參數化—-CSV Data Set Config

     概念:動態獲取並設置數據,操作數據高效安全(程序代替人工)

     實現思想+具體流程:

2、參數化—-用戶參數

實現思想:將數據單獨存儲,然後再將數據讀取到http請求的JSON 數據中

實現流程:

  • 設置執行次數(用戶數)
  • 添加組件用戶參數存儲多條記錄
  • 讀取數據格式: ${變量名 )

3、參數化—-用戶定義的變量

作用:存儲全局性數據 

添加格式:添加用戶定義的變量組件—–鍵和值

調用格式:${變量名}

4、參數化—-函數

概念:程序中的功能單元,封裝了部分實現 

實現: 

  • 打開函數功能模塊
  • 選擇要調用的函數+設置參數+生成調用格式
  • 在需要使用的位置調用即可

5、四種參數化方案比較

  •   CSV和用戶參數使用思想一致,流程上後者更簡單,但是實際應用中,使用CSV居多,因為數據量大時,CSV更方便
  •   用戶定義的變量一般用來存儲全局變量,但是使用場景較少
  •   函數實現更為靈活且內置了好多實現。

總結:最常用的是參數化方法是:CSV+函數

 

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Vue結合路由配置遞歸實現菜單欄

作者:小土豆biubiubiu

博客園:https://www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

微信公眾號:土豆媽的碎碎念(掃碼關注,一起吸貓,一起聽故事,一起學習前端技術)

作者文章的內容均來源於自己的實踐,如果覺得有幫助到你的話,可以點贊給個鼓勵或留下寶貴意見

前言

在日常開發中,項目中的菜單欄都是已經實現好了的。如果需要添加新的菜單,只需要在路由配置中新增一條路由,就可以實現菜單的添加。

相信大家和我一樣,有時候會躍躍欲試自己去實現一個菜單欄。那今天我就將自己實現的菜單欄的整個思路和代碼分享給大家。

本篇文章重在總結和分享菜單欄的一個遞歸實現方式代碼的優化菜單權限等不在本篇文章範圍之內,在文中的相關部分也會做一些提示,有個別不推薦的寫法希望大家不要參考哦。

同時可能會存在一些細節的功能沒有處理或者沒有提及到,忘知曉。

最終的效果

本次實現的這個菜單欄包含有一級菜單二級菜單三級菜單這三種類型,基本上已經可以覆蓋項目中不同的菜單需求。

後面會一步一步從易到難去實現這個菜單。

簡單實現

我們都知道到element提供了 NavMenu 導航菜單組件,因此我們直接按照文檔將這個菜單欄做一個簡單的實現。

基本的布局架構圖如下:

菜單首頁-menuIndex

首先要實現的是菜單首頁這個組件,根據前面的布局架構圖並且參考官方文檔,實現起來非常簡單。

<!-- src/menu/menuIndex.vue -->
<template>
    <div id="menu-index">
        <el-container>
            <el-header>
                <TopMenu :logoPath="logoPath" :name="name"></TopMenu>
            </el-header>
            <el-container id="left-container">
                <el-aside width="200px">
                    <LeftMenu></LeftMenu>                    
                </el-aside>
                <el-main>
                    <router-view/>
                </el-main>
            </el-container>
        </el-container>
    </div>
</template>
<script>
import LeftMenu from './leftMenu';
import TopMenu from './topMenu';
export default {
    name: 'MenuIndex',
    components: {LeftMenu, TopMenu},
    data() {
        return {
            logoPath:  require("../../assets/images/logo1.png"),
            name: '員工管理系統'
        }
    }
}
</script>
<style lang="scss">
    #menu-index{
        .el-header{
            padding: 0px;
        }
    }
</style>

頂部菜單欄-topMenu

頂部菜單欄主要就是一個logo產品名稱

邏輯代碼也很簡單,我直接將代碼貼上。

<!-- src/menu/leftMenu.vue -->
<template>
    <div id="top-menu">
        <img class="logo" :src="logoPath" />
        <p class="name">{{name}}</p>
    </div>
</template>
<script>
export default {
    name: 'topMenu',
    props: ['logoPath', 'name']
}
</script>
<style lang="scss" scoped>
    $topMenuWidth: 80px;
    $logoWidth: 50px;
    $bg-color: #409EFF;
    $name-color: #fff;
    $name-size: 18px;
    #top-menu{
        height: $topMenuWidth;
        text-align: left;
        background-color: $bg-color;
        padding: 20px 20px 0px 20px;
        .logo {
            width: $logoWidth;
            display: inline-block;
        }
        .name{
            display: inline-block;
            vertical-align: bottom;
            color: $name-color;
            font-size: $name-size;
        }
    }
</style>

這段代碼中包含了父組件傳遞給子組件的兩個數據。

props: ['logoPath', 'name']

這個是父組件menuIndex傳遞給子組件topMenu的兩個數據,分別是logo圖標的路徑產品名稱

完成后的界面效果如下。

左側菜單欄-leftMenu

首先按照官方文檔實現一個簡單的菜單欄。

<!-- src/menu/leftMenu.vue -->
<template>
    <div id="left-menu">
        <el-menu 
            :default-active="$route.path" 
            class="el-menu-vertical-demo" 
            :collapse="false">
            <el-menu-item index="1">
                <i class="el-icon-s-home"></i>
                <span slot="title">首頁</span>
            </el-menu-item>
            <el-submenu index="2">
                <template slot="title">
                    <i class="el-icon-user-solid"></i>
                    <span slot="title">員工管理</span>
                </template>
                <el-menu-item index="2-1">員工統計</el-menu-item>
                <el-menu-item index="2-2">員工管理</el-menu-item>
            </el-submenu>
            <el-submenu index="3">
                <template slot="title">
                    <i class="el-icon-s-claim"></i>
                    <span slot="title">考勤管理</span>
                </template>
                <el-menu-item index="3-1">考勤統計</el-menu-item>
                <el-menu-item index="3-2">考勤列表</el-menu-item>
                <el-menu-item index="3-2">異常管理</el-menu-item>
            </el-submenu>
            <el-submenu index="4">
                <template slot="title">
                    <i class="el-icon-location"></i>
                    <span slot="title">工時管理</span>
                </template>
                <el-menu-item index="4-1">工時統計</el-menu-item>
                <el-submenu index="4-2">
                    <template slot="title">工時列表</template>
                    <el-menu-item index="4-2-1">選項一</el-menu-item>
                    <el-menu-item index="4-2-2">選項二</el-menu-item>
                </el-submenu>
            </el-submenu>
        </el-menu>
    </div>
</template>
<script>
export default {
    name: 'LeftMenu'
}
</script>
<style lang="scss">
    // 使左邊的菜單外層的元素高度充滿屏幕
    #left-container{
        position: absolute;
        top: 100px;
        bottom: 0px;
        // 使菜單高度充滿屏幕
        #left-menu, .el-menu-vertical-demo{
            height: 100%;
        }
    }
</style>

注意菜單的樣式代碼,設置了絕對定位,並且設置topbottom使菜單高度撐滿屏幕。

此時在看下界面效果。

基本上算是實現了一個簡單的菜單布局。

不過在實際項目在設計的時候,菜單欄的內容有可能來自後端給我們返回的數據,其中包含菜單名稱菜單圖標以及菜單之間的層級關係

總而言之,我們的菜單是動態生成的,而不是像前面那種固定的寫法。因此下面我將實現一個動態生成的菜單,菜單的數據來源於我們的路由配置

結合路由配置實現動態菜單

路由配置

首先,我將項目的路由配置代碼貼出來。

import Vue from 'vue';
import Router from "vue-router";

// 菜單
import MenuIndex from '@/components/menu/menuIndex.vue';

// 首頁
import Index from '@/components/homePage/index.vue';

// 人員統計
import EmployeeStatistics from '@/components/employeeManage/employeeStatistics.vue';
import EmployeeManage from '@/components/employeeManage/employeeManage.vue'

// 考勤
// 考勤統計
import AttendStatistics from '@/components/attendManage/attendStatistics';
// 考勤列表
import AttendList from '@/components/attendManage/attendList.vue';
// 異常管理
import ExceptManage from '@/components/attendManage/exceptManage.vue';

// 工時
// 工時統計
import TimeStatistics from '@/components/timeManage/timeStatistics.vue';
// 工時列表
import TimeList from '@/components/timeManage/timeList.vue';
Vue.use(Router)


let routes = [
    // 首頁(儀錶盤、快速入口)
    {
        path: '/index',
        name: 'index',
        component: MenuIndex,
        redirect: '/index',  
        meta: {
            title: '首頁',    // 菜單標題
            icon: 'el-icon-s-home',  // 圖標
            hasSubMenu: false, // 是否包含子菜單,false 沒有子菜單;true 有子菜單

        },
        children:[
            {
                path: '/index',
                component: Index
            }
        ]
    },
    // 員工管理
    {
        path: '/employee',
        name: 'employee',
        component: MenuIndex,
        redirect: '/employee/employeeStatistics', 
        meta: {
            title: '員工管理',    // 菜單標題
            icon: 'el-icon-user-solid',  // 圖標
            hasSubMenu: true,   // 是否包含子菜單
        },
        children: [
            // 員工統計
            {
                path: 'employeeStatistics',
                name: 'employeeStatistics',
                meta: {
                    title: '員工統計',    // 菜單標題,
                    hasSubMenu: false    // 是否包含子菜單
                },
                component: EmployeeStatistics,
            },
            // 員工管理(增刪改查)
            {
                path: 'employeeManage',
                name: 'employeeManage',
                meta: {
                    title: '員工管理',    // 菜單標題
                    hasSubMenu: false    // 是否包含子菜單
                },
                component: EmployeeManage
            }
        ]
    },
    // 考勤管理
    {
        path: '/attendManage',
        name: 'attendManage',
        component: MenuIndex,
        redirect: '/attendManage/attendStatistics',
        meta: {
            title: '考勤管理',    // 菜單標題
            icon: 'el-icon-s-claim',  // 圖標
            hasSubMenu: true, // 是否包含子節點,false 沒有子菜單;true 有子菜單
        },
        children:[
            // 考勤統計
            {
                path: 'attendStatistics',
                name: 'attendStatistics',
                meta: {
                    title: '考勤統計',    // 菜單標題   
                    hasSubMenu: false    // 是否包含子菜單               
                },
                component: AttendStatistics,
            },
            // 考勤列表
            {
                path: 'attendList',
                name: 'attendList',
                meta: {
                    title: '考勤列表',    // 菜單標題   
                    hasSubMenu: false    // 是否包含子菜單                 
                },
                component: AttendList,
            },
            // 異常管理
            {
                path: 'exceptManage',
                name: 'exceptManage',
                meta: {
                    title: '異常管理',    // 菜單標題  
                    hasSubMenu: false    // 是否包含子菜單                  
                },
                component: ExceptManage,
            }
        ]
    },
    // 工時管理
    {
        path: '/timeManage',
        name: 'timeManage',
        component: MenuIndex,
        redirect: '/timeManage/timeStatistics',
        meta: {
            title: '工時管理',    // 菜單標題
            icon: 'el-icon-message-solid',  // 圖標
            hasSubMenu: true, // 是否包含子菜單,false 沒有子菜單;true 有子菜單
        },
        children: [
            // 工時統計
            {
                path: 'timeStatistics',
                name: 'timeStatistics',
                meta: {
                    title: '工時統計',    // 菜單標題
                    hasSubMenu: false    // 是否包含子菜單        
                },
                component: TimeStatistics
            },
            // 工時列表
            {
                path: 'timeList',
                name: 'timeList',
                component: TimeList,
                meta: {
                    title: '工時列表',    // 菜單標題
                    hasSubMenu: true    // 是否包含子菜單        
                },
                children: [
                    {
                        path: 'options1',
                        meta: {
                            title: '選項一',    // 菜單標題
                            hasSubMenu: false    // 是否包含子菜單        
                        },
                    },
                    {
                        path: 'options2',
                        meta: {
                            title: '選項二',    // 菜單標題
                            hasSubMenu: false    // 是否包含子菜單        
                        },
                    },
                ]
            }
        ]
    },
];
export default new Router({
    routes
})

在這段代碼的最開始部分,我們引入了需要使用的組件,接着就對路由進行了配置。

此處使用了直接引入組件的方式,項目開發中不推薦這種寫法,應該使用懶加載的方式

路由配置除了最基礎的pathcomponent以及children之外,還配置了一個meta數據項。

meta: {
    title: '工時管理',    // 菜單標題
    icon: 'el-icon-message-solid',  // 圖標
    hasSubMenu: true, // 是否包含子節點,false 沒有子菜單;true 有子菜單
}

meta數據包含的配置有菜單標題(title)、圖標的類名(icon)和是否包含子節點(hasSubMenu)。

根據titleicon這兩個配置項,可以展示當前菜單的標題圖標

hasSubMenu表示當前的菜單項是否有子菜單,如果當前菜單包含有子菜單(hasSubMenutrue),那當前菜單對應的標籤元素就是el-submenu;否則當前菜單對應的菜單標籤元素就是el-menu-item

是否包含子菜單是一個非常關鍵的邏輯,我在實現的時候是直接將其配置到了meta.hasSubMenu這個參數裏面。

根據路由實現多級菜單

路由配置完成后,我們就需要根據路由實現菜單了。

獲取路由配置

既然要根據路由配置實現多級菜單,那第一步就需要獲取我們的路由數據。這裏我使用簡單粗暴的方式去獲取路由配置數據:this.$router.options.routes

這種方式也不太適用日常的項目開發,因為無法在獲取的時候對路由做進一步的處理,比如權限控制

我們在組件加載時打印一下這個數據。

// 代碼位置:src/menu/leftMenu.vue
 mounted(){
    console.log(this.$router.options.routes);
}

打印結果如下。

可以看到這個數據就是我們在router.js中配置的路由數據。

為了方便使用,我將這個數據定義到計算屬性中。

// 代碼位置:src/menu/leftMenu.vue
computed: {
    routesInfo: function(){
        return this.$router.options.routes;
    }
}

一級菜單

首先我們來實現一級菜單

主要的邏輯就是循環路由數據routesInfo,在循環的時候判斷當前路由route是否包含子菜單,如果包含則當前菜單使用el-submenu實現,否則當前菜單使用el-menu-item實現。

<!-- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <!--  一級菜單 -->
    <!--  循環路由數據  -->
    <!--  判斷當前路由route是否包含子菜單  -->
    <el-submenu 
        v-for="route in routesInfo" 
        v-if="route.meta.hasSubMenu"
        :index="route.path">
        <template slot="title">
            <i :class="route.meta.icon"></i>
            <span slot="title">{{route.meta.title}}</span>
        </template>
    </el-submenu>
    <el-menu-item :index="route.path" v-else> 
        <i :class="route.meta.icon"></i>
        <span slot="title">{{route.meta.title}}</span>
    </el-menu-item>
</el-menu>

結果:

可以看到,我們第一級菜單已經生成了,員工管理考勤管理工時管理這三個菜單是有子菜單的,所以會有一個下拉按鈕。

不過目前點開是沒有任何內容的,接下來我們就來實現這三個菜單下的二級菜單

二級菜單

二級菜單的實現和一級菜單的邏輯是相同的:循環子路由route.children,在循環的時候判斷子路由childRoute是否包含子菜單,如果包含則當前菜單使用el-submenu實現,否則當前菜單使用el-menu-item實現。

那話不多說,直接上代碼。

<!-- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <!--  一級菜單 -->
    <!--  循環路由數據  -->
    <!--  判斷當前路由route是否包含子菜單  -->
    <el-submenu 
        v-for="route in routesInfo" 
        v-if="route.meta.hasSubMenu"
        :index="route.path">
        <template slot="title">
            <i :class="route.meta.icon"></i>
            <span slot="title">{{route.meta.title}}</span>
        </template>
        <!-- 二級菜單 -->
        <!-- 循環子路由`route.children` -->
        <!-- 循環的時候判斷子路由`childRoute`是否包含子菜單 -->
        <el-submenu 
            v-for="childRoute in route.children" 
            v-if="childRoute.meta.hasSubMenu"
            :index="childRoute.path">
            <template slot="title">
                <i :class="childRoute.meta.icon"></i>
                <span slot="title">{{childRoute.meta.title}}</span>
            </template>
        </el-submenu>
        <el-menu-item :index="childRoute.path" v-else> 
            <i :class="childRoute.meta.icon"></i>
            <span slot="title">{{childRoute.meta.title}}</span>
        </el-menu-item>
    </el-submenu>
    <el-menu-item :index="route.path" v-else> 
        <i :class="route.meta.icon"></i>
        <span slot="title">{{route.meta.title}}</span>
    </el-menu-item>
</el-menu>

結果如下:

可以看到二級菜單成功實現。

三級菜單

三級菜單就不用多說了,和一級二級邏輯相同,這裏還是直接上代碼。

<!-- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <!--  一級菜單 -->
    <!--  循環路由數據  -->
    <!--  判斷當前路由route是否包含子菜單  -->
    <el-submenu 
        v-for="route in routesInfo" 
        v-if="route.meta.hasSubMenu"
        :index="route.path">
        <template slot="title">
            <i :class="route.meta.icon"></i>
            <span slot="title">{{route.meta.title}}</span>
        </template>
        <!-- 二級菜單 -->
        <!-- 循環子路由`route.children` -->
        <!-- 循環的時候判斷子路由`childRoute`是否包含子菜單 -->
        <el-submenu 
            v-for="childRoute in route.children" 
            v-if="childRoute.meta.hasSubMenu"
            :index="childRoute.path">
            <template slot="title">
                <i :class="childRoute.meta.icon"></i>
                <span slot="title">{{childRoute.meta.title}}</span>
            </template>
            <!-- 三級菜單 -->
            <!-- 循環子路由`childRoute.children` -->
            <!-- 循環的時候判斷子路由`child`是否包含子菜單 -->
            <el-submenu 
                v-for="child in childRoute.children" 
                v-if="child.meta.hasSubMenu"
                :index="child.path">
                <template slot="title">
                    <i :class="child.meta.icon"></i>
                    <span slot="title">{{child.meta.title}}</span>
                </template>
            </el-submenu>
            <el-menu-item :index="child.path" v-else> 
                <i :class="child.meta.icon"></i>
                <span slot="title">{{child.meta.title}}</span>
            </el-menu-item>
        </el-submenu>
        <el-menu-item :index="childRoute.path" v-else> 
            <i :class="childRoute.meta.icon"></i>
            <span slot="title">{{childRoute.meta.title}}</span>
        </el-menu-item>
    </el-submenu>
    <el-menu-item :index="route.path" v-else> 
        <i :class="route.meta.icon"></i>
        <span slot="title">{{route.meta.title}}</span>
    </el-menu-item>
</el-menu>

可以看到工時列表下的三級菜單已經显示了。

總結

此時我們已經結合路由配置實現了這個動態的菜單。

不過這樣的代碼在邏輯上相關於三層嵌套for循環,對應的是我們有三層的菜單。

假如我們有四層五層甚至更多層的菜單時,那我們還得在嵌套更多層for循環。很顯然這樣的方式暴露了前面多層for循環的缺陷,所以我們就需要對這樣的寫法進行一個改進。

遞歸實現動態菜單

前面我們一直在說一級二級三級菜單的實現邏輯都是相同的:循環子路由,在循環的時候判斷子路由是否包含子菜單,如果包含則當前菜單使用el-submenu實現,否則當前菜單使用el-menu-item實現。那這樣的邏輯最適合的就是使用遞歸去實現。

所以我們需要將這部分共同的邏輯抽離出來作為一個獨立的組件,然後遞歸的調用這個組件。

邏輯拆分

<!-- src/menu/menuItem.vue -->
<template>
    <div>
        <el-submenu 
            v-for="child in route" 
            v-if="child.meta.hasSubMenu"
            :index="child.path">
            <template slot="title">
                <i :class="child.meta.icon"></i>
                <span slot="title">{{child.meta.title}}</span>
            </template>
        </el-submenu>
        <el-menu-item :index="child.path" v-else> 
            <i :class="child.meta.icon"></i>
            <span slot="title">{{child.meta.title}}</span>
        </el-menu-item>
    </div>
</template>
<script>
export default {
    name: 'MenuItem',
    props: ['route']
}
</script>

需要注意的是,這次抽離出來的組件循環的時候直接循環的是route數據,那這個route數據是什麼呢。

我們先看一下前面三層循環中循環的數據源分別是什麼。

為了看得更清楚,我將前面代碼中一些不相關的內容進行了刪減。

<!-- src/menu/leftMenu.vue -->

<!--  一級菜單 -->
<el-submenu 
    v-for="route in routesInfo" 
    v-if="route.meta.hasSubMenu">
    <!-- 二級菜單 -->
    
    <el-submenu 
        v-for="childRoute in route.children" 
        v-if="childRoute.meta.hasSubMenu">
        
        <!-- 三級菜單 -->
        <el-submenu 
            v-for="child in childRoute.children" 
            v-if="child.meta.hasSubMenu">
          
        </el-submenu>
    
    </el-submenu>
</el-submenu>

從上面的代碼可以看到:

一級菜單循環的是`routeInfo`,即最初我們獲取的路由數據`this.$router.options.routes`,循環出來的每一項定義為`route`

二級菜單循環的是`route.children`,循環出來的每一項定義為`childRoute`

三級菜單循環的是`childRoute.children`,循環出來的每一項定義為`child`

按照這樣的邏輯,可以發現二級菜單三級菜單循環的數據源都是相同的,即前一個循環結果項的children,而一級菜單的數據來源於this.$router.options.routes

前面我們抽離出來的menuItem組件,循環的是route數據,即不管是一層菜單還是二層三層菜單,都是同一個數據源,因此我們需要統一數據源。那當然也非常好實現,我們在調用組件的時候,為組件傳遞不同的值即可。

代碼實現

前面公共組件已經拆分出來了,後面的代碼就非常好實現了。

首先是抽離出來的meunItem組件,實現的是邏輯判斷以及遞歸調用自身

<!-- src/menu/menuItem.vue -->
<template>
    <div>
        <el-submenu 
            v-for="child in route" 
            v-if="child.meta.hasSubMenu"
            :index="child.path">
            <template slot="title">
                <i :class="child.meta.icon"></i>
                <span slot="title">{{child.meta.title}}</span>
            </template>
            <!--遞歸調用組件自身 -->
            <MenuItem :route="child.children"></MenuItem>
        </el-submenu>
        <el-menu-item :index="child.path" v-else> 
            <i :class="child.meta.icon"></i>
            <span slot="title">{{child.meta.title}}</span>
        </el-menu-item>
    </div>
</template>
<script>
export default {
    name: 'MenuItem',
    props: ['route']
}
</script>

接着是leftMenu組件,調用menuIndex組件,傳遞原始的路由數據routesInfo

<!-- src/menu/leftMenu.vue -->
<template>
    <div id="left-menu">
        <el-menu 
            :default-active="$route.path" 
            class="el-menu-vertical-demo"
            :collapse="false">
            <MenuItem :route="routesInfo"></MenuItem>
        </el-menu>
    </div>
</template>
<script>
import MenuItem from './menuItem'
export default {
    name: 'LeftMenu',
    components: { MenuItem }
}
</script>
<style lang="scss">
    // 使左邊的菜單外層的元素高度充滿屏幕
    #left-container{
        position: absolute;
        top: 100px;
        bottom: 0px;
        // 使菜單高度充滿屏幕
        #left-menu, .el-menu-vertical-demo{
            height: 100%;
        }
    }
</style>

最終的結果這裏就不展示了,和我們需要實現的結果是一致的。

功能完善

到此,我們結合路由配置實現了菜單欄這個功能基本上已經完成了,不過這是一個缺乏靈魂的菜單欄,因為沒有設置菜單的跳轉,我們點擊菜單欄還無法路由跳轉到對應的組件,所以接下來就來實現這個功能。

菜單跳轉的實現方式有兩種,第一種是NavMenu組件提供的跳轉方式。

第二種是在菜單上添加router-link實現跳轉。

那本次我選擇的是第一種方式實現跳轉,這種實現方式需要兩個步驟才能完成,第一步是啟用el-menu上的router;第二步是設置導航的index屬性。

那下面就來實現這兩個步驟。

啟用el-menu上的router

<!-- src/menu/leftMenu.vue -->
<!-- 省略其餘未修改代碼-->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo"
    router
    :collapse="false">
    <MenuItem :route="routesInfo">
    </MenuItem>
</el-menu>

設置導航的index屬性

首先我將每一個菜單標題對應需要設置的index屬性值列出來。

index值對應的是每個菜單在路由中配置的path

首頁        

員工管理    
    員工統計  index="/employee/employeeStatistics"
    員工管理  index="/employee/employeeManage"

考勤管理  
    考勤統計  index="/attendManage/attendStatistics"
    考勤列表  index="/attendManage/attendList"
    異常管理  index="/attendManage/exceptManage"

員工統計  
    員工統計  index="/timeManage/timeStatistics"
    員工統計  index="/timeManage/timeList"
        選項一  index="/timeManage/timeList/options1"
        選項二  index="/timeManage/timeList/options2"

接着在回顧前面遞歸調用的組件,導航菜單的index設置的是child.path,為了看清楚child.path的值,我將其添加菜單標題的右側,讓其显示到界面上。

<!-- src/menu/menuItem.vue -->
<!-- 省略其餘未修改代碼-->
<el-submenu 
    v-for="child in route" 
    v-if="child.meta.hasSubMenu"
    :index="child.path">
    <template slot="title">
        <i :class="child.meta.icon"></i>
        <span slot="title">{{child.meta.title}} | {{child.path}}</span>
    </template>
    <!--遞歸調用組件自身 -->
    <MenuItem :route="child.children"></MenuItem>
</el-submenu>
<el-menu-item :index="child.path" v-else> 
    <i :class="child.meta.icon"></i>
    <span slot="title">{{child.meta.title}} | {{child.path}}</span>
</el-menu-item>

同時將菜單欄的寬度由200px設置為400px

<!-- src/menu/menuIndex.vue -->
<!-- 省略其餘未修改代碼-->
<el-aside width="400px">
    <LeftMenu></LeftMenu>                    
</el-aside>

然後我們看一下效果。

可以發現,child.path的值就是當前菜單在路由中配置path值(router.js中配置的path值)。

那麼問題就來了,前面我們整理了每一個菜單標題對應需要設置的index屬性值,就目前來看,現在設置的index值是不符合要求的。不過仔細觀察現在菜單設置的index值和正常值是有一點接近的,只是缺少了上一級菜單的path值,如果能將上一級菜單path值和當前菜單的path值進行一個拼接,就能得到正確的index值了。

那這個思路實現的方式依然是在遞歸時將當前菜單的path作為參數傳遞給menuItem組件。

<!-- src/menu/menuIndex.vue -->
<!--遞歸調用組件自身 -->
<MenuItem 
    :route="child.children" 
    :basepath="child.path">
</MenuItem>

將當前菜單的path作為參數傳遞給menuItem組件之後,在下一級菜單實現時,就能拿到上一級菜單的path值。然後組件中將basepath的值和當前菜單的path值做一個拼接,作為當前菜單的index值。

<!-- src/menu/menuIndex.vue -->
<el-menu-item :index="getPath(child.path)" v-else> 

</el-menu-item>
<script>
import path from 'path'
export default {
    name: 'MenuItem',
    props: ['route','basepath'],
    data(){
        return {
           
        }
    },
    methods :{
        // routepath 為當前菜單的path值
        // getpath: 拼接 當前菜單的上一級菜單的path 和 當前菜單的path
        getPath: function(routePath){
            return path.resolve(this.basepath, routePath);
        }
    }
}
</script>

再看一下界面。

我們可以看到二級菜單的index值已經沒問題了,但是仔細看,發現工時管理工時列表下的兩個三級菜單index值還是有問題,缺少了工時管理這個一級菜單的path

那這個問題是因為我們在調用組件自身是傳遞的basepath有問題。

<!--遞歸調用組件自身 -->
<MenuItem 
    :route="child.children" 
    :basepath="child.path">
</MenuItem>

basepath傳遞的只是上一級菜單的path,在遞歸二級菜單時,index的值是一級菜單的path值+二級菜單的path值;那當我們遞歸三級菜單時,index的值就是二級菜單的path值+三級菜單的path值,這也就是為什麼工時管理-工時列表下的兩個三級菜單index值存在問題。

所以這裏的basepath值在遞歸的時候應該是累積的,而不只是上一級菜單的path值。因此藉助遞歸算法的優勢,basepath的值也需要通過getPath方法進行處理。

<MenuItem 
    :route="child.children" 
    :basepath="getPath(child.path)">
</MenuItem>

最終完整的代碼如下。

<!-- src/menu/menuIndex.vue -->
<template>
    <div>
        <el-submenu 
            v-for="child in route" 
            v-if="child.meta.hasSubMenu"
            :key="child.path"
            :index="getPath(child.path)">
            <template slot="title">
                <i :class="child.meta.icon"></i>
                    <span slot="title">
                        {{child.meta.title}}
                    </span>
            </template>
            <!--遞歸調用組件自身 -->
            <MenuItem 
                :route="child.children" 
                :basepath="getPath(child.path)">
            </MenuItem>
        </el-submenu>
        <el-menu-item :index="getPath(child.path)" v-else> 
            <i :class="child.meta.icon"></i>
            <span slot="title">
                   {{child.meta.title}}
            </span>
        </el-menu-item>
        
    </div>
</template>
<script>
import path from 'path'
export default {
    name: 'MenuItem',
    props: ['route','basepath'],
    data(){
        return {
           
        }
    },
    methods :{
        // routepath 為當前菜單的path值
        // getpath: 拼接 當前菜單的上一級菜單的path 和 當前菜單的path
        getPath: function(routePath){
            return path.resolve(this.basepath, routePath);
        }
    }
}
</script>

刪除其餘用來調試的代碼

最終效果

文章的最後呢,將本次實現的最終效果在此展示一下。

選項一選項二這兩個三級菜單在路由配置中沒有設置component,這兩個菜單隻是為了實現三級菜單,在最後的結果演示中,我已經刪除了路由中配置的這兩個三級菜單

此處在leftMenu組件中為el-menu開啟了unique-opened

menuIndex組件中,將左側菜單欄的寬度改為200px

關於

作者

小土豆biubiubiu

一個努力學習的前端小菜鳥,知識是無限的。堅信只要不停下學習的腳步,總能到達自己期望的地方

同時還是一個喜歡小貓咪的人,家裡有一隻美短小母貓,名叫土豆

博客園

https://www.cnblogs.com/HouJiao/

掘金

https://juejin.im/user/58c61b4361ff4b005d9e894d

微信公眾號

土豆媽的碎碎念

微信公眾號的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章

歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術

作者寄語

小小總結,歡迎大家指導~

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

突變冠狀病毒株恐降疫苗效力 丹麥全面撲殺水貂

摘錄自2020年11月5日中央社報導

丹麥總理佛瑞德里克森今(4日)表示,在水貂身上的突變冠狀病毒傳染給人類之後,對未來可能研發出來的疫苗造成風險,因此境內多達1700萬隻水貂將遭全面撲殺。

丹麥為全世界最大的水貂毛皮生產地,雖然當局自6月以來一再撲殺遭感染的水貂,但境內水貂農場疫情仍持續蔓延。

佛瑞德里克森(Mette Frederiksen)在記者會上表示,衛生當局發現人體病毒株與水貂身上的病毒株,顯示對抗體的敏感度已降低,有可能影響未來疫苗的效力。

國際新聞
丹麥
水貂

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化