enpitsulin

enpitsulin

这个人很懒,没有留下什么🤡
twitter
github
bilibili
nintendo switch
mastodon

Vue 3.3 新特性前瞻和簡單評價

雖然 3.3 當前還處於 beta 階段但是其帶來的一些特性十分激動人心,就在這裡簡單的給大家帶來新特性的前瞻,為以後的升級簡單做準備😁

泛型組件支持#

Vue一直以來都是沒辦法很好的實現泛型組件,終於在 3.3 版本增加了這一功能

首先是面向 TSX 用戶為defineComponent 工具函數增加了泛型支持,當參數傳入一個泛型函數時類型會提示正常,比如我們可以基於這個特性使用 tsx 簡單構造一個表格組件

import { defineComponent } from 'vue';

type Props<T> = { data: T[]; columns: { label: string; field: keyof T }[] };

const Table = defineComponent(<T,>(props: Props<T>) => {
  return () => (
    <table>
      <thead>
        <tr>
          {props.columns?.map((item) => (
            <th>{item.label}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {props.data?.map((row) =>
          props.columns?.map((column) => <td>{row[column.field]}</td>)
        )}
      </tbody>
    </table>
  );
});

export default Object.assign(Table, {
  name: 'GenericsTableTsx',
  props: ['columns', 'data']
});

但是值得注意的是我們仍需要為這個組件傳入props屬性,否則在使用的時候會將應該是props的的屬性掛載到$attrs上,這點其實基本上杜絕了這樣的用法,所以說僅僅只是類型正確,不太推薦生產用這樣的方法構建泛型組件。

SFC 泛型組件支持#

其實上面的功能還是為了鋪墊這個,我們了解怎麼用 SFC 來復現上面的組件

<template>
  <table>
    <thead>
      <tr>
        <th v-for="item in columns">
          <slot name="header-cell" v-bind="{ label: item.label }">
            {{ item.label }}
          </slot>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in data">
        <td v-for="column in columns">
          <slot name="cell" v-bind="{ data: row[column.field] }">
            {{ row[column.field] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>
<script setup lang="ts" generic="T">
  const { columns, data } = defineProps<{
    data: T[];
    columns: {
      label: string;
      field: keyof T;
    }[];
  }>();
</script>

[email protected]script增加了一个属性generic用于创建泛型参数,多个参数当然也像是 ts 中使用,隔开。

評價:很強的新特性,vue 終於有泛型組件了真的是可喜可賀,就是對於 TSX 的支持還是需要額外增加 props 屬性比較麻煩,這個問題也是比較久遠的了,希望 vue 團隊以後在為 TSX 的開發體驗提升上努努力

defineProps 宏支持引入的類型#

這個需求已經 2 年過去了,不過大部分開發者都有使用一些社區插件來達到這個用法,現在官方終於提供了,在 3.3 我們可以輕鬆的使用外部導入的類型創建Props

<script setup lang="ts">
import type { SomeType } from 'some-where'

const props = defineProps<{ data: SomeType }>()
</script>

評價:眾望所歸,更方便的管理在 Vue 項目中的類型,不需要再在 SFC 中寫又臭又長的類型體操了

defineEmits 宏更簡便的寫法#

對於 3.2,defineEmits基於泛型需要這樣使用

defineEmits<{
  (e: 'foo', id: string): void
  (e: 'bar',...args: any[]): void
}>()

3.3 的寫法,對於單個參數使用具名元組的方式定義,如果使用rest params的參數可以直接使用T[]來解決

defineEmits<{
  foo: [id: string]
  bar: any[]
}>()

評價:提升 DX 的小功能,函數重載的形式寫起太多的 emits 確實有點煩人

為 v-model 帶來新的工具#

這是來自智子君新特性,可以在<script setup/>中使用defineModel和非<script setup/>中使用的useModel工具

// 默認的model (通過 `v-model`)
const modelValue = defineModel()
   // ^? Ref<any>
modelValue.value = 10

const modelValue = defineModel<string>() //增加類型
   // ^? Ref<string | undefined>
modelValue.value = "hello"

// 帶有設置的默認model, 要求非undefined 
const modelValue = defineModel<string>({ required: true })
   // ^? Ref<string>

// 特定名稱的model (通過 `v-model:count` )
const count = defineModel<number>('count')
count.value++

// 具有默認值的特定名稱的model
const count = defineModel<number>('count', { default: 0 })
   // ^? Ref<number>

// 本地作用域可變的 model, 顧名思義
// 可以不需要父組件傳遞v-model
const count = defineModel<number>('count', { local: true, default: 0 })

還有useModel作為非<script setup/>中使用的工具

import { useModel } from 'vue'

export default {
  props: {
    modelValue: { type: Number, default: 0 },
  },
  emits: ['update:modelValue'],
  setup(props) {
    const modelValue = useModel(props, 'modelValue')
       // ^? Ref<number>

    return { modelValue }
  }
}

評價:又一提升 DX 的利器,定義一個v-model的屬性確實比較繁瑣,而且在 sfc 內實用性不強,一般需要搭配vueuse/useVModels使用,官方加入這個宏和工具函數確實是很不錯

defineOptions#

又是智子君的 pr,早前來自RFC,這個內容的話應該不少人都在Vue Macro中用過了

本來 Vue 如果你需要在<script setup>中定義一些原先Option Api的屬性比如inheritAttrs/name是需要創建一個<script>單獨導出這兩個屬性的,現在有了defineOptions就可以省去這一步驟

<script setup>
// 一些代碼
</script>
<script>
export default {
  name: "ComponentName"
}
</script> 
<script setup>
defineOptions({
  name: "ComponentName"
})
// 一些代碼
</script> 

評價:這個特性可以在Vue Macro使用到,先行體驗,反正我是用上了很爽

defineSlots 宏以及 slots 屬性#

還是來自智子君,TQL

允許定義slots的具體類型,首先是新增了一個SlotsType以及slots屬性可以options api中使用

import { SlotsType } from 'vue'

export default defineComponent({
  slots: Object as SlotsType<{
    default: { foo: string; bar: number }
    item: { data: number }
  }>,
  setup(props, { slots }) {
    expectType<undefined | ((scope: { foo: string; bar: number }) => any)>(
      slots.default
    )
    expectType<undefined | ((scope: { data: number }) => any)>(slots.item)
  }
})

對於這個定義的組件SlotComponent,再組件中使用的話就是

<template>
  <SlotComponent>
    <template #default="{ foo, bar }">
      {{ foo }} is string,{{ bar }} is number
    </template>
    <template #item="{ data }">
      {{ data }} is number
    </template>
  </SlotComponent>
</template>

defineSlotsslots屬性類似,不過提供一個函數語法

// 與 對象語法表現一致,謝謝ES沒有將default當屬性關鍵詞 可喜可賀可喜可賀😆
const slots = defineSlots<{
  default(props: { foo: string; bar: number }): any // or VNode[]
}>()

評價: slot 有正確的類型的話對於組件庫來說是一個挺好的消息,畢竟從引入scopeSlot到現在用戶都不能很好地確定自己該怎麼用某個scopeSlot

模板中使用 console.log#

突然的調試可能會用到的console.log但是在模板中不好使,現在 3.3 加上了額外支持,不需要再自己為模板作用域增加一個函數來打印東西了

評價:無傷大雅,提升 DX,偶爾會用到會感覺很舒服

不太重要的特性#

對 Suspense 的改進#

個人覺得 vue 的<Suspense>可以暫時不用關注,實驗性特性好久了,pr 在這裡

廢棄和修改的特性#

v-is 指令廢棄#

廢棄v-is了,全部改用:is指令(好奇真的有人還在用這個指令嗎?)

app.config.unwrapInjectedRef#

app.config.unwrapInjectedRef這個屬性沒了,在 3.3 會默認對使用Option apiinject屬性進行注入的 ref 進行拆包,

vnode hook 廢棄#

vnode hook 應該用戶比較少,就是有一個@vnode-*的指令變成了@vue:*,這個特性應該蠻少用的,甚至連網上都沒有什麼介紹,好像是為 Vnode 的生命週期提供一些類似與組件生命週期的功能,不知道有沒有清楚這個特性能幹嗎的夥伴介紹下。

對於生態開發者的改進#

app.runWithContext()#

在 App 上增加了一个runWithContext()可以用于确保对应用程序级别的全局变量存在,比如通过provide的各个值,可以用于改进 vue 生态的各个包,pinia/vue-router之类的

const app = createApp(App)

app.provide('foo', 1)

app.runWithContext(() => inject('foo')) // should return 1

hasInjectionContext#

hasInjectionContext這是面向基於 vue 的庫作者用於檢查是否可以使用inject()的工具,如果當前環境可以使用就返回 true,不可以的環境其實就是 setup 外了,庫作者使用該函數可以省去額外對當前環境的檢測。

評價:這些對生態開發者來說比較有用的功能,如果有寫庫的小夥伴可以注意一下

需要注意的#

對於 TSX 用戶,vue3.3 不在默認註冊全局 JSX 命名空間,需要手動在 tsconfig.json 中修改jsxImportSource或者使用魔法註釋/* @jsxImportSource vue */這是避免全局 jsx 類型衝突。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。