點燈坊

失くすものさえない今が強くなるチャンスよ

Component 基礎概念

Sam Xiao's Avatar 2020-08-10

Component 概念為 React 所發明,讓我們可以將 HTML、CSS 與 JavaScript 封裝成 Component,Vue、Angular 也使用 Component ,至此 3 大 Framework 都採用 Component-based 架構。

Version

macOS Catalina 10.15.6
WebStorm 2020.2
Vue 2.6.11

Introduction

Vue instance 有自己的 datamethodscomputed 、watch property,但 Vue instance 只是 HTML 的代言人,讓我們在 JavaScript 控制 HTML,但若要 重複使用 就不是那麼方便,此時我們需要 component。

除此之外,component 也讓我們在開發時實踐 Divide and Conquer 哲學,先將需求切成小小 component,然後各自擊破,最後再將 component 組合起來,如此 component 也更加 單一職責、更 容易維護重複使用

Component

overview000

以 component 實現 Hello World。

App.vue

<template>
  <hello-world></hello-world>
</template>

<script>
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

我們發現 App.vue 分兩區,<template/> 負責 HTML,而 <script/> 則負責 JavaScript。

第 1 行

<template>
  <hello-world></hello-world>
</template>

由原本的 <span>Hello World</span>,變成自訂的 hello-world tag。

自訂的 HTML tag 名稱,無論使用 camelCase,CamelCase,最後 Vue 都會改用 kebab-case (全小寫,單字間以 - 隔開),這是 W3C 所建議,且必須是 2 個單字,避免用一個單字與 HTML 預設 tag 重複

第 6 行

import HelloWorld from '@/components/hello-world.vue'

HelloWorld component 引入,component 會使用 CamelCase。

10 行

components: { 
  HelloWorld 
}

將剛剛 import 進的 HelloWorld 宣告在 components property 下。

HelloWorld.vue

<template>
  <span>Hello World</span>
</template>

<script>
export default {
  name: 'HelloWorld',
}
</script>

第 1 行

<template>
  <span>Hello World</span>
</template>

將原本的 <span>Hello World</span> 搬到 HelloWorld.vue 的 HTML template 內。

第 5 行

<script>
export default {
  name: 'HelloWorld',
}
</script>

<script/> 寫 JavaScript。

第 6 行

export default {

ES6 稱為 default export,一個 module 內只能有一個 function 或一個 object 使用 default export,其他都必須使用 named export。

Vue 習慣將 component 使用 default export,由使用端自行對 component 命名

第 7 行

name: 'HelloWorld',

Vue 的 component 名稱習慣使用 CamelCase。

MVVM vs. Component

overview004

上下使用了兩個 counter,但本質是同一個 MyCounter component,但各自維護 state。

App.vue

<template>
  <div>
    <my-counter></my-counter>
    <my-counter></my-counter>
  </div>
</template>

<script>
import MyCounter from '@/components/MyCounter.vue'

export default {
  name: 'App',
  components: {
    MyCounter
  }
}
</script>

目前 Vue component 的 data 都是直接寫在 HTML template 內,我們知道 MVVM 的精髓就是 data binding,要如何將 MVVM 與 component 兩種架構合而為一呢 ?

第 3 行

<my-counter></my-counter>

使用自訂的 <my-counter></my-counter>

第 9 行

import MyCounter from '@/components/MyCounter.vue'

export default {
  name: 'App',
  components: {
    MyCounter
  }
}

import 進 MyCounter,並在 components property 內宣告了 MyCounter

MyCounter.vue

<template>
  <div>
    <div>{{ counter }}</div>
    <button @click="add">+1</button>
  </div>
</template>

<script>
let add = function() {
  this.counter++
}

export default {
  name: 'MyCounter',
  data: () => ({ 
    counter: 0 
  }),
  methods: { 
    add 
  }
}
</script>

15 行

data: () => ({ 
  counter: 0 
}),

data 部分,由原本 Vue instance 的 data property 改成 data() function,回傳 object。

這裡有 3 種寫法:

  • OOP method
  • Function expression
  • Arrow function

OOP Method

data() {
  return {
    counter: 0
  }
}

使用 ES6 類似 OOP method 寫法。

Function Expression

data: function() {
  return {
    counter: 0
  }
}

使用 ES5 的 function expression 寫法。

Arrow Function

data: () => ({ 
  counter : 0 
})

使用 ES6 的 arrow function。

因為 Object 與 arrow function 的 function body 都使用 {},所以 ES6 特別規定當 arrow function 回傳 Object 時,需在 {} 外加上 () 識別。

個人較喜歡 ES6 的 arrow function,因為簡潔沒有贅字,此部分可依個人品味決定。

18 行

methods: { 
  add 
}

methos property 內宣告 add method。

第 9 行

let add = function() {
  this.counter++
}

定義 add() 實現 counter 累加。

Conclusion

  • Vue 提供了 component 與 Vue file,讓我們將 HTML、CSS 與 JavaScript 使用 component 包起來,方便閱讀,也更容易維護
  • MVVM 可以與 component 完美結合,但 data property 必須使用 data function
  • Vue 並非不能使用 arrow function,只要能分辨何時可用即可

Reference

Vue, Component Basics
Vue, Style Guide