關(guān)于 Vue 的使用中的一些注意事項(xiàng)和技巧

一、 一些容易忽略的特性

1. 簡(jiǎn)單傳遞多個(gè) props 的方法

當(dāng)一個(gè)組件需要分多個(gè)參數(shù)傳遞 props 值的時(shí)候,而這些參數(shù)又剛好是某個(gè)對(duì)象的屬性

<template>
  <User
    :name="user.name"
    :avatar="user.avatar"
    :email="user.email"
    />
</template>

這個(gè)時(shí)候可以直接通過(guò)v-bind綁定對(duì)象來(lái)將它的屬性值作為 props 自動(dòng)傳遞給組件

<template>
    <User v-bind="user"/>
</template>
export default {
  setup() {
    return {
      user: {
        name: 'Timfan',
        avatar: 'my-profile.jpg',
        email: 'fants0230@sina.com'
      }
    }
  }
};

這種用法同樣也適用于通過(guò) v-on 來(lái)綁定多個(gè)事件監(jiān)聽器

<template>
  <User v-on="userEventHandlers"/>
  <!-- 等效于
    <User
      @updateName="userEventHandlers.updateName"
      @deleteUser="userEventHandlers.deleteUser"
      @addFriend="userEventHandlers.addFriend"
    />
  -->
</template>
export default {
  setup() {
    return {
      userEventHandlers: {
        updateName(newName) {
          // ...
        },
        deleteUser() {
          // ...
        },
        addFriend(friend) {
          // ...
        }
      }
    }
  }
};

2. 監(jiān)聽數(shù)組和對(duì)象的變化

不知道你是否有這樣的疑惑,當(dāng)你在使用偵聽器 watch的時(shí)候,發(fā)現(xiàn)偵聽的對(duì)象變化并沒有正確地觸發(fā)回調(diào)函數(shù),通常這是因?yàn)閭陕牭膶?duì)象是一個(gè)數(shù)組或者對(duì)象,但是沒有設(shè)置deep的值為true造成的。

export default {
  name: 'ColourChange',
  props: {
    colours: {
      type: Array,
      required: true,
    },
  },
  watch: {
    // Use the object syntax instead of just a method
    colours: {
      // This will let Vue know to look inside the array
      deep: true,
      // We have to move our method to a handler field
      handler(oldVal, newVal)
        console.log('The list of colours has changed!');
        }
    }
  }
}

同理在 Vue 3 中使用的是響應(yīng)式 API 來(lái)實(shí)現(xiàn)的,方式如下

watch(
  colours,
  () => {
    console.log('The list of colours has changed!');
  },
  {
    deep: true,
  }
);

詳細(xì)用法可以參考官方文檔 https://cn.vuejs.org/guide/essentials/watchers.html

3. 給 Prop 值添加校驗(yàn)

我們可以在 Props 的定義項(xiàng)里通過(guò)validator屬性來(lái)傳遞一個(gè)回調(diào)來(lái)為它的值做校驗(yàn)

export default {
  name: 'Image',
  props: {
    src: {
      type: String,
    },
    style: {
      type: String,
      validator: s => ['square', 'rounded'].includes(s)
    }
  }
};

這個(gè)校驗(yàn)函數(shù)根據(jù)值是否有效,返回true或者false兩種值。
通常按鈕組件或者提示組件的類型(諸如:"info", "success", "danger", "warning")需要使用到該種用法作為有效值校驗(yàn)。這只是其中一種,還有更多的使用場(chǎng)景。

4. 全局組件

當(dāng)你注冊(cè)一個(gè)全局組件,你就不再需要進(jìn)行第二次導(dǎo)入就可以在 template中使用:

/ Vue 3
import { createApp } from 'vue';
import GlobalComponent from './GlobalComponent.vue';
const app = createApp({})
app.component('GlobalComponent', GlobalComponent);

在 Vue 2 中采用如下方案

// Vue 2
import Vue from 'vue';
import GlobalComponent from './GlobalComponent.vue';
Vue.component('GlobalComponent', GlobalComponent);

現(xiàn)在你可以在項(xiàng)目中的任何 template 中使用 GlobalComponent全局組件,不需要進(jìn)行額外的處理。當(dāng)然全局組件和全局變量有著同樣的利弊,請(qǐng)謹(jǐn)慎使用。

5. 監(jiān)聽嵌套的值需要帶上引號(hào)

也許你之前沒見過(guò),但是確實(shí)可以直接通過(guò)引號(hào)括起來(lái)監(jiān)聽嵌套的值,特別在深層嵌套的情況下,特別方便。

watch: {
  '$route.query.id'() {
  // ...
  }
}

6. 如何監(jiān)聽組件中任何你想監(jiān)聽的對(duì)象

事實(shí)上組件中的任何響應(yīng)式的變量都是可以通過(guò)watch來(lái)監(jiān)聽的:

export default {
  computed: {
    someComputedProperty() {
      // Update the computed prop
    },
  },
  watch: {
    someComputedProperty() {
      // Do something when the computed prop is updated
    }
  }
};

