Vue 之 Component

栏目: JavaScript · 发布时间: 6年前

内容简介:Component 概念為 React 所發明,讓我們可以重複使用 HTML,Vue、Angular 也Vue 2.5.17

Component 概念為 React 所發明,讓我們可以重複使用 HTML,Vue、Angular 也 致敬 React,採用 component ,至此 3 大 Framework 都統一採用 Component-based 架構。

Version

Vue 2.5.17

Architecture

Vue 之 Component

Introduction

Vue Instance 有自己的 datamethodscomputedwatch ,但 Vue Instance 只是 HTML 的代言人,讓我們在 JavaScript 控制 HTML,但若要 重複使用 ,就不是那麼方便,這時我們需要的是 Vue Component。

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

Global Component

之前我們只會 MVVM 的 Hello World,若改用 Component-based 的寫法呢 ?

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Global Component</title>
</head>
<body>
  <div id="app">
    <hello-world></hello-world>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="HelloWorld.js"></script>
<script src="index.js"></script>
</html>

第 9 行

<hello-world></hello-world>

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

我們會為自己的的 Vue Component 定義自己的 HTML tag

index.js

new Vue({
  el: '#app'
});

建立 Vue Instance。

HelloWorld.js

Vue.component('HelloWorld', {
  template: '<span>Hello World</span>',
});

定義 HelloWorld component。

使用 Vue Vue.component() 定義 Vue Component。

  • 第 1 個參數:string,傳入自訂的 HTML tag 名稱
  • 第 2 個參數:object,類似傳入 Vue Instance 的 constructor 參數

Vue Component 的使用,有幾點要注意:

  • Vue 規定 Vue Component 一定要定義在 Vue Instance 之前 ,否則 Life Cycle 在 compile HTML 階段,會不知道 Vue Component 所自訂的 HTML tag
  • 自訂的 HTML tag 名稱,無論使用 camelCase,CamelCase,最後 Vue 都會改用 Kebab-case (全小寫,單字間以 - 隔開),這是 W3C 所建議,且必須是 2 個單字,避免用一個單字與 HTML 預設 tag 重複
  • 關於 Component (HTML tag) 與 JavaScript 檔案命名方式,Vue 官方的 Style Guide :
    • CamelCaseHelloWorldHelloWorld.jsHelloWorld.vue
    • kebab-casehello-worldhello-world.jshello-world.vue
    • Vue CLI 使用 CamelCase

Local Component

使用 Vue.component() 所宣告的是 Global Component,也就是每個 Vue Instance 都可使用,若你想定義只有某個 Vue Instance 能使用的 component,則要使用 Local Component。

inex.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Local Component</title>
</head>
<body>
  <div id="app">
    <hello-world></hello-world>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="local_component.js"></script>
</html>

HTML 部分一樣不變使用 <hello-world></hello-world>

index.js

new Vue({
  el: '#app',
  components: {
    'HelloWorld': {
      template: '<span>Hello World</span>',
    }
  }
});

在傳入 Vue Instance 的 constructor 參數內,加上 components Property,為 object。

以自訂的 HTML tag hello-world 為 key,將 Vue.component() 第二個參數的 object 為 value。

MVVM vs. Component

目前 Vue Component 的 data 顯示都是寫死的,我們知道 MVVM 的精髓就是 Data Binding,要如何將 MVVM 與 Component-based 兩種架構合而為一呢 ?

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Counter Component</title>
</head>
<body>
  <div id="app">
    <my-counter></my-counter>
    <my-counter></my-counter>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="MyCounter.js"></script>
<script src="index.js"></script>
</html>

第 9 行

<my-counter></my-counter>

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

index.js

new Vue({
  el: '#app'
});

建立 Vue Instance。

MyComponent.js

Vue.component('MyCounter', {
  template: `
    <div>
      <span>{{ counter }}</span>
      <p></p>
      <button @click="add">+1</button>
    </div>
  `,
  data() {
    return {
      counter: 0,
    };
  },
  methods: {
    add() {
      this.counter++;
    }
  }
});

