· Frontend  · 7 min read

使用很夯的Vue Vine實現多元件於一身

於2024/07/06的Vue Conf上,沈青川大大介紹了新專案 - Vue Vine,其中 "於一檔案寫入多組元件",就是它最核心的功能,其保有Vue的模板與邏輯分離特色,但也融入React多元件於一身精神,使管理上更加簡單。同時非常感謝有各路大神們,不斷創造新工具,讓大家嘗鮮又能滿足各種開發願望,希望此篇開箱 & 小踩坑可以幫助到想將多元件寫於同一檔案作管理的讀者。

於2024/07/06的Vue Conf上,沈青川大大介紹了新專案 - Vue Vine,其中 "於一檔案寫入多組元件",就是它最核心的功能,其保有Vue的模板與邏輯分離特色,但也融入React多元件於一身精神,使管理上更加簡單。同時非常感謝有各路大神們,不斷創造新工具,讓大家嘗鮮又能滿足各種開發願望,希望此篇開箱 & 小踩坑可以幫助到想將多元件寫於同一檔案作管理的讀者。

序言

於2024/07/06的Vue Conf上,沈青川大大介紹了新專案 - Vue Vine,其中 “於一個檔寫入多組元件”,就是它最核心的功能。有寫過React是否就會立馬聯想到Function Components,當時看到這一專案時,眼睛為之一亮,可能以前受React影響,有時在寫Vue專案,就想把一些相關元件直接寫在同一檔案,但受於Vue本身SFC限制,只能乖乖地 cmd+n 一個個新的Vue檔。

Vine component 是以Function所組成,全名為 ‘Vine Component Function’(官方稱為”VCF”),結合了SFC & JSX優點,讓一個檔案可以寫入多元件外,同時繼續享受Vue的模板語法,將相關元件集中於同一檔案,減少於不同元件中來回切換的痛苦,是不是讓各方面都滿足了呢~

而為什麼為說它是Function Component呢? 因為你可以將template & setup script寫在一塊,像極了React,但仍保有Vue的模板與邏輯分離特色,如下:

// IDComponents.vine.ts

export function AddIDBtn() {
  function addId() {
    const id = uuidv4();
    sharedIDs.value.push(id);
  }

  return vine`
    <IDBtn
      title="新增"
      @action="addId"
    />
  `;
}

需注意的點

  • 只支援 Vue 3.0+Vite
  • 只支援 TypeScript
  • 製作多元件時,需使用 vine 標籤回傳元件字串
  • 元件需使用 .vine.ts 作為副檔名
  • 使用 ${} 表達式於模板字串中會報錯,因Vine會將其傳遞給 @vue/compiler-dom ,進行編譯,所以一樣記得使用{{}}表達式

了解vue-vine的原由及其特色後,就可以開始入門著手寫元件了!

搭建Vue Vine環境

Installation

  pnpm i -D vue-vine

配置Vite

於vite的配置檔案中,加入以下內容:

// vite.config.ts
import { VineVitePlugin } from 'vue-vine/vite';

export default defineConfig({
  plugins: [
    // ...其他插件
    VineVitePlugin(),
  ],
});

安裝vue-vine的VS Code插件

建議可以安裝,因為它會將語法高亮,以免開發時看到的都是字串格式,最後會寫得眼花撩亂。

實作新增與刪除ID的vue-vine元件

先來看實作出來效果

創建多組元件共用的vine檔

// IDComponents.vine.ts

/**
 * 此為按鈕父元件
 */
export function IDBtn() {
  const title = vineProp<string>();
  const emit = vineEmits(['action']);

  return vine`
  <button @click="emit('action')">
    {{title}}
  </button>
  `;
}

/**
 * 此兩組引用,用於下方ID列表顯示與操作
 */
import { v4 as uuidv4 } from 'uuid'; // $pnpm install uuid,安裝後再引用
import { sharedIDs } from '../store/sharedState'; // 另一個檔案,存放共用變數

/**
 * 列表元件
 */
export function IDList() {
  return vine`
    <span v-if="sharedIDs.length === 0">請點選'新增',創建ID</span>
    <ul v-else>
      <li v-for="id in sharedIDs" :key="id">
        {{ id }}
      </li>
    </ul>
  `;
}

/**
 * 新增ID按鈕
 */
export function AddIDBtn() {
  function addId() {
    const id = uuidv4();
    sharedIDs.value.push(id);
  }

  return vine`
    <IDBtn
      title="新增"
      @action="addId"
    />
  `;
}

/**
 * 刪除ID按鈕
 */
export function DeleteIDBtn() {
  function deleteId() {
    sharedIDs.value.pop();
  }

  return vine`
    <IDBtn
      title="刪除"
      @action="deleteId"/>
  `;
}
// sharedState.ts
import { ref } from 'vue';

export const sharedIDs = ref<string[]>([]);

從上述代碼可以看到,真實將操作ID相關的’模板’ & ‘操作邏輯’,全都寫在同一個ts檔中。

IDList()中更是將vue原有的習慣寫法都保留,v-if & v-for…等關鍵字都是可正常運作。 而於export的function component,也可將所需的function放於自身定義中,一個function component就像我們在寫SFC一樣。

vineProp & vineEmits,即是於vine中欲傳遞參數給子元件時的關鍵字,使用方式與Vue幾乎一樣,只要在腦中覺得應該是這樣的寫法,照著自己的想法走,就能與vine融於一塊了!

引用vine檔元件

// App.vue
<script setup lang="ts">
import { AddIDBtn, DeleteIDBtn, IDList } from './components/IDComponents.vine';
</script>

<template>
  <div class="action-block">
    <AddIDBtn />
    <DeleteIDBtn />
  </div>
  <hr />
  <IDList />
</template>

<style scoped>
.action-block {
  display: flex;
  gap: 16px;
  justify-content: center;
  align-items: start;
}
</style>

vine中每一function即是一組元件,因此於vue中如何引用元件,在此如法炮製地引用,學習曲線平緩得很。

後話

在寫vue-vine時,其實寫到後來有一點暢快,因為可以參有React彈性寫於Vue之中,滿足我什麼都要的欲望。 但在一開始,還是踩了一點點的坑,在此跟大家分享一下。

建議可以直接使用vite創建專案,而後再選擇vue + ts,因很習慣直接使用vue create,所以在建置時,想說怎麼一直跑不起來,結果…是忘了vue-vine也要使用vite才會運作。現在會記得Vue + Vite + TypeScriptVue-Vine

另一個坑是,雖然有React的精神在裡面,但還是需要提醒自己這仍是vue專案,因為在寫IDList()元件時,一度卡住,想說是不是應該使用for in + return來實現,而寫出這樣的code:

export function IDList() {
  return vine`
    <ul>
      {{ sharedIDs.map(id => `<li>${id}</li>`) }}
    </ul>
  `;
}

直接又踩了vue-vine的規則,不能使用 ${},後來才想到,它就是vue,幹嘛寫複雜呢!

非常感謝有各路大神們,不斷創造新工具,讓大家嘗鮮又能滿足各種開發願望,希望此篇開箱 & 小踩坑的vue vine入門文可以幫助到想將多元件寫於同一檔案作管理的讀者。