看到賺到!重讀vue2.0風格指南,我整理了這些關鍵規則

又是一個陽光明媚,風和日麗的週末,有人陪女神去逛街,有人陪女神去看電影,小編卻默默的拿出電腦。哈哈哈,不是小編屌絲,女神正坐在旁邊玩手機(感受不是屌絲纔怪)。javascript

這兩天小編重讀了一遍vue2.0官網的風格指南,整理了這九條關鍵規則。css

v-for設置鍵值

提到v-for須要設置鍵值,許多人第一反應會從diff算法的角度去講緣由,我更喜歡舉一個例子來演示一下緣由html

假設有這樣的一個頁面,頁面的列表是經過遍歷數組得來的,以下圖所示前端

示例圖片

示例代碼以下vue

<!--模板部分-->
<div id="app">
    <div v-for="item in arr">
        {{item}}
        <input/>
    </div>

    <button @click="deleteData">刪除第二個元素</button>
</div>
複製代碼
// js 部分
new Vue({
    el: '#app',
    data() {
        return {
            arr: [1,2,3]
        }
    },
    methods:{
        deleteData() {
            this.arr.splice(1,1)
        }
    }
})
複製代碼

如今須要刪除第二個元素。下面咱們分別在渲染列表是 不使用key,使用索引做爲key, 使用惟一值id做爲key,看三種場景刪除第二個元素以後的效果java

能夠看到,不使用key,刪除第二個元素以後,輸入框前面的數字顯示正確的,可是數字3後面的輸入框的內容顯示錯了,應該顯示 我是第三個

  • v-for 使用索引做爲key 點擊查看代碼演示
    能夠看到,使用索引做爲key以後,與不使用key的效果同樣,刪除第二個元素以後,輸入框前面的數字顯示正確的,可是數字3後面的輸入框的內容顯示錯了,應該顯示 我是第三個
  • v-for 使用惟一值id做爲key 點擊查看代碼演示

使用id做爲key,顯示正確算法

爲何v-for須要設置key,緣由很簡單。 對比數組 [1,2,3]和[1,3],咱們很容易發現刪掉了2,可是計算機不是這樣的邏輯數組

  1. 計算機對比新舊數組,發現1===1,保持不變
  2. 而後再對比2,發現2變成了3,那麼就把2修改成3,原來第二行的元素均可以複用,只把數字改一下就能夠了
  3. 而後在對比3與undefined,發現3被刪了,索引把第三行的元素刪掉

那麼爲何不能用索引做爲key呢? 當刪掉[1,2,3]中的2以後,數組的長度由3變成了2,那麼原來數字3的索引就變成了數字2的索引了。bash

  1. 計算機對比key爲0的值,發現都是1,保持不變
  2. 計算機對比key爲1的值,發現從2變成了3,元素複用, 修改元素上面的文字
  3. 計算機對比key爲2的值,發現被刪掉了,因此刪掉第三行元素

而對於使用id做爲key,那麼每條數據都有了惟一的標識,當刪掉[{id:'1',value: 1},{id: '2',value: 2}, {id: '3', value:3}]中的第二個,整個過程以下app

  1. 計算機取出新數據第一項的id,而後在原來數據裏面尋找,發現存在相同id的數據,並且數據沒有變化,因此保持不變
  2. 計算機繼續取第二項的id,發現是3,而後從原來數據裏面也找到了3,因此3保留
  3. 這時候舊數據裏面剩了id爲2的數據,而新的裏面沒有了,因此刪掉。

沒得問題嘛!!!!

模板中的複雜邏輯使用計算屬性代替

vue在模板可使用表達式是很是方便的,但表達式在設計之初是爲了進行簡單邏輯處理的,若是在模板中使用太多或太複雜的邏輯,會讓模板的可讀性和可維護性變得不好,整個模板顯得很臃腫。

\color{red}{Bad}

<!--v-if使用了一連串的條件判斷,可讀性比較差-->
 <button v-if=" user.roles && user.roles.includes('Workflowbeheer') && data.userId === user.id && data.status === 1 " >
    刪除
  </button>
複製代碼

\color{green}{Good}

<template>
  <button v-if="deletable">
    刪除
  </button>