第 2 行

template: `
  <div>
    <span>{{ counter }}</span>
    <p></p>
    <button @click="add">+1</button>
  </div>
`,

將 HTML Template 宣告在 template property 下。

由於 HTML Template 在實務上會很多行,用普通字串不方便,建議改用 ECMAScript 2015 的 string template,就不必再 字串相加

第 9 行

data() {
  return {
    counter: 0,
  };
},

data 部分,由原本 Vue Instance 的 data property 改成 data() function。

改回傳 data object。

14 行

methods: {
  add() {
    this.counter++;
  }
}

data 內的 counter 累加。

Q : 為什麼寫成 Vue Component 後,要從 data property 改成 data() function ?

data: {
  counter: 0
},

若改成 data property 寫法,Vue 會無法執行,且出現 warning。

Vue 之 Component

Vue 之 Component

在正統 OOP,兩個 component 應該是兩個 instance,而 data 包在 instance 內,因此 Component 間的 data 不會互相影響,也就是 OOP 的 封裝

Vue 之 Component

但 Vue 底層並不是採用 OOP 方式,而是共用同一份 component instance,只有 data 是不同份。

這也是為什麼為什麼 Vue 要你改用 data() function,而且是回傳全新對 data object。

只要寫 Vue Component,就一定要改用 data() function,不能使用 data property

使用 Vue Component 時,還有一點值得注意:

  • 不可使用 HTML self closing 語法
<div id="app">
  <my-counter/>
  <my-counter/>
</div>

這種寫法,Vue 不會出錯,但只有一個 component 能動。

<div id="app">
  <my-counter></my-counter>
  <my-counter></my-counter>
</div>

要這樣寫,Vue 才能正常執行。

Component vs. DOM Parser

有時候在使用 Vue Component 時,會發現無法如預期顯示在 Browser 裏。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>DOM Parse Error</title>
</head>
<body>
  <div id="app">
    <select>
      <my-option></my-option>
    </select>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="index.js"></script>
<script src="MyOption.js"></script>
</html>

第 9 行

<select>
  <my-option></my-option>
</select>

<select></select> 內使用自訂的 <my-option></my-option> Vue Component。

index.js

new Vue({
  el: '#app'
});

建立 Vue Instance。

MyOption.js

Vue.component('MyOption', {
  template: '<option>Vue</option>',
});

自訂的 MyOption 只包含 <option>Vue</option> 部分。

Vue 之 Component

  1. Chrome 無法正常顯示
  2. <select></select> 以下沒有任何 <option></option>

這牽涉到各 Browser 的 DOM Parser 如何解析 HTML。

以 Chrome 而言,它認為 <select></select>只應該<option></option> ,其他的 HTML tag 都為非法,因此忽略不使用。

DOM Parser 會因 Browser 而異

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>DOM Parse OK</title>
</head>
<body>
  <div id="app">
    <my-select></my-select>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="MySelect.js"></script>
<script src="index.js"></script>
</html>

由本來的 <my-option></my-option> 改成 <my-select></my-select>

index.js

new Vue({
  el: '#app'
});

建立 Vue Instance。

MySelect.js

Vue.component('MySelect', {
  template: `
    <select>
      <option>Vue</option>    
    </select>
`,
});

<select> 一起包進 Vue Component,如此 Chrome 就無法干涉 <option>

Dynamic Component

先定義好 Vue Component,然後動態切換 component。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Dynamic Component</title>
</head>
<body>
  <div id="app">
    <button @click="selectLesson">Lessons</button>
    <button @click="selectApply">Apply</button>
    <p></p>
    <component :is="content"></component>
  </div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="MyLessons.js"></script>
<script src="MyApply.js"></script>
<script src="index.js"></script>
</html>

12 行

<component :is="content"></component>

