11 個高級 Vue 編碼技巧

英文 | https://betterprogramming.pub/advanced-vue-tricks-6e315347c378 

翻譯 | Web 前端開發

今天,我爲你帶來了一個系列精選的知識,以幫助你更快地構建 Vue 應用程序,同時,使它們更高效、更易於大規模管理。

這些高級技巧從何而來?

雖然,高級 Vue 開發人員會發現這些概念非常有用,但我已經詳細概述瞭如何完成每個概念,因此不會讓初學者掉隊!

如果你需要任何進一步的解釋,請在留言區給我留言,我將很樂意爲你提供幫助。如果你有自己的好方法?也可以隨時分享這些!

好的,讓我們深入瞭解好東西。

1、動態 SVG 組件

如果你像我一樣,喜歡手工製作你的應用程序 — 選擇獨特的 SVG 圖標更適合你的風格指南,並將它們與自定義動畫和樣式配對。

這樣做的問題是要更改 SVG 圖像填充的顏色 fill,你需要訪問模板中內聯的 SVG 代碼。根據 SVG 的大小,即使只有一兩個矢量圖像,這也會使你的模板代碼快速膨脹。

我已經測試了多種方法和包來訪問 fill 屬性,而不會炸燬我的模板,結果證明,添加 hover 或 active  CSS 狀態以更改 fill SVG 中的一個或多個填充屬性的最佳方法,實際上最有效的顯而易見的方法——讓它成爲一個組件!

所以,當我們需要這種功能時,而不是使用這個:

<img src="@/assets/images/myImg.svg" />

相反,我們將在 VSCode 中打開 .svg 圖像,然後複製圖像的 <svg~ 代碼:旁註:爲了確保正在查看正確的圖像代碼,我建議安裝一個名爲 Svg Preview 的 VSCode 擴展(如上所示)。這將打開圖像的側面板預覽,如果更改 SVG 代碼,該預覽也會更新。
現在,我們將此代碼粘貼到新組件的模板中。我建議將所有 SVG 圖標組件放入一個新文件夾中,並相應地命名它們 (components/SVG/IconMoon.vue) 以保持組織有序。一旦我們把它作爲一個組件,我們就可以使用 把它放到我們應用程序的任何其他組件或頁面中:在上面的示例中,當我將鼠標懸停在 SVG 上時,我只是在要更改的部分上設置一個類(此處稱爲 .inner),但我也可以直接訪問所有 SVG 的屬性,因此選項是無窮無盡的,可以使用此方法同時保持其他組件沒有 SVG 代碼膨脹。如果需要上述的一些擴展功能,你只需要創建一個自定義 SVG 組件。否則,可以像往常一樣簡單地使用它們,就在圖像的 src 中。除了動態圖標樣式和動畫之外,還可以傳遞道具來更改 SVG 的大小和其他方面(就像任何其他組件一樣)。如果你還不熟悉,Vue 文檔有一個很好的例子說明如何使用圖標來做到這一點。

2、使用 Vue-Router 數據實現更智能的導航鏈接

你可能沒有意識到,但是 Vue-Router 可以像任何其他數據存儲一樣使用。可以訪問應用程序的所有可用路由,添加元數據以使邏輯更智能,甚至可以自動填充導航欄、頁面麪包屑等內容!然而,這乍一看並不明顯,也不能像我們通常訪問路由的方式直接訪問。這是我們將添加到模板中的基本代碼:

v-for="(route, idx) in $router.options.routes.filter(
  (routeItem) => routeItem.name === $route.matched[0].name
)"