只要這個(gè)值是refreactive轉(zhuǎn)化的響應(yīng)式對(duì)象,或者是computed實(shí)現(xiàn)的計(jì)算屬性值,你都可以通過(guò)watch來(lái)監(jiān)聽它的變化。

7. hRender 函數(shù)

當(dāng)我們使用render函數(shù)來(lái)代替template模板的時(shí)候,通常是通過(guò)h函數(shù)來(lái)實(shí)現(xiàn)的:

<script setup>
import { h } from 'vue';
const render = () => h('div', {}, 'Hello Wurld');
</script>

它的作用是創(chuàng)建了一個(gè)虛擬節(jié)點(diǎn),一個(gè) Vue 內(nèi)部跟蹤變化的對(duì)象和最終渲染到頁(yè)面中的文本。
第一個(gè)參數(shù)也就是這個(gè)虛擬節(jié)點(diǎn)可以是任意的 HTML 元素,也可以是封裝好的可以調(diào)用的 Vue 組件

<script setup>
import { h } from 'vue';
import MyComponent from './MyComponent.vue';
const render = () => h(MyComponent, {}, []);
</script>

第二個(gè)參數(shù)可以是一系列的props值,自身的屬性,或者事件監(jiān)聽器

<script setup>
import { h } from 'vue';
import MyComponent from './MyComponent.vue';
const render = () => h(MyComponent, {
class: 'text-blue-400',
title: 'This component is the greatest',
onClick() {
console.log('Clicked!');
},
}, []);
</script>

第三個(gè)參數(shù)可以是一串文本,一個(gè)子節(jié)點(diǎn)組成的數(shù)組,或者用來(lái)定義slot的對(duì)象
h函數(shù)的使用為我們帶來(lái)通過(guò) javaScript 復(fù)雜的邏輯創(chuàng)建 html 的便利性。

8. 通過(guò) Vue 路由參數(shù)攜帶狀態(tài)

在開發(fā)中我們可以在頁(yè)面路由中攜帶參數(shù),這對(duì)于我們進(jìn)入到某個(gè)頁(yè)面預(yù)設(shè)一些操作是很有用的,比如進(jìn)入到某個(gè)有默認(rèn)選項(xiàng)的頁(yè)面或者需要滾動(dòng)到某個(gè)位置,都可以通過(guò)獲取到路由中的參數(shù)值來(lái)達(dá)到。

const dateRange = this.$route.query.dateRange;

這對(duì)于分離的兩個(gè)不同的應(yīng)用之間的通信也是通過(guò)該原理實(shí)現(xiàn)的。

9. 在 Render 函數(shù)中使用自定義指令

我們很熟悉在 template 模板中使用自定義指令的方式,直接通過(guò) v-指令名,而 Vue 提供了resolveDirectivewithDirectives兩個(gè)接口,前者可以獲取到自定義的指令添加到第二個(gè)接口的回調(diào)參數(shù)中

<script setup>
import { resolveDirective, withDirectives, h } from 'vue';
// Find the already registered directive by name
const focusDirective = resolveDirective('focus');
// Wrap the button with the directive
const render = () => withDirectives(
h('button', {}, []),
// An array of directives to apply
[
[focusDirective]
]
);
</script>

10. Vue 中定義 Web Component 的方式

我們?nèi)绾卧?Vue 應(yīng)用中自定義在 html 中可以使用的元素,可以分三步完成:
第一步:通過(guò) Vue 提供的接口defineCustomElement

import { defineCustomElement } from 'vue';
import MyVueComponent from './MyVueComponent.vue';
const customElement = defineCustomElement(MyVueComponent);

第二步:注冊(cè)自定義元素到 DOM 中:

customElements.define('my-vue-component', customElement);

第三步:在 HTML 結(jié)構(gòu)中直接使用該自定義元素

<html>
  <head></head>
  <body>
    <my-vue-component></my-vue-component>
  </body>
</html>

這樣就實(shí)現(xiàn)了一個(gè)不需要任何框架就可以在瀏覽器中運(yùn)行的自定義組件( 也就是通常說(shuō)的 Web Component)。

11. 在 script setup 中訪問(wèn)內(nèi)部屬性和方法

當(dāng)我們通過(guò)$ref來(lái)訪問(wèn)某個(gè)組件的時(shí)候,我們是無(wú)法直接訪問(wèn)組件內(nèi)部的方法或者屬性變量的

export default {
  expose: ['makeItPublic'],
  data() {
    return {
      privateData: 'Keep me a secret!',
    };
  },
  computed: {
    makeItPublic() {
      return this.privateData.toUpperCase();
    },
  },
};

但是 Vue 提供的 expose 可以將內(nèi)部的屬性或方法暴露出來(lái)供外部使用,這里可以訪問(wèn)makeItPublic方法

this.$refs.component.makeItPublic()

在 Vue3 中當(dāng)我們使用 <script setup>里面的是完全封閉狀態(tài),如何你要暴露一個(gè)值只能這么做

