Vue.js
- 學習目标
- 0.前言
- 1.認識Vue
- 2.Node和NPM
-
- 2.1.下載下傳Node.js
- 2.2.NPM
- 3.快速入門
-
- 3.1.建立工程
- 3.2.安裝vue
-
- 3.2.1.下載下傳安裝
- 3.2.2.使用CDN
- 3.2.3.推薦npm安裝
- 3.3.vue入門案例
-
- 3.3.1.HTML模闆
- 3.3.2.vue聲明式渲染
- 3.3.3.雙向綁定
- 3.3.4.事件處理
- 4.Vue執行個體
-
- 4.1.建立Vue執行個體
- 4.2.模闆或元素
- 4.3.資料
- 4.4.方法
- 4.5.生命周期鈎子
-
- 4.5.1.生命周期
- 4.5.2.鈎子函數
- 4.5.3.this
- 5.指令
-
- 5.1.插值表達式
-
- 5.1.1.花括号
- 5.1.2.插值閃爍
- 5.1.3.v-text和v-html
- 5.2.v-model
- 5.3.v-on
-
- 5.3.1.基本用法
- 5.3.2.事件修飾符
- 5.3.3.按鍵修飾符
- 5.3.4.組合按鈕
- 5.4.v-for
-
- 5.4.1.周遊數組
- 5.4.2.數組角标
- 5.4.3.周遊對象
- 5.4.4.key
- 5.5.v-if和v-show
-
- 5.5.1.基本使用
- 5.5.2.與v-for結合
- 5.5.3.v-else
- 5.5.4.v-show
- 5.6.v-bind
-
- 5.6.1.綁定class樣式
- 5.6.2.綁定style樣式
- 5.6.3.簡寫
- 5.7.計算屬性
- 5.8.watch
- 6.元件化
-
- 6.1.全局元件
- 6.2.元件的複用
- 6.3.局部元件
- 6.4.元件通信
-
- 6.4.1.props(父向子傳遞)
- 6.4.2.props驗證
- 6.4.3.動态靜态傳遞
- 6.4.4.子向父的通信:$emit
- 7.路由vue-router
-
- 7.1.場景模拟
-
- 7.1.1.編寫父元件
- 7.1.2.編寫登入及注冊元件
- 7.1.3.在父元件中引用
- 7.1.5.問題
- 7.2.vue-router簡介和安裝
- 7.3.快速入門
學習目标
- 會建立Vue執行個體,知道Vue的常見屬性
- 會使用Vue的生命周期的鈎子函數
- 會使用vue常見指令
- 會使用vue計算屬性和watch監控
- 會編寫Vue元件
- 掌握元件間通信
-
了解vue-router使用
源碼及資料:
連結:https://pan.baidu.com/s/1dMsAtWlyTiAPwYJi1iMKSA
提取碼:kx2o
0.前言
前幾天我們已經對後端的技術棧有了初步的了解、并且已經搭建了整個後端微服務的平台。接下來要做的事情就是功能開發了。但是沒有前端頁面,我們肯定無從下手,是以今天我們就要來了解一下前端的一些技術,完成前端頁面搭建。
先聊一下前端開發模式的發展。
靜态頁面
最初的網頁以HTML為主,是純靜态的網頁。網頁是隻讀的,資訊流隻能從服務端到用戶端單向流通。開發人員也隻關心頁面的樣式和内容即可。
異步重新整理,操作DOM
1995年,網景工程師Brendan Eich 花了10天時間設計了JavaScript語言.
随着JavaScript的誕生,我們可以操作頁面的DOM元素及樣式,頁面有了一些動态的效果,但是依然是以靜态為主。
ajax盛行:
- 2005年開始,ajax逐漸被前端開發人員所重視,因為不用重新整理頁面就可以更新頁面的資料和渲染效果。
- 此時的開發人員不僅僅要編寫HTML樣式,還要懂ajax與後端互動,然後通過JS操作Dom元素來實作頁面動态效果。比較流行的架構如Jquery就是典型代表。
MVVM,關注模型和視圖
2008年,google的Chrome釋出,随後就以極快的速度占領市場,超過IE成為浏覽器市場的主導者。
2009年,Ryan Dahl在谷歌的Chrome V8引擎基礎上,打造了基于事件循環的異步IO架構:Node.js。
- 基于事件循環的異步IO
- 單線程運作,避免多線程的變量同步問題
- JS可以編寫背景代碼,前背景統一程式設計語言
node.js的偉大之處不在于讓JS邁向了後端開發,而是建構了一個龐大的生态系統。
2010年,NPM作為node.js的包管理系統首次釋出,開發人員可以遵循Common.js規範來編寫Node.js子產品,然後釋出到NPM上供其他開發人員使用。目前已經是世界最大的包子產品管理系統。
随後,在node的基礎上,湧現出了一大批的前端架構:

MVVM模式
- M:即Model,模型,包括資料和一些基本操作
- V:即View,視圖,頁面渲染結果
- VM:即View-Model,模型與視圖間的雙向操作(無需開發人員幹涉)
在MVVM之前,開發人員從後端擷取需要的資料模型,然後要通過DOM操作Model渲染到View中。而後當使用者操作視圖,我們還需要通過DOM擷取View中的資料,然後同步到Model中。
而MVVM中的VM要做的事情就是把DOM操作完全封裝起來,開發人員不用再關心Model和View之間是如何互相影響的:
- 隻要我們Model發生了改變,View上自然就會表現出來。
- 當使用者修改了View,Model中的資料也會跟着改變。
把開發人員從繁瑣的DOM操作中解放出來,把關注點放在如何操作Model上。
而我們今天要學習的,就是一款MVVM模式的架構:Vue
1.認識Vue
Vue (讀音 /vjuː/,類似于 view) 是一套用于建構使用者界面的漸進式架構。與其它大型架構不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫隻關注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,當與現代化的工具鍊以及各種支援類庫結合使用時,Vue 也完全能夠為複雜的單頁應用提供驅動。
前端架構三巨頭:Vue.js、React.js、AngularJS,vue.js以其輕量易用著稱,vue.js和React.js發展速度最快,AngularJS還是老大。
官網:https://cn.vuejs.org/
參考:https://cn.vuejs.org/v2/guide/
Git位址:https://github.com/vuejs
尤雨溪,Vue.js 創作者,Vue Technology創始人,緻力于Vue的研究開發。
2.Node和NPM
前面說過,NPM是Node提供的子產品管理工具,可以非常友善的下載下傳安裝很多前端架構,包括Jquery、AngularJS、VueJs都有。為了後面學習友善,我們先安裝node及NPM工具。
2.1.下載下傳Node.js
下載下傳位址:https://nodejs.org/en/
推薦下載下傳LTS版本。
課程中采用的是8.11.3版本。也是目前最新的。大家自行下載下傳或者使用課前資料中提供的安裝包。然後下一步安裝即可。
完成以後,在控制台輸入:
node -v
看到版本資訊:
2.2.NPM
Node自帶了NPM了,在控制台輸入
npm -v
檢視:
npm預設的倉庫位址是在國外網站,速度較慢,建議大家設定到淘寶鏡像。但是切換鏡像是比較麻煩的。推薦一款切換鏡像的工具:nrm
我們首先安裝nrm,這裡
-g
代表全局安裝。可能需要一點兒時間
npm install nrm -g
然後通過
nrm ls
指令檢視npm的倉庫清單,帶*的就是目前選中的鏡像倉庫:
通過
nrm use taobao
來指定要使用的鏡像源:
然後通過
nrm test npm
來測試速度:
注意:
- 有教程推薦大家使用cnpm指令,但是使用發現cnpm有時會有bug,不推薦。
- 安裝完成請一定要重新開機下電腦!!!
- 安裝完成請一定要重新開機下電腦!!!
- 安裝完成請一定要重新開機下電腦!!!
3.快速入門
接下來,我們快速領略下vue的魅力
3.1.建立工程
建立一個新的空工程:
然後建立一個module:
選中static web,靜态web項目:
位置資訊:
3.2.安裝vue
3.2.1.下載下傳安裝
下載下傳位址:https://github.com/vuejs/vue
可以下載下傳2.5.16版本https://github.com/vuejs/vue/archive/v2.5.16.zip
下載下傳解壓,得到vue.js檔案。
3.2.2.使用CDN
或者也可以直接使用公共的CDN服務:
<!-- 開發環境版本,包含了用幫助的指令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
或者:
<!-- 生産環境版本,優化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
3.2.3.推薦npm安裝
在idea的左下角,有個Terminal按鈕,點選打開控制台:
進入hello-vue目錄,先輸入:
npm init -y
進行初始化
安裝Vue,輸入指令:
npm install vue --save
然後就會在hello-vue目錄發現一個node_modules目錄,并且在下面有一個vue目錄。
node_modules是通過npm安裝的所有子產品的預設位置。
3.3.vue入門案例
3.3.1.HTML模闆
在hello-vue目錄建立一個HTML
在hello.html中,我們編寫一段簡單的代碼:
h2中要輸出一句話:xx 非常帥。前面的xx是要渲染的資料。
3.3.2.vue聲明式渲染
然後我們通過Vue進行渲染:
<body>
<div id="app">
<h2>{{name}},非常帥!!!</h2>
</div>
</body>
<script src="node_modules/vue/dist/vue.js" ></script>
<script>
// 建立vue執行個體
var app = new Vue({
el:"#app", // el即element,該vue執行個體要渲染的頁面元素
data:{ // 渲染頁面需要的資料
name: "峰哥"
}
});
</script>
- 首先通過 new Vue()來建立Vue執行個體
- 然後構造函數接收一個對象,對象中有一些屬性:
- el:是element的縮寫,通過id選中要渲染的頁面元素,本例中是一個div
- data:資料,資料是一個對象,裡面有很多屬性,都可以渲染到視圖中
- name:這裡我們指定了一個name屬性
- 頁面中的
元素中,我們通過{{name}}的方式,來渲染剛剛定義的name屬性。h2
打開頁面檢視效果:
更神奇的在于,當你修改name屬性時,頁面會跟着變化:
3.3.3.雙向綁定
我們對剛才的案例進行簡單修改:
<body>
<div id="app">
<input type="text" v-model="num">
<h2>
{{name}},非常帥!!!有{{num}}位女神為他着迷。
</h2>
</div>
</body>
<script src="node_modules/vue/dist/vue.js" ></script>
<script>
// 建立vue執行個體
var app = new Vue({
el: "#app", // el即element,該vue執行個體要渲染的頁面元素
data: { // 渲染頁面需要的資料
name: "峰哥",
num: 5
}
});
</script>
- 我們在data添加了新的屬性:
num
- 在頁面中有一個
元素,通過input
與v-model
進行綁定。num
- 同時通過
在頁面輸出{{num}}
效果:
我們可以觀察到,輸入框的變化引起了data中的num的變化,同時頁面輸出也跟着變化。
- input與num綁定,input的value值變化,影響到了data中的num值
- 頁面
與資料num綁定,是以num值變化,引起了頁面效果變化。{{num}}
沒有任何dom操作,這就是雙向綁定的魅力。
3.3.4.事件處理
我們在頁面添加一個按鈕:
- 這裡用
指令綁定點選事件,而不是普通的v-on
,然後直接操作numonclick
- 普通click是無法直接操作num的。
效果:
4.Vue執行個體
4.1.建立Vue執行個體
每個 Vue 應用都是通過用
Vue
函數建立一個新的 Vue 執行個體開始的:
var vm = new Vue({
// 選項
})
在構造函數中傳入一個對象,并且在對象中聲明各種Vue需要的資料和方法,包括:
- el
- data
- methods
等等
接下來我們一 一介紹。
4.2.模闆或元素
每個Vue執行個體都需要關聯一段Html模闆,Vue會基于此模闆進行視圖渲染。
我們可以通過el屬性來指定。
例如一段html模闆:
<div id="app">
</div>
然後建立Vue執行個體,關聯這個div
var vm = new Vue({
el:"#app"
})
這樣,Vue就可以基于id為
app
的div元素作為模闆進行渲染了。在這個div範圍以外的部分是無法使用vue特性的。
4.3.資料
當Vue執行個體被建立時,它會嘗試擷取在data中定義的所有屬性,用于視圖的渲染,并且監視data中的屬性變化,當data發生改變,所有相關的視圖都将重新渲染,這就是“響應式“系統。
html:
<div id="app">
<input type="text" v-model="name"/>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
name:"劉德華"
}
})
- name的變化會影響到
的值input
- input中輸入的值,也會導緻vm中的name發生改變
4.4.方法
Vue執行個體中除了可以定義data屬性,也可以定義方法,并且在Vue執行個體的作用範圍内使用。
html:
<div id="app">
{{num}}
<button v-on:click="add">加</button>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
num: 0
},
methods:{
add:function(){
// this代表的目前vue執行個體
this.num++;
}
}
})
4.5.生命周期鈎子
4.5.1.生命周期
每個 Vue 執行個體在被建立時都要經過一系列的初始化過程 :建立執行個體,裝載模闆,渲染模闆等等。Vue為生命周期中的每個狀态都設定了鈎子函數(監聽函數)。每當Vue執行個體處于不同的生命周期時,對應的函數就會被觸發調用。
生命周期:
4.5.2.鈎子函數
beforeCreated:我們在用Vue時都要進行執行個體化,是以,該函數就是在Vue執行個體化時調用,也可以将他了解為初始化函數比較友善一點,在Vue1.0時,這個函數的名字就是init。
created:在建立執行個體之後進行調用。
beforeMount:頁面加載完成,沒有渲染。如:此時頁面還是{{name}}
mounted:我們可以将他了解為原生js中的window.οnlοad=function({.,.}),或許大家也在用jquery,是以也可以了解為jquery中的$(document).ready(function(){….}),他的功能就是:在dom文檔渲染完畢之後将要執行的函數,該函數在Vue1.0版本中名字為compiled。 此時頁面中的{{name}}已被渲染成峰哥
beforeDestroy:該函數将在銷毀執行個體前進行調用 。
destroyed:改函數将在銷毀執行個體時進行調用。
beforeUpdate:元件更新之前。
updated:元件更新之後。
例如:created代表在vue執行個體建立後;
我們可以在Vue中定義一個created函數,代表這個時期的鈎子函數:
// 建立vue執行個體
var app = new Vue({
el: "#app", // el即element,該vue執行個體要渲染的頁面元素
data: { // 渲染頁面需要的資料
name: "峰哥",
num: 5
},
methods: {
add: function(){
this.num--;
}
},
created: function () {
this.num = 100;
}
});
結果:
4.5.3.this
我們可以看下在vue内部的this變量是誰,我們在created的時候,列印this
methods: {
add: function(){
this.num--;
console.log(this);
}
},
控制台的輸出:
5.指令
什麼是指令?
指令 (Directives) 是帶有
v-
字首的特殊特性。指令特性的預期值是:單個 JavaScript 表達式。指令的職責是,當表達式的值改變時,将其産生的連帶影響,響應式地作用于 DOM。
例如我們在入門案例中的v-on,代表綁定事件。
5.1.插值表達式
5.1.1.花括号
格式:
{{表達式}}
說明:
- 該表達式支援JS文法,可以調用js内置函數(必須有傳回值)
- 表達式必須有傳回結果。例如 1 + 1,沒有結果的表達式不允許使用,如:var a = 1 + 1;
- 可以直接擷取Vue執行個體中定義的資料或函數
示例:
HTML:
JS:
var app = new Vue({
el:"#app",
data:{
name:"Jack"
}
})
5.1.2.插值閃爍
使用{{}}方式在網速較慢時會出現問題。在資料未加載完成時,頁面會顯示出原始的
{{}}
,加載完畢後才顯示正确資料,我們稱為插值閃爍。
我們将網速調慢一些,然後試試看剛才的案例:
重新整理頁面:
5.1.3.v-text和v-html
使用v-text和v-html指令來替代
{{}}
說明:
- v-text:将資料輸出到元素内部,如果輸出的資料有HTML代碼,會作為普通文本輸出
- v-html:将資料輸出到元素内部,如果輸出的資料有HTML代碼,會被渲染
示例:
HTML:
<div id="app">
v-text:<span v-text="hello"></span> <br/>
v-html:<span v-html="hello"></span>
</div>
JS:
var vm = new Vue({
el:"#app",
data:{
hello: "<h1>大家好,我是峰哥</h1>"
}
})
效果:
并且不會出現插值閃爍,當沒有資料時,會顯示空白。
5.2.v-model
剛才的v-text和v-html可以看做是單向綁定,資料影響了視圖渲染,但是反過來就不行。接下來學習的v-model是雙向綁定,視圖(View)和模型(Model)之間會互相影響。
既然是雙向綁定,一定是在視圖中可以修改資料,這樣就限定了視圖的元素類型。目前v-model的可使用元素有:
- input
- select
- textarea
- checkbox
- radio
- components(Vue中的自定義元件)
基本上除了最後一項,其它都是表單的輸入項。
舉例:
html:
<div id="app">
<input type="checkbox" v-model="language" value="Java" />Java<br/>
<input type="checkbox" v-model="language" value="PHP" />PHP<br/>
<input type="checkbox" v-model="language" value="Swift" />Swift<br/>
<h1>
你選擇了:{{language.join(',')}}
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
language: []
}
})
</script>
- 多個
對應一個model時,model的類型是一個數組,單個checkbox值預設是boolean類型CheckBox
- radio對應的值是input的value值
-
和text
預設對應的model是字元串textarea
-
單選對應字元串,多選對應也是數組select
效果:
5.3.v-on
5.3.1.基本用法
v-on指令用于給頁面元素綁定事件。
文法:
v-on:事件名="js片段或函數名"
示例:
<div id="app">
<!--事件中直接寫js片段-->
<button v-on:click="num++">增加一個</button><br/>
<!--事件指定一個回調函數,必須是Vue執行個體中定義的函數-->
<button v-on:click="decrement">減少一個</button><br/>
<h1>有{{num}}個女神迷戀峰哥</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
num:100
},
methods:{
decrement(){
this.num--;
}
}
})
</script>
效果:
另外,事件綁定可以簡寫,例如
v-on:click='add'
可以簡寫為
@click='add'
5.3.2.事件修飾符
在事件處理程式中調用
event.preventDefault()
或
event.stopPropagation()
是非常常見的需求。盡管我們可以在方法中輕松實作這點,但更好的方式是:方法隻有純粹的資料邏輯,而不是去處理 DOM 事件細節。
為了解決這個問題,Vue.js 為
v-on
提供了事件修飾符。修飾符是由點開頭的指令字尾來表示的。
-
:阻止事件冒泡到父元素.stop
-
:阻止預設事件發生*.prevent
-
:使用事件捕獲模式.capture
-
:隻有元素自身觸發事件才執行。(冒泡或捕獲的都不執行).self
-
:隻執行一次.once
阻止預設事件
<div id="app">
<!--右擊事件,并阻止預設事件發生-->
<button v-on:contextmenu.prevent="num++">增加一個</button>
<br/>
<!--右擊事件,不阻止預設事件發生-->
<button v-on:contextmenu="decrement($event)">減少一個</button>
<br/>
<h1>有{{num}}個女神迷戀峰哥</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
decrement(ev) {
// ev.preventDefault();
this.num--;
}
}
})
</script>
效果:(右鍵“增加一個”,不會觸發預設的浏覽器右擊事件;右鍵“減少一個”,會觸發預設的浏覽器右擊事件)
5.3.3.按鍵修飾符
在監聽鍵盤事件時,我們經常需要檢查常見的鍵值。Vue 允許為
v-on
在監聽鍵盤事件時添加按鍵修飾符:
<!-- 隻有在 `keyCode` 是 13 時調用 `vm.submit()` -->
<input v-on:keyup.13="submit">
記住所有的
keyCode
比較困難,是以 Vue 為最常用的按鍵提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 縮寫文法 -->
<input @keyup.enter="submit">
全部的按鍵别名:
-
*.enter
-
.tab
-
(捕獲“删除”和“倒退”鍵).delete
-
.esc
-
.space
-
.up
-
.down
-
.left
-
.right
5.3.4.組合按鈕
可以用如下修飾符來實作僅在按下相應按鍵時才觸發滑鼠或鍵盤事件的監聽器。
-
.ctrl
-
.alt
-
.shift
例如:
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
5.4.v-for
周遊資料渲染頁面是非常常用的需求,Vue中通過v-for指令來實作。
5.4.1.周遊數組
文法:
v-for="item in items"
- items:要周遊的數組,需要在vue的data中定義好。
- item:疊代得到的數組元素的别名
示例
<div id="app">
<ul>
<li v-for="user in users">
{{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
users:[
{name:'柳岩', gender:'女', age: 21},
{name:'峰哥', gender:'男', age: 18},
{name:'範冰冰', gender:'女', age: 24},
{name:'劉亦菲', gender:'女', age: 18},
{name:'古力娜紮', gender:'女', age: 25}
]
},
})
</script>
效果:
5.4.2.數組角标
在周遊的過程中,如果我們需要知道數組角标,可以指定第二個參數:
文法
v-for="(item,index) in items"
- items:要疊代的數組
- item:疊代得到的數組元素别名
- index:疊代到的目前元素索引,從0開始。
示例
<ul>
<li v-for="(user, index) in users">
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
效果:
5.4.3.周遊對象
v-for除了可以疊代數組,也可以疊代對象。文法基本類似
文法:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
- 1個參數時,得到的是對象的屬性值
- 2個參數時,第一個是屬性值,第二個是屬性名
- 3個參數時,第三個是索引,從0開始
示例:
<div id="app">
<ul>
<li v-for="(value, key, index) in user">
{{index + 1}}. {{key}} - {{value}}
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
user:{name:'峰哥', gender:'男', age: 18}
}
})
</script>
效果:
5.4.4.key
當 Vue.js 用
v-for
正在更新已渲染過的元素清單時,它預設用“就地複用”政策。如果資料項的順序被改變,Vue 将不會移動 DOM 元素來比對資料項的順序, 而是簡單複用此處每個元素,并且確定它在特定索引下顯示已被渲染過的每個元素。
這個功能可以有效的提高渲染的效率。
但是要實作這個功能,你需要給Vue一些提示,以便它能跟蹤每個節點的身份,進而重用和重新排序現有元素,你需要為每項提供一個唯一
key
屬性。理想的
key
值是每項都有的且唯一的 id。
示例:
<ul>
<li v-for="(item,index) in items" :key=index></li>
</ul>
- 這裡使用了一個特殊文法:
我們後面會講到,它可以讓你讀取vue中的屬性,并指派給key屬性:key=""
- 這裡我們綁定的key是數組的索引,應該是唯一的
5.5.v-if和v-show
5.5.1.基本使用
v-if,顧名思義,條件判斷。當得到結果為true時,所在的元素才會被渲染。
文法:
v-if="布爾表達式"
示例:
<div id="app">
<button v-on:click="show = !show">點我呀</button>
<br>
<h1 v-if="show">
看到我啦?!
</h1>
<h1 v-show="show">
看到我啦?!show
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
效果:
5.5.2.與v-for結合
當v-if和v-for出現在一起時,v-for優先級更高。也就是說,會先周遊,再判斷條件。
修改v-for中的案例,添加v-if:
<ul>
<li v-for="(user, index) in users" v-if="user.gender == '女'">
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
效果:
隻顯示女性使用者資訊
5.5.3.v-else
你可以使用
v-else
指令來表示
v-if
的“else 塊”:
<div id="app">
<h1 v-if="Math.random() > 0.5">
看到我啦?!if
</h1>
<h1 v-else>
看到我啦?!else
</h1>
</div>
v-else
元素必須緊跟在帶
v-if
或者
v-else-if
的元素的後面,否則它将不會被識别。
v-else-if
,顧名思義,充當
v-if
的“else-if 塊”,可以連續使用:
<div id="app">
<button v-on:click="random=Math.random()">點我呀</button><span>{{random}}</span>
<h1 v-if="random >= 0.75">
看到我啦?!if
</h1>
<h1 v-else-if="random > 0.5">
看到我啦?!if 0.5
</h1>
<h1 v-else-if="random > 0.25">
看到我啦?!if 0.25
</h1>
<h1 v-else>
看到我啦?!else
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
random: 1
}
})
</script>
類似于
v-else
,
v-else-if
也必須緊跟在帶
v-if
或者
v-else-if
的元素之後。
示範:
5.5.4.v-show
另一個用于根據條件展示元素的選項是
v-show
指令。用法大緻一樣:
<h1 v-show="ok">Hello!</h1>
不同的是帶有
v-show
的元素始終會被渲染并保留在 DOM 中。
v-show
隻是簡單地切換元素的 CSS 屬性
display
。
示例:
<div id="app">
<!--事件中直接寫js片段-->
<button v-on:click="show = !show">點選切換</button><br/>
<h1 v-if="show">
你好
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
show:true
}
})
</script>
代碼:
5.6.v-bind
html屬性不能使用雙大括号形式綁定,隻能使用v-bind指令。
在将
v-bind
用于
class
和
style
時,Vue.js 做了專門的增強。表達式結果的類型除了字元串之外,還可以是對象或數組。
<div id="app">
<!--可以是資料模型,可以是具有傳回值的js代碼塊或者函數-->
<div v-bind:title="title" style="border: 1px solid red; width: 50px; height: 50px;"></div>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
title: "title",
}
})
</script>
效果:
在将
v-bind
用于
class
和
style
時,Vue.js 做了專門的增強。表達式結果的類型除了字元串之外,還可以是對象或數組。
5.6.1.綁定class樣式
數組文法
我們可以借助于
v-bind
指令來實作:
HTML:
<div id="app">
<div v-bind:class="activeClass"></div>
<div v-bind:class="errorClass"></div>
<div v-bind:class="[activeClass, errorClass]"></div>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
activeClass: 'active',
errorClass: ['text-danger', 'text-error']
}
})
</script>
渲染後的效果:(具有active和hasError的樣式)
對象文法
我們可以傳給
v-bind:class
一個對象,以動态地切換 class:
上面的文法表示
active
這個 class 存在與否将取決于資料屬性
isActive
的 truthiness(所有的值都是真實的,除了false,0,“”,null,undefined和NaN)。
你可以在對象中傳入更多屬性來動态切換多個 class。此外,
v-bind:class
指令也可以與普通的 class 屬性共存。如下模闆:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
和如下 data:
data: {
isActive: true,
hasError: false
}
結果渲染為:
active樣式和text-danger樣式的存在與否,取決于isActive和hasError的值。本例中isActive為true,hasError為false,是以active樣式存在,text-danger不存在。
5.6.2.綁定style樣式
數組文法
數組文法可以将多個樣式對象應用到同一個元素上:
資料:
data: {
baseStyles: {'background-color': 'red'},
overridingStyles: {border: '1px solid black'}
}
渲染後的結果:
對象文法
v-bind:style
的對象文法十分直覺——看着非常像 CSS,但其實是一個 JavaScript 對象。CSS 屬性名可以用駝峰式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引号括起來) 來命名:
資料:
data: {
activeColor: 'red',
fontSize: 30
}
效果:
5.6.3.簡寫
v-bind:class
可以簡寫為
:class
5.7.計算屬性
在插值表達式中使用js表達式是非常友善的,而且也經常被用到。
但是如果表達式的内容很長,就會顯得不夠優雅,而且後期維護起來也不友善,例如下面的場景,我們有一個日期的資料,但是是毫秒值:
data:{
birthday:1529032123201 // 毫秒值
}
我們在頁面渲染,希望得到yyyy-MM-dd的樣式:
<h1>您的生日是:{{
new Date(birthday).getFullYear() + '-'+ new Date(birthday).getMonth()+ '-' + new Date(birthday).getDay()
}}
</h1>
雖然能得到結果,但是非常麻煩。
Vue中提供了計算屬性,來替代複雜的表達式:
var vm = new Vue({
el:"#app",
data:{
birthday:1429032123201 // 毫秒值
},
computed:{
birth(){// 計算屬性本質是一個方法,但是必須傳回結果
const d = new Date(this.birthday);
return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay();
}
}
})
- 計算屬性本質就是方法,但是一定要傳回資料。然後頁面渲染時,可以把這個方法當成一個變量來使用。
頁面使用:
<div id="app">
<h1>您的生日是:{{birth}} </h1>
</div>
效果:
我們可以将同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果确實是完全相同的。然而,不同的是計算屬性是基于它們的依賴進行緩存的。計算屬性隻有在它的相關依賴發生改變時才會重新求值。這就意味着隻要
birthday
還沒有發生改變,多次通路
birthday
計算屬性會立即傳回之前的計算結果,而不必再次執行函數。
5.8.watch
watch可以讓我們監控一個值的變化。進而做出相應的反應。
示例:
<div id="app">
<input type="text" v-model="message">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:""
},
watch:{
message(newVal, oldVal){
console.log(newVal, oldVal);
}
}
})
</script>
效果:
6.元件化
在大型應用開發的時候,頁面可以劃分成很多部分。往往不同的頁面,也會有相同的部分。例如可能會有相同的頭部導航。
但是如果每個頁面都獨自開發,這無疑增加了我們開發的成本。是以我們會把頁面的不同部分拆分成獨立的元件,然後在不同頁面就可以共享這些元件,避免重複開發。
在vue裡,所有的vue執行個體都是元件
6.1.全局元件
我們通過Vue的component方法來定義一個全局元件。
<div id="app">
<!--使用定義好的全局元件-->
<counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
// 定義全局元件,兩個參數:1,元件名稱。2,元件參數
Vue.component("counter",{
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
})
var app = new Vue({
el:"#app"
})
</script>
- 元件其實也是一個Vue執行個體,是以它在定義時也會接收:data、methods、生命周期函數等
- 不同的是元件不會與頁面的元素綁定,否則就無法複用了,是以沒有el屬性。
- 但是元件渲染需要html模闆,是以增加了template屬性,值就是HTML模闆
- 全局元件定義完畢,任何vue執行個體都可以直接在HTML中通過元件名稱來使用元件了。
- data必須是一個函數,不再是一個對象。
效果:
6.2.元件的複用
定義好的元件,可以任意複用多次:
<div id="app">
<!--使用定義好的全局元件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
效果:
你會發現每個元件互不幹擾,都有自己的count值。怎麼實作的?
元件的data屬性必須是函數!
當我們定義這個
<counter>
元件時,它的data 并不是像之前直接提供一個對象:
data: {
count: 0
}
取而代之的是,一個元件的 data 選項必須是一個函數,是以每個執行個體可以維護一份被傳回對象的獨立的拷貝:
data: function () {
return {
count: 0
}
}
如果 Vue 沒有這條規則,點選一個按鈕就會影響到其它所有執行個體!
6.3.局部元件
一旦全局注冊,就意味着即便以後你不再使用這個元件,它依然會随着Vue的加載而加載。
是以,對于一些并不頻繁使用的元件,我們會采用局部注冊。
我們先在外部定義一個對象,結構與建立元件時傳遞的第二個參數一緻:
const counter = {
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
};
然後在Vue中使用它:
var app = new Vue({
el:"#app",
components:{
counter:counter // 将定義的對象注冊為元件
}
})
- components就是目前vue對象子元件集合。
- 其key就是子元件名稱
- 其值就是元件對象名
- 效果與剛才的全局注冊是類似的,不同的是,這個counter元件隻能在目前的Vue執行個體中使用
6.4.元件通信
通常一個單頁應用會以一棵嵌套的元件樹的形式來組織:
- 頁面首先分成了頂部導航、左側内容區、右側邊欄三部分
- 左側内容區又分為上下兩個元件
- 右側邊欄中又包含了3個子元件
各個元件之間以嵌套的關系組合在一起,那麼這個時候不可避免的會有元件間通信的需求。
6.4.1.props(父向子傳遞)
- 父元件使用子元件時,自定義屬性(屬性名任意,屬性值為要傳遞的資料)
- 子元件通過props接收父元件資料,通過自定義屬性的屬性名
父元件使用子元件,并自定義了title屬性:
<div id="app">
<h1>打個招呼:</h1>
<!--使用子元件,同時傳遞title屬性-->
<introduce title="大家好,我是鋒哥"/>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("introduce",{
// 直接使用props接收到的屬性來渲染頁面
template:'<h1>{{title}}</h1>',
props:['title'] // 通過props來接收一個父元件傳遞的屬性
})
var app = new Vue({
el:"#app"
})
</script>
效果:
6.4.2.props驗證
我們定義一個子元件,并接收複雜資料:
const myList = {
template: '\
<ul>\
<li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
</ul>\
',
props: {
items: {
type: Array,
default: [],
required: true
}
}
};
- 這個子元件可以對 items 進行疊代,并輸出到頁面。
- props:定義需要從父元件中接收的屬性
- items:是要接收的屬性名稱
- type:限定父元件傳遞來的必須是數組
- default:預設值
- required:是否必須
- items:是要接收的屬性名稱
當 prop 驗證失敗的時候,(開發環境建構版本的) Vue 将會産生一個控制台的警告。
我們在父元件中使用它:
<div id="app">
<h2>課程:</h2>
<!-- 使用子元件的同時,傳遞屬性,這裡使用了v-bind,指向了父元件自己的屬性lessons -->
<my-list :items="lessons"/>
</div>
var app = new Vue({
el:"#app",
components:{
myList // 當key和value一樣時,可以隻寫一個
},
data:{
lessons:[
{id:1, name: 'java'},
{id:2, name: 'php'},
{id:3, name: 'ios'},
]
}
})
效果:
type類型,可以有:
注意:子元件模闆有且隻有一個根标簽
6.4.3.動态靜态傳遞
給 prop 傳入一個靜态的值:
給 prop 傳入一個動态的值: (通過v-bind從資料模型中,擷取title的值)
靜态傳遞時,我們傳入的值都是字元串類型的,但實際上任何類型的值都可以傳給一個 props。
<!-- 即便 `42` 是靜态的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個JavaScript表達式而不是一個字元串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一個變量進行動态指派。-->
<blog-post v-bind:likes="post.likes"></blog-post>
6.4.4.子向父的通信:$emit
來看這樣的一個案例:
<div id="app">
<h2>num: {{num}}</h2>
<!--使用子元件的時候,傳遞num到子元件中-->
<counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("counter", {// 子元件,定義了兩個按鈕,點選數字num會加或減
template:'\
<div>\
<button @click="num++">加</button> \
<button @click="num--">減</button> \
</div>',
props:['num']// count是從父元件擷取的。
})
var app = new Vue({
el:"#app",
data:{
num:0
}
})
</script>
- 子元件接收父元件的num屬性
- 子元件定義點選按鈕,點選後對num進行加或減操作
我們嘗試運作,好像沒問題,點選按鈕試試:
子元件接收到父元件屬性後,預設是不允許修改的。怎麼辦?
既然隻有父元件能修改,那麼加和減的操作一定是放在父元件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{ // 父元件中定義操作num的方法
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
但是,點選按鈕是在子元件中,那就是說需要子元件來調用父元件的函數,怎麼做?
我們可以通過v-on指令将父元件的函數綁定到子元件上:
<div id="app">
<h2>num: {{num}}</h2>
<counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
在子元件中定義函數,函數的具體實作調用父元件的實作,并在子元件中調用這些函數。當子元件中按鈕被點選時,調用綁定的函數:
Vue.component("counter", {
template:'\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">減</button> \
</div>',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
- vue提供了一個内置的this.$emit()函數,用來調用父元件綁定的函數
效果:
7.路由vue-router
7.1.場景模拟
現在我們來實作這樣一個功能:
一個頁面,包含登入和注冊,點選不同按鈕,實作登入和注冊頁切換:
7.1.1.編寫父元件
為了讓接下來的功能比較清晰,我們先建立一個檔案夾:src
然後建立一個HTML檔案,作為入口:index.html
然後編寫頁面的基本結構:
<div id="app">
<span>登入</span>
<span>注冊</span>
<hr/>
<div>
登入頁/注冊頁
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app"
})
</script>
樣式:
7.1.2.編寫登入及注冊元件
接下來我們來實作登入元件,以前我們都是寫在一個檔案中,但是為了複用性,開發中都會把元件放入獨立的JS檔案中,我們建立一個user目錄以及login.js及register.js:
編寫元件,這裡我們隻寫模闆,不寫功能。
login.js内容如下:
const loginForm = {
template:'\
<div>\
<h2>登入頁</h2> \
使用者名:<input type="text"><br/>\
密碼:<input type="password"><br/>\
</div>\
'
}
register.js内容:
const registerForm = {
template:'\
<div>\
<h2>注冊頁</h2> \
用 戶 名:<input type="text"><br/>\
密  碼:<input type="password"><br/>\
确認密碼:<input type="password"><br/>\
</div>\
'
}
7.1.3.在父元件中引用
<div id="app">
<span>登入</span>
<span>注冊</span>
<hr/>
<div>
<!--<loginForm></loginForm>-->
<!--
疑問:為什麼不采用上面的寫法?
由于html是大小寫不敏感的,如果采用上面的寫法,則被認為是<loginform></loginform>
是以,如果是駝峰形式的元件,需要把駝峰轉化為“-”的形式
-->
<login-form></login-form>
<register-form></register-form>
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="user/login.js"></script>
<script src="user/register.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
components: {
loginForm,
registerForm
}
})
</script>
效果:
7.1.5.問題
我們期待的是,當點選登入或注冊按鈕,分别顯示登入頁或注冊頁,而不是一起顯示。
但是,如何才能動态加載元件,實作元件切換呢?
雖然使用原生的Html5和JS也能實作,但是官方推薦我們使用vue-router子產品。
7.2.vue-router簡介和安裝
使用vue-router和vue可以非常友善的實作 複雜單頁應用的動态路由功能。
官網:https://router.vuejs.org/zh-cn/
使用npm安裝:
npm install vue-router --save
在index.html中引入依賴:
7.3.快速入門
建立vue-router對象,并且指定路由規則:
// 建立VueRouter對象
const router = new VueRouter({
routes:[ // 編寫路由規則
{
path:"/login", // 請求路徑,以“/”開頭
component:loginForm // 元件名稱
},
{
path:"/register",
component:registerForm
}
]
})
- 建立VueRouter對象,并指定路由參數
- routes:路由規則的數組,可以指定多個對象,每個對象是一條路由規則,包含以下屬性:
- path:路由的路徑
- component:元件名稱
在父元件中引入router對象:
var vm = new Vue({
el:"#app",
components:{// 引用登入和注冊元件
loginForm,
registerForm
},
router // 引用上面定義的router對象
})
頁面跳轉控制:
<div id="app">
<!--router-link來指定跳轉的路徑-->
<span><router-link to="/login">登入</router-link></span>
<span><router-link to="/register">注冊</router-link></span>
<hr/>
<div>
<!--vue-router的錨點-->
<router-view></router-view>
</div>
</div>
- 通過
來指定一個錨點,當路由的路徑比對時,vue-router會自動把對應元件放到錨點位置進行渲染<router-view>
- 通過
指定一個跳轉連結,當點選時,會觸發vue-router的路由功能,路徑中的hash值會随之改變<router-link>
效果:
注意:單頁應用中,頁面的切換并不是頁面的跳轉。僅僅是位址最後的hash值變化。
事實上,我們總共就一個HTML:index.html