使用此 v-for,您可以直接在模板中訪問路由器樹的所有子路由和單個路由元數據。我最近在一個項目中使用它來生成動態側邊欄導航組件。我在路由器中的某些路由上設置了一個 showInSidebar 元數據屬性,我想隱藏在側邊欄中。我還能夠自動生成所有側邊欄鏈接,而無需對每個鏈接進行編碼。以下是我設置路由器路由的方法:客戶端還有一個額外的要求,他們不僅需要從路由器生成這些側邊欄路由,還需要從他們的 API 數據生成這些側邊欄路由。上述方法也以一種乾淨且可管理的方式解決了這個任務。我能夠控制如何直接從路由器顯示本地路由以及是否使用 API 提供的路由。我還用它來製作自動麪包屑以顯示用戶的路線歷史。在此下方(此處未顯示)我還有一個單獨的部分,允許側邊欄切換到使用從 API 發送的一組路由。爲了觸發它,我簡單地使用了一個 a v-if,如果它們存在就使用它們,否則它將恢復使用來自 vue-router 的路由。在我的 SideNavbar 組件模板中:你可能已經注意到了 exact-active-class 代碼:有了這個,如果路由器鏈接的目的地與當前路由匹配,Vue 會自動設置一個活動類。這是一個很好的技巧,可以繞過我們用來實現這一點的典型邏輯,並將其縮短爲 exact-active-class=”className”。我經常將它用於導航欄鏈接——它既減少了模板大小,又使事情變得更乾淨。

3、從子組件訪問父數據

有時,我們想從父級訪問數據,但又不想經歷傳遞 props 的麻煩。如果你只需要從 a 的數據對象中快速獲取一個值,你可以簡單地通過引用 parent 的數據對象中快速獲取一個值,你可以簡單地通過引用 parent 來完成:

// In parent 
data() {
  return {
    message: 'This is my message'
  }
}

// In child template
<div>{{ $parent.message }}</div> // <-- results in 'This is my message'

如果你想要在組件之間傳遞數據的更多好方法,Erik Hanchett 有一個很棒的視頻,地址:https://www.youtube.com/watch?v=rKWSj3zfBAs&t=46s,你可以去了解一下,還有一些其他選項。

4、簡化你的 :class 和 v-if 與邏輯. includes()

憑藉 v-directives 的所有功能,很容易忘記我們仍然可以在我們的模板中訪問純 JavaScript 的高級功能。例如,假設你想設置一個類,但僅當用戶位於三個特定路線中的任何一個時。當你第一次編寫此邏輯時,它可能如下所示:

:class="
  $route.name === 'Home'
  || $route.name === 'Gallery' 
  || $route.name === 'Profile' 
  ? 'classOnlyOnThesePages' 
  : ''
"

然後,你可能會學會像這樣縮短它:

:class="{ 
  'classOnlyOnThesePages' : 
  $route.name === 'Home'
  || $route.name === 'Gallery' 
  || $route.name === 'Profile'
}"

但最好的寫法絕對是這樣的:

:class="{ 
  'classOnlyOnThesePages' : 
  ['Home', 'Gallery', 'Profile'].includes($route.name)
}"

這不僅更具可讀性,而且以後也更容易擴展。

5、路線更改時滾動到頂部

當更改路線 ni 時,Vue 會保持在頁面上的當前位置。這有時很有用,但主要是麻煩。如果向下滾動一個長列表,然後轉到另一個頁面,滾動條將位於新頁面的底部,而不是期望的頂部。解決這個問題很簡單。只需在 app.js 文件中添加一個 watch: 在每次路由更改後觸發滾動到頂部:

// In App.vue
watch: {
    $route() {
      window.scrollTo(0, 0)
    }
  },

6、對 DRYer 代碼使用全局實用方法

幾乎每個 Vue 項目都有在應用程序的多個地方重用的邏輯。爲了保持我們的代碼 DRY(不要重複自己)和可管理,我們應該創建一個單獨的 utils.js 文件來保存這個重用的邏輯並且可以從任何地方訪問。注意:你可能認爲 Vuex 非常適合這種情況,但是除非,你想將實用程序方法的結果值存儲在 state 中,否則它真的不適合這種情況。在這種情況下,你只想從全局函數返回一個值,單獨的 utils.js 文件是與 Vue.prototype 配對的鍵(如下所示)。但是,如果你仍然想在實用程序中訪問 Vuex 狀態,可以這樣做:import store from '../store',然後執行
store.getters 或 store.state 訪問你的 Vuex 狀態的屬性。 所以,首先我們要設置我們的 utils.jsfile 並添加一個全局方法來將文本複製到用戶的剪貼板:

