大家好,這里是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......
- github:https://github.com/Daotin/Web
- 微信公眾號:Web前端之巔
- 博客園:http://www.cnblogs.com/lvonve/
- CSDN:https://blog.csdn.net/lvonve/
在這里我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。現在就讓我們一起進入 Web 前端學習的冒險之旅吧!
一、Vue組件
什么是組件: 組件的出現,就是為了拆分 Vue 實例的代碼量的,能夠讓我們以不同的組件,來划分不同的功能模塊,將來我們需要什么樣的功能,就可以去調用對應的組件即可;
組件化和模塊化的不同:
- 模塊化: 是從代碼邏輯的角度進行划分的;方便代碼分層開發,保證每個功能模塊的職能單一;
- 組件化: 是從UI界面的角度進行划分的;前端的組件化,方便UI組件的重用;
二、定義組件
1、定義全局組件
定義全局組件有三種方式:
1、使用 Vue.extend
配合 Vue.component
方法:
// 1.使用 Vue.extend 來創建全局的Vue組件
var login = Vue.extend({
// 通過 template 屬性,指定了組件要展示的HTML結構
template: '<h1>登錄</h1>'
});
// 2.使用 Vue.component('組件的名稱', 創建出來的組件模板對象)
Vue.component('login', login);
// 3.使用組件
<div id="app">
<!-- 如果要使用組件,直接,把組件的名稱,以 HTML 標簽的形式,引入到頁面中即可 -->
<login></login>
</div>
注意:
使用 Vue.component 定義全局組件的時候,組件名稱使用了 駝峰命名(如myLogin),則在引用組件的時候,需要把 大寫的駝峰改為小寫的字母,同時在兩個單詞之前,使用 - 鏈接(
<my-login></my-login>
);如果不使用駝峰,則直接拿名稱來使用即可;
當然,上面兩步可以合成一個步驟完成:
Vue.component('login', Vue.extend({
template: '<h1>登錄</h1>'
}));
2、直接使用 Vue.component 方法:
Vue.component('login', {
template: '<div><h3>注冊</h3><span>123</span></div>'
});
注意:不論是哪種方式創建出來的組件,組件的 template 屬性指向的模板內容,必須有且只能有唯一的一個根元素,否則會報錯。
3、將模板字符串,定義到 template 標簽中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<!-- 3. 使用組件 -->
<mycom></mycom>
</div>
<!-- 2.在 被控制的 #box 外面,使用 template 元素,定義組件的HTML模板結構 -->
<template id="tmp1">
<!-- 還是需要遵從template 模板內容,必須有且只能有唯一的一個根元素 -->
<div>
<h3>登錄</h3>
<p>p標簽</p>
</div>
</template>
<script>
// 1.定義組件
Vue.component('mycom', {
template: '#tmp1'
});
var vm = new Vue({
el: "#box",
data: {},
methods: {}
});
</script>
</body>
</html>
注意:
1、
template: '#tmp1'
是定義模板標簽的 id ,# 別忘寫了。2、被控制的 #box 外面,使用 template 標簽;
3、 template 標簽里面,還是遵從只能有唯一的一個根元素的原則。
2、定義私有組件
定義私有組件,就是再VM實例中定義組件。
如下,box中可以使用,box2不可以使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<mycom></mycom>
</div>
<div id="box2">
<mycom></mycom>
</div>
<template id="temp">
<h3>自定義私有屬性</h3>
</template>
<script>
var vm = new Vue({
el: "#box",
data: {},
methods: {},
// 定義私有組件
components: {
mycom: {
template: '#temp'
}
}
});
var vm2 = new Vue({
el: "#box2",
data: {},
methods: {}
});
</script>
</body>
</html>
3、組件的data和methods屬性
組件中也可以有自己的data和methods屬性,可以傳入template中使用。
特點:
- data屬性為一個匿名函數,其返回值為一個對象。
- data 函數返回值為一個對象(最好是新開辟的對象,否則如果多次引用組件,不是新開辟的對象給的話,對象是同一份,而我們需要每一個組件有自己的對象),對象中可以放入數據。
- 組件中 的data和methods,使用方式,和實例中的 data 和methods使用方式完全一樣
<div id="box2">
<login></login>
</div>
<template id="temp2">
<div>
<input type="button" value="按鈕" @click="myclick">
<h3>自定義私有屬性</h3>
<p> {{msg}} </p>
</div>
</template>
<script>
Vue.component('login', {
template: '#temp2',
data: function () {
return {
msg: '這是組件中的data'
}
},
methods: {
myclick() {
console.log("點擊按鈕");
}
}
});
</script>
三、組件切換
我們在登錄注冊一個網站的時候,經常看到兩個按鈕,一個登錄,一個注冊,如果你沒有賬號的話,需要先注冊才能登錄。我們在點擊登錄和注冊的時候,網頁會相應的切換,登錄頁面就是登陸組件,注冊頁面就是注冊組件,那么點擊登錄和注冊,如何實現組件的切換呢?
1、方式一
使用flag
標識符結合v-if
和v-else
切換組件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<!-- 給a注冊點擊事件,切換flag狀態 -->
<a href="javascript:;" @click.prevent="flag=true">登錄</a>
<a href="javascript:;" @click.prevent="flag=false">注冊</a>
<!-- 使用v-if v-else切換組件 -->
<login v-if="flag">
</login>
<register v-else="flag">
</register>
</div>
<script>
Vue.component('login', {
template: '<h3>登錄組件</h3>'
});
Vue.component('register', {
template: '<h3>注冊組件</h3>'
});
var vm = new Vue({
el: "#box",
data: {
flag: true
},
methods: {}
});
</script>
</body>
</html>
缺陷:由於flag的值只有true和false,所以只能用於兩個組件間 的切換,當大於兩個組件的切換就不行了。
2、方式二
使用 component元素的:is
屬性來切換不同的子組件
使用 <component :is="componentId"></component>
來指定要切換的組件。
componentId:為需要顯示的組件名稱,為一個字符串,可以使用變量指定。
componentId: 'login'
// 默認顯示登錄組件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<!-- 給a注冊點擊事件,切換flag狀態 -->
<a href="javascript:;" @click.prevent="componentId='login'">登錄</a>
<a href="javascript:;" @click.prevent="componentId='register'">注冊</a>
<component :is="componentId"></component>
</div>
<script>
Vue.component('login', {
template: '<h3>登錄組件</h3>'
});
Vue.component('register', {
template: '<h3>注冊組件</h3>'
});
var vm = new Vue({
el: "#box",
data: {
componentId: 'login' // 默認顯示登錄
},
methods: {}
});
</script>
</body>
</html>
為組件切換添加過渡:
很簡單,只需要用 transition 將 component 包裹起來即可。
<transition>
<component :is="componentId"></component>
</transition>
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<link rel="stylesheet" href="./lib/animate.css">
<style>
.loginDiv {
width: 200px;
height: 200px;
background-color: red;
}
.registerDiv {
width: 200px;
height: 200px;
background-color: blue;
}
</style>
</head>
<body>
<div id="box">
<!-- 給a注冊點擊事件,切換flag狀態 -->
<a href="javascript:;" @click.prevent="componentId='login'">登錄</a>
<a href="javascript:;" @click.prevent="componentId='register'">注冊</a>
<transition mode="out-in" enter-active-class="animated bounceInRight" leave-active-class="animated bounceOutRight">
<component :is="componentId"></component>
</transition>
</div>
<template id="login">
<div class="loginDiv">
</div>
</template>
<template id="register">
<div class="registerDiv">
</div>
</template>
<script>
Vue.component('login', {
template: '#login'
});
Vue.component('register', {
template: '#register'
});
var vm = new Vue({
el: "#box",
data: {
componentId: 'login'
},
methods: {}
});
</script>
</body>
</html>
mode="out-in"
:可以設置切換組件的模式為先退出再進入。
四、組件傳值
1、父組件向子組件傳值
我們先通過一個例子看看子組件可不可以直接訪問父組件的數據:
<body>
<div id="box">
<mycom></mycom>
</div>
<template id="temp">
<h3>子組件 --- {{msg}}</h3>
</template>
<script>
var vm = new Vue({
el: "#box",
data: {
msg: '父組件的msg'
},
methods: {},
components: {
mycom: {
template: '#temp'
}
}
});
</script>
</body>
由於 components 定義的是私有組件,我們直接在子組件中調用父組件的msg會報錯。
那么,怎么讓子組件使用父組件的數據呢?
父組件可以在引用子組件的時候, 通過 屬性綁定(v-bind:) 的形式, 把需要傳遞給子組件的數據,以屬性綁定的形式,傳遞到子組件內部,供子組件使用 。
<body>
<div id="box">
<mycom v-bind:parentmsg="msg"></mycom>
</div>
<template id="temp">
<h3>子組件 --- 父組件:{{parentmsg}}</h3>
</template>
<script>
var vm = new Vue({
el: "#box",
data: {
msg: '父組件的msg'
},
methods: {},
components: {
mycom: {
template: "#temp",
// 對傳遞給子組件的數據進行聲明,子組件才能使用
props: ['parentmsg']
}
}
});
</script>
</body>
注意:父組件綁定的屬性名稱不能有大寫字母,否則不會顯示,並且在命令行會有提示:
組件data數據和props數據的區別:
- data數據是子組件私有的,可讀可寫;
- props數據是父組件傳遞給子組件的,只能讀,不能寫。
案例:發表評論功能
父組件為評論列表,子組件為ID,評論者,內容和按鈕的集合,在輸入ID,評論者等內容,然后點擊添加的時候,需要首先獲取子組件的list列表,然后再添加新的列表項到列表中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<mycom :plist="list"></mycom>
<ul>
<li v-for="item in list" :key="item.id">
ID:{{item.id}} --- 內容:{{item.content}} --- 評論人:{{item.user}}
</li>
</ul>
</div>
<template id="tmp1">
<div>
<label>
ID:
<input type="text" v-model="id">
</label>
<br>
<label>
評論者:
<input type="text" v-model="user">
</label>
<br>
<label>
內容:
<textarea v-model="content"></textarea>
</label>
<br>
<!-- 把父組件的數據作為子組件的函數參數傳入 -->
<input type="button" value="添加評論" @click="addContent(plist)">
</div>
</template>
<script>
var vm = new Vue({
el: "#box",
data: {
list: [{
id: Date.now(),
user: 'user1',
content: 'what'
}, {
id: Date.now(),
user: 'user2',
content: 'are'
}]
},
methods: {},
components: {
mycom: {
template: '#tmp1',
data: function () {
return {
id: '',
user: '',
content: '',
}
},
methods: {
addContent(plist) {
plist.unshift({
id: this.id,
user: this.user,
content: this.content
});
}
},
props: ['plist']
}
}
});
</script>
</body>
</html>
把添加ID,評論人,內容作為子組件,把列表作為父組件,然后把添加的數據放到父組件列表上,由於要獲取到父組件列表的數據,所以必然涉及到父組件向子組件傳值的過程。這里還通過子組件方法參數來保存父組件的數據到子組件的數據中。
2、父組件向子組件傳方法
既然父組件可以向子組件傳遞數據,那么也可以向子組件傳遞方法。
<body>
<div id="box">
<mycom v-bind:parentmsg="msg" @parentfunc="show"></mycom>
</div>
<template id="temp">
<div>
<input type="button" value="調用父組件方法" @click="sonClick">
<h3>子組件 --- 父組件:{{parentmsg}}</h3>
</div>
</template>
<script>
var vm = new Vue({
el: "#box",
data: {
msg: '父組件的msg'
},
methods: {
show(data1, data2) {
console.log("這是父組件的show方法" + data1 + data2);
}
},
components: {
mycom: {
template: "#temp",
// 對傳遞給子組件的數據進行聲明,子組件才能使用
props: ['parentmsg'],
methods: {
sonClick() {
// 調用父組件的show方法
this.$emit("parentfunc", 111, 222);
}
}
}
}
});
</script>
</body>
1、
@parentfunc="show"
綁定父組件的show方法。2、
<input type="button" value="調用父組件方法" @click="sonClick">
點擊按鈕調用父組件的show方法3、在 子組件的 sonClick 方法中使用
this.$emit("parentfunc");
來調用父組件的show方法4、父組件的show方法也可以傳參,在調用的時候,實參從 this.$emit 的第二個參數開始傳入。
5、如果 this.$emit 的第二個參數傳的是子組件的data數據,那么父組件的方法就可以獲得子組件的數據,這也是把子組件的數據傳遞給父組件的方式。
3、使用 ref 獲取DOM和組件的引用
我們知道Vue不推薦直接獲取DOM元素,那么在Vue里面怎么獲取DOM及組件元素呢?
我們呢可以在元素上使用 ref
屬性來獲取元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="box">
<input type="button" value="獲取元素" @click="getrefs" ref="mybtn">
<h3 ref="myh3">這是H3</h3>
<mycom ref="mycom"></mycom>
</div>
<template id="tmp1">
</template>
<script>
// 定義組件
Vue.component('mycom', {
template: '#tmp1',
data: function () {
return {
msg: '子組件的msg',
pmsg: ''
}
},
methods: {
show(data) {
console.log('調用子組件的show');
this.pmsg = data;
console.log(this.pmsg);
},
}
});
var vm = new Vue({
el: "#box",
data: {
parentmsg: '父組件的msg'
},
methods: {
getrefs() {
console.log(this.$refs.myh3);
console.log(this.$refs.mycom.msg);
this.$refs.mycom.show(this.parentmsg);
}
}
});
</script>
</body>
</html>
總結:
1、ref 屬性不僅可以獲取DOM元素,也可以獲取組件(無論全局還是私有組件)元素。
2、獲取到組件元素后,就可以獲取組件元素的data數據和methods方法。
3、獲取到組件中的方法后,可以傳入VM的data數據,就可以把VM的data數據傳入組件中。