</template>
<script> export default { computed: { // 判斷是否能夠刪除 deletable() { const { data, user } = this // 若是當前用戶不是流程管理員,則不能編輯 if (user.roles && user.roles.includes('Workflowbeheer')) { // 若是當前用戶爲流程發起者且狀態爲未啓動,則能夠刪除 return data.userId === user.id && data.status === 1 } return false } } } </script>
複製代碼

避免v-for與v-if混用

\color{red}{永遠不要將v-for和v-if同時用在同一個元素上。}

在開發vue項目中,你們可能會遇到這樣的代碼

<ul>
    <li v-for="item in list" v-if="item.visible" :key="item.id">
        {{ item.name }}
    </li>
 </ul>
複製代碼

若是在項目中啓用了eslint,則可能會看到下面這樣的異常提示(須要啓用eslint vue/no-use-v-if-with-v-for 規則)

The 'list' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.
複製代碼

在vue處理指令的時候,v-forv-if會有更高的優先級,那麼上述的代碼用js能夠模擬爲

list.map(item => {
    if(item.visible) {
        return item.name
    }
})
複製代碼

經過上述代碼能夠看到,即便大部分數據的visible都是false,也會將整個list所有遍歷一次。若是每一次都須要遍歷整個數組,將會影響速度,尤爲是當之須要渲染很小一部分的時候。

對於上述的問題,可使用計算屬性來處理

<ul>
    <li v-for="item in getList" :key="item.id">
        {{ item.name }}
    </li>
 </ul>
 
 computed: {
    getList() {
      return this.list.filter(item => {
        return item.visible
      })
    }
  }
複製代碼

經過上述的代碼,咱們能夠得到如下好處

  • 過濾後的列表只會在 list 數組發生相關變化時才被從新運算,過濾更高效。
  • 使用 v-for="item in list" 以後,咱們在渲染的時候只遍歷須要顯示的數據,渲染更高效。
  • 解耦渲染層的邏輯,可維護性比較高。

儘可能使用私有屬性/方法

在開發vue組件的時候,咱們能夠經過組件的ref來調用組件對外提供的方法,可是一個組件內部有些方法是內部私有的,不該該被外部調用,但實際上js中並無像其餘語言同樣有私有方法(好比javaprivate),因此在js中通常約定使用_開頭的屬性或者方法表示私有的。

{
    // 公共的
    selectRows(rows) {},
    // 私有的 外部雖然能夠調用到,可是由於是`_`開頭,因此按照約定不該該去調用
    _select(rows){}
 }
複製代碼

在vue中定義私有屬性/方法又與js常規約定有所不一樣。在Vue內部,已經使用了_開頭去定義Vue原型上面的私有屬性/方法,若是在組件內上面繼續使用_開頭去定義私有屬性/方法可能會出現覆蓋實例上面屬性/方法的狀況,好比下面這種狀況:

methods: {
    // 初始化組件的數據方法
  _init() {
    fetch().then(data => {
      
    })
  }
}
複製代碼

上面的代碼看似沒有問題,實際上運行的時候會報錯,由於_init方法會覆蓋Vue.prototype上面的同名方法,以下圖爲Vue原型鏈的方法,第一個即是_init

在Vue2.0風格指南中,建議使用$_來定義私有方法,能夠確保不會和Vue自身發生衝突。修改上例爲

methods: {
    // 初始化組件的數據方法
  $_init() {
    fetch().then(data => {
      
    })
  }
}
複製代碼

組件數據必須是一個函數,並返回一個對象

在說爲何組件的數據必須返回一個函數以前,咱們先來了解一下js中的基本類型與引用類型。

  1. 基本類型

在es2020發佈了bigint類型以後,js中的基本類型一種包含七種,分別是

  • string 字符類型
  • number 數值類型
  • boolean 布爾類型
  • undefined
  • null
  • Symbol
  • Bigint

基本類型的特色包括

  • 基本類型的值是存放到棧內存裏面的
  • 基本類型的比較是它們的值的比較
  • 基本類型的值是不可變的,對值的修改會在棧內存中開闢新的空間
  • 基本類型上面不能掛載新的屬性
let a = 2
let b = a
// 對a的值的修改,會在棧內存開闢新的空間,因此不會影響到b的值
a = 3
// 輸出 3 2
console.log(a,b)

// 不能給基本類型上面掛載新的屬性
a.testProp = '掛載的屬性'
// 輸出undefined
console.log(a.testProp)
複製代碼
  1. 引用類型 在js中,除了八種基本類型,其餘都屬於引用類型,像Object,Array,Function,RegExp,Date等等

引用類型的特色包括

  • 引用類型的值保存在堆內存中,而引用保存到棧內存中
  • 引用類型的值是按引用訪問的
  • 引用類型的值是可變的(在堆內存中直接修改)
  • 引用類型上面能夠掛載新的屬性
let obj1 = {a: 1, b: 2}
let obj2 = obj1
// 由於引用類型的值是保存到堆內存的,obj1與obj2引用的是同一塊堆內存空間,因此對obj1的值進行
// 修改,會直接影響到obj2的值
obj1.a = 3
// 輸出 3
console.log(obj2.a)

// 掛載新的屬性
obj1.testProp = '掛載的新屬性值'
// 輸出 "掛載的新屬性值"
console.log(obj1.testProp)
複製代碼

經過上面的對比,我想你們其實也清楚了爲何vue的數據必須返回一個函數了。

假設咱們如今開發了一個組件,組件上面的data是一個普通的對象,那麼當咱們實例化多個組件的時候,全部的實例將共享引用同一個數據對象,任何一個實例對數據的修改都會影響到其餘實例。而將組件上面的數據定義爲一個函數以後,當實例化多個組件的時候,每一個實例經過調用 data 函數,從而返回初始數據的一個全新副本數據對象,這時候就避免了對象引用。

爲組件樣式設置做用域

在前端發展突飛猛進的今天,全部的一切都在飛速的發展,前端項目規模愈來愈大,而css做爲一個只有全局做用域的語言,樣式衝突會帶來不少麻煩。JS語言模塊已經標準化,CSS仍是在不斷探索,同時這也是一個急需解決的問題。如今人們提出了許多爲css添加做用域的解決方法,好比BEM樣式規範,好比css module。

在Vue中,使用了經過給元素添加scoped attribute的方式爲css添加做用域,具體代碼以下

<template>
  <button class="button">按鈕</button>
</template>
<!--給style標籤添加scoped屬性-->
<style scoped> .button { width: 50px; height: 40px; } </style>
複製代碼

編譯以後的結果以下

<!--html 添加了一個新屬性 data-v-039c5b43,對於組件內的全部元素,都會添加同一個屬性data-v-039c5b43,這樣保證了同一個組件內全部元素都在同一個做用域內-->
<button data-v-039c5b43="" class="button">按鈕</button>
複製代碼
/*經過爲樣式添加屬性選擇器,去限制樣式的做用域*/
.button[data-v-039c5b43] {
    width: 50px;
    height: 40px;
}
複製代碼

雖然咱們建議爲組件樣式添加做用域,可是不必定必須使用vue提供的attribute scoped,對於組件庫之類可能須要在外部覆蓋樣式,若是使用attribute scoped,由於屬性名不肯定,且樣式權重較高,致使樣式覆蓋很困難.

這時候更建議使用相似BEM之類的命名規範來約束,這讓覆寫內部樣式更容易,使用了常人可理解的 class 名稱且沒有過高的選擇器優先級,並且不太會致使衝突。好比element ui 和 vant 均使用了BEM

將複雜頁面拆分紅多個多個組件文件

你有沒有見過一個Vue文件裏面有一大坨密密麻麻的模板代碼,模板代碼裏面還加載了大量的v-if,v-for,v-show之類的指令,我不知道你看到以後感受怎麼樣,對於小編來講,這無疑是地獄,各類邏輯耦合到一塊兒,改bug比蜀道還要難 對於一個複雜的頁面,咱們建議將頁面按照模塊/功能進行拆分,而後寫成多個小的,單一的組件,而後在主入口文件中引用。好比,對於一個複雜的頁面,咱們能夠拆分紅

header.vue main.vue footer.vue

三個文件,而後在三個文件內完成各自的邏輯,最後經過將三個組件都引入主入口文件,來實現頁面的拼裝。 這樣作的好處包括

  • 將複雜的邏輯進行解耦,代碼結構更清晰,邏輯更簡單,可讀性更強
  • 對功能進行組件化抽取抽象,組件複用變得更簡單
  • 便於多人協做開發,不一樣的人能夠同時開發一個複雜的頁面

prop應該儘可能詳細

對比下面的兩段代碼

// 第一段
export default {
  props:['status','data']
}
// 第二段
export default {
  props:{
    status: {
      type: Boolean,
      default: true
    },
    data:{
      type: Array,
      required: true
    }
  }
}
複製代碼

對比上面兩段代碼,經過第二段代碼咱們能夠很清楚的知道組件須要什麼屬性,屬性的類型是什麼,默認值是什麼,是不是必須的,這樣作的好處包括:

  • 詳細的定義了屬性的各方面信息,因此很容易看懂組件的用法;
  • 在開發環境下,若是向一個組件提供格式不正確的 prop,Vue將會獲得警告,能夠更快的發現潛在的問題。

組件名應該由多個單詞組成

對於組件名應該由多個單詞組成的必要性,我想到了本身曾經見過的一段代碼

<header class="header">
    <!--欄目-->
    <ul>
      <li>首頁</li>
      <li>關於</li>
    </ul>
</header>
複製代碼

看到這段代碼,而後感受很正常,沒啥毛病,而後我看了一眼界面,誒,爲何header左側有一個logo呢?我笑着說,這必定是樣式裏面加的咯,而後看了一眼樣式,wtf,什麼鬼,樣式裏面也沒有加啊,這是怎麼作到的,好神奇。後來就看到了這樣的一段代碼

import Header from '@/components/header'
export default {
    components: {
        Header
    }
}
複製代碼

個人四十米長大砍刀呢!!!!!!

爲何組件名應該又多個單詞組成,由於這樣作能夠避免跟現有的以及將來的 HTML 元素相沖突。更關鍵的是,這樣作不會被打,固然你也能夠作,祝你好運,(手動調皮)。

本文主要參考了:

cn.vuejs.org/v2/style-gu…

juejin.im/post/5edcc2…

感謝做者,如侵權當即刪除。若是您以爲本文對您有幫助,但願您能夠給小編一個贊,不勝感激。小手一讚,豔遇不斷,哈哈哈哈哈

相關文章
相關標籤/搜索