// import store from '../store' <-- To access your Vuex store
import Vue from 'vue' // <-- used for vue-toastification

class Utils {
  // Copy a string to user's clipboard
  copyToClipboard(text) {
    let copyText = document.createElement('input')
    document.body.appendChild(copyText)
    copyText.value = text
    copyText.select()
    document.execCommand('copy')
    document.body.removeChild(copyText)

    // Show toast on copy success
    // (using the vue-toastification package here)
    Vue.$toast.success('Copied address to clipboard: ' + text, {
      position: 'top-right',
      timeout: 3000
    })
  }
}

export default new Utils()

現在,我們有了我們的實用程序方法,我們只需要讓它在整個應用程序中都可以訪問。我們可以將它們導入到一個組件中並以這種方式使用它們,但我發現讓它們在全球範圍內可用會產生更清晰、更易讀的代碼,同時使事情更容易訪問。爲了使這些函數全局可用,我們將編輯我們的 main.js 文件。Vue 2 和 Vue 3 的設置略有不同,因此,請相應地選擇你的風格。在 Vue2 中

// Utils
import Utils from './utils/utils.js'

// Init Global Utils
Vue.prototype.$utils = Utils

在 Vue3 中

// Utils
import Utils from './utils/utils.js'   // <-- import file
const app = createApp(App)
 // Init Global Utils
 app.config.globalProperties.$utils = Utils   // <-- set globally
app.mount('#app')

現在我們可以通過簡單地使用以下內容在任何地方訪問我們的實用程序方法:

// In template
$utils.copyToClipboard(text)
// In methods
this.$utils.copyToClipboard(text)

7、檢測用戶是在桌面還是移動設備上

檢測用戶在哪個平臺上的方法經常變化。在評估了你可以執行此操作的多種方法後,我決定使用一個做得很好的包,並且會在這些因素髮生變化時保持更新。使用 npm i vue-mobile-detection --save 安裝 vue-mobile-detection。在你的 App.vue 文件中,添加一個 mount() 鉤子來在你的 Vuex 商店中存儲用戶的平臺。

 async mounted() {
    // Detect if user is on Desktop or Mobile platform, then save to store
    this.$isMobile()
      ? this.$store.commit('setPlatform''mobile')
      : this.$store.commit('setPlatform''desktop')
  },

如果你使用 Vuex,你現在可以爲上面的 $store.commit 創建一個 mutation,將值設置爲 state,然後使用 getter 訪問應用程序中任何地方的平臺值。 我最近在一個基於區塊鏈的項目中使用了它,在該項目中,瞭解用戶的平臺以觸發正確的區塊鏈錢包(瀏覽器擴展錢包或移動應用程序錢包)至關重要,並且它的作用非常吸引人。注意:如果你沒有使用 Vuex,你可以使用 localStorage.setItem 將此值存儲在用戶的 localStorage 中,然後使用 localStorage.getItem 在你的應用程序中的任何位置使用它。

// in App.vue inside of your mounted() hook:
const currentPlatform = this.$isMobile() ? ‘mobile’ : ‘desktop’
localStorage.setItem('platform', currentPlatform)

// Use it anywhere
localStorage.getItem(‘platform’)

8、當用戶按下 ENTER 時關注下一個表單輸入

表格是一個巨大的 PITA。一些軟件包大大減少了這種情況(我最喜歡的是 vue-formulate),但無論你使用什麼軟件包,或者你是否從頭開始編寫表單,這都是你的用戶會喜歡的概念。如果用戶在關注此輸入時按下 Enter 鍵,則會將光標焦點設置到以下輸入框:

