Vue 之 Component

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

内容简介: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 有自己的 data 、 methods 、 computed 、 watch ,但 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 :
    • CamelCase : HelloWorld 、 HelloWorld.js 、 HelloWorld.vue
    • kebab-case : hello-world 、 hello-world.js 、 hello-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


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

查看所有标签

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

程序员第二步

程序员第二步

尹华山 / 人民邮电出版社 / 2013-11 / 45.00元

这本书是写给程序员和项目经理的。作者结合自身的丰富成长历程,通俗易懂地讲述了一名程序员如何才能成为一名优秀的项目经理。内容涉及职业规划、学习方法、自我修炼、团队建设、项目管理等,书中理清了项目管理领域中典型的误区及具有迷惑性的观点,并对项目中的难点问题提出了针对性的解决方法。 全书行文流畅,严谨中带着活泼,理智中透着情感,给读者带来轻松愉快的阅读感受。书中诸多富有创见的观点,让人耳目一新,引......一起来看看 《程序员第二步》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具