<script setup>
import { ref, computed } from 'vue';
const privateData = ref('Keep me a secret!');
const makeItPublic = computed(
() => privateData.value.toUpperCase()
);
// We don't need to import this because it's a compiler macro
defineExpose({
makeItPublic
});
</script>

注意:這里的 defineExpose是一個(gè)編譯宏,不是某個(gè)具體的函數(shù),不用導(dǎo)入即可使用。

12. Vue 中特殊的幾個(gè) CSS 偽選擇器

當(dāng)你僅需要對(duì)插槽中內(nèi)容中含有的標(biāo)簽進(jìn)行樣式設(shè)置時(shí),你可以使用 :slotted偽選擇器:

<style scoped>
/* Add margin to <p> tags within the slot */
:slotted(p) {
  margin: 15px 5px;
}
</style>

你也可以使用:global偽選擇器進(jìn)行全局范圍的樣式設(shè)置,即使在<style scoped>樣式塊中也生效

<style scoped>
:global(body) {
margin: 0;
padding: 0;
font-family: sans-serif;
}
</style>

當(dāng)然,你也可以很方便地通過(guò)添加第二個(gè)<style>樣式塊,剔除scoped的限制,作用于全局樣式中

<style scoped>
/* Add margin to <p> tags within the slot */
:slotted(p) {
margin: 15px 5px;
}
</style>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
</style>

13. 實(shí)現(xiàn)自定義指令

在 Vue 中有一系列的預(yù)設(shè)指令,比如經(jīng)常使用的v-forv-if,除此之外,我們還可以自定義一些指令:

<template>
  <h1 v-bg-colour="'skyblue'">This background is blue</h1>
</template>
import { createApp } from "vue";
const app = createApp({});
app.directive("bg-colour", {
  mounted(el, { value }) {
    // Update the background colour
    el.style.background = value;
  }
});

這個(gè)指令的作用是實(shí)現(xiàn)對(duì)指定標(biāo)簽中文本顏色的設(shè)置
你甚至還可以指定特定參數(shù)進(jìn)行設(shè)置:

<template>
<h1 v-bg-colour:colour="'skyblue'">
This background is blue
</h1>
</template>
app.directive("bg-colour", {
  mounted(el, { value, arg }) {
    // Update the background colour
    el.style.background = value;
    console.log(arg); // "colour"
  }
});

這里可能和你預(yù)想的不一樣,當(dāng)多個(gè)參數(shù)時(shí) Vue 會(huì)把它們當(dāng)不同的指令來(lái)處理:

<template>
  <!-- Two directives will be mounted -->
  <h1
    v-bg-colour:colour="'skyblue'"
    v-bg-colour:animate="true"
    >
    This background is blue
  </h1>
</template>

索性這里的值最終會(huì)被評(píng)估為 javaScript 表達(dá)式,我們可以把它當(dāng)做選項(xiàng)對(duì)象來(lái)傳值:

<template>
<!-- Two directives will be mounted -->
<h1
    v-bg-colour="{
        colour: 'skyblue',
        animate: true,
    }"
>
This background is blue
</h1>
</template>
app.directive("bg-colour", {
  mounted(el, { value }) {
    // Update the background colour
    el.style.background = value.colour;
    // Do something cool with value.animate
  }
});

14. nextTick: 等到 DOM 更新完再執(zhí)行

Vue 提供了很方便的途徑幫助我們等待 DOM 完成更新在執(zhí)行事件

// Do something that will cause Vue to re-render
changeTheDOM();
// Wait for the re-render to finish
await nextTick();
// Now we can check out the freshly minted DOM
inspectTheDOM();

或者在選項(xiàng)式 API 中:

await this.$nextTick();

一個(gè)tick即為一個(gè)頁(yè)面渲染周期,Vue 監(jiān)聽所有響應(yīng)式變化然后一次性完成 DOM 的更新。然后再進(jìn)行下一次周期。
當(dāng)你需要在數(shù)據(jù)更新,頁(yè)面完成渲染后再觸發(fā)某個(gè)動(dòng)作,它能保證你在下一個(gè)tick執(zhí)行之前完成這個(gè)動(dòng)作。

15. 對(duì)v-for中的變量進(jìn)行解構(gòu)

你知道在v-for中就可以直接進(jìn)行解構(gòu)操作嗎?

<li
  v-for="{ name, id } in users"
  :key="id"
  >
  {{ name }}
</li>

或者你對(duì)數(shù)組解構(gòu)出index這種方式更熟悉

<li v-for="(movie, index) in [
'Lion King',
'Frozen',
'The Princess Bride'
]">
{{ index + 1 }} - {{ movie }}
</li>

當(dāng)需要對(duì)對(duì)象解構(gòu)出屬性和值時(shí)

<li v-for="(value, key) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
{{ key }}: {{ value }}
</li>

你甚至可以同時(shí)解構(gòu)出屬性和index

<li v-for="(value, key, index) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
#{{ index + 1 }}. {{ key }}: {{ value }}
</li>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容