使用 Vue 擴充的 <component></component> ,綁定其 is ,當 content 指定什麼 Vue Component 時, <component></component> 就會動態切換該 Vue Component。

並沒有在 HTML 內事先使用特定 Component tag,只使用 <component></component> 保留其動態彈性

index.js

new Vue({
  el: '#app',
  data: {
    content: 'my-lessons'
  },
  methods: {
    selectLesson() {
      this.content = 'my-lessons';
    },
    selectApply() {
      this.content = 'my-apply';
    }
  }
});

第 3 行

data: {
  content: 'my-lessons'
},

data 內定義 content model,其中預設值 my-lessons 為 component 名稱。

第 6 行

methods: {
  selectLesson() {
    this.content = 'my-lessons';
  },
  selectApply() {
    this.content = 'my-apply';
  }
}

由 method 動態改變 content ,MVVM 會再動態改變 <component></component>:is ,達到動態組件的需求。

MyLessions.js

Vue.component('MyLessons', {
  template: `
    <ul>
      <li>React</li>
      <li>Angular</li>
      <li>Vue</li>
    </ul>
  `,
});

定義了 MyLessions component。

MyApply.js

Vue.component('MyApply', {
  template: `
    <form>
      <textarea></textarea>
      <p></p>
      <button>Submit</button>
    </form>
  `,
});

定義了 MyApply component。

但這兩個 Vue Component 都沒在 HTML 內被使用,將由 JavaScript 動態指定

Keep-Alive

由於 <component></component> 類似 v-if ,其 Dynamic Component 是藉由 刪除 DOM element,並建立新的 DOM element 的方式,所以原本的 user 輸入的資料,也會一併被刪除。

若要保留原本 user 輸入的資料,就必須搭配 <keep-alive><keep-alive>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Keep Alive</title>
</head>
<body>
<div id="app">
  <button @click="selectLesson">Lessons</button>
  <button @click="selectApply">Apply</button>
  <p></p>
  <keep-alive>
    <component :is="content"></component>
  </keep-alive>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="MyLessons.js"></script>
<script src="MyApply.js"></script>
<script src="index.js"></script>
</html>

12 行

<keep-alive>
  <component :is="content"></component>
</keep-alive>

<component></component> 外部加上 <keep-alive></keep-alive> ,則 Dynamic Component 內的資料將獲得保留。

JavaScript 的寫法不用改變。

Vue 底層會將 user 的輸入保留,然後切換 component 時,除了建立新的 component 外,還會將 user 原本所輸入的資料也 重新 填回新建立的 component,讓動態切換 component 更方便

Conclusion

  • Vue 提供了 Vue Component,讓我們將 HTML 會重複的部分可以使用 component 包起來,方便閱讀,也更方便維護
  • MVVM 可以 Component-based 完美結合,但 data property 必須改用 data function
  • Component 有時候會違背 Browser 的 DOM Parser,此時必須改變寫法繞過 Browser
  • Dynamic Component 讓我們可以根據商業邏輯自行切換 component
  • 透過神奇的 <keep-alive></keep-alive> ,user 原本的輸入將保留在 component 內

Sample Code

完整的範例可以在我的 GitHub 上找到

Reference

Vue , Component Basics

Vue , Style Guide


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据结构

数据结构

霍罗威茨 / 机械工业出版社 / 2006-7-1 / 48.00元

《数据结构》(C语言版)针对采用ANSI C实现数据结构进行了全面的描述和深入的讨论。书中详细讨论了栈、队列、链表以及查找结构、高级树结构等功能,对裴波那契堆、伸展树、红黑树、2-3树、2-3-4树、二项堆、最小-最大堆、双端堆等新的数据结构进行了有效分析。《数据结构》(C语言版)对一些特殊形式的堆结构,诸如应用在双端优先队列中的最小-最大堆和双端堆的数据结构以及左高树、裴波那契堆、二项堆等数据结......一起来看看 《数据结构》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具