<input 
 type="text"
 @keyup.enter="$event.target.nextElementSibling.focus()"
/>

9、動態刷新(重新加載)特定組件

有許多邊緣情況需要重新加載組件而不影響它所在頁面的其餘部分。你有時需要強制它使用新屬性刷新,或者因爲你使用的包在傳遞新屬性時沒有按預期更新。

<template>
  <component-to-re-render :key="reloadMe" />
</template>

<script>
export default { 
  data() { 
    return { 
      reloadMe: 0, 
    }
  }; 

  methods: { 
    forceRerender() { this.reloadMe += 1; } 
  } 
}
</script>

當然,如果你想重新加載整個頁面,那也很簡單:

// Using traditional JS (If you don't have access to $router)
window.location.reload()
// Using vue-router
this.$router.go(0)

10、從父組件調用子組件的方法

通常,父組件通過 props 將數據向下發送給子組件,子組件通過 $emit 事件向上發送給父組件。但是有時我們可能希望從僅存在於子組件內部的父組件內部觸發一個方法。聽起來很複雜?事實並非如此,Vue refs 提供了完美的解決方案!

// Parent.vue
<template>
  <ChildComponent ref="child" />
</template>


// In Parent.vue's methods
this.$refs.child.methodName()

這是一個更清晰的例子,以防上面的內容太簡短:

11、驗證組件道具

驗證你的道具有兩件事。它會告訴你是否向組件傳遞了不正確的 prop,並且可以輕鬆查看該組件旨在接受哪些選項。你可以附加任何你想要創建自定義道具驗證器的邏輯,但以下可能是你最常用的情況(驗證字符串選項):在下面的示例中,我創建了一個在我的應用程序中使用的自定義 Button 組件。請注意,我有變體和類型道具。** 注意:** 對於自定義 Button 組件,當你實際上是指樣式變體時,請確保不要將 type 作爲 prop 使用,因爲 type 已經是 HTML 按鈕上的一個東西,應該是一個單獨的 prop 來處理表單提交和重置等事情。對於這些道具中的每一個,我聲明我只想接受幾個不同的選項。如果我傳遞了錯誤的東西,這將幫助我調試我的代碼。它還將幫助其他人查看我的代碼以瞭解該組件可以接受哪些選項。

<template>
  <button
    :disabled="disabled"
    :type="type"
    class="btn"
    :class="[
      variant ? `btn-${variant}` : '',
      disabled ? disabled : ''
    ]"
    @click="!disabled ? $emit('clicked') : ''"
  >
  // Use slot to add text to it like a normal button
    <slot />
  </button>
</template>

<script>
export default {
  props: {
    variant: {
      type: String,
      validator: s =['outline''success''warning''danger'].includes(s)
    },
    type: {
      type: String,
      validator: s =['submit''reset'].includes(s)
    },
    disabled: {
      type: Boolean,
      default: false
    }
  }
}
</script>

<style lang="scss" scoped>
// limited styles here for brevity
.btn {
  line-height: 25px;
  padding: 5px 20px;
}
.disabled {
  border: 2px solid var(--grey-ultralight) !important;
  cursor: default !important;
  background: var(--grey-ultralight) !important;
  color: var(--grey-light) !important;
}
.btn-outline {
  border: 2px solid var(--blue);
  background: var(--bg);
  color: var(--blue);
  &:hover {
    background: var(--blue);
    color: var(--white);
    border: 2px solid var(--blue);
  }
}
.btn-success {
  // Success styles here
}
.btn-warning {
  // Warning styles here
}
.btn-danger {
  // Danger styles here
}
</style>

我希望你喜歡這些技巧並發現它們很有用。如果你有自己的高級技巧,請隨時在評論中分享。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/auO06B0KwLbKe37m6C9VFA