首發日期:2019-01-26
上節內容回顧
- 數據綁定:v-model
- 樣式綁定:v-bind:class,v-bind:style
- 事件:v-on
- Vue指令
- 數組操作(知道哪些數組操作是能被vm層監聽到並能響應式更新到視圖上的)
- Vue的元素復用問題(不使用key時會盡量復用)
組件
【官方的話】組件系統是 Vue 的另一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可復用的組件構建大型應用。仔細想想,幾乎任意類型的應用界面都可以抽象為一個組件樹:
小菜鳥的話:定義組件就好像定義了一堆“帶名字”的模板,比如說可能會有叫做“頂部菜單欄”的組件,我們可以多次復用這個“頂部菜單欄”而省去了大量重復的代碼。
什么是組件
- 在以前的多頁面開發的時候,我們可能會經常需要一個“頂部菜單欄”,於是我們在每個html文件中都要加上關於“頂部菜單欄”的代碼。可能你會想這份代碼能夠“復用”就好了。而組件可以定義模板,從而達到復用代碼的作用。
- 組件可以大大提高我們構建頁面的效率。
- 你可以將組件進行任意次數的復用
下面用代碼來演示一下"復用效果":
<body>
<div id="app">
<my-template></my-template><!-- 利用組件名來使用定義的“模板” -->
<my-template></my-template>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('my-template',{ // 第一個參數是組件名,第二個參數是模板的內容
template: '<div><span>我的模板</span></div>'
})
var vm = new Vue({
el: '#app'
})
</script>
代碼效果:
組件注冊
組件注冊就是“定義模板”,只有注冊了的組件,Vue才能夠了解怎么渲染。
全局注冊組件
- 全局注冊的組件可以用在其被注冊之后的任何 (通過 new Vue) 新創建的 Vue 實例中,也包括其組件樹中的所有子組件的模板中。【一個Vue應用只有一個根實例,但還允許有其他的實例。在 Vue 里,一個組件本質上是一個擁有預定義選項的一個 Vue 實例。】
- 全局注冊的行為必須在根 Vue 實例 (通過 new Vue) 創建之前發生
- 全局注冊的組件可以在另一個組件中使用。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<ol>
<!-- 使用組件 -->
<todo-item></todo-item>
<todo-item></todo-item>
<todo-item></todo-item>
</ol>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//全局定義組件,第一個參數是組件名,template的值是組件的內容
Vue.component('todo-item', {
template: '<li>這是個待辦項</li>'
})
// 實例化是必須的,要把使用組件的區域交給vue管理
var app = new Vue({
el: '#app',
})
</script>
</html>
局部注冊組件
全局注冊往往是不夠理想的。比如,如果你使用一個像 webpack 這樣的構建系統,全局注冊所有的組件意味着即便你已經不再使用一個組件了,它仍然會被包含在你最終的構建結果中。這造成了用戶下載的 JavaScript 的無謂的增加。
在這些情況下,你可以通過一個普通的 JavaScript 對象來定義組件:
<body>
<div id="app">
<my-component></my-component><!-- 利用組件名來使用組件 -->
<my-component></my-component>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var ComponentA = { // 定義一個組件
template: '<div><span>我的模板</span></div>'
}
var vm = new Vue({
el: '#app',
components: { // 然后在實例中聲明要使用這個組件,key是在這個實例中的組件名,value是組件
'my-component': ComponentA
}
})
</script>
上面的全局注冊說了允許在組件中使用其他組件,但注意局部注冊的組件要聲明使用其他組件才能夠嵌套其他組件。例如,如果你希望 ComponentA 在 ComponentB 中可用,則你需要這樣寫:
<body>
<div id="app">
<my-component></my-component>
<my-component-b></my-component-b>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var ComponentA = { // 1.定義一個組件
template: '<div><span>我的模板</span></div>'
}
var ComponentB = { // 2.定義一個組件
components: { //3.聲明使用A組件
'my-component': ComponentA
},
template: '<my-component></my-component>' // 4.需要在組件的template中寫上另一個組件
}
var vm = new Vue({
el: '#app',
components: {
'my-component': ComponentA,
'my-component-b': ComponentB
}
})
</script>
使用細節
組件注冊的命名規范:
組件名可以使用類my-component-name(kebab-case (短橫線分隔命名))或MyComponentName的格式(PascalCase 首字母大寫命名法),使用組件的時候可以<my-component-name>
或<MyComponentName>
,但在有些時候首字母大寫命名法定義組件的是不行的,所以通常推薦使用<my-component-name>
【當你使用首字母大寫命名法來定義組件的時候,不能直接在body中直接寫組件名,而要求寫在template中,如下例】。
<body>
<div id="app">
<my-component></my-component>
<my-component-demo></my-component-demo>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('my-component',{
template: '<div><span>我的模板A</span></div>'
})
Vue.component('my-component-demo',{ // 這個是用來測試第二種命名法定義組件的,首字母大寫時要寫在字符串模板中才能顯示(不然顯示不了)
template: '<MyComponentB></MyComponentB>'
})
Vue.component('MyComponentB',{
template: '<div><span>我的模板B</span></div>'
})
var vm = new Vue({
el: '#app'
})
</script>
組件中只有一個根元素
每個組件必須只有一個根元素!!
所以下面是不合法的:
如果你確實要有多個元素,那么要有一個根元素包裹它們:
組件也是一個實例
組件也是一個實例,所以組件也可以定義我們之前在根實例中定義的內容:data,methods,created,components等等。
但一個組件的 data 選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝
組件在某些元素中渲染出錯
在一些html元素中只允許某些元素的存在,例如tbody元素中要求有tr,而不可以有其他的元素(有的話會被提到外面)。下面是一個元素被提到外面的例子【而ul並沒有太嚴格,所以我們在前面的todo-list的例子中能夠演示成功】
下圖可以看的出來div被提到table外面了:
這是為什么呢?目前來說,我們在頁面中其實是先經過html渲染再經過vue渲染的(后面項目話后是整體渲染成功再展示的),當html渲染時,它就發現了tr里面有一個“非法元素”,所以它就把我們自定義的組件提到了table外面。
解決方案:
使用tr元素,元素里面有屬性is,is的值是我們要使用的組件名
<body>
<div id="app">
<table>
<tr is='mtr'></tr>
</table>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('mtr', {
template: `<tr><td>123</td></tr>` // 這個成功的檢測要使用檢測元素來看
});
var app = new Vue({
el: '#app'
})
</script>
但不會在以下情況中出錯: 1. 定義組件的時候,template中包含自定義的組件 2. [單文件組件,也就是說引用vue文件來注冊一個組件的時候(這個東西會在后面講)。](https://cn.vuejs.org/v2/guide/single-file-components.html) 3. 因為博客園的策略,下面的代碼會顯示有問題,我直接markdown顯示吧。 ```` [] (https://cn.vuejs.org/v2/guide/components-edge-cases.html#X-Templates)
<br>
------------------
# 組件間的數據傳遞
<br>
在上面定義的組件中使用的數據都是固定的數據,**通常我們都希望模板能根據我們傳入的數據來顯示**。
<br>
## 父子組件傳遞數據
(子組件的意思是當前組件的直接子組件,**在目前的單個html文件為例時,你可以僅認為是當前頁面的非嵌套組件。后面講到多個組件的合作時由於多個組件之間的嵌套,就形成了組件的父子、祖孫、兄弟關系**)
要給子組件傳遞數據主要有兩個步驟
1. 在**定義組件**時,使用props:['數據名'] (可以有多個數據名) 來聲明來傳入的數據的名字
2. 使用組件時,**給組件中名為"數據名"的屬性賦值**
<br>
演示代碼如下:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<ol>
<!-- 2.給數據賦值 -->
<todo-item todo="第一個值"></todo-item>
<todo-item todo="第二個值"></todo-item>
<todo-item :todo="msg"></todo-item>
<!-- 傳入的數據可以是父組件實例中定義的數據。注意要用:來綁定,不然不會識別成實例數據,而是一個字符串 -->
</ol>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('todo-item', {
props: ['todo'], // 1.使用props來定義這個組件支持傳的參數
template: '<li>{{ todo }}</li>' // 3.這里演示一下在組件中使用這個傳入的參數
})
var app = new Vue({
el: '#app',
data: {
msg: 'hello world'
}
})
</script>
</html>
代碼效果:很明顯的,我們的值成功傳給子組件了。
子組件向父組件傳輸數據
- 我們可以在子組件中使用emit來觸發事件,然后在使用這個組件的時候綁定這個事件就可以監聽到這個事件的發生(這時候調用的函數是父組件的處理函數),從而使得父組件接受到子組件傳遞的消息了。
要給父組件傳遞數據主要有兩個步驟
- 在定義組件時,定義一個包含觸發事件的元素,這個事件觸發時將會調用emit來觸發事件【例如可以在按鈕上定義一個onclick事件,這個onclick事件觸發時將會調用emit】
$emit()
可以有多個參數,第一個參數是觸發的事件的名稱,后面的參數都是隨着這個事件向外拋出的參數。- 使用組件時,對組件進行事件監聽,監聽的事件與組件內拋出的事件一致
- 定義處理目標事件的函數,函數的參數是隨事件向外拋出的多個參數。
演示代碼如下:
<body>
<div id="app" >
<my-component-c v-on:change-color="doSomething"></my-component-c>
<!-- 2.使用組件的時候監聽一下事件,這里將會調用父組件的函數來處理事件 -->
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('my-component-c', {
template: `
<button @click="$emit('change-color','hello')">改變顏色</button>
` // 1.定義組件,在組件內定義一個能觸發click事件的按鈕,onclick事件發生時將會調用emit來向外拋出事件【這里觸發的事件不能使用駝峰命名法。推薦使用短橫線命名法】
// 【第一個參數是自定義事件名稱,第二個和后續的參數可以用於向組件拋出值,這些值會作為事件響應函數的參數】
})
var app = new Vue({
el: '#app',
data: {
msg: 'hello world'
},
methods: {
// 3.將會調用下面的函數來處理事件
doSomething: function(value){ // 這里的value是從子組件中拋出的。
console.log(value) // 打印一下
}
}
})
</script>
【小tips:上面有多重字符串的使用,普通的雙引號和單引號已經不足以嵌套使用了,在外層可以使用反引號
`【esc下面那個鍵】來包裹,它也可以達到字符串包裹的效果,特別的是它支持多行字符串。】
非父子組件之間的傳值
祖孫組件傳數據、兄弟組件傳數據都屬於非父子組件之間的傳值。
- 【如果是祖孫組件傳數據,可以使用父組件傳給子組件,子組件傳給孫組件。但這是一個費事的做法。】
- 一般都會使用vuex,vuex就像一個變量存儲器,我們可以把一些多個組件都需要用到數據存儲到vuex的store中。【這個由於內容較重要,留到后面再講】
- 只有少量組件使用某個數據的時候也可以使用bus模式,bus相當於給每一個組件都加上“同一個”新的vue實例,由於bus是實例之間共享的,當數據發生改變時,可以利用這個vue實例來調用emit方法來拋出新值,而其他組件監聽bus中的事件就可以獲取到新的值,這樣就實現了非父子組件之間的傳值。
使用bus傳輸數據的步驟:
- 在Vue的原型上定義vue:
Vue.prototype.bus = new Vue()
- 當數據發生變化時,調用emit:
this.bus.$emit('change',當前組件的數據)
- 在組件上監聽bus的事件:
this.bus.$on('change',一個用於賦值的函數)
- 在函數中獲取事件觸發帶過來的參數,賦給當前組件,從而實現兩邊數據同步。
下面的代碼是點擊某個組件發生數據變化時,另一個組件的數據也發生變化:
<body>
<div id="app">
<child-a></child-a>
<child-a></child-a>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 1. 給Vue的原型添加一個bus,bus是一個Vue實例
Vue.prototype.bus = new Vue()
Vue.component('child-a', {
data: function() {
return {
content: 0
}
},
methods: {
// 2.定義一個函數,能夠在數據發生變化時調用emit(這里定義一個點擊事件的響應函數)
handleClick: function() {
this.content+=1
this.bus.$emit('change',this.content)
}
},
mounted: function(){
var this_= this // 這里要有一個這樣的賦值,下面不能直接使用this,因為在函數中this指向的已經不是當前對象了,而用_this保存的才是當前對象
// 3. 在組件中對bus中觸發的事件進行監聽。(當emit觸發事件時會調用)
this.bus.$on('change',function(msg){ // 4.函數中的參數是emit觸發事件帶的參數。
this_.content = msg // 5.修改當前組件中的數據
})
},
template: `<div @click='handleClick'>{{ content }}</div>`
});
var app = new Vue({
el: '#app'
})
</script>
單向數據流
- 單向數據流:props使得父組件的數據能夠傳輸到子組件,而且傳輸的源數據發生改變時,子組件也會發生改變。但如果在子組件中更改prop,這是不行的,會報警告。
- 每次父級組件發生更新時,子組件中所有的 prop 都將會刷新為最新的值。如果允許你在子組件中修改父組件傳入的數據的話,使用了父組件的這個數據的所有子組件的數據都會被修改(這樣就降低了組件的復用效果,導致數據流行不確定,難以確定這個數據是在哪個組件中修改的,而且是一個相對危險的行為)
- 你不應該在一個子組件內部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制台中發出警告。【這就是單向數據流】
- 如果你確實需要修改:
- 那么你應該創建一個子組件內部的數據,這個內部數據由傳入的prop的數據進行初始化。(也就是進行數據拷貝)
- 又或者你可以使用計算屬性。
Props屬性
## 命名規范:
大小寫問題
【有個建議,建議寫屬性名的時候都使用kebab-case (短橫線分隔命名) 命名,因為這個的兼容效果最好】
HTML 中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。如果你在props中使用了駝峰命名法,那你在定義屬性的時候需要使用kebab-case (短橫線分隔命名) 命名才能正確傳輸數據【因為短橫線后面的字符可以識別成大寫,從而能夠匹配到】。
如果在屬性中也使用駝峰命名法命名屬性的時候會報這樣的錯:Prop "mycontent" is passed to component
, but the declared prop name is "myContent". Note that HTML attributes are case-insensitive and camelCased props need to use their kebab-case equivalents when using in-DOM templates. You should probably use "my-content" instead of "myContent"
<body>
<div id="app">
<!--下面這里會報錯 -->
<child myContent='hello-1'></child>
<!-- 下面會正常 -->
<child my-content='hello-2'></child>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child', {
props:['myContent'],
template: `<div>{{myContent}}</div>`
});
var app = new Vue({
el: '#app'
})
</script>
同樣的,如果在組件的template屬性中使用駝峰命名法的屬性,那么這個限制就不存在了。
參數校驗
限制props的類型
有時候需要使用第三方的組件的時候,所以會需要傳輸數據給這個組件來渲染。但如何限制傳入的數據的類型呢?
- 可用於限制數據類型的類型:
- String:字符串
- Number:數字
- Boolean:布爾值
- Array:數組
- Object:對象
- Date:日期
- Function
- Symbol
格式:
props: {
// 數據名:數據類型
title: String,
likes: Number,
...
}
如果傳入的類型不對,那么會報Invalid prop: type check failed for prop "xxx". Expected String with value "xxx", got Number with value xxx.
的錯誤。
如果允許多種值,可以定義一個數組:
props: {
content: [String,Number]
}
設置默認值
我們也可以給props中的數據設置默認值,如果使用default設置值,那么沒有傳某個數據時默認使用這個數據。
props: {
content: {
type:[String,Number],
default:'我的默認值'
}
}
如果使用default給數組或對象類型的數據賦默認值,那么要定義成一個函數。
要求數據必傳
如果要求某個數據必須傳給子組件,那么可以為它設置required。
格式:
props: {
content: {
type: String,
required: true
}
}
如果沒傳,會報Missing required prop: "xxx"
的錯。
自定義驗證函數:
如果想要更精確的校驗,可以使用validator,里面是一個函數,函數的第一個參數就是傳入的值,當函數內返回true時,這個值才會校驗通過。
以下的代碼是要求傳入的字符串長度大於6位的校驗:
Vue.component('child', {
props: {
content: {
type: String,
validator: function(value) {
return (value.length > 6)
}
}
}
如果驗證不通過,會報Invalid prop: custom validator check failed for prop "xxx"
的錯。
傳遞靜態或動態Prop
- 傳遞靜態或動態的prop意思就是傳遞的是一個常量還是變量。
- 傳遞常量的時候:
- 如果是字符串,可以不使用v-bind。
- 如果是數字,布爾值,數組,對象,那么需要使用vue的v-bind來設置屬性,因為使用普通的屬性設置會被認為是字符串,使用v-bind的時候會被認為是js表達式(從而成功識別成正確的類型)。
- 傳遞變量的時候都要使用v-bind,不然無法識別成一個變量。
補充:
- 沒有講的內容:非 Prop 的特性
給組件綁定原生的事件
用下面的代碼來說一個問題:
<html lang="en">
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<div id="app">
<child @click='myFunction'></child>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child',{
template:`
<button>按鈕</button>
`
});
var vm = new Vue({
el: '#app',
methods: {
myFunction: function() {
console.log('haha')
}
}
})
</script>
</html>
上面的代碼你會發現點擊了按鈕卻沒有調用函數。
而下面的按鈕按了會打出child。
<html lang="en">
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<div id="app">
<child @click='myFunction'></child>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child',{
template:`
<button @click='childFunction'>按鈕</button>
`,
methods: {
childFunction: function() {
console.log('child')
}
}
});
var vm = new Vue({
el: '#app',
methods: {
myFunction: function() {
console.log('haha')
}
}
})
</script>
</html>
- 在上面我們提過了父子數據傳遞,我們知道了父組件的數據需要props來傳遞給子組件,說明了父組件的數據是不跟子組件共享的。
- 而事件也是這樣的,在我們使用組件的時候,並不能直接監聽一些事件的發生。【例如上面的子組件傳數據給父組件也需要使用emit。】
- 對於父組件來說,這個組件是子組件下的按鈕,所以直接點擊這個按鈕並不會觸發事件【或者說你可以認為點擊了事件了,但是沒有emit出來,所以父組件監聽不到】。
- 而如果希望點擊按鈕的時候能夠觸發出按鈕的原生事件(不把它當作子組件下的按鈕),那么需要把它綁定成原生事件。我們可以使用.native來修飾事件來說明監聽的是一個原生事件。
下面的代碼是使用了emit來達到同樣效果的代碼:
<html lang="en">
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<div id="app">
<child @click='myFunction'></child>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child',{
template:`
<button @click="$emit('click')">按鈕</button>
`
});
var vm = new Vue({
el: '#app',
methods: {
myFunction: function() {
console.log('haha')
}
}
})
</script>
</html>
template
- template是vue中的一個元素。它是一個不會渲染到頁面的元素,通常地它有如下幾個用處:
- 由於不會被渲染處理,可以在template上使用使用v-if,把一個
<template>
元素當做不可見的包裹元素,讓成組的元素能夠統一被渲染出來。 - 也可以使用v-for,也是把
<template>
元素當做不可見的包裹元素 - 其他特殊的用途:例如下面要說插槽的slot-scope會用到。
- 由於不會被渲染處理,可以在template上使用使用v-if,把一個
在template上使用v-if
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
使用v-for
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
插槽
- 我們可以用prop來傳遞數據給子組件,而插槽是用來分發內容的。
- 我們有時候希望在使用子組件的時候順便傳給它一些內容,那么可以使用插槽。
- 【props適合於傳輸數據,而slot通常用來達到“插入內容”的效果;使用props的話需要子組件聲明使用,而slot相較隨意】
通過插槽分發內容
具名插槽
插槽也可以分發多份數據。使用指定的插槽名就可以獲取指定的插槽數據。
插槽的默認內容
如果沒有數據傳過來,那么插槽可以定義一個默認的數據用來顯示。
作用域插槽
- 有時候我們希望獲取組件中的slot的值來進行其他操作。比如,可能需要根據一個值來顯示不同的內容。這時候我們可以使用slot-scope來獲取組件中傳過來的slot的值。
- slot-scope的值是可以隨意的,它代表作用於插槽的名字。
- 這時候不再是插入數據給插槽,而是從插槽中獲取數據了。
<body>
<div id="app">
<child-a>
<template slot-scope='props'> <!--2. slot-scope的屬性值是可以隨意的,代表作用域插槽的名字 -->
<h3>{{props.index}}</h3> <!--3. props.xxx 是傳過來的值的名字,值需要綁定到slot中 -->
</template>
</child-a>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.prototype.bus = new Vue()
Vue.component('child-a', {
data: function() {
return {
list: [2,4,6,8,10]
}
},
// 1. 綁定屬性,名為index,那么作用域插槽就可以獲取名為index的數據
template: `<div>
<slot v-for='item of list' :index=item></slot>
</div>`
});
var app = new Vue({
el: '#app'
})
</script>
動態組件
is
- 動態組件是一個有趣的東西,它相當於一張空白的組件,只要你告訴它它是什么組件,它就變成了什么組件。
- 有時候我們希望發生了xxx事件之后,頁面的某個組件變成另一個組件。【你可能會產生如下圖的需求:點擊按鈕,切換登錄方式】
- 我們只需要給component元素設置上is屬性就可以把component元素渲染成目標組件了。
<component :is='組件名'></component>
如果改變了is屬性的值,那么渲染的組件就會發生變化。 - is可以是v-bind的,也可以是非v-bind的,如果希望它是動態可變化的,那么應該使用v-bind:is,這樣才能傳入一個變量,從而動態渲染組件。【非v-bind的時候傳入的值會被認為一個字符串,然后直接把這個字符串認為是組件名】
下面是上圖的演示代碼:
<body>
<div id="app">
<button @click='changeLogin'>切換登錄方式</button>
<component :is='loginType'></component>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('userInfoLogin',{
template:`
<div>
<div>用戶名登陸:<input type="text"></div>
<div>請輸入密碼:<input type="password"></div>
</div>
`
});
Vue.component('phoneLogin',{
template:`
<div>
<div>請輸入手機號:<input type="text"></div>
<div>請輸入驗證碼:<input type="text"></div>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
loginType:'userInfoLogin'
},
methods: {
changeLogin: function() {
this.loginType = this.loginType === 'userInfoLogin'?'phoneLogin':'userInfoLogin';
}
}
})
</script>
keep-alive
- 被keep-alive元素包裹的組件會被緩存,緩存之后組件重新渲染時會直接從緩存中獲取,避免了每次組件都重新渲染。
下面以上面的動態組件切換為例:
如果給上面的登錄組件都加上一個created用來顯示渲染次數的話,我們就可以看到是不是每次切換都會重新渲染。
如果加了keep-alive之后不再重復輸出,那么就說明組件被緩存了。
<body>
<div id="app">
<button @click='changeLogin'>切換登錄方式</button><!-- 2.多次點擊切換,查看組件渲染情況 -->
<keep-alive>
<component :is='loginType'></component>
<!-- 4.注意在keep-alive包裹之前和之后的控制台輸出情況 -->
</keep-alive>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('userInfoLogin',{
template:`
<div>
<div>用戶名登陸:<input type="text"></div>
<div>請輸入密碼:<input type="password"></div>
</div>
`,
created: function(){
console.log('userInfoLogin')// 1.用來說明渲染了userInfoLogin組件,多次渲染則多次輸出
}
});
Vue.component('phoneLogin',{
template:`
<div>
<div>請輸入手機號:<input type="text"></div>
<div>請輸入驗證碼:<input type="text"></div>
</div>
`,
created: function(){
console.log('phoneLogin') // 2.用來說明渲染了phoneLogin組件,多次渲染則多次輸出
}
})
var vm = new Vue({
el: '#app',
data: {
loginType:'userInfoLogin'
},
methods: {
changeLogin: function() {
this.loginType = this.loginType === 'userInfoLogin'?'phoneLogin':'userInfoLogin';
}
}
})
</script>
補充
- 沒有寫的內容:異步組件
$refs
- 有時候你可能需要獲取某個元素來進行操作,在沒有學refs之前,你可能需要document.xxx來獲取某個元素。
- 學了refs之后,你可以使用refs來獲取元素
使用步驟:
1.在元素中使用ref屬性給元素起一個有標識意義的名字。
2.this.\(refs可以獲取當前組件實例的所有使用了ref的元素,`this.\)refs.名字`代表指定的元素。
3.然后你可以進行dom操作了。
<body>
<div id="app">
<div ref='demo'>1</div> <!-- 1. 設置ref -->
<button @click='changeColor'>+1</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
methods: {
changeColor:function() {
console.log(this.$refs.demo.innerText) // 獲取ref對象,有了dom對象你就可以進行dom操作了。
this.$refs.demo.style = 'color:red'
this.$refs.demo.innerText+=this.$refs.demo.innerText
}
}
})
</script>
獲取組件的引用
refs也可以用在組件上,可以獲取組件的數據(但這時候的ref獲取的不是一個dom對象,而是一個vue實例對象,所以不能獲取innerText這類dom屬性)。
<body>
<div id="app">
<child ref='demo'></child>
<button @click='changeColor'>+1</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child',{
data: function(){
return {
msg:'hello world!'
}
},
template:'<div>hello</div>'
})
var app = new Vue({
el: '#app',
methods: {
changeColor:function() {
console.log(this.$refs.demo.msg) // 獲取組件實例的數據。
}
}
})
</script>
動畫效果
如果說前面講的都是基礎,必須要掌握的話,那么動畫效果是錦上添花,可有可無(對於我這些不追求動畫效果就不顯得重要了),所以這里就不講了,這里僅僅是為了顯示vue能有實現動畫效果的功能。
有興趣的可以看一下官網:
vue官網動畫效果直通車