回到部落格

2024-01-05

【React-Vue】Vue components

【React-Vue】Vue components

原文發表於 Medium

如同React,Vue.js也可製作重複使用的網頁元件component,在Vue初始專案中的components資料夾就是提供給我們放至網頁子元件的空間。(當然若您很反骨也可以放置在自己喜歡的資料夾內。)

一個Vue檔案的架構基本上是由 script、template、style這三個部分組成,其中的template是最重要的部分,其他兩個部分甚至可以不要,例如Vue團隊就把 Logo的SVG tag直接做成一個component:

//src/components/icons/IconCommunity.vue

<template>  
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">  
    <path  
      d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"  
    />  
  </svg>  
</template>

template之中的資料呈現我們希望的html結構,這邊的使用方式跟React Functional Component就很像了,下面是程式碼寫法的對比:

//App.vue
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />

    <div class="wrapper">  
      <HelloWorld msg="You did it!" />  
    </div>  
  </header>  
  
  <main>  
    <TheWelcome />  
  </main>  
</template>  
  
  
//App.jsx (相匹配的寫法)  
  
import HelloWorld from './components/HelloWorld.vue'  
import TheWelcome from './components/TheWelcome.vue'  
  
export default function App(){  
  return(  
  <header>  
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />  
  
    <div class="wrapper">  
      <HelloWorld msg="You did it!" />  
    </div>  
  </header>  
  
  <main>  
    <TheWelcome />  
  </main>  
)  
  
}

所以若要React開發者改寫Vue.js的話,在許多方面可以說是相當輕鬆。

子元件與props

廢話說完,我們來看一下Vue的component元件寫法,首先依照上方程式碼,我們可以找到HelloWorld跟TheWelcome兩個元件。他們的嵌入方式跟React類似,Import之後就可以直接掛在template中。在此處我們看一下HelloWorld的內部長相。

<script setup> defineProps({ msg: { type: String, required: true } }) </script>
<template>  
  <div class="greetings">  
    <h1 class="green">{{ msg }}</h1>  
    <h3>  
      You’ve successfully created a project with  
      <a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +  
      <a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.  
    </h3>  
  </div>  
</template>  
  
<style scoped>  
h1 {  
  font-weight: 500;  
  font-size: 2.6rem;  
  position: relative;  
  top: -10px;  
}  
  
h3 {  
  font-size: 1.2rem;  
}  
  
.greetings h1,  
.greetings h3 {  
  text-align: center;  
}  
  
@media (min-width: 1024px) {  
  .greetings h1,  
  .greetings h3 {  
    text-align: left;  
  }  
}  
</style>

同樣是一個非常標準的Vue檔,script、template、style三者皆備。但這個元件有個特別之處,就是有資料從上層的父元素傳來:

//父元素的嵌入狀態
<HelloWorld msg="You did it!" />
//利用msg這個attr,把"You did it!"這個值傳入到HelloWorld子元件

傳遞資料的方式跟React差不多,但子元素接收資料的方式則差異較大。在HelloWorld中接收資料的方式是在script區塊使用defineProps方法來取得:

//HelloWorld.js

<script setup>  
//defineProps方法不需要import,但須注意script必須有setup這個attr.  
defineProps({  
  msg: {  
    type: String, //型別  
    required: true   //是否為必須值  
  }  
})  
</script>  
  
//HelloWorld.tsx 參考寫法(由於有指定型別,這在普通的React做不到,必須使用TypeScript)  
  
type props = {  
  msg : string  
}  
  
export default function HelloWorld({msg}: props){  
      //其餘程式碼  
}

這個defineProps中需要一個object入參,object中的key值就會是在父元素上的attribute名稱,而每個key的value又是另外一個object(宣告type、是否為必須值)。

子元素透過props接收到的資料透過下面的程式碼呈現在template中:

<template> <div class="greetings"> <h1 class="green">{{ msg }}</h1> //... </div> </template>

使用{{ }}雙層的花括號(double curly braces)來表示資料綁定,意思是說雙層花括號中會指向一個值,它可以是變數,也可以是一個運算式(例如 3–1)。這種方式跟React感覺上不太一樣,但其實很像:

//HelloWorld.tsx 參考寫法(由於有指定型別,這在普通的React做不到,必須使用TypeScript)

type props = {  
  msg : string  
}  
  
export default function HelloWorld({msg}: props){  
      return(  
      <div class="greetings">  
        <h1 class="green">{ msg }</h1>  
      //...  
      </div>  
      )  
}

當然這個資料綁定的內容不是僅限於props,只要是在同檔案中的script區塊定義的資料都能綁定:

<script setup> const props = defineProps({ msg: { type: String, required: true }, test:{ type: Number } }) const insideTestValue = "inside" </script>
<template>  
  <div class="greetings">  
    <h1 class="green">{{ msg+test }}</h1>  
    <p>{{test}}</p>   //另一個props資料  
    <p>{{ insideTestValue }}</p>  //內部資料"inside"字串  
  </div>  
</template>

因此React開發者在資料綁定上,必須要記住需使用 {{雙層的花括號}} 來實作。相信改個方式之後,仍然是勝任愉快的。

到此為止,都還是在React的舒適圈內,但接下來的幾篇我將會開始介紹Vue的一些特別的Feature,對於React開發者來說,可能就會有點坐不住了。