Vue JS 2 Tutorial part 2

tags: Javascript, Vue.js

The Vue CLI

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

使用方式:

  1. 安裝node.js
  2. 確認是否安裝成功輸入 node -v 出版本就是成功摟
  3. npm install -g vue-cli
  4. vue create <project-name>

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

這邊作者選擇Maunally select features

可以視專案需求做選擇

下一步選擇vue.js的版本

下一步選擇 In dedicated config files

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

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

下一步使用

1
npm run serve

就可以成功叫出來瞜!

Vue Files & The Root Component

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

Vue Files

assets

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

main.js

控制所有的components的檔案

1
2
3
4
5
6
7
import Vue from 'vue'
import App from './App.vue'

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

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

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

App就是指這個部分的檔案,也就是The Root Component

並創造一個新的Vue實體:

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

html內部抓取的div

The Root Component

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

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

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

  • style
    處理畫面

特別注意

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>

印出內容:

Nesting Components

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

Nesting Globally

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

  1. import 資料夾名稱 from 資料夾位置
  2. 並且components建立在這裡Vue.component('ninjas', Ninjas);
1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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的內容成功渲染到頁面上

Nesting locally

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

  1. 把import的部分寫在App.vue內
  2. 把components的內容寫在App.vue內部script內
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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效果

印出結果:

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

Nesting Components Examples

成品:

成品功能:

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

HTML

畫面呈顯處index.html

html程式碼:

1
2
3
4
5
6
7
8
9
10
11
<!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內
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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會出現,不是則否
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<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

範例

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

  1. 把ninjas的資料都搬回Root
  2. 在Content.vue寫接收 props:['ninjas']
  3. 在App.vue 寫 輸出prop <app-content v-bind:ninjas="ninjas"></app-content>
  • App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<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的方式一樣使用
  2. 使用在scirpt 內部的methods的部分也是一樣可以直接調用
    1
    2
    3
    4
    5
    methods:{
    test:function(){
    this.ninjas
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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的檔案
1
2
3
4
5
6
7
8
9
10
11
12
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的內容
  2. 在App.vue多設置一對app-content讓內容呈現兩個
  3. 按下刪除按鈕會發現兩邊都會一起刪除內容

Content.vue

1
2
3
4
5
methods: {
deleteContent:function(){
this.ninjas.pop()
}
}

App.vue

1
2
3
4
5
6
7
8
9
10
11
<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操作的是同一個物件,任何改動都是同步的

Primitive 只會改變當前檔案

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

重點來了:

  1. 當我們在接受prop的檔案修改Primitive時,其他檔案不會像Reference一樣改變,而是保持原樣
  2. 在Header.vue使用methods changeTitle 當點擊header時修改其title為Vue Wizards
  3. header修改了,此時的footer保持原樣
1
2
3
4
<template>
<header>
<h1 v-on:click="changeTitle">{{title}}</h1></header>
</template>
1
2
3
4
5
methods: {
changeTitle:function(){
this.title ="Vue Wizards"
}
}

印出結果:

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

Events (child to parent)

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

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

  • 首先在Header.vue的部分做修改並送出資料:
  1. 使用 v-on:click觸發changeTitle來修改title內容
  2. function changeTitle的內容使用this.$emit('changeTitle', "Vue Wizards")來把資料回傳回去Root
1
2
3
4
<template>
<header>
<h1 v-on:click="changeTitle">{{title}}</h1></header>
</template>
1
2
3
4
5
methods: {
changeTitle:function(){
this.$emit('changeTitle', "Vue Wizards")
}
}
  • 在App.vue做接收Event的動作:
  1. 使用v-on:changeTitle="updateTitle($event)"接受Header.vue的資料
  2. 並使用其內容”Vue Wizards”修改Title
  3. methods的部分使用參數就是$event也就是”Vue Wizards”(名稱可以自訂)
1
2
3
4
5
6
7
<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>
1
2
3
4
5
methods: {
updateTitle:function(updatedTitle){
this.title = updatedTitle;
}
}

The Event Bus

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

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

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

範例說明Events Bus

  • 通常使用在想要改變 siblings或是任何其他的componets但不想經過Root來更動時
  1. 在main.js創造其Vue實體並且輸出

export const bus = new Vue();

  1. 在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”

1
2
3
4
5
6
methods: {
changeTitle:function(){
this.title = 'Vue Wizards';
bus.$emit('titleChanged',"Vue Wizards");
}
}
  1. Footer.vue做接收

引入bus也就是Bus Event的實體

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

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

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

1
2
3
4
5
created(){
bus.$on('titleChanged',(data)=>{
this.title = data;
})
}
  1. 通過Event Bus 的傳遞就不須經過Root而是透過Vue實體Bus來傳遞兩者的資料

  2. 需要注意的點是

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

  • 除了傳送資料的函式$emit之外,他還有使用this.title = 'Vue Wizards';修改當前自己目前的資料
  • 這個部份如果沒有處理則只會改變Footer.vue的title
  • 原因在於我們只有監聽Footer.vue的event而Header.vue的部分就必須這樣自己做修改
1
2
3
4
5
6
7
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 出現的時機

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<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,畢竟是在實體出現之前所以不會有畫面呈現

第二個出現的是created實體已經被創造了,但是還沒被mounted上去,故也沒有畫面

第三個出現的是beforeMounted,在mounted之前的可以做一些處理依舊沒有畫面

第四個出現的是mounted點擊OK之後畫面產生!

畫面產生

第五按下delete Content後會出現beforeUpdate按下OK後出現update

第六後會出現updated按下OK後即更新畫面

畫面更新

Slots

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

範例 slot的使用

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

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

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

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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

範例二 使用name抓取Root的slot

  • 這時候如果我想要h1, p 各別在不同位置該怎麼處理
  • 藉由命名的方式子層的slot就知道該抓取哪個部分:
  1. 在App.vue命名後,在子層 使用name去抓取

App.vue

1
2
3
4
5
6
7
8
<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

1
2
3
4
5
6
7
<template>
<div>
<slot name="title"></slot>
<h1> I am the form helper</h1>
<slot name="text"></slot>
</div>
</template>

印出結果:

就可以分開呈現內容

修飾子層的內容

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

formHelper.vue

1
2
3
4
5
<style scoped>
h1{
color:red;
}
</style>

處出結果:

動態顯示文字則在Root處理

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

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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>

印出結果:

範例三 slot真正的用法

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

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

在Root這邊處理內容:

  1. 使用form-helper tag包住要傳送到子層的內容
  2. 使用slot=”form-相關插槽”的方式抓取子層的架構並且填上內容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<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表個瞜

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
1
2
3
4
5
6
7
8
9
10
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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

Form Two

formOne, formTwo 程式碼