Create by jsliang on 2018-11-8 13:34:30
Recently revised in 2019-1-12 19:23:17
Hello 小夥伴們,如果覺得本文還不錯,記得給個 star , 你們的 star 是我學習的動力!GitHub 地址
推薦通過 目錄
以及使用 返回目錄
按鈕,獲得更好的閱讀體驗。
一 目錄
不折騰的前端,和鹹魚有什麼區別~
二 正文
飲水思源:Vue 官方文件
Vue (讀音 /vjuː/,類似於 view) 是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。另一方面,當與現代化的工具鏈以及各種支援類庫結合使用時,Vue 也完全能夠為複雜的單頁應用提供驅動。
學習版本:v2.5.21
編寫時間:2019-1-10
如版本更迭太大或者時間小夥伴們看到這篇文章太久沒更新,小夥伴們請檢視 Vue 官方文件學習最新的 Vue。
2.1 初識 Vue
那麼,Vue 是怎麼折騰的呢?
話不多說,我們直接來看程式碼實現:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: '#app',
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<p>Hello World</p>
</div>
`
})
</script>
</body>
</html>
複製程式碼
現在,我們解析下程式碼執行:
- 首先,建立一個空白的 html 模板檔案,通過 CDN 引用 Vue:
Vue 一般分兩個版本:
開發版本:開發中有友好的錯誤提示。
生產版本:上線部署使用的版本,程式碼包比較小
index.html 程式碼片段
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
複製程式碼
- 然後,我們編寫一個掛載點,即我們的 Vue,最終會在哪個 DOM 裡面進行操作:
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
複製程式碼
- 最後,我們通過 New 一個 Vue 例項物件,對我們 id 為
app
的 DOM 節點進行操作:
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容。
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<p>Hello World</p>
</div>
`
})
複製程式碼
這樣,我們最終就顯示了 Vue 的簡單引用,是不是覺得非常簡單:
2.2 掛載資料 - data
如果 Vue 僅僅是隻有 template
這個模板裝載,那麼它跟 jQuery 就顯得沒多大差別了,下面我們使用下 Vue 的 data
進行資料渲染:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: '#app',
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<p>{{ text }}</p>
</div>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
text: 'Hello World!'
}
}
})
</script>
</body>
</html>
複製程式碼
在這裡,我們可以看到,我們在 template
中加了一個 <p>
標籤,通過 {{ text }}
形式,引入了一個叫 text
的 data
資料:
<p>{{ text }}</p>
複製程式碼
接著我們在 <scirpt>
中定義了 text
的內容,從而實現資料渲染:
data() {
return {
// template 中要使用的資料
text: 'Hello World!'
}
}
複製程式碼
這樣,我們就知道了,我們不僅可以通過模板 template
來渲染 <div>
標籤,我們也可以將 js 中定義的資料或者變數,通過操作 data
從而改變 html 裡面的內容。
2.3 進一步優化 el
在 2.1
章節 及 2.2
章節中,我們使用 el
的方式是:
el: '#app',
複製程式碼
該 el
掛載形式,在 Vue 內部執行機制中,它會根據你傳入的值,進行查詢:
- 如果傳入的是
#app
,那它就判斷查詢id
為app
的節點; - 如果傳入的是
.app
,那它就查詢class
為app
的節點; - 如果傳入的是節點名
div
,那它就查詢節點名……
大家應該清楚,這樣判斷查詢是需要時間的,多執行一個判斷都是罪惡。
所以我們可以:
el: document.getElementById('app'),
複製程式碼
這般操作,使得 Vue 直接將掛載點掛載到 id
上去,從而獲得更好的載入速度。這算是對 el
的一個小優化。
2.4 插值表示式 - {{ }}
如果小夥伴有點印象,應該還記得,我們在章節 2.2
中通過 {{}}
這個插值表示式的使用,在 data
中對其裡面的資料進行操作。
下面,我們進一步講解這個插值表示式 {{}}
還可以進行哪種騷操作:
- 物件:{{ {name: 'jack'} }}
- 字串 {{ 'Hello World!' }}
- 布林值: {{ isTrue == -1 }}
- 三元表示式: {{ isTrue ? '正確' : '錯誤' }}
光字面理解是不夠的,我們通過程式碼進行操作演示:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<p>{{ text }}</p>
<p>{{ {name: 'jack'} }}</p>
<p>{{ 'Hello World!' }}</p>
<p>{{ isTrue == -1 }}</p>
<p>{{ isTrue ? '真' : '假' }}</p>
</div>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
text: 'Hello World!',
isTrue: true
}
}
})
</script>
</body>
</html>
複製程式碼
它在瀏覽器的展示為:
關鍵程式碼講解:
<div>
<!-- 賦值 text 到 <p> 標籤中 -->
<p>{{ text }}</p>
<!-- 賦值物件到標籤中 -->
<p>{{ {name: 'jack'} }}</p>
<!-- 直接賦值字串到標籤中 -->
<p>{{ 'Hello World!' }}</p>
<!--
直接進行布林判斷,isTrue 在 data 中設定為 true,
而 -1 轉為 布林值 是 false,所以兩者不相等
輸出值為 false
-->
<p>{{ isTrue == -1 }}</p>
<!-- 執行三元表示式,isTrue 為 true,輸出 真 -->
<p>{{ isTrue ? '真' : '假' }}</p>
</div>
複製程式碼
通過三元表示式的運用,我們可以做到一些判斷:陣列最後一個元素、是否動態顯示隱藏等。
2.5 指令 - v-*
在 Vue 中,如果單單使用 {{}}
這種插值表示式,是滿足不了我們對資料的操作慾望的。所以,Vue 以 v-if
、v-bind
等形式,提供了一些對於頁面 + 資料的更為方便的操作:指令
v-text
v-html
v-if
v-else-if
v-else
v-show
v-bind
v-on
v-model
v-for
這裡採用一個頁面展示所有指令,如果小夥伴想逐個詳細瞭解指令,推薦去官網檢視學習:Vue 指令
那麼,上面的指令都是怎麼使用的呢?這裡通過一個 index.html
及一張圖向大家演示其基本用法:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
<style>
/* 顏色樣式:紅、綠、藍 */
.color-red {
color: red;
}
.color-blue {
color: blue;
}
.color-green {
color: green;
}
</style>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<p>v-text 演示</p>
<p v-text='vTextOrHtml'></p>
<br/>
<p>v-html 演示</p>
<p v-html='vTextOrHtml'></p>
<br/>
<p>v-if -> v-else-if -> v-else 演示</p>
<p v-if='vIf == 1'>Hello v-If</p>
<p v-else-if='vIf == 2'>Hello v-else-if</p>
<p v-else>Hello v-else</p>
<br/>
<p>v-show 演示</p>
<p v-show='isTrue'></p>
<br/>
<p>v-bind:××× -> :××× 演示</p>
<input v-bind:value="vBind" v-bind:class="colorRed" type="text"/>
<input v-bind:other1="other1" :other2="other2" :other3=" 'other3' " value="Hello :屬性值" type="text"/><br/>
<br/>
<p>v-on:click -> @click 演示</p>
<button v-on:click=" vBind= 'Hello v-on:click' ">v-on:click - 點選直接改變 vBind 的值</button><br>
<button @click="changevBindValue">v-on:click - 點選通過事件改變 vBind 的值</button><br>
<br/>
<p>v-model 演示</p>
<input v-model="vModel" type="text" />
<p>{{ vModel }}</p>
<br/>
<p>v-for 演示</p>
<ul v-for="(item, index) in vFor" :class="item.classStyle">
<li>{{index+1}}. {{item.name}} - {{item.age}}</li>
</ul>
</div>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
// v-text 及 v-html 使用資料
vTextOrHtml: '<span style="color: red">我是紅的</p>',
// v-if 使用資料
vIf: 2,
// v-show 使用資料
isTrue: false,
// v-bind 使用資料
vBind: "Hello v-bind",
// v-bind 通過動態繫結 class 修改樣式
colorRed: 'color-red',
// v-bind 的 :屬性 的使用形式
other1: 'other1',
// 同上
other2: 'other2',
// v-model 使用資料
vModel: 'Hello v-model',
// v-for 使用資料
vFor: [{
name: '張三', // 姓名
age: 22, // 年齡
classStyle: "color-red" // 樣式
},
{
name: '李四',
age: 23,
classStyle: "color-blue"
},
{
name: '王五',
age: 24,
classStyle: "color-green"
}
]
}
}
})
</script>
</body>
</html>
複製程式碼
我們看下頁面:
在這裡,我們對程式碼進行下講解:
<div>
<!--
1. v-html
這裡直接將 vTextOrHtml 中的文字
當成 string 渲染到頁面中去
-->
<p v-text='vTextOrHtml'></p>
<br/>
<!--
2. v-html
這裡在渲染 vTextOrHtml 的過程中,
如果遇到標籤,則對標籤頁進行渲染
-->
<p v-html='vTextOrHtml'></p>
<br/>
<!--
3. v-if/v-else-if/v-if
判斷 data 中 vIf 的值是多少,
這裡有三種情況:v-if、v-else-if、v-else。
如果專案中有更多情況,則再新增 v-else-if 即可
-->
<p v-if='vIf == 1'>Hello v-If</p>
<p v-else-if='vIf == 2'>Hello v-else-if</p>
<p v-else>Hello v-else</p>
<br/>
<!--
4. v-show
判斷 isTrue 是真還是假,
它不同於 v-if 的方面是:
v-if 如果是假,則在 Element 中沒有渲染
v-show 如果是假,則該標籤為 display: none
-->
<p v-show='isTrue'></p>
<br/>
<!--
5. v-bind
v-bind 有兩種格式:
1. v-bind:value - 全寫
2. :value - 簡寫
我們還可以通過 v-bind:class 來動態賦值
v-bind:other1="other1" 在頁面中顯示就是:
<input other1="other1" />>
-->
<input v-bind:value="vBind" v-bind:class="colorRed" type="text"/>
<input v-bind:other1="other1" :other2="other2" :other3=" 'other3' " value="Hello :屬性值" type="text"/><br/>
<br/>
<!--
6. v-on
v-on:click 有兩種格式:
1. v-on:click - 全寫
2. @click - 簡寫
v-on:click 除了可以直接在裡面寫表示式,還可以填寫方法
-->
<button v-on:click=" vBind= 'Hello v-on:click' ">v-on:click - 點選直接改變 vBind 的值</button><br>
<button @click="changevBindValue">v-on:click - 點選通過事件改變 vBind 的值</button><br>
<br/>
<!--
7. v-model
v-model 是雙向資料繫結,
在這裡,上面 input 的修改
會影響到下面 p 顯示的內容
-->
<input v-model="vModel" type="text" />
<p>{{ vModel }}</p>
<br/>
<!--
8. v-for
v-for 迴圈體遍歷輸出
-->
<ul v-for="(item, index) in vFor" :class="item.classStyle">
<li>{{index+1}}. {{item.name}} - {{item.age}}</li>
</ul>
</div>
複製程式碼
v-bind 和 v-model 的區別:
- v-bind:將 Vue 中的資料同步到頁面,即該值大部分用於前端向瀏覽器傳固定資料。v-bind 可以給任何屬性賦值,是從 Vue 到頁面的單向資料流,即 Vue -> html。
- v-model:雙向資料繫結,前端向瀏覽器傳資料,使用者操作瀏覽器的更改前端可以察覺到。v-model 只能給具有 value 屬性的元素進行雙向資料繫結(必須使用的是有 value 屬性的元素),即 Vue -> html -> Vue
關於 Vue 的指令,這裡我們先對它進行了個全面的簡單瞭解,知道它是如何使用的。
想詳細學習的小夥伴,記得前往官方文件:Vue 文件
2.6 事件 - methods
在上一章 2.5
中,我們通過在 button
中使用 v-on:click
時,給它繫結了事件方法。
但是,在 2.5
中,我們大體講述了事件方法的使用,但是我們只是一知半解。
在這裡,我們抽取出來做下講解:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<button @click="addStyle">新增行內樣式</button>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
addStyle(e) {
e.toElement.style.background = "red"
}
}
})
</script>
</body>
</html>
複製程式碼
此時頁面的點選效果如下所示:
此刻我們分析下頁面:
- 首先,在上面的
<button>
中,我們通過@click
繫結了事件addStyle
:
<button @click="addStyle">新增行內樣式</button>
複製程式碼
- 接著,方法的編寫,需要寫到與
data
同級的methods
中:
methods: { // 方法
addStyle: function(e) {
e.toElement.style.background = "red"
}
}
複製程式碼
- 然後,我們通過傳遞引數
e
,可以獲取到點選的時候的元素,通過查詢,我們發現它的樣式所在的目錄結構如下:
- button
- toElement
- style
- background
複製程式碼
- 最後,我們在使用者點選按鈕的時候,直接修改了它的背景。
2.7 元件 - components
敲黑板!敲黑板!敲黑板!
元件是 Vue 學習的重點,元件化的 SPA 或者 SSR 頁面的製作,使得我們開發起來更加隨心應手。
2.7.1 初始元件
在上面的章節中,我們一直使用 template: ``
的形式,編寫 html
標籤。但是,隨著專案的不斷擴大,如果全部程式碼都寫在一個 template
中,那麼我們修改起來就複雜了。所以,我們應該想辦法對它進行劃分,例如將一個頁面劃分為 header
、content
、footer
三部分。這樣,我們需要修改 nav
的時候,只需要在 header
中修改就可以了。
頁面結構
- app
- header
- content
- footer
複製程式碼
這樣的思想,在 Vue
中體現為元件(組合起來的部件)。那麼,在 Vue
中,需要如何做,才能比較好的做到元件的劃分呢?
首先,我們捋捋邏輯:
在前面的章節中,在 Vue 的定義上,我們將首個 template
掛載到了 id 為 app
的節點上。然後將 template
劃分為三個塊:header
、content
、footer
。
在這裡,我們將
#app
的template
叫做父元件,header
等叫子元件,就好比父親下面有三個兒子一樣。
然後,我們嘗試從 new Vue
中抽離單個元件出來:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
// 宣告入口元件
var App = {
template: `<h1>我是入口元件</h1>`
}
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: '<app/>',
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
app: App
}
})
</script>
</body>
</html>
複製程式碼
這時候頁面如下所示:
接著,我們分析下進行的三部曲:
- 在
component
中定義並抽離App
- 在
new Vue
外定義App
- 在
template
中使用App
這樣,我們就做到了單個元件的抽離,及 new Vue
是 App
的父元件,App
是 new Vue
的子元件。
最後,既然上面做到了單個元件的抽離,現在我們實現多個元件的抽離:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue學習</title>
</head>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 宣告頭部元件
var MyHeader = {
template: `<div>我是頭部</div>`
};
// 宣告內容元件
var MyContent = {
template: `<div>我是軀體</div>`
};
// 宣告底部元件
var myFooter = {
template: `<div>我是底部</div>`
}
new Vue({
el: document.getElementById('app'),
components: { // 宣告要用的元件們
// key 是元件名,value 是元件物件
'my-header': MyHeader,
'my-content': MyContent,
'my-footer': myFooter
},
template: `
<div>
<my-header/>
<my-content/>
<my-footer/>
</div>
`
})
</script>
</body>
</html>
複製程式碼
這樣,我們就做到了元件的抽離。
注意:template
有且只有一個根節點,如果沒有根節點,Vue 會給你報錯。
template: `
<my-header/>
<my-content/>
<my-footer/>
`
複製程式碼
上面那種寫法是錯誤的,謹記。
做到這裡,我們又可以愉快玩耍了,而且 myHeader
、myContent
、myFooter
中是可以跟 new Vue
一樣寫 data
、methods
的哦~
例如:
var MyHeader = {
data() {
return {
// ... 定義資料
}
},
template: `<h1>我是頭部</h1>`,
methods: {
// 定義方法
}
};
複製程式碼
2.7.2 父子元件通訊
既然前面章節已經劃分了父子元件,那麼在這裡,我們講件更有趣的事:父子元件通訊。
在元件間,我們 new Vue
相當於父親(父元件),他有自己的 data
。然後,子元件也會有自己的 data
。
- 假如某天,父親找到自己的兒子,想告訴他:
“其實你不是我親生的,你的姓名是***”
。
那麼,在 Vue
中,我們要怎麼做,才能讓它的兒子(子元件),知道它的姓究竟是什麼呢?我們來看程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
// 子元件
var Son = {
template: `
<div>我的名字:{{name}}</div>
`,
props: ['name']
}
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<son :name="name"></son>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
name: '皮皮蝦'
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
son: Son
}
})
</script>
</body>
</html>
複製程式碼
編寫完程式碼後,我們可以在瀏覽器看到,瀏覽器顯示出了:我的名字:皮皮蝦
,這幾個大字。
哦了,原來父親的兒子姓 皮
。同時,我們也就知道了,在父元件中的資料,通過 v-bind:***
的形式,將父元件中的 data
,傳送給子元件。而子元件呢,通過 props
的定義,獲取到了父親的資料。
這樣我們就做到了父元件傳遞資料給子元件。
2.7.3 共用元件
在上面中,我們提到:
- App
- my-header
- my-content
- my-footer
複製程式碼
在 App
這個元件上,我們掛載了三個子元件:myHeader
、myContent
、myFooter
。
- 但是,如果某天,出現了一個女孩(共有元件),這個女孩的名字叫:
beautifulGirl
。然後不僅三個兒子(子元件)想追求她,就連父親(父元件)也想追求她(夠瘋狂)。
那麼,在 Vue
中,是通過什麼方式,使父親和兒子都有機會接觸到這個女孩呢?(父子元件如何能夠都可以使用共用元件)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
// 宣告頭部元件
var MyHeader = {
template: `
<div>我是頭部,我想了解<beautiful-girl></beautiful-girl></div>
`
};
// 宣告內容元件
var MyContent = {
template: `
<div>我是內容區,我想了解<beautiful-girl></beautiful-girl></div>
`
};
// 宣告底部元件
var myFooter = {
template: `
<div>我是底部,我想了解<beautiful-girl></beautiful-girl></div>
`
}
// 宣告共用元件
Vue.component('beautiful-girl', {
template: `<span>—— 美麗女孩 ——</span>`
})
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<my-header/>
<my-content/>
<my-footer/>
</div>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
'my-header': MyHeader,
'my-content': MyContent,
'my-footer': myFooter,
}
})
</script>
</body>
</html>
複製程式碼
在這裡,我們通過 Vue.component('元件名',{ })
的形式,註冊了個全域性元件 beautiful-girl
,這樣,父子元件都可以直接呼叫該元件,從而在瀏覽器顯示為:
現在,父親和兒子都可以和漂亮女孩溝通了。究竟是父親給他們的兒子找了個後媽,還是他們兒子找到自己所愛呢?敬請期待……
2.8 過濾器 - filter
在工作中,我們經常需要對一些後端傳回來的資料進行過濾。例如:我司 Java 小哥傳回來的金錢,就是分進位制的,即:1元 = 100分。所以傳回個 2000,其實是 20 元。那麼,在 Vue 中,我們該如何對資料進行過濾呢?
2.8.1 區域性過濾
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
// 宣告頭部元件
var MyHeader = {
template: `
<div>我是頭部,我想了解<beautiful-girl></beautiful-girl></div>
`
};
// 宣告內容元件
var MyContent = {
template: `
<div>我是內容區,我想了解<beautiful-girl></beautiful-girl></div>
`
};
// 宣告底部元件
var myFooter = {
template: `
<div>我是底部,我想了解<beautiful-girl></beautiful-girl></div>
`
}
// 宣告共用元件
Vue.component('beautiful-girl', {
template: `<span>—— 美麗女孩 ——</span>`
})
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<p>我是錢多多,我有 {{money}} 多一點: ¥{{money | addDot}},跟我混有出息~</p>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
money: 1000000
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
},
// 8. filters - 元件內的過濾器
filters: {
addDot(money) {
return (money / 1000000 + ".000000");
}
}
})
</script>
</body>
</html>
複製程式碼
在上面,我們通過 filters
中的 addDot
方法,對資料進行了過濾,將 money
的資料,從 10000000
變成了 1.000000
。
2.8.2 全域性過濾
然後,在嘗試了區域性 filters
的好處之後,我們還可以試試它的全域性過濾器寫法:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
// 全域性過濾器
Vue.filter('addDot', function(money) {
return (money / 1000000 + ".000000");
})
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<p>我是錢多多,我有 {{money}} 多一點: ¥{{money | addDot}},跟我混有出息~</p>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
money: 1000000
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
},
// 8. filters - 元件內的過濾器
filters: {
}
})
</script>
</body>
</html>
複製程式碼
最後在頁面中顯示為:
2.9 監聽資料
在 Vue
中,我們通過 v-model
做了雙向資料繫結,即在頁面的 <input>
中輸入的值,在我們的 Vue
中可以獲得資料;在 Vue
中定義的資料,也會即時渲染到頁面中。
但是,在程式碼中,我們怎樣才能獲取到它即時輸入的資料呢?
2.9.1 偵聽屬性 - watch
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<div>
<input type="text" v-model="money" />
<span>{{money}}</span>
</div>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
money: ''
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
},
// 8. filters - 元件內的過濾器
filters: {
},
// 9. watch - 偵聽屬性
watch: {
// key: data 屬性的屬性名
money(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
})
</script>
</body>
</html>
複製程式碼
這樣,當我們輸入 11 個 1 的過程中,瀏覽器的 Console
對應輸出為:
2.9.2 計算屬性 - computed
在上面,我們講了通過 watch
來監聽 data
中 number
、string
等欄位的改變。但是,在 Vue 中,為了方便我們的監聽操作,Vue 還定義了個方法:computed
,我們可以通過 computed
,監控我們在 data
中定義的全部資料。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue學習</title>
</head>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: document.getElementById('app'),
template: `
<div>
<input type="text" v-model="number1" />
+
<input type="text" v-model="number2" />
*
<input type="text" v-model="number3" />
=
{{result}}
</div>
`,
data: {
number1: 0,
number2: 0,
number3: 0,
result: '',
},
computed: {
// 如果原值不變,快取不調函式的優化機制
result: function() {
// 監視物件,寫在了函式內部,
// 凡是函式內部有 this. 相關屬性,改變都會觸發當前函式
let addSum = parseInt(this.number1) + parseInt(this.number2);
let allSum = addSum * this.number3;
return allSum;
}
}
})
</script>
</body>
</html>
複製程式碼
其結果如下面 GIF 圖所示:
2.9.3 watch、computed 與 methods 對比
在上面,我們涉及了兩個知識點:watch
與 computed
。
那麼,又到 “玄學” 的時刻了,都是跟監聽資料打交道,我們平時使用 Vue 的時候,什麼時候使用 watch
,什麼時候使用 computed
?然後,如果我們在加上 methods,那麼什麼時候我們又用 methods 呢?
首先,我們對比下 computed
與 methods
:
computed
是根據data
中的資料變化,而進行的操作。即this.任意資料
改變了,那麼,computed
就會進行改變;而如果this.任務資料
不變,那麼computed
就會執行它的快取策略,不會更新methods
一般是根據點選之類的事件來觸發的,例如使用者通過@click="方法"
來進行資料的改變。
然後,我們對比下 computed
與 watch
:
如果上面章節的 computed
方法換成 watch
:
index.html 程式碼片段
// 9. watch - 偵聽屬性
watch: {
// key: data 屬性的屬性名
result(newVal, oldVal) {
console.log(newVal, oldVal);
this.result = this.number1 + this.number2 * this.number3;
}
},
複製程式碼
你會發現,result
資料不變化了,因為這是 computed
才特有的玩意,如果你需要將上面章節的 computed
方法換成 watch
,那麼你需要:
index.html 程式碼片段
// 9. watch - 偵聽屬性
watch: {
// key: data 屬性的屬性名
number1(val) {
this.result = parseInt(this.number1) + parseInt(this.number2) * parseInt(this.number3);
},
number2(val) {
this.result = parseInt(this.number1) + parseInt(this.number2) * parseInt(this.number3);
},
number3(val) {
this.result = parseInt(this.number1) + parseInt(this.number2) * parseInt(this.number3);
}
},
複製程式碼
如此,小夥伴應該瞭解到,watch
如果需要完成 computed
的功能,那麼,它需要監聽每一個需要改變的屬性。
最後,在這裡,我們大致描述下 watch
與 computed
的區分:
computed
強調計算,例如c = a + b
,b
是外界傳來不斷變化的,因為你只要顯示c
,所以使用computed
。而watch
屬性強調自身值的變化前後的動作,如果需要完成c = a + b
,那麼你需要watch
資料a
與b
的變化,在這兩者變化的時候,在方法中執行c = a + b
。watch
在處理非同步操作或者開銷較大的操作上有優勢。- 執行非同步操作不能序列返回結果,使用
watch
; - 開銷較大的操作,避免堵塞主執行緒,使用
watch
; - 簡單且序列返回的,使用
computed
。
- 執行非同步操作不能序列返回結果,使用
computed
對繫結的值有依賴,如果每次操作的值不變化,則不進行計算,具有快取特性。watch 會偵聽前後變化的狀態,無論操作的值是否變化,都會執行定義的函式體,所以會有 data(newVal, oldVal)。
如果小夥伴們較真上了,那麼請檢視官方文件:計算屬性和偵聽器
2.10 傳遞 DOM - slot
在日常工作中,我們對一些常用的功能,例如:側邊欄、頂部導航欄等,會進行常用的封裝,等我們想用的時候,就可以直接引用。那麼,在 Vue 中,想實現這類功能,我們還需要了解什麼?
2.10.1 slot 單個傳遞
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var myLi = {
template: `
<li><slot></slot></li>
`
};
Vue.component('my-li', myLi);
var App = {
template: `
<div>
<ul>
<my-li><button>我是第一行 button 按鈕</button></my-li>
<my-li><h3>我是第二行 h3 標籤</h3></my-li>
<my-li><a href="javascript:void(0)">我是第三行 a 導航</a></my-li>
<my-li><span>我是第四行 span 標籤</span></my-li>
</ul>
</div>
`
};
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<app/>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
app: App
},
// 8. filters - 元件內的過濾器
filters: {
},
// 9. watch - 偵聽屬性
watch: {
// key: data 屬性的屬性名
},
// 10. computed - 計算屬性
computed: {
// 如果原值不變,computed 會執行快取,即不呼叫方法
}
})
</script>
</body>
</html>
複製程式碼
其結果如下圖所示:
那麼,上面程式碼中,我們幹了什麼?
首先,如上程式碼及其結果圖,我們的 new Vue
中掛載了一個元件 App
。
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
複製程式碼
然後,該 App
的目的,是想動態引用一個 li
元件
var App = {
template: `
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
`
};
複製程式碼
接著,我們在全域性定義 myLi
元件的同時,通過 <slot></slot>
插槽,使它能夠動態地載入 dom
節點。
var myLi = {
template: `
<li><slot></slot></li>
`
};
Vue.component('my-li', myLi);
複製程式碼
最後,我們在 App
中,傳遞給它不同的 dom
節點,從而動態生成 App
。
var App = {
template: `
<div>
<ul>
<my-li><button>我是第一行 button 按鈕</button></my-li>
<my-li><h3>我是第二行 h3 標籤</h3></my-li>
<my-li><a href="javascript:void(0)">我是第三行 a 導航</a></my-li>
<my-li><span>我是第四行 span 標籤</span></my-li>
</ul>
</div>
`
};
複製程式碼
這樣,我們就思路清晰地知道,如何通過 <slot></slot>
來動態地載入 dom
節點,對我們 Vue 開發又有了更好的幫助。
2.10.2 具名 slot
在上面中,我們談論到了單個插槽 slot
的用法。但是,如果元件想根據父元件是否傳遞某個變數來存放插槽的數量,要怎麼做呢?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var mySlot = {
template: `
<ul>
<li>
<slot></slot>
</li>
<li>
<slot name="one"></slot>
</li>
<li>
<slot name="two"></slot>
</li>
<li>
<slot name="three"></slot>
</li>
</ul>
`
};
Vue.component('my-slot', mySlot);
var App = {
template: `
<div>
<my-slot>
<p>Helo World!</p>
<button slot="one">按鈕</button>
<a href="javascript:void(0)" slot="two">連結</a>
</my-slot>
</div>
`
};
new Vue({
// 3. el - 掛載目標,即渲染在哪個掛載點
el: document.getElementById('app'),
// 4. template - 模板,即渲染到掛載點的內容
// 最外層必須有一層包裹,例如 <div>
template: `
<app/>
`,
// 5. data - 資料,即在操作中需要用到的資料
// 可以理解為在 jQuery 中 var text = "Hello World!"
// {{ text }} 為資料渲染到 DOM 的方式之一
data() {
return {
// template 中要使用的資料
}
},
// 6. methods - 方法,即我們的頁面事件
// 可以理解為在 jQuery 中定義 Function
methods: {
},
// 7. components - 元件名稱
components: {
// key 是元件名,value 是元件物件
app: App
},
// 8. filters - 元件內的過濾器
filters: {
},
// 9. watch - 偵聽屬性
watch: {
// key: data 屬性的屬性名
},
// 10. computed - 計算屬性
computed: {
// 如果原值不變,computed 會執行快取,即不呼叫方法
}
})
</script>
</body>
</html>
複製程式碼
效果圖如下:
下面我們分析下,我們在程式碼中做了啥:
首先,我們通過下面程式碼可以知道,第一個 li
的 slot
是未命名的預設 slot
,所以它在頁面中展示為 p
的資料。
var mySlot = {
template: `
<ul>
<li>
<slot></slot>
</li>
</ul>
`
};
var App = {
template: `
<div>
<my-slot>
<p>Helo World!</p>
<button slot="one">按鈕</button>
<a href="javascript:void(0)" slot="two">連結</a>
</my-slot>
</div>
`
};
複製程式碼
然後,再觀察下 App
中的程式碼 <button slot="one">按鈕</button>
和 <a href="javascript:void(0)" slot="two">連結</a>
,發現它們使用了 slot="***"
,這說明了它指定了要求元件中 <slot name="***"></slot>
的程式碼接收。所以第二行第三行顯示為按鈕和連結。
最後,由於最後一個 li
中 <slot name="three"></slot>
,這個 name="three"
在 App
元件中沒有用到,所以它表現為空。
2.11 Vue 元件生命週期
在 Vue 中,什麼時候進行虛擬 dom
渲染成 dom
,或者什麼時候銷燬程式碼,都是有對應的鉤子的:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestory
- destory
關於生命週期,Vue 官方文件是有相關圖示及文件的:官方文件 - Vue 生命週期
下面我們通過程式碼演示,講解這 5 組生命週期的用法。
2.11.1 beforeCreate & created
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var lifeCycle = {
template: `
<div>
我是生命週期元件
</div>
`,
data: function() {
return {
text: 'Hello World!'
}
},
beforeCreate: function() {
// 元件建立之前
console.log(this.text); // [Console] undefined
},
created: function() {
// 元件建立之後
console.log(this.text); // [Console] Hello World!
}
/*
* 使用 lifeCycle 元件,就會觸發以上的事件函式(鉤子函式)
* created 中可以運算元據,並且可以實現 Vue -> 頁面 的影響
* 應用:發起 ajax 請求
*/
}
var App = {
components: {
'life-cycle': lifeCycle
},
template: `
<div>
<life-cycle></life-cycle>
</div>
`
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
</script>
</body>
</html>
複製程式碼
在程式碼中可以看到,我們在 App
中引用了 lifeCycle
這個元件。
我們通過鉤子函式 beforeCreate
(元件建立之前) 與 created
(元件建立之後),結合 console
發現,這兩個鉤子函式對於 data
來說,一個在 data
掛載前(beforeCreate),所以列印出來的是:undefined
,而另外一個發生在 data
掛載後,所以列印出來的是:Hello World!
。
2.11.2 beforeMount & mounted
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var lifeCycle = {
template: `
<div>
我是生命週期元件
</div>
`,
data: function() {
return {
text: 'Hello World!'
}
},
beforeMount: function() {
// Vue 起作用之前
console.log(document.body.innerHTML);
},
mounted: function() {
// Vue 起作用,裝載資料到 DOM 之後
console.log(document.body.innerHTML);
}
}
var App = {
components: {
'life-cycle': lifeCycle
},
template: `
<div>
<life-cycle></life-cycle>
</div>
`
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
</script>
</body>
</html>
複製程式碼
那麼,雖說它們的作用,一個是 Vue 起作用之前,一個是 Vue 起作用,裝載資料到 DOM 之後。
我們應該怎樣才能觀察到它的作用?
看到上圖的紅框,也許你會恍然大悟:“喔,beforeMount
就是我裝載之前的鉤子函式,而 mounted
是我裝載之後的鉤子函式,它是 Vue 作用以後的 DOM”
2.11.3 beforeUpdate & updated
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var lifeCycle = {
template: `
<div id="update">
<p>我是生命週期元件</p>
<p>{{text}}</p>
<button @click="text = '!dlroW olleH'">點選改變 text</button>
</div>
`,
data: function() {
return {
text: 'Hello World!'
}
},
// 基於資料改變,影響頁面
beforeUpdate: function() {
// 改變前
console.log(document.getElementById('update').innerHTML);
},
updated: function() {
// 改變後
console.log(document.getElementById('update').innerHTML);
}
/*
* 在日常工作中,我們可以在事件前後拿到它的 DOM,從而做一些我們想要的操作
*/
}
var App = {
components: {
'life-cycle': lifeCycle
},
template: `
<div>
<life-cycle></life-cycle>
</div>
`
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
</script>
</body>
</html>
複製程式碼
在解析程式碼前,我們先檢視它的輸出:
可以看出,beforeUpdate
可以獲取原 DOM,而 updated
可以獲取新 DOM。
它們在上面程式碼中變現為:獲取 <button>
按鈕觸發的事件前後 DOM 的變化,通過這個變化,我們可以在當中做一些操作,從而更好的滿足我們的業務需求。
- 小結:(
beforeMount
&mounted
) VS (beforeUpdate
&updated
)
那麼問題來了,beforeMount
這組和 beforeUpdate
都能監控到 DOM 的變化,它們有什麼區別呢?
答案是,一般我們如果需要在頁面載入的時候,監控 DOM 的變化,那就使用 beforeMount
和 mounted
;但是,如果我們想監控使用者的操作(點選事件等),那麼,我們就需要使用 beforeUpdate
和 updated
,因為它們不像 beforeMount
和 mounted
只會在頁面掛載初期執行一次,它們可以根據使用者的操作被執行多次。
2.11.4 beforeDestory & destory
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var lifeCycle = {
template: `
<div id="update">
<p>我是生命週期元件</p>
</div>
`,
// 對應父元件 v-if == false 的時候,就產生下面鉤子函式,銷燬當前元件
beforeDestroy: function() {
// 銷燬之前
console.log('例項銷燬之前呼叫。在這一步,例項仍然完全可用。');
},
destroyed: function() {
// 銷燬之後
console.log('Vue 例項銷燬後呼叫。呼叫後,Vue 例項指示的所有東西都會解繫結,所有的事件監聽器會被移除,所有的子例項也會被銷燬。');
}
}
var App = {
components: {
'life-cycle': lifeCycle
},
data: function() {
return {
isExist: true
}
},
template: `
<div>
<life-cycle v-if="isExist"></life-cycle>
<button @click="isExist = !isExist">點選改變 子元件 狀態</button>
</div>
`
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
</script>
</body>
</html>
複製程式碼
在這裡,我們在點選 <button>
的時候,控制檯顯示為:
可以看出,當我們點選 <button>
的時候,我們的 isExist
狀態(第一次時)被改變為 false
,從而觸發了 lifeCycle
的銷燬鉤子函式,在控制檯列印了上面兩行話。
相應的,如果是當 isExist
狀態變為 true
的時候,會觸發我們的 beforeCreate
和 created
這兩個鉤子函式,有興趣的小夥伴可以嘗試一下,這裡不做過多演示。
2.11.5 activated & deactivated
經過長期的工作,我們知道,如果頻繁的操作 DOM,進行影響到鉤子函式 beforeCreate
和 created
及 beforeDestory
和 destory
的操作,是對我們的效能會產生影響的。我們要如何防止某部分程式碼的頻繁操作 DOM,並且監聽到它的操作呢?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var lifeCycle = {
template: `
<div id="update">
<p>我是生命週期元件</p>
</div>
`,
activated: function() {
console.log("元件被啟用了");
},
deactivated: function() {
console.log("元件被停用了");
}
}
var App = {
components: {
'life-cycle': lifeCycle
},
data: function() {
return {
isExist: true
}
},
template: `
<div>
<keep-alive>
<life-cycle v-if="isExist"></life-cycle>
</keep-alive>
<button @click="isExist = !isExist">點選改變 子元件 狀態</button>
</div>
`
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `
<app/>
`
})
</script>
</body>
</html>
複製程式碼
在程式碼中,我們通過 <keep-alive></keep-alive>
這個 Vue 的內建元件,對我們子元件進行了包裹。
然後,當我們進入頁面和點選按鈕時,做到了 activated
和 deactivated
這兩個鉤子函式的觸發:
可以看出,當我們進來頁面的時候,它就告訴我們,該元件被啟用了。當我們第一次點選 <button>
按鈕的時候,isExist
的狀態變成了 false
,即該元件被停用了。最後,我們再次點選了 <button>
,這時候控制檯再次列印 元件被啟用了
。
- 小結:這時候,希望小夥伴回到生命週期章節的開頭,看官方關於生命週期的解析圖,它將有助於我們更加理解宣告週期。如果還是不夠清晰,可以點選圖片旁邊的按鈕,進入官方文件,檢視官方關於生命週期的解析。【返回加深學習】
2.12 獲取 DOM 元素
在日常開發中,可能有小夥伴會想到操作 DOM 元素。如果用原生的 document.getElementById
吧,可能太 low
了,所以,有沒有類似於 jQuery 的 $("#id")
之類的呢?
2.12.1 單個 DOM 元素獲取
話不多說,先上程式碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var App = {
template: `
<div>
<button ref="btn">按鈕</button>
</div>
`,
beforeCreate: function() {
// 這裡不能運算元據,只是初始化了事件等……
console.log(this.$refs.btn); // [Console] undefined
},
created: function() {
// 可以運算元據了
console.log(this.$refs.btn); // [Console] undefined
},
beforeMount: function() {
// new Vue 發生裝載,替換 <div id="app"></div> 之前
console.log(this.$refs.btn); // [Console] undefined
},
mounted: function() {
// 裝載資料之後
console.log(this.$refs.btn.innerHTML); // [Console] 按鈕
}
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `<app/>`
})
</script>
</body>
</html>
複製程式碼
我們先檢視下頁面:
首先,我們在元件的 DOM 部分(<button>
),寫上 ref = "btn"。
然後,我們發現只有在 mounted
資料裝載之後這個鉤子函式中,通過元件物件 this.$refs.btn
可以獲取到元素
這樣,我們就知道在一些場景,如何可以方便地通過 Vue 獲取到 DOM 元素了。
2.12.2 元件 DOM 元素獲取
在上面,我們獲取到了單個 DOM 節點的部分,假如我們需要獲取到整個子元件,那麼要怎麼做呢?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var tempComponent = {
template: `
<div>我是臨時元件</div>
`
}
Vue.component('temp', tempComponent);
var App = {
template: `
<div>
<temp ref="temp" />
</div>
`,
mounted: function() {
// 裝載資料之後
console.log(this.$refs.temp.$el);
}
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `<app/>`
})
</script>
</body>
</html>
複製程式碼
我們先不急著分析,先看控制檯列印出了什麼;
在這裡可以看到它列印出了一堆關於該元件的東西,其中
$children
- 當前元件的子元件$el
- 當前元件的元素節點$parent
- 當前元件的父元件$root
- 獲取new Vue
例項
然後發現元素 $el
是 DOM 節點的內容,我們嘗試列印出來看一下:
console.log(this.$refs.temp.$el);
複製程式碼
Console
<div>我是臨時元件</div>
複製程式碼
通過 Console 可以看出,$el
就可以列印出其中的 <button>
元素了。
2.12.3 Vue.nextTick()
當然,我們有時候操作 DOM,是想在 data
資料變更的時候進行操作,如果是使用上面方法,有些時候是搞不定的。
那麼,我們應該怎麼做呢?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue 學習</title>
</head>
<body>
<!-- 2. Vue 掛載點 - Vue 的虛擬 DOM 在這裡操作到實際渲染 -->
<!-- 簡單理解為 jQuery 的拼接字串(並不全是) -->
<div id="app"></div>
<!-- 1. 引用 Vue -->
<!-- Vue CDN - 提供 Vue 服務 -->
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.js"></script>
<!-- Vue Router CDN - 管理路由 -->
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.js"></script>
<!-- Axios CDN - 呼叫介面 -->
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.js"></script>
<script>
var App = {
template: `
<div>
<input v-if="isShow" ref="input" />
</div>
`,
data: function() {
return {
isShow: true
}
},
mounted: function() {
// 希望在 Vue 真正渲染 DOM 到頁面之後進行下面操作
this.$nextTick(function() {
this.$refs.input.focus();
})
}
}
new Vue({
el: document.getElementById('app'),
components: {
app: App
},
template: `<app/>`
})
</script>
</body>
</html>
複製程式碼
如上,通過 Vue 的全域性 API Vue.nextTick()
,我們在下次 DOM 更新迴圈結束之後執行延遲迴調。在修改資料之後立即使用這個方法,獲取更新後的 DOM。
這個操作我們可想象下 Promise
的執行流程,會獲得更好的體驗。
三 實戰
那麼,學到這裡,我們應該進行一個簡單的操練,來回顧我們所學知識了:
四 總結
如上,我們入門了基礎的 Vue,可能小夥伴們會覺得還是很暈。
但是,沒關係,我們接下來在講解 VueRouter、VueCli 的時候還是會使用 Vue 基礎語法的,正如那句話:萬丈高樓平地起,地基還得自己起。
多實操,多做筆記,總能熟練上去的,加油~
後記
如果小夥伴需要存放 jsliang.top 這樣的純靜態頁面或者 company.jsliang.top 這樣的具有 Node 後端支援的頁面,推薦購買雲伺服器來存放。
如果小夥伴們不知道該怎麼選擇雲伺服器,可以檢視 詳細介紹 或者加 jsliang QQ:1741020489
諮詢。
jsliang 的文件庫 由 樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議 進行許可。
基於 github.om/LiangJunron… 上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。