---
# System prepended metadata

title: Vue JS 2 Tutorial part 2
tags: ['Javascript, Vue.js']

---

#  Vue JS 2 Tutorial part 2

###### tags: `Javascript, Vue.js`


# The Vue CLI


* 創造一個可以使用webpack的開發環境
* 使用ES6
* 編譯並且讓檔案容量縮小成一個JS檔
* 使用single file template給網頁的不同app使用
* 在渲染到畫面上前，編譯所有的內容在本地端而不是在瀏覽器上
* 使用在即時更新的伺服器上


使用方式:

1. [安裝node.js](https://nodejs.org/en/)
2. 確認是否安裝成功輸入 node -v 出版本就是成功摟
3. `npm install -g vue-cli`
4. `vue create <project-name>`

接下來會出現一些問題需要回答:

這邊作者選擇Maunally select features
![](https://i.imgur.com/jW0ghVg.png)

可以視專案需求做選擇
![](https://i.imgur.com/vVCw2Mk.png)

下一步選擇vue.js的版本
![](https://i.imgur.com/YFRho9t.png)

下一步選擇 In dedicated config files
![](https://i.imgur.com/xb3SfHX.png)

下一步會詢問是否儲存設定建議選擇N 畢竟每次專案設定不同看需求而定


接下來我們進入資料夾 cd vue-crash-2021會發現需要的資料都已經載入

![](https://i.imgur.com/hyCcMPd.png)

下一步使用
```
npm run serve
```
![](https://i.imgur.com/eXlTVHv.png)

就可以成功叫出來瞜!
![](https://i.imgur.com/fnsycfI.png)


# Vue Files & The Root Component

針對剛剛產生出來的vue-cli檔案做一些解析

## Vue Files

### assets 

* 放置圖片Logo的區域
* 需要使用到的img都可以放置在這區

### main.js

控制所有的components的檔案


```javascript=
import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})
```

從最基礎的檔案中可以看出它引入了:

1. Vue(就是整個框架內容)
1. App


App就是指這個部分的檔案，也就是The Root Component
![](https://i.imgur.com/ypz1R8a.png)


並創造一個新的Vue實體:

抓取id=app這個元素並且把App.vue的內容渲染到元素上面位於index.html內

![](https://i.imgur.com/hy0EJSM.png)

html內部抓取的div
![](https://i.imgur.com/fP0zRQ7.png)

## The Root Component

也就是剛剛提到的App的部分

可以發現這樣的vue檔案其實就是vue components的延伸，但是拆分到不一樣的檔案

* script
這邊處理把內容輸出到main.js裡面

* style
處理畫面

### 特別注意

template內部只能用一個div包裹住全部的html如果有兩個會報錯

![](https://i.imgur.com/sPJtlZO.png)


```htmlembedded=
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <ul>
      <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
    </ul>
    <h2>Ecosystem</h2>
    <ul>
      <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
      <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
      <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
```

## 範例

使用App.vue的內容來渲染index.html內部的id=app的tag

App.vue

```javascript=
<template>
  <div>
  <h1>{{title}}</h1>
  <p>{{greeting()}}</p>
  </div>
  
</template>

<script>
export default {
  data () {
    return {
      title:'yout first vue file'
    }
  },methods:{
    greeting() {
    return 'heeeeeeee cowboy!'
    }
  },
}
</script>

<style></style>
```

印出內容:

![](https://i.imgur.com/mmMmAZt.png)


# Nesting Components

* 創造components以及import它們、nest它們到其他的components

![](https://i.imgur.com/oFKUY5F.png)


## Nesting Globally

主要把import寫在main.js內部，並且所有的components都可以使用

1. import 資料夾名稱 from 資料夾位置
1. 並且components建立在這裡`Vue.component('ninjas', Ninjas);`


```javascript=
import Vue from 'vue'
import App from './App.vue'
import Ninjas from './Ninjas.vue'


Vue.component('ninjas', Ninjas);

new Vue({
  el: '#app',
  render: h => h(App)
})
```

在App.vue檔案內則要使用ninjas tag來使用這個components


App.vue
```javascript=
<template>
  <div>
  <h1>{{title}}</h1>
  <ninjas></ninjas>
  </div>
  
</template>

<script>
export default {
  data () {
    return {
      title:'New title for nesting components'
    }
  }
}
</script>

<style></style>
```

印出結果:

把Ninja.vue這個components的內容成功渲染到頁面上

![](https://i.imgur.com/U1zBixE.png)


## Nesting locally

不會操作在main.js上面，而是在想要渲染的components上面操作

1. 把import的部分寫在App.vue內
1. 把components的內容寫在App.vue內部script內

```javascript=
<template>
  <div>
  <h1>{{title}}</h1>
  <ninjas></ninjas>
  </div>
  
</template>

<script>
import Ninjas from './Ninjas.vue'

export default {
  components:{
    'ninjas': Ninjas
  },
  data () {
    return {
      title:'New title for nesting components'
    }
  }
}
</script>

<style></style>
```

# Component CSS (scoped)

在style tag加上 scoped讓其CSS只會影響到檔案本身


* 有socped的CSS會針對每個components的CSS新增一個屬性
* 避免複寫其他的components的CSS效果

![](https://i.imgur.com/a6nrbG6.png)


印出結果:

在App.vue寫的是h1 color:purple
在Ninjas.vue寫的是 h1 color:blue

![](https://i.imgur.com/EaH9rPq.png)


# Nesting Components Examples

## 成品:

![](https://i.imgur.com/snxMPqK.png)


## 成品功能:

1. 點擊人物名稱會顯示使用技能

## HTML

畫面呈顯處index.html

### html程式碼:

```htmlembedded=
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>vuejs-playist</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/build.js"></script>
  </body>
</html>
```

## vue-app

* 引入其他components的地點
* 使用components屬性添進進去templates內


```javascript=
<template>
<div>
  <app-header>
  </app-header>
  <app-content></app-content>
  <app-footer>
  </app-footer>
</div>
</template>

<script>
import Header from'./components/Header.vue'
import Footer from'./components/Footer.vue'
import Content from'./components/Content.vue'

  export default{
  components:{
    'app-header':Header,
    'app-footer':Footer,
    'app-content':Content,
  },
  data() {
    return {
    }
  }
  }
</script>
<style></style>
```

## Header.vue

```javascript=
<template>
    <header>
    <h1>{{title}}</h1>
    </header>
</template>

<script>
  export default{
  data() {
    return {
        title:'nesting example'
    }
  }
  }
</script>

<style scoped>
header{
    background:lightgreen;
    padding:10px;
}
h1{
    color:#222;
    text-align:center;
}
</style>
```

## Footer.vue

```javascript=
<template>
<footer>
    <p>{{copyright}}</p>
</footer>
</template>

<script>
  export default{
  data() {
    return {
        copyright:"Copyright 2021 Vue"
    }
  }
  }
</script>

<style scoped>
    footer{
        background:#222;
        padding:6px;
    }
    p{
        color:lightgreen;
        text-align:center;
    }
</style>
```
## Content.vue

使用:

* v-for 依序印出物件
* v-on:click 讓其內容可以被toggle
* v-show 當v-show為true時speciality會出現，不是則否

```javascript=
<template>
    <div id="ninjas">
    <ul>
        <li v-for="ninja in ninjas" v-on:click="ninja.show = !ninja.show">
            <h2>{{ninja.name}}</h2>
            <h3 v-show="ninja.show">{{ninja.speciality}}</h3>
        </li>
    </ul></div>
</template>

<script>
  export default{
  data() {
    return {
        ninjas:[
            {name:'Ryu',speciality:'Vue Components', show:false},
            {name:'Crystal',speciality:'HTML Wizardry', show:false},
            {name:'Hitoshi',speciality:'Click Events', show:false},
            {name:'Tango',speciality:'Conditionals', show:false},
            {name:'Kami',speciality:'Webpack', show:false},
            {name:'Yoshi',speciality:'Data Diggin', show:false},
        ]
    }
  }
  }
</script>

<style scoped>
  #ninjas{
      Width:100%;
      max-width:1200px;
      margin:40px auto;
      padding:0 20px;
      box-sizing:border-box;
  }

  ul{
      display:flex;
      flex-wrap:wrap;
      list-style-type:none;
      padding:0;
  }

  li{
      flex-grow:1;
      flex-basis:300px;
      text-align:center;
      padding:30px;
      border:1px solid #222;
      margin:10px
  }

</style>
```

# Props

* 從Root component 傳送資料給nesting components
* 使用情境是當需要不同的components需要相同的資訊時可以使用Props

![](https://i.imgur.com/kQZCG8H.png)

## 範例

把上面實作的範例做改寫:

1. 把ninjas的資料都搬回Root
1. 在Content.vue寫接收 `props:['ninjas']`
1. 在App.vue 寫 輸出prop `<app-content v-bind:ninjas="ninjas"></app-content>`

* App.vue

```javascript=
<template>
<div>
  <app-header>
  </app-header>
  <app-content v-bind:ninjas="ninjas"></app-content>
  <app-footer>
  </app-footer>
</div>
</template>

<script>
import Header from'./components/Header.vue'
import Footer from'./components/Footer.vue'
import Content from'./components/Content.vue'

  export default{
  components:{
    'app-header':Header,
    'app-footer':Footer,
    'app-content':Content,
  },
  data() {
    return {
        ninjas:[
            {name:'Ryu',speciality:'Vue Components', show:false},
            {name:'Crystal',speciality:'HTML Wizardry', show:false},
            {name:'Hitoshi',speciality:'Click Events', show:false},
            {name:'Tango',speciality:'Conditionals', show:false},
            {name:'Kami',speciality:'Webpack', show:false},
            {name:'Yoshi',speciality:'Data Diggin', show:false},
        ]
    }
  }
  }
</script>
<style></style>
```
* Content.vue

1. 可以從template內部發現不需要修改，prop像是components的方式一樣使用
1. 使用在scirpt 內部的methods的部分也是一樣可以直接調用
```javascript=
methods:{
    test:function(){
    this.ninjas
    }
}
```

```javascript=
<template>
    <div id="ninjas">
    <ul>
        <li v-for="ninja in ninjas" v-on:click="ninja.show = !ninja.show">
            <h2>{{ninja.name}}</h2>
            <h3 v-show="ninja.show">{{ninja.speciality}}</h3>
        </li>
    </ul></div>
</template>

<script>
  export default{
  props:['ninjas'],
  data() {
    return {
      
    }
  }
  }
</script>
```

## Validation

* 當收到props時我們必須確認是我們需要的資料型別(String, Array...)，這邊就可以使用Validation
* required則用來確認是否Root components成功傳送props過來
* 使用在接收props的檔案

```javascript=
export default {
    props: {
      ninjas: {
        type: Array,
        required: true
      }
    },
    data(){
        return{
        }
    }
}
```


# Primitive vs Reference Types

## Primitive

Number, String, Boolean, 

如果修改了Primivtive的資料則只會影響當下檔案的內容而不會影響到Root component，這是因為 by Primitive的性質

## Reference

Object, Array

如果修改 Reference的資料Root跟使用的component都會一起改變，這是因為 by Reference的性質


## 範例說明兩者區別

### Reference 會全部一起改變

1. 在Content.vue設置一個method刪除Content的內容
1. 在App.vue多設置一對app-content讓內容呈現兩個
1. 按下刪除按鈕會發現兩邊都會一起刪除內容

Content.vue
```javascript=
methods: {
        deleteContent:function(){
            this.ninjas.pop()
        }
    }
```

App.vue
```javascript=
<template>
<div>
  <app-header>
  </app-header>
  <app-content v-bind:ninjas="ninjas"></app-content>
  <hr/>
  <app-content v-bind:ninjas="ninjas"></app-content>
  <app-footer>
  </app-footer>
</div>
</template>
```

印出結果:

因為by Reference操作的是同一個物件，任何改動都是同步的

![](https://i.imgur.com/XIAf4fw.png)

### Primitive 只會改變當前檔案

* 設置App.vue的內容 title:"Vue contnet" prop到header, footer內
* 改變App.vue的內容會讓header, footer內容跟著改變

重點來了:

1. 當我們在接受prop的檔案修改Primitive時，其他檔案不會像Reference一樣改變，而是保持原樣
1. 在Header.vue使用methods changeTitle 當點擊header時修改其title為Vue Wizards
2. header修改了，此時的footer保持原樣

```javascript=
<template>
    <header>
    <h1 v-on:click="changeTitle">{{title}}</h1></header>
</template>
```

```javascript=
methods: {
        changeTitle:function(){
            this.title ="Vue Wizards"
        } 
    }
```

印出結果:

因為這邊props傳送的是Primitive，當修改本地檔案時，不會影響其他檔案內容

![](https://i.imgur.com/0Mo9sw9.png)


# Events (child to parent)


* props是把資料從Root 傳到其他 components
* Events則是把資料從其他 components傳回 Root

![](https://i.imgur.com/5DTgLjZ.png)


## 範例-使用Event回傳修改Root進而透過prop修改其他components

* 首先在Header.vue的部分做修改並送出資料:

1. 使用 v-on:click觸發changeTitle來修改title內容
1. function changeTitle的內容使用`this.$emit('changeTitle', "Vue Wizards")`來把資料回傳回去Root

```javascript=
<template>
    <header>
    <h1 v-on:click="changeTitle">{{title}}</h1></header>
</template>
```

```javascript=
methods: {
        changeTitle:function(){
            this.$emit('changeTitle', "Vue Wizards")
        } 
    }
```

* 在App.vue做接收Event的動作:

1. 使用`v-on:changeTitle="updateTitle($event)"`接受Header.vue的資料
1. 並使用其內容"Vue Wizards"修改Title
1. methods的部分使用參數就是`$event`也就是"Vue Wizards"(名稱可以自訂)

```javascript=
<template>
<div>
  <app-header v-bind:title="title" v-on:changeTitle="updateTitle($event)"></app-header>
  <app-content v-bind:ninjas="ninjas"></app-content>
  <app-footer v-bind:title="title"></app-footer>
</div>
</template>
```


```javascript=
 methods: {
      updateTitle:function(updatedTitle){
        this.title = updatedTitle;
      }
    }
```

# The Event Bus

* 在Event的部分是我們操作過從其他components傳送資料回去Root再由Root改變其他components

* 而Event Bus做的事情則簡化這個過程:

1. 創造一個新的Vue實體
1. 並且import到想要操作的componets內
1. 這樣就可以彼此溝通而不需要傳回Root再傳出來


## 範例說明Events Bus

* 通常使用在想要改變 siblings或是任何其他的componets但不想經過Root來更動時

1. 在main.js創造其Vue實體並且輸出

`export const bus = new Vue();`

2. 在Header.vue(想要改變的檔案):

引入bus也就是Bus Event的實體

`import {bus} from '../main';`

這邊的methods處則不使用$emit把資料傳回Root

而是使用`this.title = 'Vue Wizards'`
直接修改Header.vue的title

並且使用引入的bus做$emit資料出去給想要修改內容的其他components，並設定名稱titlechanged以及內容"Vue Wizards"


```javascript=
methods: {
        changeTitle:function(){
            this.title = 'Vue Wizards';
            bus.$emit('titleChanged',"Vue Wizards");
        } 
    }
```

3. Footer.vue做接收

引入bus也就是Bus Event的實體

`import {bus} from '../main';`

使用life cycle hook: created 當實體被創建時會馬上觸發此函式

接收來自Header.vue的資料也就是titleChanged使用`$on()`
並使用callback函式處理修改title成Header.vue傳送來的資料也就是"Vue Wizards"

```javascript=
created(){
    bus.$on('titleChanged',(data)=>{
      this.title = data;
    })
  }
```

4. 通過Event Bus 的傳遞就不須經過Root而是透過Vue實體Bus來傳遞兩者的資料

5. 需要注意的點是

你可以在Header.vue檔案中發現

* 除了傳送資料的函式$emit之外，他還有使用`this.title = 'Vue Wizards';`修改當前自己目前的資料
* 這個部份如果沒有處理則只會改變Footer.vue的title
* 原因在於我們只有監聽Footer.vue的event而Header.vue的部分就必須這樣自己做修改

```javascript=
methods: {
        changeTitle:function(){
            // this.$emit('changeTitle', "Vue Wizards")
            this.title = 'Vue Wizards';
            bus.$emit('titleChanged',"Vue Wizards");
        } 
    }
```

# Life-cycle Hooks

**beforeCreate**
Vue實體初始化後立刻呼叫此函式，不過此時Vue實體還未創建所以其中的設定都還未能使用(如data observation, event, watcher setup)

**created**
Vue實例創建完成後立刻呼叫此函式，已設置 data, computed properties, methods, watch/event callbacks，但尚未開始mounting階段，且 $el 還不能在此階段使用。


**beforeMount**
在mounting階段開始前被調用：render function首次被調用。

**mounted**
選項物件中的el被新創建的vm.$el替換，並掛載到到 vm 上，並調用mounted這個鉤子。

**beforeUpdate**
數據被更新時會調用，發生在 Virtual DOM re-render 和 patch 之前(連結：Day4: Virtual DOM)，可以在此時更改狀態數據，並不會增加重新渲染的成本。

**updated**
由於數據更新導致 Virtual DOM re-render 和 patch 之後會調用updated這個鉤子。

不精確白話文為：由於updated被調用時，DOM 已經更新。所以在此時更新數據很可能會導致updated無限循環的被調用。

**beforeDestroy**
在 Vue Instance 被銷毀前被調用，因此 Vue Instance 在beforeDestroy中仍可運作。

不精確白話文為：Vue Instance 可以在此時做垂死前的掙扎。

**destroyed**
在 Vue Instance 被銷毀後被調用，此時 Vue Instance 所有東西會解除綁定，事件監聽也都會被移除，子實例也會被銷毀。

## 範例 - life cycle hook 出現的時機 

```javascript=
<template>
    <div id="ninjas">
    <ul>
        <li v-for="ninja in ninjas" v-on:click="ninja.show = !ninja.show">
            <h2>{{ninja.name}}</h2>
            <h3 v-show="ninja.show">{{ninja.speciality}}</h3>
        </li>
    </ul>
    <button v-on:click="deleteContent">Delete Content</button>
    </div>
</template>

<script>
export default {
    props: {
      ninjas: {
        type: Array,
        required: true
      }
    },
    data(){
        return{
        }
    },
    methods: {
        deleteContent:function(){
            this.ninjas.pop()
        }
    },
     // lifecycle hooks
    beforeCreate(){
        alert('beforeCreate');
    },
    created(){
        alert('created');
    },
    beforeMount(){
        alert('beforeMount');
    },
    mounted(){
        alert('mounted');
    },
    beforeUpdate(){
        alert('beforeUpdate');
    },
    updated(){
        alert('updated');
    }
}
</script>

<style scoped></style>
```

重整畫面第一個出現的就是beforeCreate，畢竟是在實體出現之前所以不會有畫面呈現
![](https://i.imgur.com/jBDv9Hb.png)


第二個出現的是created實體已經被創造了，但是還沒被mounted上去，故也沒有畫面
![](https://i.imgur.com/DCR9cer.png)

第三個出現的是beforeMounted，在mounted之前的可以做一些處理依舊沒有畫面
![](https://i.imgur.com/tWJuOUR.png)


第四個出現的是mounted點擊OK之後畫面產生!
![](https://i.imgur.com/O8iFfJw.png)

畫面產生
![](https://i.imgur.com/F3i8VOo.png)


第五按下delete Content後會出現beforeUpdate按下OK後出現update
![](https://i.imgur.com/ZLlUIz1.png)


第六後會出現updated按下OK後即更新畫面
![](https://i.imgur.com/1UDf4rm.png)

畫面更新
![](https://i.imgur.com/Kxmc2Vt.png)


# Slots

* 在子元件上面開個洞， 由外層元件將內容置放在至子層元件指定的位置中
* 可以傳送HTML tag藉由slot


## 範例 slot的使用

1. 要使用slot必須先引入子層到App.vue
`import formHelper from'./components/formHelper.vue'`

2. 輸出components處輸出到formHelper位置

```javascript=
export default{
  components:{
    'form-helper':formHelper
  }
```
3. template處放入創好的components - form-helper
4. 輸入內容



App.vue
```javascript=
<template>
<div>
  <form-helper>
    <h2>I am the slot title</h2>
    <p>I am the paragraph text for the slot</p>
  </form-helper>
</div>
</template>

<script>
import formHelper from'./components/formHelper.vue'


  export default{
  components:{
    'form-helper':formHelper
  },
  data() {
    return {
    }
  },
  methods: {
      }
    }
  
</script>
<style></style>
```

1. 在formHelper.vue則在template使用slot tag來接收來自Root的資料



formHelper.vue
```javascript=
<template>
    <div>
    <h1> I am the form helper</h1>
    <slot></slot>
    </div>
</template>

<script>
  
  export default{
    components:{

    },
    data() {
    return {
        
    }
    },
    
    methods: {
       
        } 
    }
</script>
<style scoped></style>
```

印出結果:

就可以使用這樣的方式傳遞HTML

![](https://i.imgur.com/WSfqrJ9.png)


## 範例二 使用name抓取Root的slot

* 這時候如果我想要h1, p 各別在不同位置該怎麼處理
* 藉由命名的方式子層的slot就知道該抓取哪個部分:

1. 在App.vue命名後，在子層 使用name去抓取

App.vue
```javascript=
<template>
<div>
  <form-helper>
    <h2 slot="title">I am the slot title</h2>
    <p slot="text">I am the paragraph text for the slot</p>
  </form-helper>
</div>
</template>
```

formHelper.vue
```javascript=
<template>
    <div>
    <slot name="title"></slot>
    <h1> I am the form helper</h1>
    <slot name="text"></slot>
    </div>
</template>
```
印出結果:

就可以分開呈現內容

![](https://i.imgur.com/1S58ekw.png)


### 修飾子層的內容

* 要處理子層的style則必須在子層的style處理

formHelper.vue

```javascript=
<style scoped>
h1{
    color:red;
}
</style>
```
處出結果:

![](https://i.imgur.com/tEWiLgj.png)


### 動態顯示文字則在Root處理

* 動態文字要修改的話主要處理在Root內

App.vue
```javascript=
<template>
<div>
  <form-helper>
    <h2 slot="title">{{title}}</h2>
    <p slot="text">I am the paragraph text for the slot</p>
  </form-helper>
</div>
</template>

<script>
import formHelper from'./components/formHelper.vue'


  export default{
  components:{
    'form-helper':formHelper
  },
  data() {
    return {
      title:'I am a dynamic slot title'
    }
  },
  methods: {
      }
    }
  
</script>
<style></style>
```

印出結果:

![](https://i.imgur.com/JnOI6PA.png)


## 範例三 slot真正的用法

上面的偏向展示slot怎麼用，接下來會使用真實的範例說明如何使用slot:

製作一個網站，需要數個不同的form表單，並且他們的結構要相似但是內容必須要可以修改這時候就可以使用slots這個概念


在Root這邊處理內容:

1. 使用form-helper tag包住要傳送到子層的內容
1. 使用slot="form-相關插槽"的方式抓取子層的架構並且填上內容


```javascript=
<form-helper>
    <div slot="form-header">
      <h3>This is the title of the form</h3>
      <p>information about the form</p>
    </div>
    <div slot="form-fields">
      <input type="text" placeholder="name" required>
    ![](https://i.imgur.com/LiN6jD8.png)
  <input type="password"placeholder="password" required>
    </div>
    <div slot="form-controls">
      <button v-on:click="handleSubmit">Submit</button>
    </div>
  </form-helper>
```


App.vue
```javascript=
<template>
<div>
  <form-helper>
    <div slot="form-header">
      <h3>This is the title of the form</h3>
      <p>information about the form</p>
    </div>
    <div slot="form-fields">
      <input type="text" placeholder="name" required>
      <input type="password"placeholder="password" required>
    </div>
    <div slot="form-controls">
      <button v-on:click="handleSubmit">Submit</button>
    </div>
  </form-helper>
</div>
</template>

<script>
import formHelper from'./components/formHelper.vue'


  export default{
  components:{
    'form-helper':formHelper
  },
  data() {
    return {
      title:'I am a dynamic slot title'
    }
  },
  methods: {
      }
    }
  
</script>
<style></style>
```


子層的formHelper這邊可以處理好form的架構，使用slot name的方式接受來自Root的資料

* form-header
* form-fields
* form-controls
* useful-links


formHelper.vue
```javascript=
<template>
    <div>
        <h1>Please fill out our form...</h1>
        <form >
            <div id="form-header">
                <slot name="form-header"></slot>
            </div>
            <div id="form-fields">
                <slot name="form-fields"></slot>
            </div>
            <div id="form-controls">
                <slot name="form-controls"></slot>
            </div>
            <div id="uesful-links">
                <ul>
                    <li><a href="#">link1</a></li>
                    <li><a href="#">link2</a></li>
                    <li><a href="#">link3</a></li>
                    <li><a href="#">link4</a></li>
                </ul>
            </div>
        </form>
    </div>
</template>

<script>
  export default{
    components:{
    },
    data() {
    return {
    }
    },
    methods: {  
        } 
    }
</script>
<style scoped>~這邊我就省略~</style>
```


印出結果:

下次想要打造form表格就是直接在App.vue內 使用template form-helper並且抓取想要修改的slot修改好內容後就可以在做出另一個form表個瞜

![](https://i.imgur.com/o3DM1ho.png)


# Dynamic components

在上一篇課程中我們製作了格式固定但是內容可以更改的表格，這次我們針對這個表格想要動態的切換表格內容(新增了兩個表格檔案)，比方 點擊按鈕切換表格一變成二


App.vue

1. 首先在引入檔案進App.vue
2. 註冊components
3. 使用component template 並且使用 is 抓取要使用的component
4. 使用v-bind讓 is的部分做動態並在data處理component
5. 針對按鈕使用click事件 動態顯示當點哪個按鈕呈現哪個內容
6. 最後為了保存表格input內的資料輸入不會因為切換表格被刪除使用 keep-alive template

```html=
<template>
    <div>
        <keep-alive>
          <component v-bind:is='component'></component>
        </keep-alive>
        <button v-on:click="component = 'form-one'">Show form one</button>
        <button v-on:click="component = 'form-two'">Show form two</button>

    </div>
</template>
```

```javascript=
<script>
// Imports
import formOne from './components/formOne.vue';
import formTwo from './components/formTwo.vue';
export default {
    components: {
        'form-one': formOne, // 註冊component
        'form-two': formTwo
    },
    data () {
        return {
            component: 'form-one' // v-bind 處理的動態部分
        }
    },
    methods: {
        handleSubmit: function(){
            alert('thanks for submitting');
        }
    }
}
</script>
```

在下兩個頁面中切換並且儲存input輸入的內容不會因為切換頁面而不見

Form One
![](https://i.imgur.com/erbTJLe.png)

Form Two
![](https://i.imgur.com/GWqU9ls.png)



[formOne, formTwo 程式碼](https://github.com/iamshaunjp/vuejs-playlist/tree/lesson-28/src/components)

